How to create a custom command
Custom commands are plain Markdown files stored in .claude/commands/. The filename determines the command name: review-pr.md becomes /review-pr, summarize.md becomes /summarize. Claude Code scans that directory automatically — no registration step required.
To create your first command:
# Create the commands directory if it doesn't exist
> mkdir -p .claude/commands
# Create a command file
> touch .claude/commands/review-pr.md
Open review-pr.md and write the prompt you want Claude to receive when you type /review-pr. No frontmatter required — the file content is the prompt template. Any text typed after the command name is passed in as $ARGUMENTS.
For example, if your file contains Review the pull request at $ARGUMENTS for correctness and style and you type /review-pr #142, Claude receives Review the pull request at #142 for correctness and style.
direction: right
cmd: "/review-notebook notebooks/churn.ipynb"
file: .claude/commands/review-notebook.md
prompt: "Prompt with arguments"
claude: Claude Code
cmd -> file
file -> prompt
prompt -> claude Template
Here is a complete, realistic command for a DS/ML workflow. It reviews a Jupyter notebook for common issues that affect reproducibility and model correctness.
Review the notebook at $ARGUMENTS for:
1. Reproducibility — are random seeds set? are paths relative?
2. Data leakage — does any preprocessing use future information?
3. Code quality — are functions documented? is logic reusable?
4. Output — are all cells run in order? are large outputs cleared?
Provide a structured report with a severity level (high/medium/low) for each issue found.
To invoke it:
/review-notebook notebooks/churn_model.ipynb
Claude replaces $ARGUMENTS with notebooks/churn_model.ipynb and runs the prompt. You can reuse the same command on any notebook in your project — no edits needed.
$ARGUMENTS captures everything after the command name as a single string. For instance, /compare-models models/v1.pkl models/v2.pkl passes models/v1.pkl models/v2.pkl as one block of text.
When you need individual arguments separately, use positional access: $ARGUMENTS[0] for the first, $ARGUMENTS[1] for the second — or the shorthand $0, $1, $2. A command that compares two model versions might use:
Compare $0 and $1. Focus on accuracy, latency, and memory footprint.
Output a structured table with one row per metric.
Running /compare-models models/v1.pkl models/v2.pkl substitutes each argument by position.
A few things worth getting right when writing command templates:
- Be explicit about the output format. If you want a numbered list with severity labels, say so.
- Do not assume Claude knows your project context. If the command depends on a convention (like “all data is in
data/raw/”), state it in the template. - Write the prompt as carefully as you would write it by hand. The command is not magic — it is just a saved prompt.
Dos and don’ts
| Do | Don’t |
|---|---|
| Write commands for workflows you repeat more than twice a week | Create commands for one-off tasks |
Use $ARGUMENTS to make commands reusable | Hardcode specific file paths |
Keep command files in .claude/commands/ and commit them | Store commands in ad-hoc places outside the project |
| Write the prompt as clearly as you’d write it manually | Assume Claude will infer missing context |
| Test the command on a real case before sharing | Share untested commands with the team |
The test-before-sharing rule matters more than it looks. A command that works well on one notebook may produce shallow results on another if the prompt is underspecified. Run it on two or three real cases before committing it as a team standard.