Slash Commands — Reusable Prompts as Commands
A slash command is a prompt you've already debugged — saved as a markdown file, invoked with a word, and version-controlled with the project.
Introduction
Every experienced Claude Code operator has a set of prompts they type over and over: "Write a PR description for these changes," "Generate a commit message from the diff," "Run the test coverage check and summarize gaps." A slash command turns those prompts into a single word.
Anatomy of a Slash Command
A command is a markdown file with optional YAML frontmatter. The body is the prompt itself; the frontmatter (every field optional) carries metadata like description, argument-hint, allowed-tools, and model. A bare markdown file with no frontmatter is a perfectly valid command — start there.
The filename is the command name. pr-description.md becomes /pr-description. The entire file contents become the prompt Claude receives when the command is invoked. The $ARGUMENTS placeholder, if present, is replaced with everything the user types after the command name.
That's the full mechanism. No configuration required. No registration step. Drop the file in the right directory and the command appears.
Project vs. Personal Scope
Project commands (.claude/commands/) are the team's shared vocabulary. They ship with the repo, version-controlled alongside the code. When Knox's academy repo has a /deploy-check command, every developer working on the academy gets that same command. Changes to the command are tracked in git history. The command evolves with the project.
Personal commands (~/.claude/commands/) are your private workflow library. They follow you across every project you open. /daily-brief, /my-standup, /debug-session — commands that reflect how you personally work, not how a specific project works.
Both scopes are active simultaneously. Claude Code merges them into a single discovery list. If you have both a personal and project command with the same name, the project command takes precedence (since it's more specific to the current context).
The $ARGUMENTS Pattern
When a command needs input that changes per invocation, use $ARGUMENTS:
<!-- ~/.claude/commands/explain.md -->
Explain the following concept as if I have a software engineering background
but no prior exposure to this specific topic:
$ARGUMENTS
Include:
- A one-sentence definition
- A concrete analogy from software engineering
- One common misconception to avoid
Invoked as /explain context windows, the command receives "context windows" as $ARGUMENTS. The prompt structure stays stable; only the topic changes.
Frontmatter and Dynamic Content
Beyond $ARGUMENTS, the command format supports a handful of constructs worth knowing:
YAML frontmatter at the top of the file configures the command:
---
description: Generate a PR description from the current branch diff
argument-hint: [issue-number]
allowed-tools: Bash(git diff:*), Bash(git log:*)
model: claude-sonnet-4-6
---
description is what appears in the command list; argument-hint documents what to pass; allowed-tools scopes which tools the command may use while it runs; model overrides the session model for this command only.
Positional arguments — $1, $2, etc. — split the input by whitespace when you need individual pieces rather than the whole string $ARGUMENTS gives you.
Inline bash execution — a line like !`git diff --stat` runs the command at invocation time and injects its output into the prompt. This is how a /pr-description command sees the actual diff.
File references — @path/to/file inlines that file's contents into the prompt.
These are conveniences, not requirements. Most commands need none of them — but allowed-tools and !`cmd` are exactly what turn a saved prompt into a self-contained workflow.
This is the right pattern for:
- Summarizing a specific file:
/summarize src/trading/calibrator.py - Generating a PR description for a specific issue:
/pr-description closes #42 - Researching a topic:
/deep-dive gradient descent
When a Command Beats a Chat Turn
A direct chat turn is exploration. You're discovering what the prompt needs to look like, adjusting tone, iterating on structure. A command is the result of that exploration — the prompt you'd write again exactly if you had to.
The signals that a prompt is ready to become a command:
- You've typed it (or a close variant) more than twice
- The output quality depends heavily on the exact wording
- You want the same prompt to be available to your team
- You've refined it to the point where variations feel like regressions
Knox's /pr-description command took four iterations before it stabilized. The first version produced verbose summaries; the refined version constrains length and focuses on "why" over "what." That constraint is in the command file — every invocation benefits from the refinement.
Building Your First Command
The fastest path: find the last prompt you typed that produced exactly what you wanted. Save it verbatim as .claude/commands/that-task.md. Run it once to confirm it works. Then commit it.
That's the full workflow. The discipline is resisting the urge to over-engineer. A command is a saved prompt — not a template system, not a configuration schema, not a DSL. The simplicity is the feature.
Once you have three or four project commands, you'll find you're onboarding new contributors faster ("we have a /deploy-check command that validates everything before you push"), and you'll stop re-typing prompts you've already perfected.
Now author one yourself. Take the /pr-description prompt Knox iterated to and turn it into a committed command file — frontmatter, a constrained body, and $ARGUMENTS for the per-PR input.
The next lesson introduces skills — the more powerful cousin of slash commands. Where commands are saved prompts, skills are saved workflows: multi-step expert processes that encapsulate domain knowledge, tool sequences, and verification gates.