
Create Agent
- 34 installs
- 269 repo stars
- Updated June 11, 2026
- gupsammy/claudest
Run a structured senior code review on a fresh chunk of code before you merge or move on to the next feature.
About
This agent packages a senior reviewer mindset for solo builders who ship without a human pair on every PR. Install it when you have finished a meaningful slice of code and want correctness, clarity, idioms, and light security smell checks before you commit or open a review. The workflow reads the referenced files, infers intent, hunts edge cases and error paths, flags naming and structure problems, and outputs prioritized findings with concrete next steps. It is deliberately not a substitute for a dedicated security or architecture audit—those are separate agents in the same pattern. For Prism’s journey, it sits on the Ship shelf in Review but remains useful during Build when you iterate in tight loops. Intermediate complexity: you need enough code to review and judgment to triage critical versus style noise.
- Seven-step review flow: context, correctness, clarity, idioms, security smell checks, then severity ranking
- Explicitly defers deep security work to security-auditor and architecture to architecture-auditor
- Reports only real issues with a one-line fix direction per finding—no generic padding
- Severity buckets: critical (breaks functionality), major (correctness/security), minor (style)
- Language-agnostic: uses Read, Grep, Glob to inspect whatever stack you just wrote
Create Agent by the numbers
- 34 all-time installs (skills.sh)
- Ranked #648 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/gupsammy/claudest --skill create-agentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 34 |
|---|---|
| repo stars | ★ 269 |
| Security audit | 3 / 3 scanners passed |
| Last updated | June 11, 2026 |
| Repository | gupsammy/claudest ↗ |
What it does
Run a structured senior code review on a fresh chunk of code before you merge or move on to the next feature.
Files
Agent Creator
Generate well-structured Claude Code agents — markdown files with YAML frontmatter that delegate complex multi-step work to autonomous subprocesses with isolated context windows.
Agents vs Skills — know the difference before generating:
- Agents run in isolated context, have second-person system prompts ("You are..."), use concise
>folded scalar descriptions (50-70 tokens, no<example>blocks), and are spawned via the Agent tool - Skills inject inline into the current conversation, use imperative body instructions for Claude to follow, and route via description matching on trigger phrases
Phase 0: Understand Requirements
Parse $ARGUMENTS for hints. Gather:
Identity: 1. Domain & purpose — What problem does this agent solve? 2. Expert persona — What specialist identity should it embody?
Triggering: 3. Trigger conditions — When should Claude delegate to this agent? What user messages activate it? 4. Proactive vs reactive — Should it fire automatically after events (e.g., after code is written), or only on explicit request?
Capabilities: 5. Tool access — What tools are actually needed? Least-privilege: an analysis agent doesn't need Write. 6. Memory — Should this agent learn across sessions? (e.g., accumulate codebase patterns, recurring issues, architectural decisions.) If yes, choose scope: project (recommended default, shareable via VCS), user (global across projects), or local (project-specific, not in VCS). 7. MCP servers — Does it need external tools (browser, database, API) not in the parent session?
Execution: 8. Session mode — Will this run as a subagent (delegated by Claude) or as the main session agent (claude --agent <name>)? Session agents need broader tool access, more self-contained prompts, and may use initialPrompt to self-start. 9. Background execution — Should it run concurrently while the user continues? Background agents auto-deny unpre-approved permissions and cannot ask clarifying questions. 10. Context isolation — Does it generate heavy output or modify files? Should it run in a worktree (isolation: worktree)? 11. Effort level — Does it need deep reasoning (high/max) or is it a fast classification task (low)?
If $ARGUMENTS is empty or insufficient, use AskUserQuestion to gather domain, trigger conditions, and proactive intent before proceeding. Proceed to Phase 1 once these are established.
Phase 1: Generate
Apply throughout: second-person for system prompt body, intensional over extensional reasoning, minimum viable frontmatter.
Step 1 — Choose identifier
Naming rules (enforced by validate_agent.py):
- 3–50 characters, lowercase letters/numbers/hyphens only
- Must start and end with alphanumeric
- Avoid generic terms:
helper,assistant,agent
Good: code-reviewer, test-generator, api-docs-writer Bad: ag (too short), -start (leading hyphen), my_agent (underscore)
Step 2 — Write frontmatter
Read ${CLAUDE_PLUGIN_ROOT}/skills/create-agent/references/agent-frontmatter.md for the full field catalog, color semantics, model options, tool selection framework, and execution modifiers.
Required: name, description Always set: model: inherit (unless specific model capability needed), color (visual ID in UI)
Intensional rule for `tools`: Restrict to the minimum needed because agents run autonomously — over-permission has no human in the loop to catch it. ["Read", "Grep", "Glob"] for analysis. Add Write for generation. Add Bash only when shell execution is essential, never by default. For session-mode agents that orchestrate subagents, use Agent(type1, type2) to scope spawning.
Set if applicable from Phase 0:
memory— if cross-session learning was identified; add memory maintenance instructions to bodyeffort— if the task warrants non-default thinking depthinitialPrompt— if this is a session agent that should self-startbackground: true— if concurrent execution was identifiedmcpServers— if external tools were identified
Step 3 — Write description
The description field is loaded into context every session. Token budget matters — write the minimum needed for accurate routing.
Use a > folded scalar with:
- "Use this agent when [trigger conditions]."
- Proactive hint if applicable ("Recommended PROACTIVELY after...")
- Scope boundary ("Not for X — use Y-agent.")
- Target: 50-70 tokens. No
<example>blocks — they waste context without improving routing.
Step 4 — Write system prompt
The markdown body (after ---) becomes the agent's system prompt. Write entirely in second person, addressing the agent directly. This is the critical authoring difference from skills: agents need a persona and process, not instructions for Claude to follow.
Standard structure:
You are [role] specializing in [domain].
**Your Core Responsibilities:**
1. [Primary responsibility]
2. [Secondary responsibility]
**Process:**
1. [Step — imperative]
2. [Next step]
**Quality Standards:**
- [Standard]
**Output Format:**
[Structure and content of what to return]
**Edge Cases:**
- [Situation]: [How to handle]Intensional rules for the system prompt:
- Persona first — the expert identity shapes all downstream decisions; establish it in the first sentence
- Process steps prevent "winging it" on complex tasks; each step is an explicit decision boundary
- Output format is non-negotiable — callers need predictable structure to consume results
- Define edge cases in the system prompt; discovered-at-runtime errors cost retries
If `memory` is set: Include a section instructing the agent to maintain its knowledge base: "Update your agent memory as you discover codepaths, patterns, and key architectural decisions. Consult your memory before starting work." This enables cross-session learning.
Keep under 3,000 words. Detailed domain reference belongs in references/ preloaded via the skills: frontmatter field, not embedded directly in the system prompt.
See ${CLAUDE_PLUGIN_ROOT}/skills/create-agent/examples/proactive-code-reviewer.md for a complete working example demonstrating the proactive trigger pattern.
Step 5 — Script opportunity scan
Read ${CLAUDE_PLUGIN_ROOT}/skills/create-agent/references/script-patterns.md and apply the five signal patterns to every step in the agent's system prompt:
| Signal | Question | If yes → |
|---|---|---|
| Repeated Generation | Does any step produce the same structure across invocations? | Parameterized script in scripts/ |
| Unclear Tool Choice | Does any step combine tools in a fragile sequence? | Script the procedure |
| Rigid Contract | Can you write --help text for this step right now? | CLI candidate |
| Dual-Use Potential | Would a user run this step from the terminal independently? | Design as proper CLI |
| Consistency Critical | Must this step produce identical output for identical inputs? | Script — never LLM generation |
Step 6 — Check delegation
Scan existing agents and skills before finalizing:
Glob: .claude/agents/*.md, ~/.claude/agents/*.md (project + global agents)
Glob: .claude/skills/*/SKILL.md, ~/.claude/skills/*/SKILL.md (project + global skills)- Does an existing agent cover this domain? Extend it, or tighten scope of the new one
- Are there skills or reference files to preload via
skills:frontmatter for domain knowledge? - Are there commands or MCPs this agent should delegate sub-tasks to?
Always use fully qualified names:
Agent: subagent_type=plugin-dev:agent-creator(not just "agent-creator")Skill: claude-skills:create-skill(not just "create-skill")
Step 7 — Validate
When creating a new agent file:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/create-agent/scripts/validate_agent.py <agent-file> --output jsonExit 0 = proceed to Phase 2. Exit 1 = parse the errors array; each entry has field, message, severity. Resolve all critical and major items before writing to disk.
Phase 2: Deliver
Output Paths
| Scope | Location |
|---|---|
| User agent (global) | ~/.claude/agents/<name>.md |
| Project agent | .claude/agents/<name>.md |
| Plugin agent | <plugin-root>/agents/<name>.md |
Agents in agents/ are auto-discovered — no registration needed. Plugin agents are namespaced automatically as plugin-name:agent-name.
Initialize agent file (optional scaffold)
When creating from scratch:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/create-agent/scripts/init_agent.py <name> --path <agents-dir>Exit 0 = file created with placeholders, proceed to fill content. Exit 1 = naming collision; ask user to rename or confirm overwrite.
Explain Your Choices
Present the generated agent with brief rationale:
- What you set and why — "Set
model: sonnetbecause this agent performs complex multi-file reasoning" - What you excluded and why — "Left
isolationunset; no git state management needed" - Tools selected and why — explicitly justify each tool; undefended tool access is a design smell
Write and Confirm
Before writing:
Writing to: [path]
This will [create new / overwrite existing] file.
Proceed?After Creation
Summarize:
- Name and file path
- When it triggers (key trigger conditions)
- Tools granted and why
- Suggested test scenario
Proceed to Phase 3.
Phase 3: Evaluate
| Dimension | Criteria |
|---|---|
| Clarity (0-10) | System prompt unambiguous, objective and persona clear |
| Trigger Precision (0-10) | Description + examples cover intended trigger space, not broader |
| Efficiency (0-10) | System prompt token economy — maximum guidance per token |
| Completeness (0-10) | Covers domain requirements; output format defined; edge cases addressed |
| Safety (0-10) | Tools restricted to minimum needed; no runaway permission grants |
Target: 9.0/10.0. If below, refine once addressing the weakest dimension, then deliver.
Phase 3 is complete when score ≥ 9.0 or one refinement pass has run. Deliver: agent file path, key trigger conditions, tools granted and why.
Validation Checklist
Structure:
- [ ] File is
<name>.mdin anagents/directory - [ ] Valid YAML frontmatter with
nameanddescription - [ ] Markdown body is present and substantial
Description Quality:
- [ ] Starts with "Use this agent when..."
- [ ] Concise
>scalar, 50-70 tokens, no<example>blocks - [ ] Covers scope boundaries (what it's NOT for)
- [ ] Proactive hint included if agent should fire after events
System Prompt Quality:
- [ ] Written in second person ("You are...", "You will...")
- [ ] Has clear persona/role statement as first sentence
- [ ] Process steps are numbered and imperative
- [ ] Output format is defined
- [ ] Edge cases addressed
- [ ] Under 3,000 words; domain detail offloaded to references/
Frontmatter:
- [ ]
model: inheritunless specific model needed - [ ]
colorset and semantically meaningful - [ ]
toolsrestricted to minimum needed - [ ]
memoryset if cross-session learning identified, with maintenance instructions in body - [ ]
effortset if non-default thinking depth needed - [ ]
initialPromptset if session-mode agent that self-starts - [ ]
background: trueset if concurrent execution identified - [ ]
isolation: worktreeset if agent modifies files that need review before merging - [ ] No TODO placeholders remaining
Error Handling
| Issue | Action |
|---|---|
| Unclear domain | Ask: what does success look like for this agent? |
| Scope too broad | Split into 2–3 focused agents with non-overlapping trigger conditions |
| Conflicts with existing agent | Note overlap; narrow triggering scope or extend the existing one |
| Vague trigger conditions | Ask for 3 concrete user messages that should activate this agent |
You are a senior code reviewer specializing in correctness, clarity, and maintainability across any programming language.
Your Core Responsibilities: 1. Identify bugs, edge cases, and incorrect assumptions in the code 2. Flag violations of language idioms, style conventions, and best practices 3. Spot performance, security, and maintainability concerns 4. Prioritize findings by severity so the author knows where to focus
Process: 1. Read the code provided or referenced in the conversation 2. Identify the language, context, and apparent intent 3. Check for correctness: edge cases, error states, off-by-ones, type assumptions 4. Check for clarity: meaningful names, readable structure, non-obvious logic 5. Check for idioms: language-appropriate patterns, standard library usage 6. Check for security: untrusted input handling, injection risks, credential exposure 7. Rank findings: critical (breaks functionality) → major (correctness/security) → minor (style)
Quality Standards:
- Report only real issues; do not pad with generic advice that applies to any code
- Provide a one-line fix or direction alongside each issue — findings without direction are noise
- Scope to recently written or modified code unless explicitly asked to review the full codebase
Output Format: Summary: [1–2 sentence overall assessment]
Issues found:
- [CRITICAL] [location]: [problem] → [fix direction]
- [MAJOR] [location]: [problem] → [fix direction]
- [MINOR] [location]: [problem] → [fix direction]
If no issues: "No issues found — code looks solid."
Edge Cases:
- No code in context: ask the user to share the code to review
- Very large file: focus on the diff or recently changed sections unless the user specifies otherwise
- Unfamiliar language: state the limitation, review what you can, flag uncertainty explicitly
Agent Frontmatter Reference
Authoritative source for agent frontmatter. Keep current with Claude Code releases — this file is the single source of truth used by create-agent. No live documentation fetch is performed; accuracy depends on this file being maintained.
Load before writing frontmatter in Phase 1, Step 2. Contains the full field catalog, description format, color semantics, tool selection, and execution modifiers for agents.
---
Required Fields
name
Unique agent identifier within its scope.
- Format: lowercase letters, numbers, hyphens only
- Length: 3–50 characters
- Pattern: must start and end with alphanumeric; no consecutive hyphens
- Good:
code-reviewer,test-generator,api-docs-writer,security-analyzer - Bad:
helper(too generic),ag(too short),-agent-(leading/trailing hyphen),my_agent(underscore)
description
Defines when Claude delegates to this agent. Loaded into context every session — token budget matters.
Use > folded scalar. Target 50-70 tokens. No <example> blocks.
Format:
- Start with "Use this agent when [trigger conditions]."
- Add proactive hint if applicable ("Recommended PROACTIVELY after...")
- Add scope boundary if adjacent agents exist ("Not for X — use Y-agent.")
- State when NOT to trigger if ambiguity with other agents exists
---
Optional Fields
model
Model the agent uses. Default: inherit (recommended for most cases).
Accepts aliases or full model IDs (e.g., claude-opus-4-6, claude-sonnet-4-6).
| Value | Use when |
|---|---|
inherit | Agent should use same model as parent conversation |
sonnet | Complex multi-step reasoning, code analysis, generation tasks |
haiku | Fast, cheap tasks with simple structure (classification, extraction) |
opus | Highest-complexity reasoning; use sparingly — cost scales |
| Full model ID | Pin to a specific model version (e.g., claude-sonnet-4-6) |
Model resolution order (first match wins): 1. CLAUDE_CODE_SUBAGENT_MODEL environment variable 2. Per-invocation model parameter from the caller 3. Agent definition's model frontmatter 4. Main conversation's model
color
Visual identifier in the Claude Code UI. Choose distinct colors for agents in the same plugin.
| Color | Semantic signal | Suitable for |
|---|---|---|
blue | Analysis, review | Code review, security audit, quality analysis |
cyan | Information gathering | Research, documentation, data extraction |
green | Generation, creation | Code generation, content writing, scaffolding |
yellow | Validation, caution | Linting, testing, configuration validation |
red | Critical, destructive | Security scanning, dangerous operations |
purple | Transformation, creative | Refactoring, reformatting, creative tasks |
orange | Operations, infrastructure | Build, deploy, CI/CD, configuration |
pink | Communication, social | Notifications, messaging, collaboration |
tools
Restrict the agent to a specific allowlist of tools. If omitted, agent has access to all tools. Apply least-privilege — agents run autonomously with no human in the loop to catch errors.
Common minimal sets:
# Read-only analysis
tools:
- Read
- Grep
- Glob
# Code generation
tools:
- Read
- Write
- Grep
- Glob
# Testing / validation
tools:
- Read
- Bash
- Grep
- Glob
# Full access (use sparingly)
# Omit the field entirelyScoping Bash: Prefer scoped patterns like Bash(git:*), Bash(npm:*), Bash(pytest:*). Unscoped Bash grants full shell access — the highest blast-radius tool.
Scoping Agent: For agents running as main thread via --agent, restrict which subagents they can spawn using Agent(worker, researcher) syntax. Without parentheses (Agent), any subagent can be spawned. If Agent is omitted from tools entirely, the agent cannot spawn subagents. This restriction only applies to --agent mode — subagents cannot spawn other subagents regardless.
disallowedTools
Explicitly remove tools from the inherited/specified set. Useful when you want most tools but need to block one destructive operation. If both are set, disallowedTools removes from the inherited pool first, then tools restricts to its allowlist. A tool in both is removed.
disallowedTools:
- Write
- EditpermissionMode
How the agent handles permission prompts. Default: default.
| Value | Behavior |
|---|---|
default | Standard permission handling, inherits from parent |
acceptEdits | Auto-approve file edits without prompting |
auto | Background classifier reviews commands and protected-directory writes |
dontAsk | Auto-deny permission prompts (explicitly allowed tools still work) |
bypassPermissions | Skip all permission checks (dangerous — use only in controlled contexts) |
plan | Plan mode (read-only exploration) |
If the parent uses bypassPermissions, it takes precedence and cannot be overridden. If the parent uses auto, the subagent inherits auto mode and any permissionMode in frontmatter is ignored.
maxTurns
Maximum agentic turns before stopping. Prevents runaway loops on unbounded tasks. Set when the agent's task has a predictable completion horizon.
maxTurns: 10skills
Skills to preload into the agent's context at startup. Full skill content is injected, not just made available. Use to equip the agent with domain knowledge without embedding it in the system prompt.
skills: code-conventions, api-patternsbackground
Run agent as a background task. Default: false.
background: trueisolation
Run agent in a temporary git worktree — an isolated copy of the repository. Auto-cleaned if the agent makes no changes; worktree path returned if changes were made.
isolation: worktreeUse when: agent makes file modifications that shouldn't pollute the working tree until reviewed, or when multiple parallel agents need independent working state.
memory
Persistent memory directory that survives across conversations. Enables cross-session learning — the agent accumulates codebase patterns, debugging insights, and architectural decisions over time.
| Value | Directory | Use when |
|---|---|---|
user | ~/.claude/agent-memory/<name>/ | Learnings apply across all projects |
project | .claude/agent-memory/<name>/ | Knowledge is project-specific; shareable via VCS (recommended default) |
local | .claude/agent-memory-local/<name>/ | Knowledge is project-specific but should not be checked into VCS |
When memory is enabled, three things happen automatically: 1. The system prompt includes instructions for reading/writing to the memory directory 2. The first 200 lines or 25KB of MEMORY.md in the memory directory is injected into context 3. Read, Write, and Edit tools are auto-enabled so the agent can manage its memory files
When setting memory, add instructions in the system prompt body for the agent to maintain its knowledge base (e.g., "Update your agent memory as you discover codepaths, patterns, and key architectural decisions.").
effort
Overrides the session effort level for this agent. Controls thinking depth. Default: inherits from session.
| Value | Use when |
|---|---|
low | Fast, cheap tasks — classification, extraction, simple lookups |
medium | Balanced reasoning — most agents |
high | Deep multi-step reasoning, complex code analysis |
max | Maximum thinking depth (Opus 4.6 only) |
initialPrompt
Auto-submitted as the first user turn when this agent runs as the main session agent (via --agent <name> or the agent setting in .claude/settings.json). Commands and skills in the prompt are processed. Prepended to any user-provided prompt.
Use for self-starting agents that should begin work immediately without waiting for user input. Only relevant for agents designed to run as session agents, not subagents.
initialPrompt: "/review-pr"hooks
Lifecycle hooks scoped to this agent's execution. Only run while the agent is active; cleaned up when it finishes. Supported events: PreToolUse, PostToolUse, Stop (auto-converted to SubagentStop at runtime).
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/validate-command.sh"
PostToolUse:
- matcher: "Edit|Write"
hooks:
- type: command
command: "./scripts/run-linter.sh"Each entry has an optional matcher (regex against tool name) and a hooks array of {type: command, command: "..."} objects. Hook commands receive JSON via stdin with the tool input; exit code 2 blocks the operation.
mcpServers
MCP servers available to this agent. Each entry is either a string reference (reuses a server already configured in the parent session) or an inline definition (scoped to this agent only — connected on start, disconnected on finish).
mcpServers:
# Inline definition: scoped to this agent only
- playwright:
type: stdio
command: npx
args: ["-y", "@playwright/mcp@latest"]
# Reference by name: reuses an already-configured server
- githubInline definitions use the same schema as .mcp.json server entries (stdio, http, sse, ws), keyed by the server name. Define servers inline here rather than in .mcp.json to keep their tool descriptions out of the main conversation context.
Plugin agents cannot use hooks, mcpServers, or permissionMode — these fields are ignored when loading agents from a plugin. Copy the agent to .claude/agents/ or ~/.claude/agents/ if needed.
---
Field Summary Table
| Field | Required | Default | Notes |
|---|---|---|---|
name | Yes | — | lowercase-hyphens, 3-50 chars |
description | Yes | — | "Use this agent when..." — concise > scalar, 50-70 tokens, no examples |
model | No | inherit | inherit/sonnet/haiku/opus or full model ID (e.g., claude-sonnet-4-6) |
color | No | — | red/blue/green/yellow/purple/orange/pink/cyan |
tools | No | all tools | Least-privilege allowlist; supports Agent(type) scoping |
disallowedTools | No | none | Explicit denylist; removes from inherited pool before tools allowlist |
permissionMode | No | default | default/acceptEdits/auto/dontAsk/bypassPermissions/plan |
maxTurns | No | unlimited | Positive integer |
skills | No | none | Comma-separated skill names to preload |
background | No | false | Run as background task (auto-denies unpre-approved permissions) |
isolation | No | none | Only value: worktree |
memory | No | none | user/project/local — enables persistent cross-session learning |
effort | No | inherit | low/medium/high/max (max is Opus 4.6 only) |
initialPrompt | No | none | First user turn when running as session agent via --agent |
hooks | No | none | PreToolUse/PostToolUse/Stop with matcher/command structure |
mcpServers | No | none | String references or inline server definitions |
---
Proactive Agent Pattern
For agents that should trigger after an event (not just on explicit request), include "Recommended PROACTIVELY after [event]" in the description. The description itself stays concise — the proactive behavior is a hint to the routing model, not a worked example.
---
Minimal Valid Agent
---
name: simple-agent
description: >
Use this agent when [trigger conditions]. Not for [adjacent concern] — use [other-agent].
model: inherit
color: blue
---
You are an expert [role] specializing in [domain].
**Process:**
1. [First step]
2. [Second step]
**Output:** [What to return]Script & CLI Patterns Reference
Intelligence for recognizing when a workflow step should be a script, how to design it as a proper CLI, and how to wire it into a skill. Load before auditing Dimension 4 or scanning for script opportunities during generation.
---
Signal Patterns: When a Step Should Be a Script
A workflow step is a CLI candidate when any of the following are true. The more signals present, the stronger the case for scripting.
Signal 1 — Repeated Generation
The step produces the same structure with different parameters across invocations. Examples: scaffolding a directory tree, generating a frontmatter block, creating a boilerplate file from a template. If Claude is re-generating the same code on every invocation, a parameterized script produces it once and runs reliably thereafter.
Test: Would two different users invoking this skill with different inputs cause Claude to write nearly identical code blocks with just the variable parts swapped? → Script it.
Signal 2 — Unclear Tool Choice
The step needs to do something but no standard Claude tool (Read, Grep, Bash, Edit, etc.) covers it cleanly without combining multiple tools in a fragile sequence. Example: "validate frontmatter YAML and report structured errors" requires reading a file, parsing YAML, and applying rules — awkward as a tool sequence, natural as a script.
Test: Does the skill body describe a multi-step procedure that would be done the same way every time, using tools as primitives? → The procedure is a script waiting to be named.
Signal 3 — Rigid Input/Output Contract
The step takes a specific input shape (a file path, a name + target directory) and produces a specific output shape (a scaffolded directory, a JSON report, a validation result). Rigid contracts are the shape of good CLIs — the interface is clear enough to parameterize immediately.
Test: Can you write the --help text for this step right now, without ambiguity? If yes, it's a CLI. If the args feel unclear, it's still agentic reasoning.
Signal 4 — Dual-Use Potential
The step would be useful to run independently, outside the skill workflow. Example: a validation script is useful during skill creation, during repair, and as a standalone pre-commit check. A scaffolding script is useful both when the skill generates a new artifact and when a user wants to scaffold manually.
Test: Would a user want to run this from the terminal directly, without triggering the full skill? → Design it as a proper CLI from the start, not an internal helper.
Signal 5 — Consistency Critical
The step must produce identical output for identical inputs — not "similar" output, but bit-for-bit reproducible results. LLM generation has variance; scripts don't. File naming conventions, path construction, structural templates — anything where variance causes downstream breakage should be scripted.
Test: Would a subtle difference in output (different field order, different whitespace, slightly different file name) break something? → Deterministic script, not LLM generation.
---
CLI Design for Skill Context
A script in a skill directory is also a CLI. Design it to be invoked both by Claude during a workflow and by users from the terminal.
Interface Design
Positional arguments — use for required, ordered inputs where the meaning is unambiguous from context. Best for 1–2 inputs: init_skill.py <name> <target-dir>.
Named flags — use for optional inputs, boolean toggles, and anything where the label clarifies meaning: --model sonnet, --dry-run, --output json.
Flag for output format — always add --output [text|json] when the script produces structured data. Claude parses JSON efficiently; humans prefer text. Defaulting to text with --output json as the machine-readable mode covers both callers.
Stdin input — use when the script is meant to be piped to: cat file | script.py. Useful for transform scripts. Use sys.stdin.read() with a flag fallback for file paths.
Explicit help text — every script needs -h/--help output. This is documentation that Claude reads when deciding how to invoke the script, and that users see when running it manually. Include: what the script does, each argument/flag with type and default, and an example invocation.
Output Conventions
Stdout for result data — the primary output goes to stdout. Claude captures stdout.
Stderr for diagnostic messages — progress notes, warnings, verbose logging go to stderr. Claude ignores stderr by default; it doesn't pollute the captured result.
Exit codes — 0 for success, 1 for usage errors (wrong args), 2 for runtime errors (file not found, parse failure). Claude checks exit codes implicitly; a non-zero exit signals failure and stops the workflow.
Structured output for multi-field results — if the script returns more than one piece of data, output JSON on stdout. A script that returns {"valid": true, "errors": []} is easier for Claude to parse than "Validation passed with 0 errors."
Script Anatomy (Python template)
#!/usr/bin/env python3
"""
One-line description of what this script does.
Usage:
script.py <required-arg> [--flag value]
Examples:
script.py input.yaml --output json
"""
import argparse
import json
import sys
def main():
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("input", help="Description of required input")
parser.add_argument("--output", choices=["text", "json"], default="text",
help="Output format (default: text)")
parser.add_argument("--dry-run", action="store_true",
help="Show what would happen without making changes")
args = parser.parse_args()
# Core logic here
result = process(args.input, dry_run=args.dry_run)
if args.output == "json":
print(json.dumps(result))
else:
print(format_text(result))
sys.exit(0 if result["success"] else 1)
if __name__ == "__main__":
main()---
Common Script Archetypes
These archetypes cover most script candidates that appear in skill workflows.
Init — Scaffold a structure
Creates a directory tree or file set from a template. Takes a name and target path; produces the scaffolded output. Should be idempotent with a --force flag for overwriting, or fail fast on collision by default.
Canonical args: init.py <name> [target-dir] [--force] [--output json]
Validate — Check preconditions
Reads an artifact (file, directory, config), applies a rule set, and reports violations. Output should be structured (list of {field, message, severity} objects). Exit 0 on clean, exit 1 on violations. Never modifies anything.
Canonical args: validate.py <path> [--strict] [--output json]
Transform — Convert input to output
Takes structured input, applies a deterministic transformation, produces structured output. The purest CLI form: one input, one output, no side effects unless --write is passed. Use stdin/stdout for pipeline composability.
Canonical args: transform.py <input-path> [--output-path path] [--dry-run]
Package — Assemble an artifact
Collects files or content from multiple sources and assembles a distributable artifact (zip, tarball, manifest). Should validate inputs before assembling and report what was included. Dry-run support is valuable here.
Canonical args: package.py <source-dir> [output-dir] [--dry-run] [--output json]
Query — Read state, return structured result
Reads from a data source (DB, file, API) and returns structured data. Never writes. The primary consumer is Claude reading the output during a skill workflow, but users should be able to run it for inspection.
Canonical args: query.py [--filter key=value] [--limit N] [--output json]
---
Wiring Scripts into a Skill
A script that isn't referenced in SKILL.md is invisible to Claude.
In SKILL.md body, reference each script with: 1. When to invoke it (the trigger condition — which phase, what signals) 2. The exact invocation with relevant flags 3. How to interpret the output (what to do with exit codes, what fields matter)
Example reference pattern:
**Validate before proceeding:**~/.claude/skills/skill-name/scripts/validate.py "$PATH" --output json
Exit 1 = validation failed; parse the `errors` array and report to user before
continuing. Exit 0 = proceed to Phase 3.Avoid vague references like "run the validation script if needed" — Claude won't know which script or when "if needed" applies. State the trigger condition explicitly.
---
Delegation Pattern
When a skill workflow step is identified as a script candidate, delegate interface design to the create-cli skill rather than designing it ad-hoc. create-cli covers argument structure, help text, output formats, error messages, exit codes, and config/env precedence in depth.
Invocation pattern from within a skill workflow:
Skill: create-cli
Args: "<description of what the script does and what inputs it takes>"The generated CLI spec can then be scaffolded into scripts/ and referenced from SKILL.md. This ensures the script is designed for both Claude invocation and direct user use from the start.
#!/usr/bin/env python3
"""
Agent Initializer - Creates a new Claude Code agent file from template.
Usage:
init_agent.py <name> --path <agents-dir> [--output json]
Examples:
init_agent.py code-reviewer --path ~/.claude/agents
init_agent.py test-generator --path .claude/agents --output json
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
MAX_AGENT_NAME_LENGTH = 50
AGENT_TEMPLATE = """\
---
name: {name}
description: >
Use this agent when [TODO: describe trigger conditions].
[TODO: "Recommended PROACTIVELY after..." if applicable.]
Not for [TODO: out-of-scope tasks] — use [TODO: correct agent].
model: inherit
color: blue
# memory: project
# effort: medium
---
You are [TODO: expert role] specializing in [TODO: domain].
**Your Core Responsibilities:**
1. [TODO: primary responsibility]
2. [TODO: secondary responsibility]
**Process:**
1. [TODO: first step — imperative voice]
2. [TODO: second step]
3. [TODO: third step]
**Quality Standards:**
- [TODO: standard 1]
- [TODO: standard 2]
**Output Format:**
[TODO: describe what the agent returns and how it structures results]
**Edge Cases:**
- [TODO: edge case]: [TODO: how to handle]
"""
def normalize_name(name):
"""Normalize agent name to lowercase hyphen-case."""
normalized = name.strip().lower()
normalized = re.sub(r"[^a-z0-9]+", "-", normalized)
normalized = normalized.strip("-")
normalized = re.sub(r"-{2,}", "-", normalized)
return normalized
def validate_name(name):
"""Return (valid, error_message) for an agent name."""
if len(name) < 3:
return False, f"Name too short ({len(name)} chars). Min: 3"
if len(name) > MAX_AGENT_NAME_LENGTH:
return False, f"Name too long ({len(name)} chars). Max: {MAX_AGENT_NAME_LENGTH}"
if not re.match(r"^[a-z0-9][a-z0-9-]*[a-z0-9]$", name):
return False, (
"Name must be lowercase letters/numbers/hyphens, "
"starting and ending with alphanumeric"
)
return True, None
def init_agent(name, path, output_format):
agents_dir = Path(path).expanduser().resolve()
agent_file = agents_dir / f"{name}.md"
result = {"name": name, "path": str(agent_file), "success": False, "message": ""}
if agent_file.exists():
result["message"] = f"Agent file already exists: {agent_file}"
if output_format == "json":
print(json.dumps(result))
else:
print(f"[ERROR] {result['message']}")
return False
try:
agents_dir.mkdir(parents=True, exist_ok=True)
except Exception as e:
result["message"] = f"Cannot create directory: {e}"
if output_format == "json":
print(json.dumps(result))
else:
print(f"[ERROR] {result['message']}")
return False
content = AGENT_TEMPLATE.format(name=name)
try:
agent_file.write_text(content)
except Exception as e:
result["message"] = f"Cannot write file: {e}"
if output_format == "json":
print(json.dumps(result))
else:
print(f"[ERROR] {result['message']}")
return False
result["success"] = True
result["message"] = f"Agent '{name}' created at {agent_file}"
if output_format == "json":
print(json.dumps(result))
else:
print(f"[OK] {result['message']}")
print("\nNext steps:")
print(f" 1. Edit {agent_file}")
print(" - Complete all [TODO] placeholders")
print(" - Keep description to 50-70 tokens (no <example> blocks)")
print(" - Write system prompt in second person ('You are...')")
print(f" 2. Validate: validate_agent.py {agent_file} --output json")
return True
def main():
parser = argparse.ArgumentParser(
description="Create a new agent file with template frontmatter and system prompt",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("name", help="Agent name (normalized to hyphen-case)")
parser.add_argument("--path", required=True, help="Output directory (e.g., ~/.claude/agents)")
parser.add_argument(
"--output",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
args = parser.parse_args()
name = normalize_name(args.name)
if not name:
msg = "Agent name must include at least one letter or digit."
if args.output == "json":
print(json.dumps({"success": False, "message": msg}))
else:
print(f"[ERROR] {msg}")
sys.exit(1)
valid, error = validate_name(name)
if not valid:
if args.output == "json":
print(json.dumps({"success": False, "message": error}))
else:
print(f"[ERROR] {error}")
sys.exit(1)
if name != args.name and args.output != "json":
print(f"Note: Normalized '{args.name}' to '{name}'")
success = init_agent(name, args.path, args.output)
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Agent Validator - Validates Claude Code agent file structure and frontmatter.
Usage:
validate_agent.py <agent-file> [--strict] [--output json]
Examples:
validate_agent.py ~/.claude/agents/code-reviewer.md
validate_agent.py agents/test-generator.md --output json
validate_agent.py agents/my-agent.md --strict --output json
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
import yaml
MAX_AGENT_NAME_LENGTH = 50
ALLOWED_MODEL_ALIASES = {"inherit", "haiku", "sonnet", "opus"}
ALLOWED_COLORS = {"red", "blue", "green", "yellow", "purple", "orange", "pink", "cyan"}
ALLOWED_PERMISSION_MODES = {
"default", "acceptEdits", "auto", "dontAsk", "bypassPermissions", "plan",
}
ALLOWED_ISOLATION = {"worktree"}
ALLOWED_EFFORT = {"low", "medium", "high", "max"}
ALLOWED_MEMORY = {"user", "project", "local"}
ALLOWED_FRONTMATTER = {
"name", "description", "model", "color", "tools", "disallowedTools",
"permissionMode", "maxTurns", "skills", "mcpServers", "hooks",
"memory", "background", "isolation", "effort", "initialPrompt",
"version", "license",
}
def build_error(field, message, severity="critical"):
return {"field": field, "message": message, "severity": severity}
def parse_frontmatter(content):
"""Extract YAML frontmatter. Returns (frontmatter_text, body, error)."""
if not content.startswith("---"):
return None, content, "No YAML frontmatter found (file must start with ---)"
match = re.match(r"^---\n(.*?)\n---\n?", content, re.DOTALL)
if not match:
return None, content, "Invalid frontmatter format (missing closing ---)"
return match.group(1), content[match.end():], None
def validate_agent(agent_path, strict=False):
"""Validate an agent file. Returns list of error dicts."""
errors = []
path = Path(agent_path).expanduser().resolve()
if not path.exists():
return [build_error("file", f"File not found: {path}")]
if not path.is_file():
return [build_error("file", f"Not a file: {path}")]
if path.suffix != ".md":
errors.append(build_error("file", "Agent file should have .md extension", "major"))
content = path.read_text()
frontmatter_text, body, parse_error = parse_frontmatter(content)
if parse_error or frontmatter_text is None:
return [build_error("frontmatter", parse_error or "Could not extract frontmatter")]
try:
frontmatter = yaml.safe_load(frontmatter_text)
if not isinstance(frontmatter, dict):
return [build_error("frontmatter", "Frontmatter must be a YAML dictionary")]
except yaml.YAMLError as e:
return [build_error("frontmatter", f"Invalid YAML: {e}")]
# Unexpected fields
unexpected = set(frontmatter.keys()) - ALLOWED_FRONTMATTER
if unexpected:
severity = "major" if strict else "minor"
errors.append(build_error(
"frontmatter",
f"Unexpected field(s): {', '.join(sorted(unexpected))}",
severity,
))
# name
if "name" not in frontmatter:
errors.append(build_error("name", "Missing required 'name' field"))
else:
name = str(frontmatter["name"]).strip()
if len(name) < 3:
errors.append(build_error("name", f"Name too short ({len(name)} chars). Min: 3"))
elif len(name) > MAX_AGENT_NAME_LENGTH:
errors.append(build_error(
"name", f"Name too long ({len(name)} chars). Max: {MAX_AGENT_NAME_LENGTH}"
))
elif not re.match(r"^[a-z0-9][a-z0-9-]*[a-z0-9]$", name):
errors.append(build_error(
"name",
"Name must be lowercase letters/numbers/hyphens, starting and ending with alphanumeric",
))
# description
if "description" not in frontmatter:
errors.append(build_error("description", "Missing required 'description' field"))
else:
desc = str(frontmatter["description"]).strip()
if not desc:
errors.append(build_error("description", "Description is empty"))
elif "[TODO" in desc:
errors.append(build_error("description", "Description contains TODO placeholder"))
else:
if not desc.startswith("Use this agent when"):
errors.append(build_error(
"description",
"Description should start with 'Use this agent when...'",
"major",
))
token_estimate = len(desc.split())
if token_estimate > 80:
errors.append(build_error(
"description",
f"Description is ~{token_estimate} tokens — target 50-70 for context budget",
"minor",
))
if "<example>" in desc:
errors.append(build_error(
"description",
"<example> blocks waste context without improving routing — use concise prose instead",
"major",
))
# model — accepts aliases or full model IDs (e.g., claude-sonnet-4-6)
model = frontmatter.get("model")
if model:
model_str = str(model)
if model_str not in ALLOWED_MODEL_ALIASES and not re.match(
r"^claude-[a-z0-9-]+$", model_str
):
errors.append(build_error(
"model",
f"Invalid model '{model}'. Must be an alias "
f"({', '.join(sorted(ALLOWED_MODEL_ALIASES))}) or a full model ID (claude-*)",
))
# color
color = frontmatter.get("color")
if color and str(color) not in ALLOWED_COLORS:
errors.append(build_error(
"color",
f"Invalid color '{color}'. Must be one of: {', '.join(sorted(ALLOWED_COLORS))}",
"minor",
))
# permissionMode
pm = frontmatter.get("permissionMode")
if pm and str(pm) not in ALLOWED_PERMISSION_MODES:
errors.append(build_error(
"permissionMode",
f"Invalid permissionMode '{pm}'. Must be one of: {', '.join(sorted(ALLOWED_PERMISSION_MODES))}",
))
# isolation
iso = frontmatter.get("isolation")
if iso and str(iso) not in ALLOWED_ISOLATION:
errors.append(build_error(
"isolation",
f"Invalid isolation '{iso}'. Only 'worktree' is supported",
))
# maxTurns
max_turns = frontmatter.get("maxTurns")
if max_turns is not None:
try:
n = int(max_turns)
if n < 1:
errors.append(build_error("maxTurns", "maxTurns must be a positive integer"))
except (ValueError, TypeError):
errors.append(build_error("maxTurns", "maxTurns must be an integer"))
# effort
effort = frontmatter.get("effort")
if effort and str(effort) not in ALLOWED_EFFORT:
errors.append(build_error(
"effort",
f"Invalid effort '{effort}'. Must be one of: {', '.join(sorted(ALLOWED_EFFORT))}",
))
if str(effort) == "max" and model:
model_str = str(model)
if model_str not in ("opus",) and not model_str.startswith("claude-opus"):
errors.append(build_error(
"effort",
f"effort: max requires an Opus model, but model is '{model_str}'",
"minor",
))
# memory
memory = frontmatter.get("memory")
if memory and str(memory) not in ALLOWED_MEMORY:
errors.append(build_error(
"memory",
f"Invalid memory '{memory}'. Must be one of: {', '.join(sorted(ALLOWED_MEMORY))}",
))
# body
body_text = body.strip() if body else ""
if not body_text:
errors.append(build_error("body", "Agent body (system prompt) is empty"))
elif "[TODO" in body_text:
errors.append(build_error(
"body",
"Body contains TODO placeholder — complete the system prompt before delivering",
"major",
))
elif strict and not (body_text.startswith("You are") or body_text.startswith("You're")):
errors.append(build_error(
"body",
"System prompt should start with 'You are...' (second-person)",
"minor",
))
return errors
def main():
parser = argparse.ArgumentParser(
description="Validate a Claude Code agent .md file.",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("agent_file", help="Path to the agent .md file")
parser.add_argument(
"--strict",
action="store_true",
help="Enable stricter checks (second-person body, unexpected field severity)",
)
parser.add_argument(
"--output",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
args = parser.parse_args()
errors = validate_agent(args.agent_file, strict=args.strict)
valid = len(errors) == 0
if args.output == "json":
result = {
"valid": valid,
"errors": errors,
"path": str(Path(args.agent_file).expanduser().resolve()),
}
print(json.dumps(result, indent=2))
else:
if valid:
print(f"[OK] Agent is valid: {args.agent_file}")
else:
print(f"[INVALID] {args.agent_file} — {len(errors)} issue(s):")
for e in errors:
severity = e["severity"].upper()
print(f" [{severity}] {e['field']}: {e['message']}")
sys.exit(0 if valid else 1)
if __name__ == "__main__":
main()
Related skills
FAQ
Is Create Agent safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.