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
  • $ARGUMENTS is everything typed after the command; $1, $2 address individual words.
  • allowed-tools pre-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/file in 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
TokenExpands toExample
$ARGUMENTSEvery word after the command/review the upload path
$1, $2Positional words/explain src/a.test.js
!cmdOutput of the shell command!git diff --stat
@fileContents 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

  1. Write the prompt once you have typed it by hand three times - not before.
  2. Keep one command to one outcome; a command that reviews, fixes and commits is one you will not trust.
  3. Pin allowed-tools to the narrowest glob that works, so the command cannot wander outside its job.
  4. Commit project commands with the repository and review them like code: they run with your permissions.
  5. 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/commands

Because 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.

Scoped instructions: memory, rules and path-specific context Skills, plugins and marketplaces

Last refreshed 2026-09-18.