A Markdown file at .claude/commands/deploy.md becomes /deploy, and so does a skill at .claude/skills/deploy/SKILL.md. Custom commands were merged into skills, so both work and the skill form is the one gaining features. Arguments arrive as $ARGUMENTS, or by position as $ARGUMENTS[N] and its $N shorthand. A backtick-wrapped ! block runs a shell command and injects its output before the prompt is sent, which is what lets a command gather its own context.
- Commands are now skills.
.claude/commands/*.mdstill works and still becomes/name. $ARGUMENTSfor everything,$ARGUMENTS[N]or$Nby position.$0is the first.- A
!shell block runs before the prompt is sent, so the command gathers its own context. allowed-toolsgrants permission for the turn. It does not restrict; that isdisallowed-tools.disable-model-invocation: truefor anything with side effects, so only you can fire it.
Custom commands are skills now
The most important thing to know in August 2026 is that custom commands have been merged into skills. Nothing you already wrote broke: a file at .claude/commands/deploy.md still creates /deploy and still behaves the same. What changed is that the skill form is where new capability lands.
Two layouts, one command.
| Layout | Command | Gets you |
|---|---|---|
.claude/commands/deploy.md | /deploy | A single file. Everything below still applies |
.claude/skills/deploy/SKILL.md | /deploy | A directory for supporting files, invocation control, and automatic loading when relevant |
Where they live, and who wins on a name clash.
| Scope | Path | Precedence |
|---|---|---|
| Enterprise | Managed settings directory | Highest |
| Personal | ~/.claude/skills/<name>/SKILL.md | Overrides project |
| Project | .claude/skills/<name>/SKILL.md | Overrides bundled |
| Plugin | <plugin>/skills/<name>/SKILL.md | Namespaced as /plugin:name, cannot clash |
The mechanics
Create the file
.claude/skills/<name>/SKILL.md for the project, or ~/.claude/skills/<name>/SKILL.md for yourself. The directory name becomes the command.
Write the frontmatter before the body
A description so it appears usefully in the listing, an argument-hint so autocomplete shows what it expects, and disable-model-invocation: true if it has side effects.
Make it gather its own context
Replace "look at the diff" with a shell block that puts the diff in the prompt. That is the single change that turns a saved prompt into a tool.
Run it, then tighten it
The first version will do one thing you did not want. Add the "do not" line for exactly that, then commit it so everyone gets the fixed version.
---
description: Fix a failing test
argument-hint: [test file path]
arguments: [testfile]
disable-model-invocation: true
allowed-tools: Read Edit Bash(npm run test *)
model: sonnet
---
Fix the failing test in `$testfile`.
Current failure:
!`npm run test -- "$0" 2>&1 | tail -40`
Recent changes to that area:
!`git log --oneline -5 -- "$0"`
Rules:
- Fix the implementation, not the test, unless the test asserts
something genuinely wrong. Say which you chose and why.
- Do not weaken an assertion to make it pass.
- Re-run the single test to confirm before reporting done.
Argument substitutions. Every field is optional.
| Syntax | Expands to |
|---|---|
$ARGUMENTS | Everything after the command name |
$ARGUMENTS[0] | The first argument, zero-indexed |
$0, $1 | Shorthand for the same, so $0 is the first argument |
$name | A named argument declared in the arguments frontmatter list, mapped in order |
${CLAUDE_PROJECT_DIR} | The project root |
${CLAUDE_SKILL_DIR} | The directory holding this SKILL.md, for bundled scripts |
${CLAUDE_SESSION_ID} | The current session id, for logging |
${CLAUDE_EFFORT} | The active effort level |
Arguments stack too. Typing /write-tests /fix-issue 123 loads both skills and passes 123 to each. Claude Code expands the first skill plus up to five more, and stops at the first token that is not an inline user-invocable skill.
Four worth stealing
/review - check your own work before pushing
---
description: Review uncommitted changes for defects
disable-model-invocation: true
allowed-tools: Read Grep Glob Bash(git diff *) Bash(git status)
disallowed-tools: Edit Write
---
Review these uncommitted changes:
!`git diff HEAD`
Report only defects that will actually occur, each with the input that
triggers it. Ignore style and naming. If there is nothing real, say so
in one line rather than inventing something. Do not fix anything.
/commit - a message that describes the change
---
description: Write a commit message for staged changes
disable-model-invocation: true
allowed-tools: Bash(git diff *) Bash(git log *)
model: haiku
---
Staged changes:
!`git diff --cached`
Recent history, for style:
!`git log --oneline -10`
Write a conventional commit message. Subject under 72 characters.
Body only if the change needs a why. Output the message alone, no
commentary and no code fences.
/explain - orient in unfamiliar code
---
description: Explain what a file does and who calls it
argument-hint: [file path]
arguments: [target]
allowed-tools: Read Grep Glob
context: fork
agent: Explore
---
Explain `$target`.
Cover, briefly:
1. What it is responsible for, in two sentences.
2. Who calls it. Grep for the imports.
3. What it depends on.
4. Anything surprising a newcomer would get wrong.
Do not summarise line by line.
/tests - coverage for something that already works
---
description: Add unit tests for a file
argument-hint: [file path]
arguments: [target]
allowed-tools: Read Write Glob Bash(npm run test *)
model: haiku
---
Write unit tests for `$target`.
Existing conventions in a neighbouring test file:
!`ls "$(dirname "$0")" | head -20`
Match those conventions exactly; do not introduce a new style. Test the
public interface, cover the error paths, and never modify the
implementation. Run the tests before reporting done.
allowed-tools grants. It does not restrict.
This is the field most people have backwards, and getting it wrong is why a "read-only" review command starts editing files.
What each field actually does, as of August 2026.
| Field | Effect | Lifetime |
|---|---|---|
allowed-tools | Pre-approves the listed tools so Claude uses them without prompting. Every other tool is still callable under your normal permission rules | The turn that invoked the skill |
disallowed-tools | Removes the listed tools from the pool while the skill is active | Until your next message |
permissions.deny in settings | Blocks the tool everywhere | Always |
Deciding who can fire it
Two frontmatter fields control invocation.
| Frontmatter | You can invoke | Claude can invoke | When it loads |
|---|---|---|---|
| (default) | Yes | Yes | Description always in context; body loads on invoke |
disable-model-invocation: true | Yes | No | Nothing in context until you invoke it |
user-invocable: false | No | Yes | Description always in context |
Set disable-model-invocation: true on anything with side effects: /deploy, /commit, /send-slack-message. You do not want Claude deciding to deploy because the code looks ready. It has a second benefit worth knowing: the description is then kept out of context entirely, so a library of manual commands costs nothing until used.
Running one in its own context
Some commands produce a lot of intermediate noise: a codebase survey, a dependency audit, a PR summary that reads three gh outputs. Setting context: fork runs the command in a subagent, so only its answer comes back to your conversation.
---
description: Summarize changes in a pull request
context: fork
agent: Explore
background: false
allowed-tools: Bash(gh *)
---
## Pull request context
- Diff: !`gh pr diff`
- Comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`
## Your task
Summarize what this PR changes and why, in under 200 words. Then list
anything a reviewer should look at closely, with file and line.
The two fields that go with context: fork.
| Field | Purpose |
|---|---|
agent | Which subagent type runs it, for example Explore |
background | Defaults to true. Set false to wait for the result in the same turn |
What makes one good
- Gather context in the command. A command that runs
git difffor itself beats one that asks the agent to. - Restrict with
disallowed-tools, notallowed-tools. The first restricts; the second only grants. - Name your arguments.
arguments: [target]plus$targetis immune to the zero-based counting trap. - Pick the model. Commit messages are a Haiku job; reviews are worth a frontier model. The override lasts for the turn and is not saved to settings.
- Say what not to do. "Do not weaken the assertion" prevents the specific failure that makes agent-written tests worthless.
- Put the trigger first in the description. The description plus
when_to_useis truncated at 1,536 characters in the listing, so the key use case belongs at the front. - Commit them. The point is that the whole team runs the same debugged prompt.
Questions people ask
Create a Markdown file at .claude/commands/name.md, which becomes /name, or a skill at .claude/skills/name/SKILL.md, which becomes /name and can carry supporting files. Use ~/.claude/skills/ for commands available in every project.
No. Custom commands were merged into skills, and existing .claude/commands/ files keep working exactly as before. Skills are where new capability lands, so prefer them for anything new. If a skill and a command share a name, the skill takes precedence.
$ARGUMENTS captures everything after the command name. For positions, use $ARGUMENTS[N] or the $N shorthand, which is zero-based, so $0 is the first argument. Declaring arguments: [name] in frontmatter and using $name avoids the off-by-one entirely.
Yes. A backtick-wrapped ! block runs before the content reaches Claude, and its stdout replaces the placeholder, which lets a command gather its own context. Pair it with an allowed-tools Bash rule so the command does not prompt.
No, and this is the most common mistake. allowed-tools pre-approves the listed tools for the turn; it does not remove anything. Use disallowed-tools to take tools away, or run the command with context: fork so a subagent tool allowlist applies instead.
Set disable-model-invocation: true in the frontmatter. Only you can then invoke it with /name. Use it for anything with side effects, such as deploy or commit commands. It also keeps the description out of context until you invoke it.
Yes, via model in the frontmatter. The override applies for the rest of the current turn and is not saved to settings, so the session model resumes on your next prompt. Routing mechanical commands such as commit-message generation to a cheaper model is a real saving.
Project commands, yes. Sharing a debugged prompt is one of the cheapest ways to make a whole team consistent. Be aware that a project skill granting itself tools through allowed-tools applies only after a reviewer accepts the workspace trust dialog.
Sources
Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.