Claude code custom commands are markdown files: drop review.md into .claude/commands/ and /review exists. The interesting part is what to put in the frontmatter, so we read every command file Anthropic ships — 30 files across 14 plugins, 183,702 bytes — and checked them against the official reference. One documented field is used by none of them, and one field two of them set has no entry in the reference at all. This guide covers the file format, both argument styles, and what the shipped corpus says about length. Measured 2026-09-15 on Claude Code 2.1.270.
Key takeaways
- A command is a markdown file; the filename is the command name, and the body is the prompt.
- The reference documents five fields. In 30 shipped commands:
description30,argument-hint19,allowed-tools14. modelis documented across 66 lines and set by zero of the 30.hide-from-slash-command-toolis set by two shipped commands and is not in the reference.- Shipped commands average 6,123 bytes — a procedure, not a one-liner.
What a custom command is
A custom command is a saved prompt with a name. When you type /review, Claude Code finds review.md, expands its body into the conversation, and the model responds to that text as though you had typed it.
That is the whole mechanism, and it explains what commands are good for. They do not add capability — the session could always have done the work. They remove the retyping, and more importantly they freeze the wording, so a procedure you refined once runs the same way next month.
It also marks the boundary with the neighbouring surface. A skill carries a description the model reads in order to decide whether to invoke it on its own. A command waits to be named. If you want something to happen when you ask for it, write a command; if you want it to happen because the situation arose, you are looking at a different tool.
Where the file goes
Two locations, and the only difference is scope:
.claude/commands/ ← this project only, commit it review.md ← becomes /review deploy-check.md ← becomes /deploy-check ~/.claude/commands/ ← every project on this machine standup.md ← becomes /standup
The filename is authoritative — there is no name field, and renaming the file renames the command. Plugins are the third source: an installed plugin's commands/ directory contributes its commands too, which is where all 30 files in this census come from.
The five documented fields
Anthropic's own authoring reference ships inside the plugin-dev plugin, at skills/command-development/references/frontmatter-reference.md, and runs to 463 lines. It documents exactly five fields:
--- description: Review the working tree for correctness bugs argument-hint: "[path]" allowed-tools: Read, Grep, Glob model: sonnet disable-model-invocation: false --- Review the changes in $1 for correctness bugs only...
description— what the command does. It is what the reader sees in the picker.allowed-tools— restricts the command's run to a named tool set.model— pins the command to a specific model.argument-hint— the placeholder text shown after the command name.disable-model-invocation— prevents the model invoking the command itself.
What 30 shipped commands actually set
That is the documentation. Here is the corpus: every .md file under a commands/ directory in the official plugin marketplace, as cached on this machine.
| Field | Documented | Files (of 30) |
|---|---|---|
description | Yes | 30 |
argument-hint | Yes | 19 |
allowed-tools | Yes | 14 |
disable-model-invocation | Yes | 2 |
hide-from-slash-command-tool | No | 2 |
model | Yes | 0 |
description is the only field with unanimous adoption, which is the practical rule: treat it as required even though the loader does not. Past that, adoption falls off fast — allowed-tools is on fewer than half, and the two remaining documented fields are close to unused.
The two fields the reference gets wrong
Two rows above are worth separating out, because they point in opposite directions.
model is documented and dead. It gets a full section in the reference — type, whether it is required, worked examples, 66 lines of specification — and not one of the 30 commands Anthropic ships sets it. That is not proof the field is broken; it is evidence about what experienced command authors actually reach for. Pinning a model inside a command means the command stops inheriting the session's model, which is usually the wrong default: the person invoking it picked their model for a reason.
hide-from-slash-command-tool is used and undocumented. Two shipped commands set it. The string does not appear anywhere in the 463-line reference. The name is self-describing — keep this command out of the tool the model uses to invoke commands — and it is the natural companion to disable-model-invocation, which is documented and also appears twice.
Arguments, and which style wins
Two substitution styles exist and the corpus splits almost evenly between them — $ARGUMENTS in 11 files, positional $1/$2 in 12.
--- description: Fix a GitHub issue end to end argument-hint: "[issue-number] [branch]" --- Fix issue #$1 and open the PR against $2. Read the issue first with `gh issue view $1`.
The choice is structural, not stylistic:
$ARGUMENTStakes everything after the command name as one string. Right when the input is free text — a description, a question, a path list.$1,$2take fixed slots. Right when the command has a real signature, andargument-hintexists to advertise it.
Two other body syntaxes appear, each in two files: a ! prefix that runs a shell command and interpolates its output, and @ to pull a file into the prompt. Both are minority usage, and both replace a step the session would otherwise spend a turn on.
How long a command should be
The shipped commands average 6,123 bytes. That is not a one-line instruction — it is a procedure, with steps, constraints, and usually a specified output format.
This is the most useful signal in the corpus. The instinct with a new command is to write "review my code for bugs", which saves four seconds and changes nothing about the result. The commands worth keeping are the ones carrying the context you would otherwise retype every time: which files to look at, which failure modes matter here, what to do when the answer is ambiguous, what the output should look like.
Put differently — a command earns its place when it encodes a decision, not a request. That is the same economics as a CLAUDE.md section, with one difference in your favour: a command's body costs nothing until it is invoked, where CLAUDE.md is paid on every single turn.
The /verify command is the useful boundary case: Anthropic ships a bundled version, but a project .claude/skills/verify/SKILL.md can replace it with the exact launch and observation recipe your application needs.
Writing one worth keeping
--- description: Run the publish gate before shipping an article allowed-tools: Bash, Read, Grep argument-hint: "[slug]" --- Run the publish gate for $1 and stop at the first red result. 1. `npm run typecheck`, then `npm run lint`. 2. `npm run check:anchors` — every toc href must match a generated heading id. 3. Confirm `cover` and `coverAlt` are set in the frontmatter of $1. 4. Report each step as pass or fail with the command's own output. Do not fix anything. Report only.
Four things make that worth saving rather than typing. It names the commands, so the run is identical every time. It sets allowed-tools, so the command cannot wander. It uses $1 with a matching argument-hint, so the signature is visible at the prompt. And the last line removes an ambiguity — report, don't fix — that would otherwise be resolved differently on different days.
What did not work
The first census counted 34 files and every figure was wrong. The glob was -path "*commands*", which matches by substring, so it swept in the commit-commands/ plugin directory, the command-development/ skill directory, and a README.md. Anchoring it to */commands/* gives 30. Two files in the bad set had no description, which would have shipped as "description is on 32 of 34 commands" — a real-looking finding about optional frontmatter that was entirely an artifact of counting a README as a command.
No command was invoked. Every nested claude -p invocation on this machine is refused by its auto-mode classifier, so this is a census of what 30 authored commands declare, not a measurement of what the loader does with them. The field counts are solid; any claim about runtime behaviour would not be.
The corpus is Anthropic's, not the world's. 30 files from 14 first-party plugins is a strong signal about house style and a weak one about what every team does. The model result in particular says these authors don't pin models — not that pinning is wrong.
Best practices
- Always set
description. All 30 shipped commands do, and it is what makes the command findable by the person who did not write it. - Set
argument-hintwhenever you use$1. The signature is invisible otherwise, and 19 of 30 shipped commands set one. - Reach for
allowed-toolson anything read-only. A review command that cannot write is a different risk profile from one that can. - Leave
modelalone unless the command genuinely requires a specific one. Anthropic's own commands never set it. - Put the procedure in the body, not the request. The average shipped command is 6 KB because that is where the value lives.
- Commit project commands. A command in
.claude/commands/that is gitignored helps exactly one machine.
Common mistakes
Adding a name field. There isn't one. The filename is the command name; rename the file.
Writing a command that restates your prompt. If the body is one sentence you would have typed anyway, it adds a step and no information.
Pinning a model out of habit. It overrides the choice the person invoking it already made, and none of the 30 shipped commands does it.
Expecting a command to fire on its own. That is a skill's job. A command waits to be named.
Trusting a *commands* glob. It matched three non-command directories here and produced a wrong count that looked entirely plausible.
Conclusion
Write the command as a markdown file, name it for what it does, always set description, add argument-hint when it takes slots, and put the whole procedure in the body — that is what the 30 commands Anthropic ships actually look like. Skip model; none of them uses it. The number to carry is 6,123 bytes: a command that is worth having is one you would otherwise have retyped, and those are never one line long. Start with the procedure you already run by hand most often, and save it before you refine it.
Frequently asked questions
How do I create a custom slash command in Claude Code?
What frontmatter fields does a Claude Code command file support?
How do I pass arguments to a custom command?
What is the difference between a custom command and a skill?
Should a custom command be short?
Muhammad Kashif
Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.




