- name
- agent-create
- description
- Create new Claude Code sub-agent files following the official schema and token-efficiency budgets. Handles both review agents (JSON output, read-only tools, ≤ 40-line body) and team agents (prose output, action tools, ≤ 75-line body). Use when the user says "add an agent", "create a reviewer for X", "new team agent for Y", or when /agent-add is invoked. Validates against /plugin-audit before writing. Updates the agent registry and plugin CLAUDE.md after success.
- role
- worker
- user-invocable
- true
# Agent Create
Automates production of Claude Code sub-agent files that pass schema validation
and stay within token-efficiency budgets. For conventions, anti-patterns, and
registration checklists, see `skills/agent-skill-authoring/SKILL.md`.
## Constraints
- Do not write any file until validation passes and the user confirms the draft
- Name validation is a hard gate — exit immediately if the name is invalid
- Never include `hooks`, `mcpServers`, or `permissionMode` without explicit user
confirmation after the plugin warning
- Body line budgets are hard limits enforced at generation time; trim content is
shown to the user before any file is written
- Registry and CLAUDE.md updates are append-only; never edit existing rows
---
## Step 0 — Resolve Target Plugin
Determine `$PLUGIN` (the target plugin directory) and `$NAME` (the plugin's
`name` field from `.claude-plugin/plugin.json`):
1. If `--plugin <dir>` was passed, use that directory as `$PLUGIN`.
2. Else if exactly one `plugins/*/` directory exists in the repo root, use it.
3. Else if the current working directory is inside a directory containing
`.claude-plugin/plugin.json`, use that directory.
4. Else ask: `Which plugin directory should the agent be added to?`
Read `$PLUGIN/.claude-plugin/plugin.json` and extract the `name` field as
`$NAME`. All subsequent path references use `$PLUGIN/` and `$NAME`.
---
## Step 1 — Parse Arguments
Accept these inputs (from arguments or interactive prompts):
| Input | Required | Notes |
|-------|----------|-------|
| `name` | yes | file stem of the new agent |
| `type` | yes | `review` or `team` |
| `description` | yes | one-line summary for frontmatter |
| `tools` | no | comma-separated tool list |
| `--plugin <dir>` | no | target plugin directory (resolved in Step 0) |
| `--model sonnet\|opus\|haiku\|fable\|inherit\|<full model ID>` | no | model the agent runs on; a value is suggested and confirmed in Step 5 if omitted |
| `--effort low\|medium\|high\|xhigh\|max` | no | reasoning effort for this agent; a value is suggested and confirmed in Step 5 if omitted |
| `--memory user\|project\|local` | no | persistent memory scope; omitted unless specified |
| `--isolation worktree` | no | run in an isolated git worktree; omitted unless specified |
| `--color red\|blue\|green\|yellow\|purple\|orange\|pink\|cyan` | no | display color in the task list/transcript; omitted unless specified |
| `--max-turns <int>` | no | cap on agentic turns before the subagent stops; omitted unless specified |
| `--background true\|false` | no | always run as a background task; omitted unless specified |
| `--skills <name1,name2,...>` | no | skill names to preload into context at startup; omitted unless specified |
| `--context diff-only\|full-file\|project-structure` | no | sets `Context needs:` field in review body |
| `--lang <exts>` | no | adds language scope line to review body (e.g. `Scope: .ts, .tsx files only`) |
| `--dry` | no | display generated content without writing file or updating registry |
Agents declare the native Claude Code sub-agent contract — `model:` (an alias,
a full model ID, or `inherit`) and `effort:` (a reasoning-effort level),
resolved by the harness itself at dispatch, no plugin-owned resolution step.
`${CLAUDE_PLUGIN_ROOT}/knowledge/agent-contract.json` is the single source of
truth for every field's valid values.
**Reject an invalid `--model` or `--effort` value.** If either flag is given
and doesn't match `agent-contract.json`'s enum for that field, stop and emit
the valid set: `model`: `sonnet|opus|haiku|fable|inherit` (or a full model
ID); `effort`: `low|medium|high|xhigh|max`. Example:
`Invalid effort 'frontier'. Valid values: low, medium, high, xhigh, max.`
**Reject an invalid `--memory`/`--isolation`/`--background` value** the same
way, against that field's enum in `agent-contract.json`. **`--color` is
validated against only the 4 mechanically-valid colors**
(`purple|green|yellow|cyan`, ADR 0027) — not the contract's full 8-color
enum — because Step 5 below suggests a value the fleet's color test gate
(`tests/agents/test_agent_fleet_conventions.py`) checks by exact equality
with no escape valve; a `--color` value from the other 4 contract colors
(`red|blue|orange|pink`) would pass this validation and then fail that gate.
`--max-turns` must be a positive integer; `--skills` entries are not
validated against a fixed set (skill names are plugin-specific) but each
must resolve to a real `$PLUGIN/skills/<name>/SKILL.md` — warn (not fail) if
one doesn't.
---
## Step 2 — Validate Name (hard gate)
The name must match `^[a-z][a-z0-9-]*$` exactly.
If it does not:
1. Emit: `Name must match ^[a-z][a-z0-9-]*$ — use lowercase letters, digits, and hyphens only`
2. Compute a kebab-case correction:
- Lowercase all characters
- Replace runs of non-alphanumeric characters with a single hyphen
- Strip leading/trailing hyphens
- If result starts with a digit: strip leading digits and any adjacent hyphens from the front; if the result is then valid, use it; if empty or still invalid, skip the suggestion
3. If a valid correction exists, emit: `Did you mean: <corrected-name>?`
4. **Stop immediately. Do not write any file.**
---
## Step 3 — Detect Agent Type
If `type` was not provided:
- Scan `description` for keywords:
- `review`, `audit`, `check`, `validate`, `detect`, `scan`, `lint` → infer `review`
- `engineer`, `architect`, `manager`, `writer`, `planner`, `designer`, `specialist` → infer `team`
- If inference is confident, state the inferred type and continue
- If ambiguous or no keywords match, ask: `Agent type: review or team?`
---
## Step 4 — Prompt for Missing Tools
If `tools` was not provided, emit exactly:
```
Which tools does this agent need?
Read, Grep, Glob (read-only) | add Edit, Write (file changes) | add Bash (shell) | add Skill (skill invocation) | add Agent (spawn subagents)
```
Wait for the user's selection before continuing.
If tools were provided, validate each against known Claude Code tool names
(`Read`, `Grep`, `Glob`, `Bash`, `Edit`, `Write`, `Agent`, `Skill`,
`WebFetch`, `WebSearch`, `NotebookRead`, `NotebookEdit`). Flag unknown names
as a warning (not an error — custom tools are allowed).
---
## Step 5 — Suggest and Confirm Model/Effort/Color/Skills/Memory; Apply the Tools Default
`model`, `effort`, and `color` are never silently defaulted — always
suggested, then confirmed with the user:
1. If `--model` was not provided, suggest by type: review→`haiku`,
team→`sonnet`. If `--effort` was not provided, suggest `high` for either
type.
2. Emit: `Suggested model: <model>, effort: <effort> — accept? (yes/change)`
3. On `yes`: use the suggested values and continue.
4. On `change`: ask `Model?` and/or `Effort?` for whichever the user wants to
change, validate each against `agent-contract.json`'s enum (the rejection
rule in Step 1), then continue with the confirmed values.
If the user passed `--model` and/or `--effort` explicitly in Step 1, skip
this prompt for that field entirely — an explicit flag is already a
decision, not something to re-confirm.
**Color** follows the same suggest-and-confirm shape, computed from the
mechanical rule (ADR 0027): `Agent` in the resolved `tools:` → `purple`;
else `Edit`/`Write` in `tools:` → `yellow`; else the name ends `-review` or
starts `plan-review-` → `green`; else → `cyan`.
1. If `--color` was not provided, compute the suggestion from the rule above
using the tools resolved in Step 4/this step and the agent's `name`.
2. Emit: `Suggested color: <color> — accept? (yes/change)`.
3. On `yes`: use the suggested value and continue.
4. On `change`: ask `Color?`, validate against the same 4-color set the
Step 1 rejection rule uses (`purple|green|yellow|cyan` — not the
contract's full 8-color enum, since the fleet's color test gate checks
exact equality with the rule-computed value and has no escape valve for
the other 4). On an invalid answer, emit the same 4-value rejection
message as Step 1 and re-ask rather than aborting the whole flow.
If the user passed `--color` explicitly in Step 1, skip this prompt — an
explicit flag is already a decision, not something to re-confirm.
`tools` keeps its silent default (`Read, Grep, Glob` for review agents when
not specified) — no confirmation needed, it isn't a cost/capability choice
the way model/effort/color are.
**Skills and memory** also follow the suggest-and-confirm shape, pinned to
the same wording pattern color uses above (issue #1335):
1. If the generated agent body includes a `## Skills` section, list every
skill name that section documents and emit:
`Suggested skills: [<name1>, <name2>, ...] — accept? (yes/change)`.
On `yes`: use the listed names. On `change`: ask which names to add/remove;
each must resolve to a real `$PLUGIN/skills/<name>/SKILL.md` (warn, don't
fail, per the existing `--skills` validation rule). If the body has no
`## Skills` section, skip this prompt — omit `skills:` entirely.
2. If the resolved `tools:` includes `Edit` or `Write`, emit:
`Suggested memory: project — accept? (yes/change)`. On `yes`: use
`project`. On `change`: ask `Memory scope?` and validate against
`project` — the fleet's memory test gate
(`test_every_file_mutating_agent_declares_memory_project`) checks exact
equality against `project` for every `Edit`/`Write` agent and has no
escape valve, so `--memory` cannot be omitted or set to anything else
here; on an invalid answer, restate that `project` is the only accepted
value for a file-mutating agent and re-ask rather than aborting the whole
flow. If `tools:` has neither `Edit` nor `Write`, skip this prompt — omit
`memory:` entirely.
If the user passed `--skills` and/or `--memory` explicitly in Step 1, skip
the respective prompt — an explicit flag is already a decision.
`isolation`, `maxTurns`, and `background` have no forced default and no
suggestion — genuinely free-form operational flags with no mechanical
default; omit each from frontmatter entirely unless the user passed it.
---
## Step 6 — Check for Existing File
Glob `$PLUGIN/agents/<name>.md`.
If the file exists:
1. Read its `description` frontmatter field
2. Emit: `$PLUGIN/agents/<name>.md already exists (description: <existing-description>)`
3. Ask: `Overwrite? (yes/no)`
4. On `no`: emit `Cancelled. Existing agent: $PLUGIN/agents/<name>.md — <existing-description>` and **stop with no changes**
5. On `yes`: continue
---
## Step 7 — Check Scope Overlap (Review Agents Only)
For review agents, scan existing agents for topical overlap:
1. Read `description` frontmatter of all files in `$PLUGIN/agents/`
2. For each existing agent, also read the first 20 lines of its `## Detect` section if present
3. If the LLM judges ≥ 60% topical overlap between the new description and an existing agent's scope, emit:
`Possible overlap with <agent-name>: <one-sentence description of shared concept>. Continue anyway? (yes/no)`
4. On `no`: stop with no changes
5. On `yes`: continue
6. This check is advisory — the user can always continue
For team agents: compare descriptions only (no `## Detect` scan).
---
## Step 8 — Handle Plugin-Unsupported Fields
If the user has requested `hooks`, `mcpServers`, or `permissionMode`, emit:
```
hooks/mcpServers/permissionMode are silently ignored for plugin agents — move the file to .claude/agents/ if you need them to take effect
```
Then ask: `Include anyway? (yes/no)`
- On `no`: omit the field from generated frontmatter
- On `yes`: include the field as requested
Do not emit this warning for fields the user did not request.
---
## Step 9 — Generate Frontmatter
Emit only official fields with non-empty values. Use this structure:
```yaml
---
name: <name>
description: <description>
tools: <comma-separated tool list>
model: <alias|full-id|inherit>
effort: <level>
[memory: <user|project|local>]
[isolation: worktree]
[color: <color>]
[maxTurns: <int>]
[background: <true|false>]
[skills: [<name>, ...]]
[any additional fields the user requested and confirmed]
---
```
Emit each bracketed optional field only when the user passed the
corresponding flag in Step 1 — there is no forced default for any of them.
Do not include `hooks`, `mcpServers`, or `permissionMode` unless the user
confirmed their inclusion in Step 8.
#### `Cites:` (citation drift defense)
When a review agent inlines normative **numeric thresholds** (e.g. "functions
MUST be under 50 lines", "coverage SHOULD reach 80%") that are owned by a
canonical skill or knowledge file, declare those sources with a body-level
`Cites:` list — **not frontmatter**: `cites` is not part of the official
Claude Code sub-agent contract (`agent-contract.json`), so it lives in the
generated body, near the top, alongside `Context needs:`:
```markdown
Cites: [complexity, object-calisthenics] # skill names or knowledge file stems
```
Each entry resolves to `$PLUGIN/skills/<name>/SKILL.md` or `$PLUGIN/knowledge/<name>.md`.
The citation lint flags any threshold the agent states on an RFC-2119 line
that is absent from every cited source. The lint is advisory (Phase 1,
non-blocking) either way.
**Pre-write check.** After Step 10 generates the body, before Step 11 writes
the file:
1. Scan the generated body for **threshold-bearing RFC-2119 lines** — any
line carrying `MUST|SHOULD|SHALL|REQUIRED|NEVER|ALWAYS` AND a numeric
token (`\d+(\.\d+)?%?`), outside code fences and blockquotes.
2. If any are found AND no `Cites:` line was set, emit exactly:
```text
⚠ Agent body states N numeric threshold(s) on RFC-2119 lines but no
Cites: was declared. Eval drift defense will be blind on this agent.
(a) add Cites: (b) proceed without
```
3. On `(a)`: prompt for a comma-separated list, validate each entry resolves
to a real `$PLUGIN/skills/<name>/SKILL.md` or `$PLUGIN/knowledge/<name>.md`,
insert a `Cites: [<list>]` line into the body, and continue to Step 11.
4. On `(b)`: continue to Step 11 with no `Cites:` — the lint will flag the
agent as `advisory` going forward, which is the correct deferred state.
Omit the check entirely when the body states no thresholds; nothing for the
lint to verify means the field is genuinely optional.
---
## Step 10 — Generate Body
### Review Agent Body Structure (required order)
If `--context` was provided, use it for the `Context needs:` field. Otherwise infer a sensible default from the description (simple detectors → `diff-only`; agents that need full file context → `full-file`; agents that need project structure → `project-structure`).
If `--lang` was provided, insert a language scope line immediately after the title: `Scope: <exts> files only. Skip if no <exts> files are present.`
```markdown
# <Title Case Name>
View on GitHub