How to Write a Custom Slash Command in Claude Code
A claude code custom slash command is a Markdown file in .claude/commands/. Drop in review.md, type /review, and run a saved prompt with args and frontmatter.
Hören Sie auf zu konfigurieren. Fangen Sie an zu bauen.
SaaS-Builder-Vorlagen mit KI-Orchestrierung.
A custom slash command in Claude Code is a reusable prompt you save as a Markdown file. Drop review.md into .claude/commands/ and you can run it any time by typing /review. The file body is the prompt, and optional YAML frontmatter controls arguments, allowed tools, and which model runs it.
By the end of this guide you'll have a working /review command, know exactly where the file lives, and be able to pass arguments, inject live shell output, and pin a model. Every example below is copy-paste ready.
Hören Sie auf zu konfigurieren. Fangen Sie an zu bauen.
SaaS-Builder-Vorlagen mit KI-Orchestrierung.
What a custom slash command actually is
One Markdown file equals one reusable prompt. The body of the file is the text Claude reads. The filename becomes the command you type after the slash. That's the whole idea. Instead of pasting the same "review my diff for bugs" prompt fifty times, you write it once and call it with /review.
There's one thing every current guide needs to say up front. As of 2026, custom commands and skills are the same feature. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy and both support the same frontmatter. The official docs label the single-file .claude/commands/ form "legacy" and recommend skills, but your existing command files keep working. They are not broken or removed.
So this guide teaches the single-file command first, because it's the fastest path and it's what most people mean when they search for a custom slash command. Then it shows the skill upgrade path, which is the same thing with more power. If a skill and a command share a name, the skill wins. For more on the skill form, see what Claude skills are.
Where the file goes (project vs personal)
There are two scopes that matter for a solo builder, and the difference is who gets the command.
Project commands live in .claude/commands/ inside your repo. They ship with the code, so anyone who clones the project gets them. Personal commands live in ~/.claude/commands/ in your home directory. They follow you across every project on your machine but never get committed.
| Scope | Command file | Skill form | Applies to |
|---|---|---|---|
| Personal | ~/.claude/commands/<name>.md | ~/.claude/skills/<name>/SKILL.md | All your projects |
| Project | .claude/commands/<name>.md | .claude/skills/<name>/SKILL.md | This project only (commit to share) |
When the same name exists at more than one level, enterprise-managed settings win over personal, and personal wins over project. For a command file the command name is the filename without its extension. For a skill it's the directory name. The name frontmatter field only sets the display label in listings, it does not change what you type after the slash.
You can also organize command files into subfolders, like .claude/commands/frontend/component.md. The subfolder shows up in the command's description for context but does not become part of the invocation. That file is still /component, not /frontend:component. One caveat that follows from that rule: because the subfolder isn't in the name, two files both named component.md in different subfolders collide on /component. Keep your base filenames unique.
Your first command in 60 seconds
Make the directory and write the file. The body is just the prompt you'd otherwise type by hand.
mkdir -p .claude/commands
cat > .claude/commands/review.md << 'EOF'
Review the current git diff for bugs, security issues, and missing tests.
Be specific. Cite file and line.
EOFNow type /review in Claude Code. That's it. The body of the file becomes the prompt, and Claude does the work. No restart needed for a command file in a directory that already existed.
This is the same idea as keeping standing facts in your CLAUDE.md, except a command is a procedure you trigger on demand instead of context that's always loaded. For rules that should apply automatically rather than on a keystroke, see the rules directory.
Adding arguments
Most commands get more useful once they take input. There are three ways to read what the user typed after the command.
The simplest is $ARGUMENTS, which inserts everything typed after the command as one string. If you type /review auth flow, then $ARGUMENTS becomes auth flow. If you leave $ARGUMENTS out of the body entirely, Claude Code still appends ARGUMENTS: <value> so your input reaches Claude anyway.
Review the current git diff. Focus on $ARGUMENTS if provided.
Report correctness bugs, security issues, and missing tests.For positional arguments, use $0, $1, and so on. This is the single most error-prone fact in the whole feature, so read it carefully. $N is zero-based shorthand for $ARGUMENTS[N], which means $0 is the first argument and $1 is the second. Many older blog posts say $1 is the first argument, the way shell scripts work. That is wrong in current Claude Code. $0 is first.
Create a new component named `$0` in the directory `$1/`.Indexed arguments use shell-style quoting. If you run /scaffold "user card" components, then $0 is user card and $1 is components. To print a literal dollar sign before a digit, escape it with a backslash, like \$1.00.
The third way is named arguments. Declare them in an arguments: frontmatter field and a matching argument-hint: for autocomplete. Names map to positions in order.
---
argument-hint: "[issue] [branch]"
arguments: [issue, branch]
---
Fix issue $issue and open a pull request from branch $branch.With arguments: [issue, branch], the value $issue is the first argument and $branch is the second.
Frontmatter reference
Frontmatter is the optional YAML block between --- markers at the top of the file. All fields are optional. Only description is recommended, because Claude uses it to understand what the command does. These are the fields that matter day to day.
| Field | What it does |
|---|---|
description | What the command does and when to use it. Falls back to the first paragraph of the body if omitted. |
argument-hint | Autocomplete hint for the expected arguments, like [issue-number]. |
allowed-tools | Tools Claude may use without a permission prompt while the command runs. |
disable-model-invocation | Set to true so only you can trigger the command. Claude won't auto-fire it. |
model | Which model runs the command. Reverts after the turn. |
For the model field, use a friendly name: Opus 4.8, Sonnet 4.6, Haiku 4.5, Fable 5, or inherit to keep the current one. The choice applies only while the command runs and is not saved to your settings. Pin Haiku 4.5 on a cheap formatting command and Opus 4.8 on a hard review.
The disable-model-invocation: true field is the right pattern for any command with side effects. A /commit or /deploy command should never auto-fire on its own. You decide when it runs.
The note names are exact and hyphenated. There is no allowed_tools underscore variant. If you've used the hooks frontmatter field for deterministic enforcement, that's a separate topic covered in the hooks guide.
Injecting live data with bash
A static prompt is fine, but the real power comes from feeding Claude fresh data at the moment the command runs. The !`command` syntax does exactly that. Prefix a line with an exclamation mark and a backtick-wrapped shell command, and Claude Code runs it first, then replaces the placeholder with the command's output. Claude only ever sees the result, not the command.
## Changes to review
!`git diff HEAD`When /review runs, that line becomes the actual diff text. The inline form is recognized only when the ! is at the start of a line or right after whitespace. Something like KEY=!`cmd` is treated as plain text and does not run.
For a multi-line shell block, open a fenced block with ```! instead of triple backticks alone.
```!
npm test
npm run lint
```One requirement makes this smooth. To run those commands without a permission prompt every time, grant the tools under allowed-tools. The injection runs as preprocessing, and allowed-tools is what pre-approves it.
---
allowed-tools: Bash(git diff *) Bash(git status *)
---Substitution runs once over the original file, and the injected output is not re-scanned for more ! placeholders. To pull a file's contents into the prompt, the safe and verified approach is to inject it with bash, like !`cat docs/component-guidelines.md`. That works anywhere and needs only a Bash(cat *) grant under allowed-tools. If you want deeper reasoning while a command runs, drop the word ultrathink anywhere in the body.
Four commands to copy right now
These four cover the workflows most builders repeat daily. Each is a complete file. The location comment at the top tells you where it goes.
A diff reviewer. The /review command injects your current diff and asks for a focused review.
---
description: Review the current git diff for bugs, security issues, and missing tests.
argument-hint: "[optional focus area]"
allowed-tools: Bash(git diff *) Bash(git status *)
model: Sonnet 4.6
---
## Changes to review
!`git diff HEAD`
## Your task
Review the diff above. Focus on $ARGUMENTS if provided. Report:
1. Correctness bugs
2. Security issues (injection, auth, secrets)
3. Missing or weak tests
Be specific. Cite file and line.A commit writer. Because committing has side effects, disable-model-invocation: true keeps it manual so Claude never fires it on its own.
---
description: Stage all changes and write a conventional commit message.
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *) Bash(git diff *)
---
## Current state
!`git status --short`
!`git diff HEAD`
## Your task
1. Stage the relevant changes with `git add`.
2. Write a Conventional Commits message (feat/fix/chore/docs) summarizing the diff above.
3. Create the commit. Do not push.A test runner. This one uses the multi-line bash block to run the suite, passing any argument through to target a single file.
---
description: Run the test suite and fix any failures.
argument-hint: "[test file or pattern]"
allowed-tools: Bash(npm test *) Bash(npx *) Read Edit
---
## Test run
```!
npm test $ARGUMENTS
```
## Your task
If tests pass, summarize coverage. If any fail, read the failing files,
diagnose the root cause, fix the code (not the test, unless the test is
wrong), and re-run until green.A scaffolder. This one uses positional arguments and injects a reference file's contents with bash so the new component matches your conventions.
---
description: Scaffold a new component following our conventions.
argument-hint: "[ComponentName] [directory]"
allowed-tools: Write Read Bash(cat *)
---
Create a new React component named `$0` in `$1/`.
Follow the conventions below and match the existing structure.
## Conventions
!`cat docs/component-guidelines.md`
Generate the component file, an index export, and a basic test.Remember $0 is the first argument (ComponentName) and $1 is the second (directory).
Command or skill, which should you write?
The single-file command is the fast path. One Markdown file, one prompt, zero ceremony. Reach for it when the command is self-contained and you just want a saved prompt with arguments.
Upgrade to a skill when you need more. A skill is a directory, .claude/skills/<name>/SKILL.md, so it can ship supporting files alongside the prompt. Skills can also load automatically when Claude judges them relevant, and editing one takes effect within the session. The invocation is the same /name, and the frontmatter is the same set of fields. Moving from a command to a skill is mostly a matter of relocating the file. See what Claude skills are for the full picture.
If you find yourself chaining several commands together for one repeated workflow, that's worth its own pattern. The guide on batching commands covers how to keep those chains simple.
Start with a command today. Save the /review and /commit files above, and you've already cut the two most repeated prompts out of your day. Reusable workflows like these are the foundation of building faster with Claude Code.
Frequently asked questions
What is a custom slash command in Claude Code?
A custom slash command is a reusable prompt saved as a Markdown file in .claude/commands/, run by typing a slash plus the filename. The file body is the prompt, and optional YAML frontmatter controls arguments, allowed tools, and which model runs it.
Where do I put a custom slash command file?
Project commands go in .claude/commands/ in the repo, so they ship with your code. Personal commands go in ~/.claude/commands/ and follow you across every project on your machine.
Are custom commands the same as skills now?
Yes. Custom commands have merged into skills. A file at .claude/commands/deploy.md and a skill at .claude/skills/deploy/SKILL.md both create /deploy and support the same frontmatter. Command files still work; skills are the recommended form.
How does the command name come from the filename?
For a command file, the name is the filename without its extension, so review.md becomes /review. For a skill, the name is the directory name, so skills/deploy-staging/SKILL.md becomes /deploy-staging.
Is $1 the first or second argument in Claude Code?
$1 is the second argument. In current Claude Code, $N is zero-based shorthand for $ARGUMENTS[N], so $0 is the first argument and $1 is the second.
How do I pass arguments to a slash command?
Use $ARGUMENTS for everything typed after the command as one string, $0 and $1 for positional args by zero-based index, or named $name args declared in the arguments frontmatter field.
How do I run a bash command inside a slash command?
Prefix a line with a backtick-wrapped command after an exclamation mark to run it before the prompt reaches Claude and inject the output. Pre-approve the tool under allowed-tools so you are not prompted.
How do I stop Claude from running my command automatically?
Add disable-model-invocation: true to the frontmatter. Then only you can trigger it by typing the slash command, which is the right pattern for side-effect commands like /commit and /deploy.
What frontmatter fields can a command file use?
The common ones are description, argument-hint, allowed-tools, model, and disable-model-invocation. All fields are optional, and only description is recommended so Claude knows what the command does.
How do I pick which model a command uses?
Set the model field in frontmatter to a friendly model name like Opus 4.8, Sonnet 4.6, Haiku 4.5, or Fable 5, or use inherit. The model reverts after the command runs and is not saved to settings.
Hören Sie auf zu konfigurieren. Fangen Sie an zu bauen.
SaaS-Builder-Vorlagen mit KI-Orchestrierung.
Claude Code Costs After June 15: What Actually Changed
The June 15, 2026 Claude Code billing split was paused before it shipped. Here is what really changed, what it costs now, and the cost-cutting levers that are still live.
Compound Engineering: The AI Loop Where Every Task Makes the Next Easier
Compound engineering is an AI coding loop (plan, build, review, compound) where every fix becomes a permanent lesson. Here is the method and how to set it up in Claude Code.