Slash commands and reusable prompts
Turn the prompts you type every day into files under .claude/commands, with arguments, allowed tools and a pinned model.
A command is a markdown file
Any markdown file in .claude/commands becomes a slash command named after the file. The body is the prompt; the frontmatter controls the invocation. Project commands live in the repository and are shared with the team; personal commands in ~/.claude/commands follow you between projects.
---
description: Review the staged diff for bugs and missing tests
allowed-tools: Read, Grep, Bash(git diff:*), Bash(git status:*)
argument-hint: [focus area]
model: sonnet
---
Review the staged changes.
Focus: $ARGUMENTS
Work through the diff hunk by hunk and report:
1. Logic errors and unhandled error paths
2. Boundary conditions with no test
3. Anything that changes public behaviour without a changelog entry# after saving as .claude/commands/review.md
/review error handling in the upload path
# subdirectories create a namespace
# .claude/commands/db/migrate.md -> /db:migrate$ARGUMENTSis everything typed after the command;$1,$2address individual words.allowed-toolspre-approves exactly the tools the command needs, so the run does not stall on permission prompts.!-prefixed lines run a shell command and inline its output before the model sees the prompt.@path/to/filein the body inlines that file's contents.
Arguments and shell substitution
---
description: Explain a failing test file
allowed-tools: Read, Bash(npm test:*)
---
The test file $1 is failing. Run it, then explain the failure.
Recent commits touching it:
!git log --oneline -5 -- $1| Token | Expands to | Example |
|---|---|---|
$ARGUMENTS | Every word after the command | /review the upload path |
$1, $2 | Positional words | /explain src/a.test.js |
!cmd | Output of the shell command | !git diff --stat |
@file | Contents of the file | @CLAUDE.md |
⚠️
A
! line executes a shell command the moment the command is invoked, before any permission prompt for the model. Never build one from unsanitised arguments - a command like !git log $1 is a shell injection waiting for a hostile branch name. Quote and validate, or drop the substitution.Keeping a command library useful
- Write the prompt once you have typed it by hand three times - not before.
- Keep one command to one outcome; a command that reviews, fixes and commits is one you will not trust.
- Pin
allowed-toolsto the narrowest glob that works, so the command cannot wander outside its job. - Commit project commands with the repository and review them like code: they run with your permissions.
- Prune aggressively. A library of forty commands nobody remembers is worse than five that are muscle memory.
claude
> /help # commands appear alongside the built-ins
> /review # project command, from .claude/commands/review.md
# personal shorthand, available in every repo
mkdir -p ~/.claude/commandsBecause commands are just files, they review and version like anything else. Put the intent in description - it is what the agent and your teammates see when deciding whether to run it.
FAQ
How is a command different from a CLAUDE.md instruction?
CLAUDE.md is always loaded and shapes every turn; a command is loaded only when you type it. Put standing rules in CLAUDE.md and repeatable procedures in commands, otherwise the always-on file grows until every session pays for guidance it does not need.
Can a command take a file path with spaces?
Quote it at the call site and again inside any
! shell line. Positional expansion is textual, so the shell sees whatever you typed.Related
Scoped instructions: memory, rules and path-specific context Skills, plugins and marketplaces
Last refreshed 2026-09-18.