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.
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]
| Frontmatter | You can invoke | Claude can invoke |
|---|---|---|
| (default) | Yes | Yes |
disable-model-invocation: true | Yes | No |
user-invocable: false | No | Yes |
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:
---
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:
---
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: trueand keep the trigger yourself. - Pure context, no action? Add
user-invocable: false. - Want both? Write a good
descriptionand 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]Skills
Claude Code Docs
- [2]Slash commands
Claude Code Docs
Founder & Engineer
Builds software at MangaTech in Kochi. Writes about shipping fast on the web without giving up on the details.