ES · Leer en español Explore for free

Recipe · Intermediate · 10 min

Claude Code subagents: split the work and save context

Create a subagent in .claude/agents/ with name, description, tools and model, and use it as a read-only reviewer of your diff before every commit.

Tested with Claude Code 2.1.316 · reviewed

The problem

You ask Claude Code to review your changes, search the whole repository or run the tests, and the conversation fills up with diffs, search results and logs you never look at again. Context runs out fast and Claude starts forgetting what you discussed at the beginning. And for a review, you don't want it to be able to edit files "along the way".

The solution

A subagent is a specialized assistant with its own context window, its own instructions and its own list of tools. Claude hands it the task, the subagent works separately, and only its summary comes back to your conversation.

1. Choose where to save it

LocationScope
.claude/agents/<name>.mdThis project (committed to git and shared with the team)
~/.claude/agents/<name>.mdAll your projects

Since version 2.1.198, /agents no longer opens a wizard. In 2.1.316 it prints this:

The /agents wizard has been removed.

Ask Claude to create or update subagents for you (e.g. "create a code-reviewer subagent that ..."),
or edit the files directly:
  • .claude/agents/       (this project)
  • ~/.claude/agents/     (all projects)

You can ask Claude "create a subagent that…" or write the file yourself, as in this recipe.

2. Know the frontmatter fields

Only name and description are required.

FieldWhat it does
nameUnique identifier (no :). This is what counts, not the file name.
descriptionWhen Claude should use it. Claude decides whether to delegate based on this text.
toolsAllowed tools. If omitted, it inherits every tool from the conversation.
disallowedToolsDenied tools (applied before tools).
modelsonnet, opus, haiku, fable, a full model ID, or inherit (your conversation's model).

There are more optional fields (hooks, permissionMode, maxTurns, effort, isolation…). Misspelled fields are ignored silently.

3. Create the read-only "revisor" (reviewer) subagent

At the root of your project:

mkdir -p .claude/agents .claude/hooks

Save this as .claude/agents/revisor.md:

---
name: revisor
description: Reviews the uncommitted diff (git diff) before committing and reports bugs, security risks and missing tests. Use it when the user asks to review their changes or before committing. Does not modify files.
tools: Read, Grep, Glob, Bash
model: sonnet
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/solo-git-lectura.py\""
---

You are a demanding, read-only code reviewer. You never modify files.

When invoked:
1. Run `git status --short` and then `git diff HEAD`, each in its own Bash call (no `&&` or `|`).
2. Use Read on the full file of each change if you need context.
3. Look for, in this order: logic bugs, security issues (secrets, injections), unhandled edge cases and missing tests.

Reply in this format:
- **Blocking** (must be fixed before the commit): file:line, what fails and how to fix it.
- **Could be better**: same, shorter.
- **Verdict**: "Ready to commit" or "Don't commit yet".

If you find no problems, say so in one line. Don't invent problems.

4. Limit Bash to read-only git commands

A tools list without Write or Edit prevents editing with those tools, but the reviewer needs Bash to see the diff, and Bash can write. The tools field doesn't accept rules like Bash(git diff *), so the documentation recommends a PreToolUse hook. Save this as .claude/hooks/solo-git-lectura.py:

#!/usr/bin/env python3
"""Reviewer PreToolUse hook: only allows git diff/status/log/show; blocks everything else (exit 2)."""
import json, re, sys

cmd = json.load(sys.stdin).get("tool_input", {}).get("command", "")
if "--output" not in cmd and re.fullmatch(r"git (diff|status|log|show)( [^;&|<>`$]*)?", cmd.strip()):
    sys.exit(0)
print("Blocked: the reviewer can only use git diff, status, log or show.", file=sys.stderr)
sys.exit(2)
chmod +x .claude/hooks/solo-git-lectura.py

Exit code 2 blocks the command and sends the message back to the subagent. It also blocks ;, &&, |, redirections and --output, so nobody can sneak a write in after a git diff.

5. Use it

If .claude/agents/ didn't exist when you opened the session, restart Claude Code. Then:

claude -p "use the revisor subagent to review my changes"

In an interactive session you can also type @"revisor (agent)" review my changes to force that subagent to run.

Check it

Before using it in your project, test the hook by hand:

echo '{"tool_input":{"command":"git diff HEAD"}}' | python3 .claude/hooks/solo-git-lectura.py; echo $?
echo '{"tool_input":{"command":"git diff; rm -rf src"}}' | python3 .claude/hooks/solo-git-lectura.py; echo $?

Expected output: 0 for the first one; the "Blocked…" message and 2 for the second.

Real test with 2.1.316 in a repository with this uncommitted change in precio.py:

def total_con_iva(precio, iva=0.21):
    """Devuelve el precio con IVA incluido."""
    return precio * iva


def descuento(precio, porcentaje):
    return precio - precio * porcentaje / 10

Claude delegated to revisor (Agent tool, subagent_type: "revisor"). Inside the subagent, the hook blocked ls -la . and git status && ls ... with its "Blocked…" message, let git diff HEAD through, and the final answer (in Spanish, the prompt's language) found both bugs:

1. precio.py:3, total_con_iva: returns only the tax, not the total. It does precio * iva,
   so total_con_iva(100) gives 21.0 instead of 121.0. [...] Fix: precio * (1 + iva).
2. precio.py:7, descuento: divides by 10 instead of 100. descuento(100, 10) gives 0.0
   instead of 90.0 [...] Fix: / 100.

Only that summary reached your conversation, not the full diff or the file reads.

Why it works

Each subagent starts with a fresh context window: it doesn't see your history, only its system prompt, the task Claude writes for it, the CLAUDE.md files and a git status snapshot. It works separately and returns only its summary, so the noise (diffs, searches, test logs) doesn't fill your context. Claude decides when to delegate based on the description, and tools plus the hook limit what it can do. Source: official subagents documentation.

When to use them: research that reads many files, code review, running tests and summarizing failures, and any self-contained task that should return a summary or have limited tools.

When not to: tasks with a lot of back-and-forth with you, phases that share a lot of context (planning, implementing and testing the same thing), small quick changes, or when latency matters: the subagent starts from scratch and takes time to gather context.

Common mistakes

FAQ

Where are Claude Code subagents stored?

In .claude/agents/ for the project and in ~/.claude/agents/ for all your projects. If two share the same name, the project one wins.

Does a subagent use fewer tokens?

Not necessarily: it works in its own window and also consumes tokens. What you save is context in your main conversation, because only its summary comes back.

Can I use a cheaper model for the subagent?

Yes, with model: haiku or model: sonnet. With inherit it uses the same model as your conversation.

Can subagents launch other subagents?

Yes, up to three levels by default. To prevent it, don't include Agent in tools; the reviewer already leaves it out.

What happened to the /agents wizard?

It was removed in version 2.1.198. Now /agents only reminds you to ask Claude or to edit the files in .claude/agents/.

All recipes