Install
$ agentstack add skill-anilcancakir-claude-code-command-creator ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
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 4.8. The same shape works for Sonnet 5 and Haiku 4.5 at lower effort levels.
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 throughac: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
.mdvs 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 4.8 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//SKILL.md, the user-global equivalents, managed dirs, and plugin paths (/commands/,/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,$(perargumentSubstitution.ts);${CLAUDE_SKILL_DIR}(only when the file is in skill-directory format);${CLAUDE_SESSION_ID};${CLAUDE_EFFORT}. - Shell injection. Inline `
\!and fenced\``\! ... \`blocks are executed with BashTool (or PowerShellTool whenshell: 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: `/.claude/skills//SKILL.md`
│ (or `/skills//SKILL.md` for plugins). The `${CLAUDE_SKILL_DIR}` token resolves.
└── NO → use the flat command file: `/.claude/commands/.md`
(or `/commands/.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):
$ARGUMENTSsubstitutes the raw string the user typed, verbatim.$ARGUMENTS[N]and$Nsubstitute the Nth shell-quoted token, 0-indexed./cmd "hello world" fooproduces$0 = "hello world"and$1 = "foo".$only substitutes when the name appears in thearguments:frontmatter list. Without that frontmatter,$myvarstays literal in the body.- Named arguments cannot be digits (
arguments: [0, 1]is rejected, since those would conflict with$0/$1shorthand). - If the body contains no
$ARGUMENTSplaceholder and the user typed arguments, the loader appends\n\nARGUMENTS: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: `
\!is replaced with the command's stdout. The exact CC regex is(?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:
#
## Phase 1: Context
**Goal**: Read the state needed to proceed.
**Actions**:
1.
2.
## Phase 2: Analyze / Plan
**Goal**: Decide what to do based on Phase 1.
**Actions**:
1.
2.
## 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**:
**Success criterion**:
## Phase 5: Report
**Goal**: Tell the user what happened.
, pushed to /"
## Error Handling
- ****:
- ****:
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 (
--interactiveflag) 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/.md or /commands/.md | Not substituted (no baseDir) | None (the body is the whole command) | Simple command with no references or scripts to bundle | | Skill-directory | .claude/skills//SKILL.md or /skills//SKILL.md | Resolves to the skill's directory | references/, scripts/, assets/ work | The command needs supporting files |
A flat command file is the simpler choice and matches the "command" mental model best. Use it for context-gathering recipes, simple actions, single-script orchestrations. If the command grows references or scripts, migrate to the skill-directory format (the slash invocation stays the same).
Plugin-only path substitutions (the loader behavior is different for plugins than for user/project skills):
${CLAUDE_PLUGIN_ROOT}is substituted in the body of every plugin command and plugin skill (perutils/plugins/loadPluginCommands.ts:339-343). Resolves to the plugin's root directory. Use this when a flat plugin command at/commands/.mdneeds to reference a plugin-level file: write${CLAUDE_PLUGIN_ROOT}/templates/foo.md.${CLAUDE_SKILL_DIR}is additionally substituted for plugin skills in skill-directory format (per the same loader,isSkillModebranch), pointing at the skill's subdirectory inside the plugin. Use this when files live inside the skill's own folder.${user_config.X}substitutes per-plugin user config values. Sensitive keys resolve to a placeholder. Set up via plugin manifestuserConfigfield.- For non-plugin skills (user, project, managed at
.claude/skills//SKILL.md), only${CLAUDE_SKILL_DIR}substitutes;${CLAUDE_PLUGIN_ROOT}stays literal.
Both ${CLAUDE_PLUGIN_ROOT} and ${CLAUDE_SKILL_DIR} are ALSO available in hook commands, MCP configs, and LSP configs (utils/hooks.ts:818, utils/plugins/mcpPluginIntegration.ts:462, utils/plugins/lspPluginIntegration.ts:226).
Workflow
Walk these in order. Each step assumes the previous resolved.
1. Capture intent
Always-needed questions:
- What does the command do, in one sentence?
- What input does it take (free-form, positional, named, none)?
- What side effects does it have (read-only, writes locally, writes remotely, sends messages)?
- Project, user, or plugin scope?
Conditional questions (ask only when the answer to a previous question implies the need):
- Approval gates before specific side effects? Ask when side effects are irreversible.
- Allowed-tools? Ask when the body fires repeated bash commands the user would otherwise have to approve one-by-one.
- Auto mode vs interactive default? Ask when the command will be used in both human and automated contexts.
Do not pre-ask about every optional field; pull each in only when intent makes it relevant.
2. Pick storage format
Per the table above: flat for simple, skill-directory if bundled files are needed.
3. Design arguments
Decide free-form, positional, or named. Set argument-hint so autocomplete reflects the shape. Plan flag detection inside the body if the command needs --interactive, --dry-run, --skip-X. See ${CLAUDE_SKILL_DIR}/references/argument-design.md for copy-paste flag-detection snippets.
4. Draft the frontmatter
Minimal:
---
description:
when_to_use:
---
Add argument-hint, arguments, disable-model-invocation, allowed-tools only when the command actually needs them. The "Frontmatter: minimal by default" table above lists every field and the condition that justifies it.
5. Write the body
Body structure follows the phase template above. For each phase, write the Goal, then Actions with explicit steps. Place shell injection (` \!
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: anilcancakir
- Source: anilcancakir/claude-code
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.