Agent Project Structure
When to Use Agents vs Skills
Section titled βWhen to Use Agents vs Skillsβ| Use a skill whenβ¦ | Use an agent whenβ¦ |
|---|---|
| The workflow is sequential steps | The task requires independent judgment |
| You always want the same procedure | You want specialized domain knowledge |
You invoke it manually with /name | You want automatic delegation by Claude |
| Itβs a project-specific process | Itβs a reusable capability (code review, research) |
| No special tool restrictions needed | You want to restrict which tools it can use |
Anatomy of a Well-Structured Agent Project
Section titled βAnatomy of a Well-Structured Agent Projectβmy-ai-project/βββ .claude/β βββ settings.json β team permissions, hooks, envβ βββ settings.local.json β personal overrides (gitignored)β βββ CLAUDE.md β project instructions and contextβ ββ βββ rules/ β path-scoped instructionsβ β βββ api.md β applies to src/api/**β β βββ tests.md β applies to **/*.test.tsβ β βββ migrations.md β applies to migrations/**β β βββ frontend.md β applies to src/ui/**β ββ βββ skills/ β repeatable workflowsβ β βββ deploy/β β β βββ SKILL.mdβ β βββ new-feature/β β β βββ SKILL.mdβ β βββ hotfix/β β β βββ SKILL.mdβ β βββ release/β β βββ SKILL.mdβ ββ βββ agents/ β specialized Claude instancesβ βββ researcher.md β reads docs, never editsβ βββ code-reviewer.md β reviews, suggests, never editsβ βββ test-writer.md β writes tests onlyβ βββ security-auditor.md β security-focused read-onlyββββ .mcp.json β MCP server config (shared)βββ CLAUDE.md β project overviewβββ src/Step-by-Step: Building the Agent Layer
Section titled βStep-by-Step: Building the Agent LayerβStep 1: Write the CLAUDE.md First
Section titled βStep 1: Write the CLAUDE.md FirstβThis is your projectβs βbriefing document.β Claude reads it at the start of every session.
.claude/CLAUDE.md:
# Project: Payments API
## What This IsA REST API for processing payments. Built with Node.js + TypeScript + Postgres.Deployed to AWS Lambda via Serverless Framework.
## Stack- Runtime: Node.js 20, TypeScript 5- Framework: Express 4- Database: PostgreSQL 15, Prisma ORM- Tests: Vitest, Supertest- Deploy: Serverless Framework, AWS Lambda
## Critical Rules- NEVER commit secrets or API keys- NEVER modify migration files once they are applied- NEVER skip tests when deploying to production- ALWAYS run `npm run lint && npm test` before committing
## Build & Run- Install: `npm install`- Dev: `npm run dev`- Test: `npm test`- Test (watch): `npm run test:watch`- Lint: `npm run lint`- Build: `npm run build`- Deploy staging: `npm run deploy:staging`- Deploy prod: `npm run deploy:prod`
## Project Layout- `src/handlers/` β Lambda handler functions- `src/services/` β Business logic layer- `src/repositories/` β Database access layer- `src/middleware/` β Express middleware- `src/types/` β Shared TypeScript types- `prisma/` β Prisma schema and migrations- `tests/` β All tests (mirrors src/ structure)
## Common Patterns- All business logic goes in services/, not handlers/- Repositories handle all DB access; services never use Prisma directly- Use `AppError` class for all user-facing errors (see `src/types/errors.ts`)- Validate all input with Zod schemas in `src/validators/`Step 2: Add Path-Scoped Rules
Section titled βStep 2: Add Path-Scoped RulesβThese inject domain context automatically when Claude edits those files.
.claude/rules/migrations.md:
---paths: - "prisma/migrations/**" - "prisma/schema.prisma"---
# Database Migration Rules
CRITICAL: Never modify an existing migration file. They are immutable once applied.
To make database changes:1. Edit `prisma/schema.prisma`2. Run: `npx prisma migrate dev --name <description>`3. The generated migration file is what gets committed
When reviewing schema changes:- Check for missing indexes on foreign keys- Verify NOT NULL columns have defaults for existing rows- Test with `npx prisma migrate deploy --preview-feature` on staging first.claude/rules/api-handlers.md:
---paths: - "src/handlers/**" - "src/routes/**"---
# API Handler Rules
All handlers must:- Accept input validated by a Zod schema- Call service layer (never access DB directly)- Return responses using `formatResponse()` helper- Use `asyncHandler()` wrapper for async functions- Have a corresponding test in `tests/handlers/`
Error handling:- Throw `AppError` for client errors (4xx)- Let unknown errors bubble to the global error handler- Never catch errors silentlyStep 3: Create Focused Agents
Section titled βStep 3: Create Focused AgentsβEach agent should have a single clear responsibility and minimal tool access.
.claude/agents/researcher.md:
---name: "researcher"description: "Research documentation, APIs, and technical questions. Never modifies files."model: "claude-sonnet-4-6"tools: - Read - Grep - Glob - WebSearch - WebFetch---
You are a technical researcher. Your only job is to find and summarize information.
You NEVER edit files, write code, or run commands.
When researching:1. Search existing code and docs first before going to the web2. Cite specific file paths and line numbers when referencing code3. Summarize findings concisely β focus on what the caller needs to act on4. Flag any contradictions or ambiguities you find.claude/agents/code-reviewer.md:
---name: "code-reviewer"description: "Review code changes for bugs, security issues, and best practices. Read-only."model: "claude-opus-4-8"tools: - Read - Grep - Glob - Bash(git diff *) - Bash(git log *)---
You are a senior code reviewer. You NEVER modify files β only review and report.
Review checklist:- [ ] Logic errors and edge cases- [ ] Security vulnerabilities (injection, auth bypass, data exposure)- [ ] Performance issues (N+1 queries, missing indexes, memory leaks)- [ ] Error handling (uncaught errors, silent failures)- [ ] Missing or inadequate tests- [ ] Deviation from project conventions (see CLAUDE.md)
Format each finding:**Issue:** clear description**Severity:** critical / high / medium / low**File:** path:line**Why it matters:** one sentence**Fix:** specific suggestion.claude/agents/test-writer.md:
---name: "test-writer"description: "Write comprehensive tests for existing code. Only writes test files."model: "claude-sonnet-4-6"tools: - Read - Grep - Glob - Write - Edit - Bash(npm test) - Bash(npm run test:*)---
You write tests. You NEVER modify source files β only create or edit test files in `tests/`.
Before writing tests:1. Read the source file thoroughly2. Read any existing tests for context3. Identify: happy paths, edge cases, error cases, boundary conditions
Test conventions for this project:- Framework: Vitest- One test file per source file: `tests/services/payment.test.ts` for `src/services/payment.ts`- Use `describe` blocks to group related tests- Test behavior, not implementation details- Mock external dependencies (Stripe, SendGrid) but NOT internal services
Run `npm test` after writing to confirm all tests pass.Step 4: Wire Up Settings
Section titled βStep 4: Wire Up Settingsβ.claude/settings.json:
{ "permissions": { "allow": [ "Bash(npm run *)", "Bash(npm test)", "Bash(npm install)", "Bash(git status)", "Bash(git diff *)", "Bash(git log *)", "Bash(git add *)", "Bash(git commit *)", "Bash(git checkout *)", "Bash(git push *)", "Bash(npx prisma *)", "Bash(gh pr *)", "Read", "Edit", "Write", "Glob", "Grep" ], "deny": [ "Bash(rm -rf *)", "Bash(sudo *)", "Edit(prisma/migrations/**)", "Edit(.env*)", "Bash(git push --force *)" ] }, "hooks": [ { "event": "PostToolUse", "if": { "tool": "Edit", "path": "src/**/*.ts" }, "run": "sh", "args": ["-c", "npx tsc --noEmit 2>&1 | head -5 || true"], "continueOnError": true } ], "env": { "NODE_ENV": "test" }}Step 5: Delegate Intelligently
Section titled βStep 5: Delegate IntelligentlyβNow Claude can use your agent structure automatically:
"Review my changes to the payment service, write missing tests, then open a PR"Claude will:
- Delegate to
@code-reviewerto reviewsrc/services/payment.ts - Delegate to
@test-writerto write missing tests - Run
/open-prskill after tests pass
Designing Agent Boundaries
Section titled βDesigning Agent BoundariesβGood boundaries
Section titled βGood boundariesβ- read-only vs read-write β research agents never edit; implementation agents do
- domain-specific β
frontend-specialistknows React/CSS,db-specialistknows SQL/Prisma - risk-based β risky agents (deploy, delete) have
require-approval: true - tool-restricted β give each agent only the tools it legitimately needs
Bad boundaries
Section titled βBad boundariesβ- Too fine-grained: an agent per function is overhead, not value
- Overlapping responsibilities: two agents that both βfix bugsβ
- No clear trigger: if Claude canβt tell when to use an agent, it wonβt
Multi-Agent Orchestration Pattern
Section titled βMulti-Agent Orchestration PatternβFor complex tasks, describe what you need and let Claude coordinate:
I need to ship the new Stripe webhook feature:1. Review all changes in src/handlers/webhook.ts2. Write tests for every webhook event type3. Run lint and tests β fix any failures4. Open a PR with a good descriptionClaude will dispatch to @code-reviewer, then @test-writer, then run the /open-pr skill β all in the right order.
π§ Quick Recall
Section titled βπ§ Quick Recallβ- Trick: βDesign agents like an org chart: narrow roles, clear handoffs.β
π¨βπ« Teach It
Section titled βπ¨βπ« Teach Itβ- Do: design a 3-agent layout for the learnerβs real project, together.
- Theyβll trip on: creating 10 agents nobody maintains β start with 2 that earn their keep.
Learning path: β¬ 29-subagents.md Β· Index Β· β‘ 31-worktrees.md
Written by Fenil Patel