When a prompt works, do not retype it next time. Save it as a Claude Code slash command with a slot or two for the parts that change, and you can run it again with one line. A small folder of these commands becomes a personal prompt library that travels with you.
The core idea
A good prompt is mostly fixed text. Only a few pieces change each time: a file name, an issue number, a feature description. Those changing pieces are the slots. Everything else, the framing, the constraints, the output format, stays the same.
A reusable template separates the two:
- Fixed text captures what made the prompt work: the role, the rules, the format you want back.
- Slots are placeholders you fill in at call time.
In Claude Code you store this template as a Markdown file. The file name becomes the command. The slots become arguments. Typing /fix-issue 412 runs the whole template with 412 dropped into the slot.
Where do custom commands live?
Claude Code reads custom commands from two locations. Both create commands the same way.
| Location | Path | Available in |
|---|---|---|
| Personal | ~/.claude/commands/<name>.md | All your projects |
| Project | .claude/commands/<name>.md | That one project |
The file name without the .md extension is what you type. A file at ~/.claude/commands/changelog.md gives you /changelog in every project. A file at .claude/commands/deploy.md gives you /deploy only inside that repo.
Put project-specific templates in the repo so your team gets them when they clone it. Put general-purpose ones (your code-review checklist, your commit style) in ~/.claude/commands/ so they follow you everywhere.
Custom commands have merged into the skills system. A file under
.claude/commands/and a skill at.claude/skills/<name>/SKILL.mdboth create a/<name>command and work the same way. Existing command files keep working. This guide uses the simplecommands/file form. For the richer skill form, see Claude Code commands and skills.
How do I turn a prompt into a template?
Start with a prompt you already typed and liked. Say this one produced a clean fix:
That prompt has one part that changes from one run to the next: the test file. Everything else is reusable framing. To make a template, replace the changing part with a slot.
Create ~/.claude/commands/fix-test.md:
---
description: Fix a failing test without changing the test
argument-hint: [test-file]
---
The test in $ARGUMENTS is failing. Read the test and the file it
covers, find the bug, fix it without changing the test, and explain
the cause in two sentences.Now run it:
/fix-test src/auth/session.test.tsClaude Code replaces $ARGUMENTS with src/auth/session.test.ts and sends the filled-in prompt. You wrote the framing once and reuse it forever.
Slots: the placeholders you fill
A slot is a placeholder in the template body that Claude Code swaps for what you type after the command name. There are three forms.
| Placeholder | Expands to |
|---|---|
$ARGUMENTS | Everything you typed after the command name, as one string |
$0, $1, $2 | The first, second, third argument by position |
$name | A named argument you declare in frontmatter |
One slot with $ARGUMENTS
Use $ARGUMENTS when there is a single thing to fill in, or when you want the whole input dropped in one place. The fix-test template above is the pattern: one slot, one value.
If you invoke a command that has no $ARGUMENTS in its body but you pass input anyway, Claude Code appends it to the end as ARGUMENTS: <your input> so the text is still seen. Putting the slot where you want it gives you control over placement.
Several slots with positional arguments
When two or three parts change independently, use positional slots. $0 is the first argument, $1 the second. Indexed arguments use shell-style quoting, so wrap a multi-word value in quotes to keep it as one argument.
Create .claude/commands/new-endpoint.md:
---
description: Scaffold a REST endpoint with a test
argument-hint: [method] [path]
---
Add a $0 endpoint at $1 in src/api/routes.ts. Follow the existing
handler style in that file. Write a test for the happy path and one
error case in src/api/routes.test.ts. Do not edit unrelated routes.Run it:
/new-endpoint POST /users/inviteHere $0 becomes POST and $1 becomes /users/invite. For a value with spaces, quote it: /new-endpoint GET "/users/:id/profile settings" keeps the path as a single argument.Common gotchaForget the quotes on a multi-word positional value and it splits on spaces: the first word lands in that slot and the rest spill into the next one, so $1 is half a path and $2 is garbage.Quote any argument that contains a space.
Named slots for readability
For a template with more than two slots, names read better than numbers. Declare them in the arguments frontmatter field; names map to positions in order.
---
description: Open a bug report from a short note
argument-hint: [severity] [area] [summary]
arguments: [severity, area, summary]
---
Draft a bug report. Severity: $severity. Affected area: $area.
Symptom: $summary. Include steps to reproduce as numbered bullets
and a one-line suggested fix. Keep it under 150 words.With arguments: [severity, area, summary], the placeholder $severity expands to the first argument, $area to the second, and $summary to the third. To write a literal $ before a digit or a name in prose (for example a price), escape it: \$1.00.
Frontmatter: the few fields that matter
The block between the --- markers at the top of the file configures the command. These are the fields worth knowing for a template library.
| Field | What it does |
|---|---|
description | One line shown in the command list and used by Claude to decide when a command is relevant |
argument-hint | Autocomplete hint for the slots, like [issue-number] or [file] [format] |
arguments | Declares named slots, as a list or a space-separated string |
allowed-tools | Tools the command may use without asking for approval each time |
disable-model-invocation | Set true so only you can trigger the command, never Claude on its own |
Two of these shape how a template behaves in practice.
How do I stop Claude from running a command on its own?
Set disable-model-invocation: true so the command only runs when you type the slash command yourself. By default Claude can run a command on its own when it judges the command relevant, and for anything with side effects, a deploy, a commit, a message you send, that is not what you want.
---
description: Stage and commit the current changes
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)
---
Stage all changes, write a commit message in our style (imperative
mood, under 60 characters, no trailing period), and commit. Show me
the message before committing.allowed-tools here grants the three git operations without a per-call approval prompt. It does not restrict other tools; it only pre-approves the ones listed. For a project command, this takes effect after you trust the workspace, so review project commands before trusting a repo, since a command can grant itself tool access.Common gotchaThe trap is adding allowed-tools to a side-effect command but forgetting disable-model-invocation: true. You have now made the command both auto-runnable by Claude and auto-approved, so it can commit or deploy with zero confirmation the moment Claude judges it relevant.Always pair the two on anything destructive: allowed-tools skips the prompt, disable-model-invocation keeps the trigger in your hands.
How do I pull live context into a template?
A template can inline the output of a shell command before Claude reads it. Put the command in backticks after a !, and Claude Code runs it and substitutes the result.
---
description: Summarize the current uncommitted changes
allowed-tools: Bash(git diff *)
---
Here is the current diff:
!`git diff HEAD`
Summarize what changed in three bullets and flag anything risky.When you run this, Claude Code executes git diff HEAD, drops the real diff into the prompt, and Claude works from current state. You can also reference a file with @, for example Review @src/auth/session.ts for missing error handling, and that file is loaded into context.
Building a small library
A handful of well-chosen templates covers most repeated work. Start with the prompts you already retype. Aim for a tight set, not a sprawling one.
A starter set worth keeping:
| Command | Slot | Does |
|---|---|---|
/review | $ARGUMENTS (a file) | Runs your code-review checklist on one file |
/explain | $ARGUMENTS (a file or symbol) | Explains code in plain language for a newcomer |
/commit | none | Writes a commit message in your house style |
/test | $ARGUMENTS (a file) | Writes tests matching the project's test layout |
Here is /explain as a model to copy:
---
description: Explain code in plain language for a newcomer
argument-hint: [file-or-symbol]
---
Explain $ARGUMENTS to a developer who has never seen this codebase.
Cover what it does, why it exists, and one gotcha. Use short
paragraphs. Skip line-by-line narration.Names and descriptions earn their keep
The command name is what you type, so keep it short and verb-led: review, not code-review-helper. The description is what shows in the list and what Claude reads to judge relevance, so make it specific. "Runs the project code-review checklist on one file" beats "helps with code". A precise description is the difference between a command you find again and one you forget you wrote.
Keep templates sharp
Templates rot when they outlive the assumptions baked into them. A few habits keep the library useful.
- Edit the file when the prompt drifts. Claude Code picks up changes to a command file within the session, so adjusting a template is a save, not a restart.
- Delete commands you stopped using. A stale command is noise in the list. If you have not run it in a while, remove the file.
- Pin the format, not the content. The reusable part is the framing and the output shape. Anything that changes per run belongs in a slot, not hard-coded in the body.
- Commit project templates. A template under
.claude/commands/in the repo is shared with everyone who clones it, so the whole team runs the same review and the same commit style.
How do I share templates with my team?
Personal templates in ~/.claude/commands/ are yours alone. Project templates in .claude/commands/ ship with the repo. When a personal and a project command share a name, the more specific one wins per the resolution order, so a project can override a personal default for that repo without you renaming anything.
For a team, the project folder is the unit of sharing: commit the .claude/commands/ directory, and a teammate who pulls the repo gets every command with no setup. That turns a habit one person built into a shared standard the whole repo follows.
Checklist
Before you call a template done:
- Does the body hold only fixed framing, with every changing part in a slot?
- Is the slot the right kind:
$ARGUMENTSfor one value,$0/$1or named slots for several? - Does
descriptionsay specifically what the command does? - Does
argument-hintshow what to type? - For anything with side effects, is
disable-model-invocation: trueset? - Is it in the right place:
~/.claude/commands/for personal,.claude/commands/for the repo?
A template that passes this list saves you the retype, keeps the framing that made the prompt work, and reads clearly when a teammate opens it months later.