SKILL.md vs slash commands in Claude Code: which should you use?

The old answer was "commands are for you, skills are for Claude." Since the two merged, that mental model will lead you wrong. Here is what actually decides it.

Lami Mershed3 min read

Everyone building on Claude Code hits this: do I write a slash command or a SKILL.md?

The answer you will hear most often is "commands are user-invoked, skills are model-invoked." That was true once. It is now the wrong model, and it will lead you to the wrong file — because custom commands and skills have merged.

Straight from the docs: "Custom commands have been merged into skills. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy and work the same way."[2]

Both create a slash command. Both can be invoked by Claude. The thing that decides who can run your workflow is not which folder you put it in — it is two lines of frontmatter.

What actually controls invocation

By default, both you and Claude can invoke a skill. It appears in the / menu and Claude can load it on its own when your request matches. Two frontmatter fields change that:[1]

FrontmatterYou can invokeClaude can invoke
(default)YesYes
disable-model-invocation: trueYesNo
user-invocable: falseNoYes

That table is the real answer to the question. Everything else is packaging.

When to lock Claude out

Use disable-model-invocation: true for anything with side effects, where you want to decide the timing:

.claude/skills/deploy/SKILL.md
---
description: Deploy the current branch to production
disable-model-invocation: true
---
 
1. Run the test suite. Stop if anything fails.
2. Build, then deploy to production.
3. Post the deployment URL in the channel.

The docs put it well: "You don't want Claude deciding to deploy because your code looks ready."[1] Same logic for /commit, /send-slack-message, or anything that spends money.

When to hide it from yourself

The opposite case is background knowledge that is not a meaningful action. user-invocable: false keeps it out of your / menu while still letting Claude reach for it:

.claude/skills/legacy-billing/SKILL.md
---
description: Use when touching the billing service, which predates the current API conventions.
user-invocable: false
---
 
The billing service uses snake_case payloads and returns 200 on failure with an
`error` key in the body. Check the body, not the status code.

Nobody types /legacy-billing. But when Claude opens a billing file, that context is exactly what stops it writing a bug.

Descriptions are load-bearing

If you want Claude to invoke something, the description is not documentation — it is the trigger. It is the only part of a skill that is always in context, and it is what Claude matches against your request.

# Too vague — may never fire, or fire at the wrong time
description: Reviews code
 
# Specific about the situation — fires reliably
description: Use when the user asks to review a pull request or code changes
  before merging. Checks for bugs, security issues, and team style adherence.

Name the conditions, not just the intent.

The one real advantage skills still have

A skill is a directory. A command is a single file. That difference is worth more than it sounds, because a directory means supporting files:

.claude/skills/pr-review/
├── SKILL.md          # the process — loaded when the skill runs
├── style.md          # team conventions — loaded only if needed
└── security.md       # checklist — loaded only if needed

"Unlike CLAUDE.md content, a skill's body loads only when it's used, so long reference material costs almost nothing until you need it."[1] You can ship a 2,000-line style guide next to a skill and pay for it only on the turns that need it. Put the same thing in CLAUDE.md and you pay for it on every single turn.

Command files also cannot use name or paths — Claude Code ignores both there, since a command is invoked by its filename.[2]

A quick scenario

Ask Claude: "find the biggest log files in this project, sort them by size, and compress the top three."

If your compression workflow is locked to user invocation, you have to watch for the right moment and type /compress yourself. Left model-invocable, Claude works through the task, recognises the step where compression belongs, and runs your defined process as part of the larger plan — without stopping to ask.

That is the actual choice you are making. Not "command or skill," but who owns the timing.

The rule of thumb

  • Write a skill. It does everything a command does, plus supporting files.
  • Side effects? Add disable-model-invocation: true and keep the trigger yourself.
  • Pure context, no action? Add user-invocable: false.
  • Want both? Write a good description and leave the defaults alone.

And if you already have files in .claude/commands/, leave them — they keep working. Just know that if a skill and a command share a name, the skill wins.[2]

Checked against Claude Code 2.1.261 on 5 September 2026. This area moved recently, so if the behaviour you see differs, trust /help and the linked docs over any blog post — including this one.

Sources

  1. [1]
    Skills

    Claude Code Docs

  2. [2]
    Slash commands

    Claude Code Docs

Lami Mershed

Founder & Engineer

Builds software at MangaTech in Kochi. Writes about shipping fast on the web without giving up on the details.