Home/Blog/How to Write a Claude Code Slash Command from a Job
Blog

How to Write a Claude Code Slash Command from a Job

P
promptstudio
How to Write a Claude Code Slash Command from a Job

Claude Code slash commands fail when the model invents flags. You get --yes-all, a fake claude pr subcommand, or a hook that does not exist. You drop the markdown into .claude/commands/ and the command no-ops, or it runs something you never typed. The job is a command file for work you already do, with arguments the CLI actually accepts.

The matching generator is the Claude Code Slash Command from a Repeatable Job prompt. Browse related cards in the PromptDig library (Browse more prompts). When a filled run survives, share the version you actually use (Share a prompt).

Name the job, then name only the flags you have

Paste the job as you already run it: "turn merged PRs since the last tag into an internal changelog." Tool and version is Claude Code as you installed it. If you cannot prove a version, write unknown. Do not let the generator pick latest.

Inputs and outputs: what the human pastes (git log, a tag name) and what the command must emit (markdown changelog, a NOT IN INPUTS list). Guardrails: do not invent CLI flags, do not invent repo scripts, do not commit unless the job already does. Eval: five cases you will actually try, including empty log and a missing tag.

If the job uses a script that is not in the tree, that script is a gap, not a new file the command should write. Forbidden should quote the flags you refuse, not a vibe about being careful.

The artifact is a command markdown file, not a second product

Claude Code custom commands are markdown in .claude/commands/ with a name that becomes /name. The generator should output that file body: short description, argument placeholders, steps, refuse list. It should not invent a marketplace plugin, a native binary, or flags copied from some other CLI.

Quote forbidden items: --force, silent network, rewriting CLAUDE.md, committing. If Inputs did not name a flag, it does not appear. "Everyone uses skip-permissions" is not a flag you have.

Retry belongs only if Eval named a retry. Do not invent exponential backoff for a slash command that runs once. Version-lock: if Inputs named a Claude Code version, repeat it. Else unknown.

De-identify any pasted logs. Strip emails, tokens, and internal hostnames before the model sees them. This is repo automation, not legal advice.

Five eval cases and a gaps list that stays honest

Eval cases should be boring:
1. Happy path with a real sample log you pasted.
2. Empty git log.
3. Missing since-tag.
4. User asks the command to invent a flag.
5. User asks it to commit.

Each case needs an expected refuse or an expected heading. Do not mint passing scores or "95 percent reliable" claims. Gaps: missing version, missing argument names you actually use, missing install path, missing whether stdin or $ARGUMENTS, missing the refuse list from AGENTS.md. Five bullets is enough. The command ships when those gaps are filled by you, not when the markdown sounds complete.

A worked pass: weekly changelog, no fake flags

Job: internal changelog from merged PRs since a tag. IO: $ARGUMENTS is the tag; output is markdown. Guardrails: no commit, no push, no invented git changelog subcommand. Forbidden: --force, fake view counts of the changelog.

Decomposition: read log, group by PR title if present, list unknowns, stop. Artifact: .claude/commands/changelog.md that restates those rules. Retry: none, because Eval did not ask for retry. Eval cases include "tag not in repo" as a refuse, not as a guessed previous tag.

If the model adds claude -p --allowedTools Bash flags you did not name, cut them. The command file is the product. The CLI invocation stays yours. If AGENTS.md already forbids invented scripts, copy that sentence into the command. Paraphrase is how a no-commit rule becomes "prefer not to commit."

Fill the card, then run

Paste the job you already run. Leave flags blank if you do not have them. Then run the card and reject any invented CLI.

Tool and version: [Tool]
Job to automate: [Job]
Inputs/outputs: [IO]
Guardrails: [Guardrails]
Eval or retry needs: [Eval]
Forbidden: [Forbidden]

A slash command is a door into a job you already trust. When a run refuses a fake flag and keeps the refuse list word-for-word, share that filled card. That is the command other repos should copy, not the one that invented --yes-all.