# Commands and manual-only skills

Read this at step 5. Client facts were checked on 2026-11-06; re-check them at run time.

## When a job gets a command

- Give a job a command when people start it on purpose: a weekly review, an audit before a launch, a pre-meeting brief. The command is a shortcut with known inputs; the skill holds the procedure.
- One command per job. A command that branches into several jobs ("/marketing do X or Y") triggers unpredictably and is hard to test.
- Name it verb-noun or noun-noun, at most three words, lowercase with hyphens: `/pacing-review`, `/utm-audit`. A manual-only skill's command is the skill's own name, team prefix included (`/acme-dedupe-review`); see Claude Code below.
- `argument-hint` shows the required inputs in square brackets: `[spend export] [budget file]`.
- With empty or vague arguments the command asks for the missing inputs by name and never guesses a file, date or account.
- The body routes to the skill and adds only what the command needs (which inputs to ask for, what to hand back). It never copies the procedure.

Failure mode: procedure duplicated in the command. After an edit, the command and the skill disagree and nobody knows which one ran. Detect: a command body longer than about 15 lines, or one with numbered steps. Prevent: the command routes to the skill and the skill holds every step.

## Manual-only versus automatic

- Automatic (the default): the skill also loads when someone asks a matching question without the command. Right for read-only analysis people also ask about ad hoc.
- Manual-only: the skill never loads on its own; only the command or an explicit `$name` or `/name` runs it. Right when the run itself sends, schedules or produces a file someone applies as-is (a merge queue, an Editor import, a message draft), or when it must only run at a chosen moment.
- A manual-only skill's trigger test is different: every automatic run must stay at 0 of 3, and every explicit run must load 3 of 3 (`score-triggers.mjs --manual-only`, plus `--final` for the release round).

## Claude Code

- A command file in `.claude/commands/<name>.md` (subfolders become `/dir:name`) or a skill folder both create `/name`. Same name: the skill wins over the command file.
- Plugin skills are namespaced: `/plugin-name:skill-name`.
- Manual-only without touching the frontmatter: set the skill to `"user-invocable-only"` under `skillOverrides` in the project settings file, which removes it from the model's listing while keeping it in the `/` menu. The `/skills` menu stores that choice in `.claude/settings.local.json`, which is personal, so put the setting in the shared project settings and verify it is honoured on your version. `skillOverrides` does not apply to plugin skills; manage those through `/plugin`.
- Manual-only in the frontmatter: `disable-model-invocation: true`. It removes the description from context and blocks automatic use, but it is not a spec key, so chat-app and API uploads fail on it.
- For a manual-only skill, make the skill's own `/name` the command, and name the skill for the job (`acme-dedupe-review`, typed `/acme-dedupe-review`), instead of adding a routing command file. A file that says "Use the X skill" relies on the model calling the Skill tool. Claude Code blocks that call for a `disable-model-invocation: true` skill and has the agent suggest that the user run `/name` (checked 2026-11-06); the docs do not say whether a `user-invocable-only` override blocks it too. Keep a routing file only after it passes the explicit-run test below.
- Arguments: `$ARGUMENTS` holds everything after the command; `$ARGUMENTS[N]` and `$N` are 0-based positions; named placeholders come from an `arguments` key. Quote multi-word values when invoking.
- A `/name` typed at the start of a message runs the skill. Typed after other text, it does not run directly; it counts as permission the model may use while answering.

## Codex

- Explicit use: `$skill-name`, or pick it from `/skills`.
- Manual-only: add `agents/openai.yaml` to the skill folder with `policy.allow_implicit_invocation: false`. The SKILL.md frontmatter stays portable.
- Custom prompts (`~/.codex/prompts/`, `/prompts:name`) are deprecated in favour of skills, are not shared through the repository, and used different placeholders (`$1` to `$9`, uppercase named placeholders, `$$` for a literal dollar sign). Convert any you find into a skill plus a policy file.

## Cursor

- Type `/` and pick the skill to attach it to one message; use it as a Custom Mode to keep it for a session.
- Manual-only: `disable-model-invocation: true` in the frontmatter; the skill is then used only when typed. Lint the canonical folder with `--profile team --allow-keys disable-model-invocation`, which keeps the section and style checks and passes that one key. If the same skill must also upload to a chat app or API, the uploaded copy drops the key and passes the default portable profile.
- `/migrate-to-skills` converts eligible commands and dynamic rules into skills. Check the converted frontmatter and rerun the trigger tests afterwards.

## The pack's command file format, line by line

[../assets/command-template.md](../assets/command-template.md) has the blank version. Each line has one job:

1. Frontmatter `description`: one line, under about 100 characters, saying what the command produces.
2. Frontmatter `argument-hint`: the required inputs in square brackets. Quote the value when it has more than one bracket group (`"[spend export] [budget file]"`): unquoted, YAML reads the second group as an error, and a frontmatter that does not parse loses its metadata in one client (lint L02 catches this in SKILL.md).
3. Route line: "Use the [skill] skill for this task: $ARGUMENTS".
4. Context line: read `martech-context/` first if it exists, naming the files this skill needs.
5. Empty-argument line: if `$ARGUMENTS` is empty or vague, ask for the named inputs.
6. Extra asks, only when the skill needs something the user rarely volunteers (expected IDs, a date range, the approver).
7. Procedure line: follow the skill's procedure in order and finish with its acceptance checks.
8. Safety lines: what stays read-only, what needs approval, what is never asked for.
9. Hand-back line: the outputs the user gets.
10. A blank line, then the install line every command file in the pack ends with: how a colleague installs the skill when it is missing.

Keep the body under about 15 lines. Anything longer is procedure and belongs in the skill.

## Testing a command

- Empty arguments: the command asks for each required input by name. Pass: no file, date or account is guessed.
- Full arguments: the skill loads and finishes with its acceptance checks.
- Manual-only skills: the 20 trigger prompts without the command load the skill 0 of 3 times each; the command loads it 3 of 3. Score the final round with `score-triggers.mjs --manual-only --final`, which fails when no explicit rows were recorded.
- Explicit-run test: run the command 3 times in fresh sessions and check that the skill loaded each time. In a Claude Code `claude -p` run, a `/name` at the start of the prompt is expanded before the model runs, so look for a line only the draft contains rather than a Skill tool call ([testing.md](testing.md)).
- Each client in use lists the command once, from a fresh clone.
