a command, not a skill

A custom command is the simpler of the two mechanisms covered in Claude Code CLI Basics and Claude Skills: a single markdown file, invoked only when you explicitly type its /name — it never fires on its own, and it can't bundle scripts or reference files the way a skill can. That's the whole trade: a skill is worth the extra structure when you want Claude to reach for it automatically, or it needs supporting files; a command is worth building the moment you catch yourself typing the same instruction, with the same wording, more than twice.

where they live

LocationApplies to
~/.claude/commands/<name>.mdevery project on your machine
.claude/commands/<name>.mdthis project only — commit it so the team gets the same command
A project-level command with the same name as a personal one wins, the same override order as everything else in settings.json.

the file format

Frontmatter describes the command; the body is the literal prompt sent to Claude when it runs.

---
description: Draft a PR title and description from the current branch's diff.
argument-hint: [base-branch]
allowed-tools: Bash(git diff:*), Bash(git log:*)
---

## Diff against ${1:-main}

!`git diff ${1:-main}...HEAD`

## Instructions

Write a PR title (one line) and a description with a summary and a
bulleted list of notable changes. Skip anything trivial (formatting-only
diffs, generated files).
            
Save that as .claude/commands/pr-description.md and /pr-description (or /pr-description develop to diff against a different base) becomes available in every session in this project, listed in /help with the description above.

arguments

$ARGUMENTS captures everything typed after the command name as one string; $1, $2, and so on capture it positionally, so a command that takes a clear, fixed set of inputs can name each one instead of parsing a blob of text.
  • /review-file $ARGUMENTS/review-file src/auth.py passes the whole rest of the line through
  • /new-endpoint $1 $2/new-endpoint users GET gives the body $1 = users, $2 = GET to reference separately

pulling in live context

Two prefixes bring real content into the prompt instead of describing it in prose: !`shell command` runs the command and inlines its output before Claude ever sees the request (the PR-description example above uses this for the diff), and @path/to/file inlines a specific file's contents. Both resolve once, at the moment you invoke the command — not something Claude decides to do partway through.

organizing a growing set

Once there are more than a handful, subdirectories under .claude/commands/ group them — .claude/commands/git/changelog.md shows up namespaced under git in /help rather than competing for a single flat list of names with everything else in the project.

related topics

Claude Code Tips & Tricks — where a well-built command library actually pays off.
Claude Skills — the richer, auto-triggered version of the same idea.
Claude Code CLI Basics — the built-in slash commands these sit alongside.

reference

code.claude.com — slash commands