Custom commands have merged into Skills. When to migrate to `.claude/skills/`, and what you gain when you do.
The previous three posts walked through the full story of slash commands: starting from a Markdown file, then !command injection, then orchestrating subagents and MCP. None of that is obsolete — but Anthropic quietly merged two things behind the scenes: custom commands have been folded into Skills.
Your .claude/commands/review.md still runs, /review still works, nothing breaks. But if you want a command to grow real capabilities — multiple files, fork execution, auto-activation by path — the new home is .claude/skills/review/SKILL.md. This post answers two questions: how to migrate, and whether it's worth it.
Anthropic's compatibility promise is explicit: .claude/commands/deploy.md and .claude/skills/deploy/SKILL.md both register as /deploy, with identical behavior. If there's a name conflict, skills win.
Minimal steps:
cd .claude/commands
mkdir -p ../skills/review
mv review.md ../skills/review/SKILL.md
Not a single line of the file needs to change. YAML frontmatter, !`command`, @file, $ARGUMENTS — all still work.
But if that's all you do, the obvious question is: why bother? The answer is that a skill directory can hold many files, while a command is stuck with one.
SKILL.md is the entry point, and the same directory can hold arbitrary supporting files. Picture a layout like this:
.claude/skills/review/
├── SKILL.md # entry point — short instructions + references
├── checklist.md # the long code-review checklist
├── examples/
│ └── good-diff.md # a positive example
└── scripts/
└── lint.sh # a script SKILL.md can call
Reference them from SKILL.md with relative paths:
Review the current diff, following the standards in [checklist.md](checklist.md).
See [examples/good-diff.md](examples/good-diff.md) for the expected output format.
These supporting files don't get loaded into context automatically — Claude reads them on demand. Anthropic recommends keeping SKILL.md under 500 lines and pushing everything else into supporting files.
disable-model-invocation: which skills only you can triggerBy default, Claude will invoke any skill whenever it seems appropriate. That's dangerous for commands with side effects — /deploy, /commit, /send-email. You don't want Claude "deciding the code looks fine and deploying it."
---
name: deploy
description: Deploy to production
disable-model-invocation: true
allowed-tools: Bash(kamal deploy:*), Bash(git push:*)
---
With that one line, /deploy only fires when you type it. Claude won't reach for it mid-conversation.
The opposite knob is user-invocable: false — "only Claude can invoke this, and it won't appear in the / menu." Good for background-knowledge skills (e.g. legacy-system-context: Claude loads it automatically when relevant, but there's no reason for you to fire it manually).
context: fork: run the skill in a subagentThis is the biggest upgrade. Last post showed how a command can use allowed-tools: Task to get the model to spawn subagents — but you had to describe "spin up an Explore subagent to do X" inside the prompt yourself.
A skill handles it with one frontmatter line:
---
name: deep-research
description: Deep dive into a symbol
context: fork
agent: Explore
---
Research every use of $ARGUMENTS:
- all call sites
- business scenarios
- alternative implementations
Return a summary of ≤300 words.
When you fire /deep-research SomeClass, the entire SKILL.md becomes the prompt for a fresh subagent, running with the Explore agent type. Once it finishes, only the conclusion comes back. The main conversation's context stays completely clean.
It turns "spawning a subagent" from prose inside your prompt into a declarative property of the skill.
paths: auto-activate by file type---
name: rails-conventions
description: Rails coding conventions for this project
paths: ["app/**/*.rb", "config/**/*.rb"]
---
Follow the Rails conventions of this project:
- use service objects instead of fat controllers
- ActiveRecord scopes must be named
...
When you're editing app/models/user.rb, this skill gets added to context automatically; when you're editing package.json, it doesn't. More precise than CLAUDE.md's always-on background — think of it as "CLAUDE.md layered by path."
/plan to a skillThe /plan example from the last post was a single-file command:
.claude/commands/plan.md
Promoted to a skill:
.claude/skills/plan/
├── SKILL.md
├── research-prompt.md # instructions for subagent 1
└── risk-prompt.md # instructions for subagent 2
SKILL.md:
---
name: plan
description: Produce an implementation plan from a Linear ticket
disable-model-invocation: true
allowed-tools: mcp__linear__*, Task, Read, Grep, Bash(git log:*)
---
## Context
@.claude/context/architecture.md
## State
!`git log --oneline -10`
## Task
Pull the description and comments of Linear ticket $ARGUMENTS.
Use Task to spawn two subagents in parallel:
1. Code research: prompt in [research-prompt.md](research-prompt.md)
2. Risk assessment: prompt in [risk-prompt.md](risk-prompt.md)
Combine the two results and output implementation steps + risk list + suggested commit granularity.
The two subagent prompts live in separate files, so updating one doesn't touch SKILL.md. The skill itself stays crisp at under 30 lines, while the supporting files can each run hundreds of lines deep.
If you don't want to orchestrate the subagents yourself, go harder — throw the whole skill into a fork and let an Explore agent chew through it in one pass:
---
name: plan
context: fork
agent: Explore
allowed-tools: mcp__linear__*
---
Fire /plan ENG-4213 → an Explore agent in its own context takes the entire SKILL.md as its task → only the final plan comes back. The main conversation stays pristine.
Not every command deserves a promotion. A command belongs in .claude/commands/ when:
A plain /commit or /pr-desc is just a frontmatter plus a few lines of prompt — putting it in .claude/commands/ is actually clearer. Anthropic has said explicitly: both forms coexist, neither is being deprecated.
The cleanest .claude/ layout today:
.claude/
├── commands/ # lightweight: single file + simple prompt
│ ├── commit.md
│ └── pr-desc.md
├── skills/ # heavy: multi-file / fork / access control
│ ├── plan/
│ │ ├── SKILL.md
│ │ ├── research-prompt.md
│ │ └── risk-prompt.md
│ ├── deploy/
│ │ └── SKILL.md # disable-model-invocation
│ └── rails-conventions/
│ └── SKILL.md # paths glob auto-activation
└── context/
└── coding-standards.md
Don't migrate everything in one sweep. The trigger is "this command is starting to bloat" — the body crosses 200 lines, docs need to be split out, you want a subagent to run it. When that day comes, changing the path is a few minutes of work.
| Capability | commands/ |
skills/ |
|---|---|---|
| Single-file prompt shortcut | yes | yes |
!`command` / @file / $ARGUMENTS |
yes | yes |
allowed-tools access control |
yes | yes |
| Supporting files (references, scripts) | no | yes |
disable-model-invocation guardrails |
no | yes |
context: fork + agent: isolated execution |
no | yes |
paths: glob auto-activation |
no | yes |
On the first three, commands and skills are equivalent; the last four are skills-only. Use whichever matches what you actually need.
The official path is tilting toward skills, but the compatibility promise for commands/ is explicit — this isn't a "migrate now or die" change, it's a "new capabilities offered, not forced" extension. Understand the difference, migrate when you need to, and don't chase cosmetic consistency.