Compositions
Agent teams gain production-grade behavior when composed with other Claude Code primitives. Each composition addresses a specific gap: hooks enforce quality, skills package workflows, MCP connects external data, memory enables learning, worktrees prevent conflicts, and CLAUDE.md sets team conventions.
Teams + Hooks: Quality Gates
Hooks are the enforcement mechanism for team workflows. Three hook events target team coordination specifically.
TeammateIdle
Fires when a teammate is about to go idle. Exit code 2 keeps the teammate working.
{
"hooks": {
"TeammateIdle": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'INPUT=$(cat); NAME=$(echo \"$INPUT\" | jq -r \".teammate_name\"); if [ \"$NAME\" = \"implementer\" ]; then PENDING=$(find .claude/tasks/ -name \"*.json\" -exec grep -l \"pending\" {} + 2>/dev/null | wc -l); if [ \"$PENDING\" -gt 0 ]; then echo \"$PENDING tasks still pending -- keep working\" >&2; exit 2; fi; fi; exit 0'"
}
]
}
]
}
}This prevents the implementer from going idle while pending tasks remain. The conditional on teammate_name lets other teammates idle normally.
TaskCreated
Enforce naming conventions or require acceptance criteria before tasks enter the queue:
#!/bin/bash
# .claude/hooks/validate-task.sh
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
TASK_DESC=$(echo "$INPUT" | jq -r '.task_description // empty')
# Require action verb prefix
if ! echo "$TASK_SUBJECT" | grep -qE '^(Add|Fix|Implement|Refactor|Update|Remove|Test|Review)'; then
echo "Task subject must start with an action verb" >&2
exit 2
fi
# Require description for non-trivial tasks
if [ -z "$TASK_DESC" ] && ! echo "$TASK_SUBJECT" | grep -qi "review\|test"; then
echo "Non-review tasks require a description" >&2
exit 2
fi
exit 0Exit code 2 prevents task creation and sends the error message back to the model as feedback. The model adjusts and retries.
TaskCompleted
Validate work before allowing tasks to close. This is the most impactful team hook — it creates hard quality gates:
#!/bin/bash
# .claude/hooks/verify-completion.sh
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
# Implementation tasks must pass tests
if echo "$TASK_SUBJECT" | grep -qiE '(implement|add|fix|refactor)'; then
if ! npm test 2>/dev/null; then
echo "Tests must pass before marking implementation tasks complete" >&2
exit 2
fi
fi
# Writing tasks must pass lint
if echo "$TASK_SUBJECT" | grep -qiE '(implement|add|fix|refactor|write)'; then
if ! npm run lint 2>/dev/null; then
echo "Linting must pass before marking this task complete" >&2
exit 2
fi
fi
exit 0All Team Hook Events
| Event | Fires When | Exit 2 Behavior |
|---|---|---|
TeammateIdle | Teammate about to go idle | Keeps teammate working |
TaskCreated | Task being created | Prevents creation, feedback to model |
TaskCompleted | Task being marked complete | Prevents completion, feedback to model |
SubagentStart | Subagent begins execution | N/A |
SubagentStop | Subagent completes | N/A |
WorktreeCreate | Worktree being created | Fails creation |
WorktreeRemove | Worktree being removed | Cannot block (logged only) |
Teams + Skills: Packaged Workflows
Skills turn team architectures into reusable commands. The skill content becomes instructions for the lead.
# .claude/skills/review-team/SKILL.md
---
name: review-team
description: Spin up a full review team for a PR
disable-model-invocation: true
allowed-tools: Bash(gh *)
---
## PR Context
- PR diff: !`gh pr diff`
- Changed files: !`gh pr diff --name-only`
## Instructions
Create an agent team to review this PR with three reviewers:
1. **Security reviewer**: Focus on auth, input validation, injection,
secrets exposure. Use the security-reviewer agent type.
2. **Performance reviewer**: Focus on N+1 queries, unnecessary
allocations, missing indexes, expensive operations in hot paths.
3. **Correctness reviewer**: Focus on logic errors, edge cases,
error handling, race conditions, and whether tests cover the changes.
Have each reviewer submit their findings as a structured report.
After all three complete, synthesize a unified review with:
- Critical issues (must fix before merge)
- Suggestions (should fix, not blocking)
- Nits (style/preference)
Post the unified review as a PR comment using `gh pr comment`.Invoke with /review-team. The ! backtick syntax executes shell commands at skill load time, injecting the PR diff and file list into the skill context before the lead starts.
disable-model-invocation: true means the skill content is the complete instruction set. No additional model interpretation layer.
A more targeted skill that spawns a vertical-slice implementation team:
# .claude/skills/vertical-slice/SKILL.md
---
name: vertical-slice
description: Implement a feature as a vertical slice with parallel agents
disable-model-invocation: false
allowed-tools: Bash(git *) Bash(npm *)
---
## Context
- Current branch: !`git branch --show-current`
- Recent commits: !`git log --oneline -5`
## Instructions
Implement $ARGUMENTS as a vertical slice using an agent team:
1. **API agent** (Sonnet, worktree isolation):
- Implement the API route and service layer
- Write integration tests for the endpoint
- Domain: src/api/, src/services/, __tests__/api/
2. **UI agent** (Sonnet, worktree isolation):
- Implement the React component and page
- Wire up data fetching to the new API
- Domain: src/components/, src/pages/, __tests__/ui/
3. **Schema agent** (Sonnet):
- Create the database migration
- Update the Prisma schema and generate types
- Domain: prisma/, src/models/
Schema agent completes first. API and UI agents work in parallel after.
Each agent runs tests in its domain before marking tasks complete.Teams + MCP: External Data Sources
MCP servers provide external data to teammate workflows. There is a key distinction in how MCP servers load:
- Subagents: can scope MCP servers via the
mcpServersfield in their agent definition - Agent team teammates: load MCP servers from project and user settings only
# .claude/agents/db-analyst.md
---
name: db-analyst
description: Analyzes database performance and query patterns
tools: Read, Grep, Bash
mcpServers:
- postgres-mcp:
type: stdio
command: npx
args: ["-y", "@postgres-mcp/server"]
env:
DATABASE_URL: "${DATABASE_URL}"
- github
---
You are a database performance analyst. Use the PostgreSQL MCP tools
to analyze query plans, identify slow queries, and recommend index
optimizations. Cross-reference with the application code to understand
query origins.When this agent runs as a subagent, the mcpServers field applies. When used as a teammate in an agent team, it does not — configure MCP servers in .mcp.json instead.
This asymmetry matters for team design. If a teammate needs a specific MCP server, that server must be available project-wide.
Teams + Memory: Cross-Session Learning
Subagent persistent memory enables teammates to improve across sessions. A code reviewer that remembers patterns from previous reviews catches issues faster.
# .claude/agents/code-reviewer.md
---
name: code-reviewer
description: Reviews code for quality and best practices
tools: Read, Grep, Glob, Bash
memory: project
model: sonnet
---
You are a code reviewer. As you review code:
1. Check your agent memory for patterns and conventions you've seen
in this codebase before starting the review.
2. Apply known conventions to your review feedback.
3. After completing the review, update your agent memory with:
- New patterns or conventions you discovered
- Common issues you found (to check for proactively next time)
- Architectural decisions that should be preserved
Memory location: .claude/agent-memory/code-reviewer/Memory Scopes
| Scope | Location | When to use |
|---|---|---|
user | ~/.claude/agent-memory/<agent>/ | Knowledge applicable across all projects |
project | .claude/agent-memory/<agent>/ | Project-specific, shareable via git |
local | .claude/agent-memory-local/<agent>/ | Project-specific, not committed to git |
When memory is enabled, the subagent's system prompt automatically includes the first 200 lines (or 25KB) of MEMORY.md from the memory directory. The Read, Write, and Edit tools are auto-enabled so the subagent can maintain its memory files.
Project-scoped memory committed to git means the entire team benefits from accumulated knowledge — a reviewer's memory becomes shared team knowledge.
Teams + Worktrees: Filesystem Isolation
Worktrees are the filesystem-level isolation mechanism for parallel agents. Without them, two teammates editing the same file cause silent overwrites.
# Start Claude in a named worktree
claude --worktree feature-auth
# Auto-generate a worktree name
claude --worktreeWorktrees are created at <repo>/.claude/worktrees/<name> and branch from origin/HEAD.
For subagents, add isolation: worktree to the frontmatter:
---
name: parallel-implementer
description: Implements features in isolated worktrees
isolation: worktree
---Cleanup Behavior
| Condition | Behavior |
|---|---|
| No changes made | Worktree and branch removed automatically |
| Changes exist | Claude prompts to keep or remove |
| Orphaned by crash | Auto-removed at startup after cleanupPeriodDays |
Copying Gitignored Files
Worktrees don't include gitignored files by default. Use .worktreeinclude to copy necessary files:
# .worktreeinclude
.env
.env.local
config/secrets.jsonConflict Detection
The open-source tool "Clash" detects conflicts between worktrees before they happen. It runs git merge-tree (three-way merge simulation) between all worktree pairs without touching working directories. Run it before merging parallel work back together.
Teams + CLAUDE.md: Team Conventions
CLAUDE.md is loaded by every teammate automatically. Use it to set team-wide coordination rules:
# CLAUDE.md
## Agent Team Conventions
When working as part of an agent team:
- Always check the shared task list before starting new work
- Claim tasks before beginning implementation
- Send a message to the lead when you complete a task with a summary
- If you discover work not in the task list, message the lead
to create a new task rather than doing it ad hoc
- Never modify files owned by another teammate's task
## File Ownership
When tasks are assigned, respect these boundaries:
- Frontend: src/components/, src/pages/, src/styles/
- Backend: src/api/, src/services/, src/middleware/
- Database: src/models/, src/migrations/, prisma/
- Tests: __tests__/, src/**/*.test.tsFile ownership rules in CLAUDE.md are the simplest way to prevent file conflicts without worktree isolation. They work because every teammate loads the same instructions. Combine with TaskCompleted hooks that verify no out-of-scope files were modified for hard enforcement:
#!/bin/bash
# .claude/hooks/enforce-file-ownership.sh
# TaskCompleted hook: verify teammate only modified files in their domain
INPUT=$(cat)
TEAMMATE=$(echo "$INPUT" | jq -r '.teammate_name // empty')
# Map teammate names to allowed directories
case "$TEAMMATE" in
frontend-*)
ALLOWED="src/components/|src/pages/|src/styles/"
;;
backend-*)
ALLOWED="src/api/|src/services/|src/middleware/"
;;
db-*)
ALLOWED="src/models/|src/migrations/|prisma/"
;;
*)
exit 0 # No ownership rules for unnamed teammates
;;
esac
# Check which files were modified (staged + unstaged)
MODIFIED=$(git diff --name-only HEAD 2>/dev/null)
VIOLATIONS=$(echo "$MODIFIED" | grep -vE "^($ALLOWED)" | grep -v '^$')
if [ -n "$VIOLATIONS" ]; then
echo "File ownership violation for $TEAMMATE. Modified files outside allowed scope:" >&2
echo "$VIOLATIONS" >&2
exit 2
fi
exit 0Wire it into settings:
{
"hooks": {
"TaskCompleted": [
{
"hooks": [
{
"type": "command",
"command": ".claude/hooks/enforce-file-ownership.sh"
}
]
}
]
}
}