Use when creating or editing a slash command in src/agent-src/commands/ — frontmatter, numbered steps, safety gates — even when the user just says 'add a /command for X'.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add event4u-app/agent-config --skill command-writing --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Command Writing?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/event4u-app-command-writing-agent-config)More formats (shields.io, HTML) on the badges page.
---
model_tier: inherit
name: command-writing
description: "Use when creating or editing a slash command in src/agent-src/commands/ — frontmatter, numbered steps, safety gates — even when the user just says 'add a /command for X'."
domain: process
workspaces:
- agent-config-maintainer
packs:
- meta
---
<!-- cloud_safe: degrade -->
# command-writing
## When to use
* Creating a new slash command in `src/agent-src/commands/{name}.md`
* Rewriting an existing command (not a typo fix)
* Deciding whether a request should be a command at all
* Splitting an oversized command into smaller ones
Do NOT use this skill when:
* The content is a constraint the agent must always honor → use `rule-writing`
* The content is reference knowledge agents cite → use `guideline-writing`
* The content is a triggered workflow invoked by the model → use `skill-writing`
## Command vs skill — critical test
| Intent | Artifact |
|---|---|
| "User types `/foo` to explicitly run this" | **Command** |
| "Agent picks this up from description match" | **Skill** |
A command is **user-invoked** and carries `disable-model-invocation: true`.
A skill is model-invoked via description routing. If both audiences apply,
author as a skill and add a thin command that delegates to it.
**A command must EARN a top-level slot.** Only three justifications qualify:
**flow-entry**, **state-query**, or **product-surface** — everything else is a
skill. Check before authoring: [`command-clusters` § Command justification](../../../docs/contracts/command-clusters.md#command-justification--a-command-must-earn-a-top-level-slot)
([ADR-048](../../../docs/decisions/ADR-048-command-justification-rule.md)).
**Categorize at creation (checklist).** When you author a NEW top-level command,
declare its justification as a `category:` in the frontmatter — the schema enum
is `flow-entry | state-query | product-surface`:
- [ ] **flow-entry** — a daily work starting point the user TYPES to begin
(`work`, `git-commit`, `review-changes`).
- [ ] **state-query** — a read-only check (`agent-status`, `project-health`, `profile`).
- [ ] **product-surface** — a feature started deliberately (`council`, `research`, `roadmap`).
- [ ] **fits none → it is a skill.** Don't add the command; author a skill.
- [ ] **genuinely ambiguous?** Omit `category:` and note why — `category:` is
OPTIONAL (validate-when-present); deferring is intentional, not forgotten.
Sub-commands (`council:debate`) inherit the parent cluster's category — leave
`category:` off them. Why optional + not yet a blocking lint, and what triggers
the upgrade: [`command-category-governance`](../../../docs/contracts/command-category-governance.md).
## Commands ARE Claude skills (projection reality)
Every command in `src/agent-src/commands/{name}.md` is projected
into `.claude/skills/{slug}/SKILL.md` by `scripts/condense.ts`
(`generate_claude_commands`). Nested commands flatten with `-`
(`council/default.md` → `council-default`). Skills and commands share the
**same `.claude/skills/` namespace** — Claude does not distinguish them.
Consequences for authoring:
* The frontmatter `description` is the **routing surface** Claude reads.
Polite or generic phrasing causes undertriggering even with
`disable-model-invocation: true` set, because the in-host command
suggester, fuzzy search, and any tooling that scans `.claude/skills/`
rank by description match.
* `disable-model-invocation: true` blocks **automatic** invocation. It
does NOT remove the command from discovery surfaces. A weak description
means the command is invisible to the suggester even when the user's
intent matches.
* Trigger phrasing must follow the same Iron Law as skill descriptions:
name 2+ trigger classes (domains, symptoms, user phrasing), end with
the `... even when the user just says ...` tail, ≤ 200 chars. See
`skill-writing` § 1b for the canonical before/after.
* The `suggestion.trigger_description` and `suggestion.trigger_context`
blocks are **separate** from the frontmatter `description` — they
drive the in-host suggester (`command-suggestion-policy`), not Claude's
skill router. Both matter, both must be precise.
Bottom line: write the command's `description` as if a skill router will
read it — because one will.
## Procedure
### 0. Run the Drafting Protocol
Creating or materially rewriting a command **must** go through Understand →
Research → Draft from the
[`artifact-drafting-protocol`](../../rules/artifact-drafting-protocol.md) rule.
* **Understand** — what user-facing problem does `/{name}` solve in one
session? What are the inputs, outputs, side effects?
* **Research** — **inspect** `src/agent-src/templates/command.md`,
grep `commands/` for overlap, and **analyze** 1–2 peer commands
(e.g. `create-pr`, `commit`).
* **Draft** — propose frontmatter (`name`, `description`) first, then the
step skeleton. Only fill bodies after both are confirmed.
### 1. Use the template
Canonical source: `src/agent-src/templates/command.md`.
Minimum frontmatter:
```yaml
---
name: {command-name} # must match filename without .md
description: "Short human-readable summary of what /{name} does"
disable-model-invocation: true
skills: [optional-skill-1] # optional — skills this command delegates to
suggestion: # required (road-to-context-aware-command-suggestion Phase 2)
eligible: true # default; set false to opt out of auto-surfacing
trigger_description: "natural-language pattern, comma-separated examples"
trigger_context: "concrete signal — branch name, file pattern, recent tool output"
---
```
Or, when opting out:
```yaml
suggestion:
eligible: false
rationale: "one-line reason this command must be invoked deliberately"
```
Suggestion-block rules (linter-enforced):
* `eligible` is **required** and must be `true` or `false`.
* `eligible: true` → both `trigger_description` and `trigger_context` must be
non-empty (≥ 10 chars each); the linter rejects empty or overly generic
patterns. The suggestion layer never auto-executes; the user always picks.
* `eligible: false` → `rationale` must be non-empty. Use the opt-out for
intentional-only invocations (settings mutations, destructive actions,
package-internal tools, niche maintenance).
* Optional `confidence_floor` (0.0–1.0) and `cooldown` (e.g. `10m`)
override the global settings per command.
Eligibility decisions are tracked in
[`agents/settings/contexts/command-suggestion-eligibility.md`](../../../agents/settings/contexts/command-suggestion-eligibility.md).
Add or revise entries there before changing a command's `suggestion` block.
When iterating on the description, delegate to the
[`description-assist`](../description-assist/SKILL.md) skill — approval-gated,
no silent edits, max two rounds.
### 2. Structure the body
Required sections in this order:
1. `# /{name}` heading + one-line summary
2. **Source of truth note** — works on `src/`, never on
generated directories
3. `## Steps` — numbered sub-headings `### 1.`, `### 2.`, ...
4. Final step **presents findings** and asks the user before destructive
changes (numbered options per `user-interaction` rule)
5. Optional `## Rules` — short, command-specific constraints
### 3. Enforce safety gates
* No auto-apply of destructive actions without user confirmation.
* Every step with side effects (git push, file delete, PR merge) asks first.
* If the command calls external APIs, list required keys / permissions.
* If the command edits agent files, target `src/` only.
### 3b. Path conventions in command body
Body links to guidelines / contracts use the verbatim relative form
(`../../docs/guidelines/...`, `../../docs/contracts/...`); the
condense-time rewriter handles depth. Do not pre-rewrite in source. Do
not write `src/` in any markdown link target — the
file ships into `.augment/commands/` and the prefix breaks consumer
resolution. The only legitimate `src/` strings in a
command file are prose mentions and step instructions about where to
edit (per § 2 above). Canonical reference: `rule-writing` § 3b.
### 4. Enforce the size budget
Normative source: [`size-enforcement`](../../rules/size-enforcement.md) +
`docs/guidelines/agent-infra/size-and-scope.md`.
| Category | Target |
|---|---|
| Ideal | ≤ 120 lines |
| Acceptable | ≤ 200 lines |
| Split signal | > 250 lines |
Commands orchestrate, they do not implement detail. If a step needs a
multi-paragraph explanation, extract it into a skill and call it.
### 5. Validate
* Run `./scripts-run src/scripts/skill_linter src/agent-src/commands/{name}.md`
→ 0 FAIL.
* Run `bash scripts/condense.sh --sync` → regenerates `dist/agent-src/commands/{name}.md`.
* Run `./scripts-run src/scripts/condense --generate-tools` → creates the Claude symlink at
`.claude/skills/{name}/SKILL.md`.
* Run the full CI pipeline locally (see `Taskfile.yml` in this repo for
the script list) — must exit 0 except for tolerated warnings.
### 6. Governance baseline (when introducing a new linter check)
**Advisory, reviewer-checked — no CI gate.** When the same PR adds a
new check to `scripts/skill_linter.ts` (or strengthens an existing
one) such that previously-clean commands now warn, the PR body MUST
record the pre-existing violations on `main` in a Markdown table:
```markdown
### Pre-existing baseline (informational)
| Code | Count on main | Bucket |
|---|---:|---|
| {new_code} | N | (a) genuine fix · (b) accept · (c) check too aggressive |
```
Forward-only: the new check applies to **the file under review** and
to **future** edits. The baseline table is informational so reviewers
can spot intent (fix-now vs. backlog) without diffing the full lint
output. See `agents/evidence/analysis/lint-warning-triage.md` for the
3-bucket reference.
## Output format
1. Complete command file at `src/agent-src/commands/{name}.md`
2. Frontmatter populated, `disable-model-invocation: true` present
3. Linter output showing 0 FAIL
4. Generated Claude symlink verified
## Gotchas
* Forgetting `disable-model-invocation: true` — the model will auto-invoke
the command as if it were a skill.
* Numbered options without a "skip" / "no change" path.
* Steps that silently apply destructive changes — always show summary + ask.
* Referring to `.augment/` paths for editing — source of truth is
`src/`.
* Duplicating another command's workflow instead of delegating via `skills:`.
## Frugality Standards
Apply the [Frugality Charter](../../contexts/contracts/frugality-charter.md)
to every command you author.
**Examples in this artifact:**
- Per the charter's default-terse rule, command output blocks state
the action result, not "Now we will execute…".
- Per the post-action summary suppression, the success path emits
the artifact (PR URL, commit hash) without a wrapping summary.
- Per the cheap-question check, never offer "preview vs. execute"
as a numbered option when the command's role is to execute.
**Pre-save self-check:**
1. Does any command step prescribe a "Let me…" or "Found it" output
line?
2. Does the command default to multi-line summaries when a one-line
outcome suffices?
3. Is a confirmation gate used outside the Iron-Law / Routine /
Contextual taxonomy?
4. Are template placeholders (`{{var}}`) accompanied by setup prose
instead of action prose?
## Do NOT
* Do NOT set `disable-model-invocation: false`
* Do NOT auto-apply destructive actions
* Do NOT inline skill-level detail — delegate
* Do NOT edit `dist/agent-src/`, `.augment/`, or `.claude/` projections
* Do NOT exceed the hard size limit without a waiver
## Cloud Behavior
On cloud surfaces (Claude.ai Web, Skills API) the package's
`scripts/skill_linter.ts`, `scripts/condense.ts`, and the `task`
runner are not available. This skill still applies — but with
prose-only validation:
* Emit the full command file as a copyable Markdown block. Do not
attempt to write it to disk.
* Self-check the frontmatter against the rules below — `name`,
`description`, `disable-model-invocation: true` MUST all be
present.
* Self-check the body shape: numbered steps, explicit safety gates,
no inline skill-level detail.
* Tell the user to save the file under
`src/agent-src/commands/{name}.md` and run
`task sync && task lint-skills` locally before committing.
* Skip every reference to running the linter, condenseor, or
generators yourself — they only run on the user's machine.
## Examples
Good description (trigger-shaped, outcome-focused):
> "Create a GitHub PR with structured description from Jira ticket and code changes"
Bad description (vague, no outcome):
> "PR command"
## The `## Examples` section on a user-facing command
Stated once, here — never repeated per command.
A command whose `visibility` is `visible` or `advanced` carries an
`## Examples` section: **1-3 filled invocations** in a fence, then **exactly
one Why line**. Filled means a real path, a real ticket key, a real flag — a
placeholder teaches nothing about what to type.
The Why line uses one of two literals, `**Why it works:**` or a
`### Why it works` heading, and cites **exactly one** pattern id from
[`prompt-patterns.yml`](../../config/discovery/prompt-patterns.yml):
`outcome-not-steps` · `self-check-loop` · `measurable-target` ·
`give-the-artifact` · `point-at-reference` · `say-the-format`. One id, because
the Why line's job is the part that transfers to a command the reader has not
seen, and a line naming three patterns names none of them.
~~~
## Examples
```
/work fix the failing login test under tests/feature/auth
```
**Why it works:** measurable-target — it names the file instead of the symptom.
~~~
Enforced by `check_command_examples`, forward-only: the 23 in-scope commands as
of 2026-08-24 are grandfathered in a frozen set inside the gate, and a command
**added or promoted into `visible`/`advanced`** after that must comply. Two
things the gate checks that are easy to miss — an invocation must resolve to
the command's own `name:` (a rename otherwise leaves stale examples behind), and
a flag used in an example must be documented in the body **outside** the
section.
## Encode usage policy in the description
Workflow sequencing, preconditions, ID/output provenance ("copy ids verbatim,
never from memory"), a mandatory "why" intent field, and turn-end contracts
belong INSIDE this artifact's description/frontmatter — where they fire at the
decision point — not in always-on prose. See
[`tool-description-as-policy`](../../../docs/guidelines/agent-infra/tool-description-as-policy.md).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!