| name | command-creator |
| description | Authors and audits Claude Code slash commands, covering argument design (full-argument, positional, and named forms), shell injection for live context, phase-structured bodies, approval gates before side effects, and the flat-file versus skill-directory choice. Use when a `/name` command is being built or fixed. Triggers on "create a slash command", "build a /command", "add a plugin command", "shell injection in a command", "argument design", "fix this command". Use even when the user never says "command" but wants `/name` invocation with arguments or a context-gathering recipe. Pair with `ac:skill-creator` for file shape and `ac:prompt-writer` for the body. |
| when_to_use | Creating, editing, auditing, or debugging any Claude Code slash command. |
Command Creator
You are about to write or edit a Claude Code slash command another Claude will execute. A command is a markdown file that becomes a /name invocation: when the user types /foo bar baz, Claude Code reads the file, substitutes $ARGUMENTS with bar baz, runs shell injection blocks, and injects the resulting prompt as a single user message. The model then executes the body as the next turn.
This skill is the playbook for designing arguments, shell-injection-driven context gathering, phase-based body structure, approval gates, and the storage-format choice. Target is Opus 5. The same shape works for Sonnet 5 at lower cost and for Haiku 4.5, which supports no effort parameter.
Three jobs, not one
Writing a slash command splits into three tasks. Conflating them is the most common authoring mistake.
- Surrounding skill shape. Frontmatter fields, scope (project/user/plugin/managed), invocation control (
disable-model-invocation, user-invocable), paths:, allowed-tools, model, effort. Same rules as any skill. Route through ac:skill-creator (its body, references, and pre-flight checklist all apply).
- Command-specific shape. Argument design, shell injection for context gathering, phase-based body structure, approval gates, storage format (flat
.md vs skill-directory). This file teaches that.
- Body content. The markdown the model reads when the command fires. This is a prompt. Route through
ac:prompt-writer (architecture, snippets, anti-patterns, Opus 5 tuning).
A great command body in the wrong shape never gets used. A modest body in the right shape with crisp arguments and well-placed approval gates gets used every day.
What a command actually is, mechanically
Slash commands and skills share the same loader in Claude Code. The distinction is one of file shape and intended use, not runtime mechanics. Source of truth: loadSkillsDir.ts, utils/markdownConfigLoader.ts, utils/argumentSubstitution.ts, utils/promptShellExecution.ts in the CC source.
The lifecycle:
- Discovery. At session start, Claude Code scans for markdown under
.claude/commands/, .claude/skills/<name>/SKILL.md, the user-global equivalents, managed dirs, and plugin paths (<plugin>/commands/, <plugin>/skills/). It does this via ripgrep on *.md.
- Parsing. Each file's YAML frontmatter is parsed; metadata (
description, argument-hint, allowed-tools, etc.) is registered.
- Invocation. User types
/name args (or the model invokes via the Skill tool when allowed). Claude Code locates the file, reads it again, and runs the preprocessor.
- Substitution. Tokens in the body are replaced:
$ARGUMENTS, $ARGUMENTS[N], $N, $<name> (per argumentSubstitution.ts); ${CLAUDE_SKILL_DIR} (only when the file is in skill-directory format); ${CLAUDE_SESSION_ID}; ${CLAUDE_EFFORT}.
- Shell injection. Inline
\!`<cmd>` and fenced \```\! ... \``` blocks are executed with BashTool (or PowerShellTool when shell: powershell). Each match is replaced with the command's stdout. Permissions still apply; deny rules still block.
- Injection into conversation. The fully rendered body enters the conversation as a single user message and stays for the rest of the session. Auto-compact preserves the first 5,000 tokens of each invoked command across summaries.
- Execution. The model reads the rendered body and performs the work, including any subsequent tool calls the body asks for.
The model never sees the raw command syntax, only the post-substitution prompt with shell output already inlined.
Decision flow
Route by the user's request.
Is a slash command the right tool at all?
├── Single fact, no action → CLAUDE.md note, route through `ac:claude-md-rules-creator`. Not a command.
├── Reference content for the model (conventions, style) → reference skill, route through `ac:skill-creator`. Not a command.
├── Deterministic enforcement (must run on every edit) → hook, route through `update-config`. Not a command.
├── Custom subagent (isolated worker the orchestrator delegates to) → route through `agent-creator` if available.
└── User-driven slash invocation with arguments / side effects / context gathering → COMMAND, continue.
Does the command need bundled files (references, scripts, assets) the body points to?
├── YES → use the skill-directory format: `<scope>/.claude/skills/<name>/SKILL.md`
│ (or `<plugin>/skills/<name>/SKILL.md` for plugins). The `${CLAUDE_SKILL_DIR}` token resolves.
└── NO → use the flat command file: `<scope>/.claude/commands/<name>.md`
(or `<plugin>/commands/<name>.md` for plugins). Simpler, no `${CLAUDE_SKILL_DIR}` substitution.
Is this a fix or audit of an existing command?
├── YES → `${CLAUDE_SKILL_DIR}/references/anti-patterns.md` first, then specific reference (argument-design,
│ shell-injection, or phase-structure) as the symptom dictates.
└── NO → walk the Workflow below.
For everything outside command-specific concerns (frontmatter fields, scope, paths, hooks, etc.), defer to /ac:skill-creator rather than duplicating that material here.
Frontmatter: minimal by default
A working command needs only description. Everything else is opt-in. Modern Claude Code merged commands into skills, so command frontmatter accepts the same fields as a skill (see ${CLAUDE_SKILL_DIR}/references/command-vs-skill.md for the differences between the two file shapes).
Command-specific fields most often used:
| Field | Required? | When to set |
|---|
description | recommended | always; this is the trigger surface |
argument-hint | optional | the command takes positional arguments and you want autocomplete to hint at them |
arguments | optional | the command takes input and you want named-positional substitutions (e.g., $pr_number instead of $0) |
disable-model-invocation | optional | the command has side effects you want the user to control (deploy, commit, send-message); this is the common command default |
allowed-tools | optional | the body fires specific tool calls (Bash(gh:*), Bash(git commit:*)) you want pre-approved during the run |
shell | optional | the shell injection blocks should run via PowerShell on Windows (CLAUDE_CODE_USE_POWERSHELL_TOOL=1 required) |
effort | optional | the command needs more or less reasoning budget than the session default |
Fields you almost never need on a command: user-invocable: false (commands are user-driven by nature), context: fork (commands usually need to steer mid-process), paths: (commands are typed, not auto-loaded by file).
Skip everything else unless you can name the specific condition that requires it. Full per-field reference: invoke /ac:skill-creator and consult its frontmatter.md.
Escape convention used in this documentation. This SKILL.md is itself a skill body that the Claude Code loader preprocesses. Any literal full-arguments token (a plain dollar sign followed by ARGUMENTS), a literal indexed shorthand (a dollar sign followed by a digit), or the skill-directory and session-id tokens would be substituted on every invocation, corrupting the documentation. To prevent that, the docs below render those tokens with the HTML entity $ standing in for the dollar sign. In your own command body, drop the entity and write a plain dollar sign.
Argument design
A command's argument shape is the contract with the user. Get it right before writing the body.
Three shapes:
| Shape | Frontmatter | Body uses | When to pick |
|---|
| Free-form | (none; just write $ARGUMENTS in body) | $ARGUMENTS (full string as typed) | The command takes a sentence or query: /deep-research how does auth work? |
| Positional | argument-hint: "[arg1] [arg2]" | $0, $1, or $ARGUMENTS[N] | The command takes structured positional inputs: /migrate-component SearchBar React Vue |
| Named | arguments: [pr_number, target_branch] | $pr_number, $target_branch | The command takes structured inputs that read better with names: /cherry-pick 123 release |
Argument parsing rules (from argumentSubstitution.ts):
$ARGUMENTS substitutes the raw string the user typed, verbatim.
$ARGUMENTS[N] and $N substitute the Nth shell-quoted token, 0-indexed. /cmd "hello world" foo produces $0 = "hello world" and $1 = "foo".
$<name> only substitutes when the name appears in the arguments: frontmatter list. Without that frontmatter, $myvar stays literal in the body.
- Named arguments cannot be digits (
arguments: [0, 1] is rejected, since those would conflict with $0/$1 shorthand).
- If the body contains no
$ARGUMENTS placeholder and the user typed arguments, the loader appends \n\nARGUMENTS: <input> to the end of the body. Treat that as a fallback, not a design.
For flag detection (--interactive, --dry-run, --skip-X), parse $ARGUMENTS inside the body using AskUserQuestion or simple string checks. There is no built-in flag parser; the body decides. Detail and copy-paste patterns: ${CLAUDE_SKILL_DIR}/references/argument-design.md.
Shell injection (dynamic context)
The most distinctive feature of command bodies is shell injection: pre-execution of shell commands whose output is inlined into the prompt before the model reads anything. This is the canonical pattern for grounding a command in live state (git status, PR diff, server status, file contents) rather than guessing.
Two forms:
- Inline:
\!`<cmd>` is replaced with the command's stdout. The exact CC regex is (?<=^|\s)!([^]+)/gm`; the inline form requires whitespace or start-of-line before the bang.
- Fenced:
```\! opens a multi-line block; everything until the closing ``` is run as a single shell script and replaced with its output. CC regex: \``!\s*\n?([\s\S]*?)\n?```/g`.
(The docs above use \! to keep this SKILL.md itself from triggering the preprocessor. In your command, write a plain !.)
Canonical pattern from the built-in /commit command (CC source commands/commit.ts). The example below uses \! to keep this very SKILL.md from triggering the preprocessor when documenting it; in your real command body, write a plain !:
## Context
- Current git status: \!`git status`
- Current git diff (staged and unstaged changes): \!`git diff HEAD`
- Current branch: \!`git branch --show-current`
- Recent commits: \!`git log --oneline -10`
## Your task
Based on the changes above, create a single git commit...
When /commit runs, each inline injection token is replaced with that command's output before the model sees the prompt. The model gets the real diff, branch, and history inlined; it never executes those git commands itself.
Critical caveats:
- Inline injection is preprocessing, not a tool call. The user does not see the commands run; only the rendered output appears in context.
- Each shell command goes through the normal permission flow.
allowed-tools patterns are auto-applied during the injection so the user is not prompted mid-render. Deny rules still block.
- MCP-loaded commands cannot run shell injection (remote and untrusted);
${CLAUDE_SKILL_DIR} is meaningless for MCP commands too.
disableSkillShellExecution: true in settings disables injection for user/project/plugin/--add-dir sources. Bundled and managed commands are unaffected.
- The footgun: if you paste a literal
\!`<cmd>` or \```\! ... \``` block into a command body as a documentation example, it will execute on every invocation. To document the syntax without executing, escape the bang as \!. The backslash breaks the inline regex's lookbehind and the fenced regex's literal-start match.
Full security model, performance notes (the inline scan is gated on a substring check), and 8 copy-paste patterns for common context-gathering recipes: ${CLAUDE_SKILL_DIR}/references/shell-injection.md.
Body structure: phase-based workflows
Command bodies are usually multi-phase workflows: a context-gathering phase, an analysis or research phase, an approval phase, an execution phase, and a verification phase. The phase-based structure helps the model orchestrate without losing the thread.
Standard shape:
# <Command Title>
<One-line statement of what the command achieves for the user.>
## Phase 1: Context
**Goal**: Read the state needed to proceed.
**Actions**:
1. <action with `\!`shell command`` for live data, or explicit step>
2. <action>
## Phase 2: Analyze / Plan
**Goal**: Decide what to do based on Phase 1.
**Actions**:
1. <decision logic>
2. <branching: if X, do A; if Y, do B>
## Phase 3: Approve (skip in auto mode)
**Goal**: Confirm with the user before side effects.
Use AskUserQuestion with concrete options. Auto mode (default): proceed.
Interactive mode (`--interactive` in `$ARGUMENTS`): prompt.
## Phase 4: Execute
**Goal**: Perform the action.
**Actions**: <specific commands, tool calls, file edits>
**Success criterion**: <observable signal the step worked>
## Phase 5: Report
**Goal**: Tell the user what happened.
<One-line result format, e.g., "Committed: <hash> <msg>, pushed to <remote>/<branch>"
## Error Handling
- **<error case>**: <what to do>
- **<another case>**: <what to do>
Conventions worth honoring:
- Each phase has Goal + Actions + (when consequential) Success criterion. The model needs to know when each phase is done.
- Place approval gates (AskUserQuestion) directly before irreversible operations: writing to remote, sending messages, destructive git operations, dropping data.
- Have an "auto mode" default (no prompts) and an interactive escape (
--interactive flag) so the same command serves both human-driven and pipeline use.
- Lead with one-paragraph Identity or Goal if the persona matters.
- End with an Error Handling section listing the failure modes you can name and what to do for each.
- Sub-numbered steps (3a, 3b) signal steps that can run in parallel.
Detail and three worked phase structures (auto-mode workflow, interview-driven command, context-gathering report): ${CLAUDE_SKILL_DIR}/references/phase-structure.md.
Storage format: flat .md vs skill-directory
Two storage paths produce the same /name slash command but differ in capability:
| Format | Path | ${CLAUDE_SKILL_DIR} | Bundled files | Use when |
|---|
| Flat | .claude/commands/<name>.md or <plugin>/commands/<name>.md | Not substituted (no baseDir) | None (the body is the whole command) | Simple command with no references or scripts to bundle |