Module 1: Custom Subagents
2. Defining Roles with Subagent Files and System Prompts
2. Defining Roles with Subagent Files and System Prompts
Description
A custom subagent in Claude Code isn't a function or a script — it's a Markdown file. A file with YAML frontmatter that defines its identity (name, model, tools) and a body of text that acts as its system prompt. That file is all that separates a generic subagent from one that knows exactly what to do, how to do it, and what format to use for reporting.
The mechanics are direct: you create a .md file in .claude/agents/, define its frontmatter, write its system prompt, and the next time you open Claude Code that subagent shows up as available. There's no compilation, no registration, no deploy. It's a text file that lives in your repository and is shared with your team via git. But the simplicity of the format doesn't imply simplicity in the design — a well-written system prompt is the difference between an agent that produces consistent results and one that improvises each time.
By the end of this capsule you'll have 3 functional subagent files (reviewer, implementer, tester), you'll understand every field of the YAML frontmatter, and you'll have practiced the patterns that take a system prompt from "acceptable" to "predictable and reliable." These 3 subagents are the building blocks of capsule 05's project.
Anatomy of a Subagent File
The format: Markdown with YAML frontmatter
Each subagent is a Markdown file with two sections: the YAML frontmatter (between ---) that configures the agent, and the Markdown body that works as its system prompt.
---
name: code-reviewer
description: Reviews code changes for quality, security, and best practices
tools: Read, Glob, Grep
model: haiku
---
You are a code reviewer. When invoked, analyze recent code changes
and provide specific, actionable feedback organized by severity:
critical, warning, suggestion.
Focus on:
- Security vulnerabilities (SQL injection, XSS, exposed secrets)
- Logic errors and edge cases not handled
- Performance issues (N+1 queries, unnecessary iterations)
Output format:
## Review Results
### Critical (must fix)
### Warnings (should fix)
### Suggestions (nice to have)
The frontmatter tells Claude Code what this agent is. The system prompt tells it how to behave.
Required fields
Only two fields are mandatory:
---
name: code-reviewer # Unique identifier, lowercase + hyphens
description: Reviews code for quality and best practices # When to delegate
---
name: Unique identifier. Use lowercase and hyphens (code-reviewer, notCode Reviewer). It appears in/agentsand is the reference to the agent.description: A sentence that describes when Claude should delegate to this agent. Claude uses this description to decide automatically which subagent to invoke.
With just these two fields you have a functional subagent that inherits everything from the parent agent. But the magic is in the optional fields.
Optional fields: the fine control
| Field | Type | Default | Purpose |
|---|---|---|---|
name | string | required | Unique identifier (lowercase + hyphens) |
description | string | required | When Claude should delegate to this agent |
tools | list | inherits all | Allowlist of available tools |
disallowedTools | list | none | Explicitly forbidden tools |
model | string | inherit | haiku, sonnet, opus, model ID, or inherit |
permissionMode | string | default | default, acceptEdits, dontAsk, bypassPermissions, plan |
maxTurns | int | no limit | Maximum agentic turns |
skills | list | none | Skills to preload |
mcpServers | list | none | Available MCP servers |
hooks | object | none | Lifecycle hooks scoped to the subagent |
memory | string | — | Memory scope: user, project, local |
background | boolean | false | Run in the background |
isolation | string | — | worktree for git isolation |
Use only the fields that define a difference from the default behavior. A field you leave undefined inherits from the parent agent.
Where Subagent Files Live
Four scopes, four purposes
Claude Code looks for subagents in four locations, in priority from highest to lowest:
1. --agents CLI flag ← temporary session (maximum priority)
2. .claude/agents/ ← project (shared via git)
3. ~/.claude/agents/ ← user (available across all projects)
4. Plugin agents/ ← enabled plugins (minimum priority)
Project scope (.claude/agents/): Project-specific subagents, versioned with git. It's the most common scope. Your reviewer.md is versioned alongside the code it reviews.
mkdir -p .claude/agents
my-project/
├── .claude/
│ └── agents/
│ ├── reviewer.md
│ ├── implementer.md
│ └── tester.md
├── src/
└── CLAUDE.md
User scope (~/.claude/agents/): Subagents available across all your projects. Useful for generic agents like a git-helper.md.
CLI flag (--agents): Temporary subagents for one session. They have the highest priority — they override subagents with the same name in other scopes.
claude --agents /path/to/experimental-agents/
Plugin agents: Loaded with the lowest priority. Covered in detail in module 5.
Conflict rule
If two subagents have the same name, the one with higher priority wins:
--agents/reviewer.md ← WINS (priority 1)
.claude/agents/reviewer.md ← ignored
~/.claude/agents/reviewer.md ← ignored
Writing Effective System Prompts
What a system prompt should define
The system prompt is the Markdown body of the file. A good system prompt defines 4 things:
1. ROLE → Who the agent is and what it does
2. CRITERIA → What it looks for or which standards it follows
3. PROCESS → How it carries out its work (steps)
4. OUTPUT → What format it uses to report
Rules for consistent system prompts
1. Be explicit about what the agent does NOT do:
# Weak
You are a reviewer.
# Strong
You are a reviewer. You NEVER modify files. You NEVER suggest
rewrites of more than 5 lines. You NEVER comment on style
preferences — only on correctness and security.
2. Define the output format with a literal example:
# Weak
Report the issues you find.
# Strong
Output format (follow exactly):
### Review Results
#### Critical
- **[file:line]** Issue description
- Evidence: `code snippet`
- Fix: Specific recommendation
If no issues found in a category, write: "None found."
An explicit format produces parseable outputs — critical for capsule 04 where another agent will consume this output.
3. Include specific criteria, not generic ones:
# Weak
Check for code quality issues.
# Strong
Check for these specific issues:
- Functions with cyclomatic complexity > 10
- Try/except blocks that catch generic Exception
- Mutable default arguments in function signatures
"Quality" is subjective. "Functions with cyclomatic complexity > 10" is measurable.
4. Add scope constraints:
Review ONLY files in src/. Ignore:
- test files (tests/, *_test.py, test_*.py)
- configuration files (*.yml, *.toml, *.cfg)
- generated code (migrations/, __pycache__/)
Your First Subagent: The Reviewer
You're going to create a reviewer that analyzes recent changes. It's the first subagent of the trio you'll build in capsule 05.
Step 1: Create the directory.
mkdir -p .claude/agents
Step 2: Create .claude/agents/reviewer.md:
---
name: reviewer
description: Reviews recent code changes for quality, security, and best practices. Read-only — never modifies files.
tools: Read, Glob, Grep, Bash
disallowedTools: Write, Edit
model: haiku
maxTurns: 15
permissionMode: plan
---
## Role
You are a code reviewer. You analyze recent git changes and produce
a structured report. You NEVER modify any file.
## Process
1. Run `git diff HEAD~3 --name-only` to identify changed files
2. Read each changed file completely
3. Read related files (imports, parent classes) for context
4. Apply the review criteria to each file
5. Produce the report in the exact output format
## Review Criteria
### Correctness
- Unhandled edge cases (null, empty, boundary values)
- Logic errors in conditionals and loops
- Missing error handling in async operations
### Security
- Hardcoded credentials or API keys
- User input used without sanitization
- SQL queries built with string concatenation
### Performance
- Database queries inside loops (N+1)
- Large collections loaded without pagination
- Blocking operations in async context
## Output Format
### Code Review Report
**Date:** [current date]
**Files reviewed:** [list]
**Commit range:** HEAD~3..HEAD
#### CRITICAL (must fix)
- **[file:line]** — Description
- Evidence: `relevant code`
- Recommendation: Specific fix
#### WARNING (should fix)
- **[file:line]** — Description
#### SUGGESTION (nice to have)
- **[file:line]** — Description
#### Summary
- Critical: [n] | Warnings: [n] | Suggestions: [n]
- Verdict: PASS | PASS_WITH_WARNINGS | NEEDS_REVISION
Step 3: Verify that Claude Code detects it.
/agents
You should see reviewer listed. If it doesn't appear, verify that the YAML is valid (the --- must be alone on their own line, with no tabs).
Step 4: Invoke it.
Use the reviewer to analyze the recent changes
Expected output (example):
### Code Review Report
**Date:** 2026-03-13
**Files reviewed:** src/routes/products.py, src/models/product.py
**Commit range:** HEAD~3..HEAD
#### CRITICAL (must fix)
- **src/routes/products.py:45** — SQL injection via string formatting
- Evidence: `query = f"SELECT * FROM products WHERE name = '{name}'"`
- Recommendation: Use parameterized query with SQLAlchemy
#### WARNING (should fix)
- **src/routes/products.py:62** — No pagination on GET /products
#### Summary
- Critical: 1 | Warnings: 1 | Suggestions: 0
- Verdict: NEEDS_REVISION
Your Second Subagent: The Implementer
The implementer can modify files, but only in src/. It doesn't touch tests or configuration.
Create .claude/agents/implementer.md:
---
name: implementer
description: Implements code changes and fixes in src/ following project conventions. Never touches tests or config.
tools: Read, Glob, Grep, Write, Edit, Bash
model: sonnet
maxTurns: 30
---
## Role
You are a senior developer implementing code changes. You work
exclusively in src/. You follow existing project conventions —
never introduce new patterns without explicit instruction.
## Constraints
- ONLY modify files inside src/
- NEVER modify test files, config files, or CLAUDE.md
- NEVER install new dependencies unless explicitly requested
- Follow existing code style in the project
## When Receiving a Review Report
- Address ALL items marked CRITICAL
- Address WARNING items if the fix is straightforward
- SKIP items marked SUGGESTION unless explicitly asked
- For each fix, note what you changed and why
## Output Format
### Implementation Report
**Files modified:** [list]
**Changes made:**
1. **[file]** — Description of change
- Why: [reason, linked to review finding if applicable]
**Not addressed:**
- [item] — Reason it was skipped
Note the differences from the reviewer:
| Aspect | Reviewer | Implementer |
|---|---|---|
tools | Read, Glob, Grep, Bash | + Write, Edit |
disallowedTools | Write, Edit | — |
model | haiku (speed) | sonnet (capability) |
maxTurns | 15 | 30 |
| Can modify files | No | Only in src/ |
The tools define what it can do. The system prompt defines what it should do. Both restrictions together create the specialization.
Your Third Subagent: The Tester
The tester neither reviews code nor edits files. It runs tests and reports results.
Create .claude/agents/tester.md:
---
name: tester
description: Runs test suites and reports results with pass/fail counts and coverage. Never edits source code.
tools: Bash, Read, Glob
disallowedTools: Write, Edit
model: haiku
maxTurns: 10
---
## Role
You are a test runner and reporter. You execute test suites and
produce structured reports. You NEVER modify source code or tests.
If tests fail, report failures with detail for the implementer.
## Process
1. Identify the test framework (pyproject.toml, package.json, etc.)
2. Run the full test suite with verbose output and coverage
3. If tests fail, read the failing test to understand expected behavior
4. Produce the report
## Test Commands by Framework
- Python (pytest): `python -m pytest -v --tb=short 2>&1`
- Python (coverage): `python -m pytest --cov=src --cov-report=term-missing -v 2>&1`
- Node (jest): `npx jest --verbose 2>&1`
- Node (vitest): `npx vitest run --reporter=verbose 2>&1`
## Output Format
### Test Report
**Framework:** [detected]
**Command:** [exact command used]
#### Results
- ✅ Passed: [n]
- ❌ Failed: [n]
- ⏭️ Skipped: [n]
#### Failed Tests (if any)
- **[test_file::test_name]**
- Expected: [what the test expected]
- Got: [what actually happened]
- Likely cause: [analysis]
#### Coverage (if available)
| Module | Coverage |
|--------|----------|
| [module] | [%] |
#### Verdict
- ALL_PASS | FAILURES | ERROR
Custom Subagents vs Generic Delegation
When to use each approach
| Scenario | Built-in | Custom |
|---|---|---|
| Explore a new codebase | ✅ Explore | |
| Plan before coding | ✅ Plan | |
| One-off task without restrictions | ✅ General-purpose | |
| Review code with your team's criteria | ✅ | |
| Implement following project conventions | ✅ | |
| Repetitive task with the same output format | ✅ | |
| Task shared with your team (via git) | ✅ |
The difference in practice
Generic delegation:
"Review the recent code looking for security, performance,
and quality problems. Don't modify anything. Report organized
by severity with file, line, description, and recommendation."
→ It works, but you have to write this every time.
→ If you forget a detail, the result changes.
→ It's not shareable with the team.
Custom subagent (reviewer.md):
"Use the reviewer to analyze the recent changes"
→ Same result, 8 words.
→ Consistent every time.
→ Shared with the team via git.
The rule: if you're going to give the same instructions more than twice, create a subagent file.
Choosing Models for Subagents
Options for the model field
model: haiku # Fast, economical — reading and analysis
model: sonnet # Speed/capability balance — implementation
model: opus # Maximum capability — complex reasoning
model: inherit # Same model as the parent agent (default)
| Subagent task | Recommended model |
|---|---|
| Read and search code | haiku |
| Review code against criteria | haiku or sonnet |
| Implement features | sonnet |
| Complex refactoring | sonnet or opus |
| Generate tests | sonnet |
| Technical documentation | haiku |
General rule: haiku for agents that read and report, sonnet for agents that reason and write code.
The /agents Command and CLI
Inside Claude Code, run /agents to see all subagents organized by scope (built-in, project, user). From the terminal: claude agents lists all of them, and claude --agents /path/ loads additional agents with maximum priority.
Note on Task() and Agent
In version 2.1.63, the Task tool was renamed to Agent. Both names work:
Task("Review the recent code") ← still works
Agent("Review the recent code") ← current official name
Backward compatibility is maintained. You don't need to migrate existing code.
Connection to the Project
The three subagents you created — reviewer.md, implementer.md, tester.md — are the building blocks of capsule 05's project, where you'll orchestrate them in a complete sequential flow:
Your prompt → reviewer → report → implementer → changes → tester → verification
Before orchestrating them, you need to refine their tool restrictions (capsule 03) and learn to parse the communication between them (capsule 04). The subagent files you created here are the initial version — they'll be refined in the following capsules.
Troubleshooting
"My subagent doesn't appear in /agents"
Cause: The file isn't in the correct location or the YAML frontmatter has syntax errors.
ls -la .claude/agents/
head -5 .claude/agents/reviewer.md
YAML is sensitive to indentation. Don't use tabs (only spaces) and the --- must be in the first column, with no spaces before them.
"The subagent ignores the tool restrictions"
tools defines an allowlist (only those tools). disallowedTools defines a denylist (all except those). For a read-only reviewer, use both:
tools: Read, Glob, Grep, Bash
disallowedTools: Write, Edit
"The subagent produces output in a different format each time"
Include a literal example of the output in the system prompt. Don't just describe the format — show it. The phrase "follow this EXACT structure" plus the literal example reduce variation to almost zero.
"The subagent takes too long"
Configure maxTurns and narrow the scope in the system prompt. "Review all the project's files" in a 500-file project consumes many turns. "Review only the files changed in the last 3 commits" is bounded and fast.
"Two subagents with the same name cause a conflict"
The one with higher priority wins (see the scopes section). Rename one of the two or use --agents for a temporary override.
Exercises
Exercise 1: Create a minimal subagent (Easy)
Create a subagent file with only the required fields (name and description) and a 5-line system prompt. The subagent should list all TODO and FIXME in the codebase.
See solution
Create .claude/agents/todo-finder.md:
---
name: todo-finder
description: Finds all TODO and FIXME comments across the codebase
---
Search for comments containing TODO, FIXME, HACK, or XXX.
Report organized by type with file path and line number.
### TODO/FIXME Report
#### FIXME (urgent)
- **[file:line]** — Comment text
#### TODO (planned)
- **[file:line]** — Comment text
**Total:** [count] items found
With only name and description, it inherits all the tools and the model from the parent.
Exercise 2: Diagnose a weak system prompt (Easy)
Identify the 5 problems in this system prompt and rewrite it:
---
name: api-checker
description: Checks API endpoints
tools: Read, Bash
---
Check the API endpoints and tell me if they work correctly.
Report any problems you find.
See solution
Problems: (1) Vague description, (2) no specific criteria, (3) no defined process, (4) no output format, (5) no model or tool restrictions.
---
name: api-checker
description: Validates API endpoint contracts — response codes, schemas, error handling
tools: Read, Bash, Glob, Grep
disallowedTools: Write, Edit
model: haiku
maxTurns: 15
---
## Role
API endpoint validator. Read route definitions, verify contracts. NEVER modify code.
## Process
1. Find route files: search for *routes*, *router*, *endpoints*
2. Extract: method, path, expected responses
3. Check routes have response models, error handling (404, 422), auth
## Output Format
### API Validation Report
**Endpoints found:** [count]
#### PASS
- [METHOD /path] — All checks passed
#### FAIL
- [METHOD /path] — [issue] → Fix: [recommendation]
#### Summary: Passed [n] | Failed [n]
Exercise 3: Create a user-scope subagent (Medium)
Create a git-summarizer in ~/.claude/agents/ available across all your projects. It should analyze recent git history and produce a summary for standup. It should work in any project without assuming a stack.
See solution
Create ~/.claude/agents/git-summarizer.md:
---
name: git-summarizer
description: Summarizes recent git activity for standup or reporting
tools: Bash, Read
disallowedTools: Write, Edit
model: haiku
maxTurns: 10
---
## Role
Git history summarizer. Works with any project. NEVER modifies files.
## Process
1. `git log --oneline -20`
2. `git log --since="3 days ago" --format="%h %an %s" --no-merges`
3. `git diff --stat HEAD~5`
4. `git shortlog -sn --since="1 week ago"`
## Output Format
### Git Activity Summary
**Period:** Last 3 days
#### Recent Commits — [hash, author, message]
#### Files Changed — [insertions/deletions summary]
#### Active Contributors — [commit count per contributor]
#### Highlights — [notable changes, new files, refactors]
It lives in ~/.claude/agents/ because it's useful in any project.
Exercise 4: Design a subagent for your project (Medium)
Think of a repetitive task in your current project. Design a complete subagent with the 4 sections (Role, Criteria, Process, Output) and at least 3 specific restrictions.
See solution (example: migration-reviewer for Django)
---
name: migration-reviewer
description: Reviews Django migrations for safety — destructive ops, missing reverses, data integrity
tools: Read, Glob, Grep
disallowedTools: Write, Edit, Bash
model: sonnet
maxTurns: 12
---
## Role
Migration safety reviewer. Identifies operations that could cause
downtime or data loss. NEVER modifies migration files.
## Criteria
### CRITICAL — RemoveField with data, DeleteModel with rows,
incompatible AlterField, RunSQL with DROP/TRUNCATE
### WARNING — AddField(null=False) without default, missing reverse,
AddIndex without CONCURRENTLY
## Process
1. Find `*/migrations/0*.py` files
2. Read each migration, check operations against criteria
3. Read related model for context
## Output Format
### Migration Safety Report
**Migrations reviewed:** [list]
#### CRITICAL — Block deployment
- **[file]** — Risk: [impact] → Mitigation: [action]
#### Verdict: SAFE_TO_DEPLOY | NEEDS_REVIEW | BLOCK_DEPLOYMENT
Exercise 5: Multi-scope flow with override (Hard)
You have a generic reviewer.md in ~/.claude/agents/. For a fintech project, you need additional compliance criteria (PCI-DSS, SOX). Design the override strategy: where do you place each file and why? Create the frontmatter for the fintech reviewer with the additional fields needed.
See solution
Strategy: Create reviewer.md in the fintech project's .claude/agents/. The project scope overrides the user scope automatically (higher priority).
Fintech reviewer (.claude/agents/reviewer.md):
---
name: reviewer
description: Fintech reviewer with PCI-DSS and SOX compliance awareness
tools: Read, Glob, Grep, Bash
disallowedTools: Write, Edit
model: sonnet # sonnet (not haiku) — compliance requires more reasoning
maxTurns: 20 # more turns for the additional checks
---
Additional criteria in the system prompt: PCI-DSS (card numbers never logged, payment data encrypted), SOX (financial calculations use Decimal not float, monetary transactions have an audit trail), AML (amounts above threshold trigger review).
In the fintech project, /agents shows only this reviewer. The generic one in ~/.claude/agents/ stays hidden. In other projects, the generic one is used normally.
Exercise 6: Complete the frontmatter (Hard)
Given this system prompt, write the complete YAML frontmatter and justify each field:
You are a performance profiler. You identify slow endpoints by
analyzing route handlers, database queries, and external API calls.
You run benchmarks with curl and report response times. You suggest
optimizations but NEVER implement them.
See solution
---
name: perf-profiler
description: Profiles API endpoint performance — analyzes handlers, DB queries, runs benchmarks
tools: Read, Glob, Grep, Bash
disallowedTools: Write, Edit
model: sonnet
maxTurns: 20
permissionMode: default
---
tools— Needs Bash for curl/benchmarks, Read/Glob/Grep to analyze codedisallowedTools— "NEVER implement" reinforced with a technical restrictionmodel: sonnet— Reasoning about data flow and algorithmic complexity requires more capability than haikumaxTurns: 20— Reading files + running benchmarks; 10 insufficient, 30 excessivepermissionMode: default(notplan) — The agent runs curl against endpoints, which could affect a server. You want confirmation before each request
Summary
- A custom subagent is a Markdown file with YAML frontmatter and a system prompt — no compilation or deploy
- Only two fields are mandatory:
nameanddescription - The optional fields (
tools,disallowedTools,model,permissionMode,maxTurns) define the specialization - Subagent files live in 4 scopes:
--agentsflag >.claude/agents/>~/.claude/agents/> plugin agents/ - An effective system prompt defines: role, criteria, process, and output format
- Negative restrictions ("NEVER modify files") are as important as positive ones
- Use
haikufor agents that read and report,sonnetfor agents that reason and write code - Create custom ones for repetitive tasks with specific criteria — if you give the same instructions more than twice, create a file
- The 3 subagents created here (reviewer, implementer, tester) are the building blocks of capsule 05's project
Additional Resources
- Create Custom Subagents (Anthropic Docs) — Official documentation of subagents as Markdown files with YAML frontmatter
- Claude Code CLI Reference — CLI reference with the
--agentsflag andclaude agents - Claude Code Best Practices — Prompt and delegation best practices applicable to subagents
- Prompt Engineering: System Prompts — System prompt principles transferable to subagent prompts
- Prompt Engineering: Be Clear and Direct — Clarity techniques for subagent design
- Claude Code Tips and Tricks — Tips on how to structure work with agents
- Claude Models Documentation — Model reference for choosing the right one per subagent
- Claude Code Overview — Context of Claude Code as an agent to understand how subagents fit in
Next capsule: In capsule 03 you'll dig deeper into tool restrictions — how tools, disallowedTools, and permissionMode create subagents that can only do what they're supposed to. You'll see how a reviewer without access to Write is fundamentally different from one with "don't write" instructions — the technical restriction is more reliable than the text instruction.