codeflash-agent/docs/context-engineering-guide.md
2026-04-03 17:36:50 -05:00

1204 lines
38 KiB
Markdown

# Context Engineering for Claude Code Projects
A comprehensive guide to structuring CLAUDE.md files, rules, skills, hooks, and configuration for effective Claude Code projects. Sourced from official Claude Code documentation.
---
## 1. CLAUDE.md Architecture
### Discovery & Loading
Claude Code walks **up** the directory tree from the current working directory, loading every `CLAUDE.md` and `CLAUDE.local.md` it finds:
```
~/.claude/CLAUDE.md # User scope (all projects)
/path/to/project/CLAUDE.md # Project scope (team-shared)
/path/to/project/.claude/CLAUDE.md # Alternative project location
/path/to/project/CLAUDE.local.md # Local overrides (gitignored)
packages/foo/CLAUDE.md # Subdirectory (lazy-loaded)
```
**Loading order** (all files concatenate — they don't override):
| Priority | Scope | File | When Loaded |
|----------|-------|------|-------------|
| 1 (highest) | Managed | `/Library/Application Support/ClaudeCode/CLAUDE.md` | Session start, cannot exclude |
| 2 | User | `~/.claude/CLAUDE.md` | Session start |
| 3 | Project | `./CLAUDE.md` or `./.claude/CLAUDE.md` | Session start |
| 4 | Local | `./CLAUDE.local.md` | Session start, appended after project |
| 5 | Subdirectory | `subdir/CLAUDE.md` | Lazy — when Claude reads files in that directory |
**Key behavior**: All files are **concatenated in full**, not merged or replaced. When instructions conflict, Claude may pick one arbitrarily. There is no explicit override mechanism.
### What Belongs at Each Level
| Level | Content | Example |
|-------|---------|---------|
| **User** (`~/.claude/CLAUDE.md`) | Personal preferences across all projects | Work style, response format, git workflow preferences |
| **Project** (`./CLAUDE.md`) | Team-shared standards, build commands, architecture | `prek run --all-files`, module structure, coding standards |
| **Local** (`./CLAUDE.local.md`) | Machine-specific settings, gitignored | Sandbox URLs, local test data, personal overrides |
| **Subdirectory** | Package/module-specific rules | `packages/frontend/CLAUDE.md` for React conventions |
### File Imports
CLAUDE.md supports importing other files:
```markdown
See @README.md for project overview.
Git workflow: @docs/git-instructions.md
```
- Paths resolve **relative to the importing file**
- Absolute paths supported: `@~/shared/instructions.md`
- Recursive imports up to **5 levels deep**
- First external import triggers approval dialog
### What Makes Instructions Stick
**Do:**
- Be specific and concrete: `"Use 2-space indentation"` not `"format code properly"`
- Use structured markdown (headers, bullets) — Claude scans structure like a reader
- Include exact commands: `"Run npm test before committing"`
- Keep each file under **200 lines** — longer files reduce adherence
**Don't:**
- Write vague instructions (`"keep code clean"`)
- Contradict instructions across files
- Write dense prose paragraphs
- Put critical instructions only in conversation (lost after compaction)
### CLAUDE.md Survives Compaction
CLAUDE.md is **re-read from disk** after `/compact`. Instructions in CLAUDE.md persist across sessions and compaction. Instructions given only in conversation do not.
### Monorepo Exclusions
Skip irrelevant CLAUDE.md files with `claudeMdExcludes` in `.claude/settings.local.json`:
```json
{
"claudeMdExcludes": [
"**/irrelevant-package/CLAUDE.md"
]
}
```
---
## 2. Rules System (`.claude/rules/`)
### File Format
Rules are markdown files with optional YAML frontmatter in `.claude/rules/`:
```markdown
---
paths:
- "src/api/**/*.ts"
- "src/**/*.{ts,tsx}"
---
# API Development Rules
- All endpoints must include input validation
- Use standard error response format
```
### Path Scoping
**Without `paths:`** — Rule loads at session start, applies to all files (same cost as CLAUDE.md).
**With `paths:`** — Rule loads lazily when Claude reads files matching the patterns. Zero context cost until triggered.
**Supported glob patterns:**
| Pattern | Matches |
|---------|---------|
| `**/*.ts` | All TypeScript files in any directory |
| `src/**/*` | Everything under `src/` |
| `*.md` | Markdown files in the directory only |
| `src/**/*.{ts,tsx}` | Brace expansion for multiple extensions |
| `tests/**/*.test.ts` | Specific naming patterns |
Wildcards: `*` (anything except `/`), `**` (across directories), `?` (single char), `[abc]` (character class), `{a,b}` (alternation).
### Rules vs CLAUDE.md
| Aspect | Rules | CLAUDE.md |
|--------|-------|-----------|
| Location | `.claude/rules/*.md` | `./CLAUDE.md`, `~/.claude/CLAUDE.md` |
| Path scoping | Yes (`paths:` frontmatter) | No |
| Lazy loading | Yes (path-scoped rules) | No (always at startup) |
| Organization | Multiple modular files | Single file (or imports) |
| Context cost | Zero until triggered (if path-scoped) | Always costs tokens |
| Use case | File-type or directory-specific rules | Universal project standards |
**Priority**: Rules and CLAUDE.md at the same scope level have **equal priority**. All are concatenated as context.
### Organizing Rules for Monorepos
```
project/
├── .claude/rules/
│ ├── commits.md # Unconditional — always loaded
│ └── testing.md # Unconditional — always loaded
├── packages/
│ └── .claude/rules/
│ ├── patterns.md # paths: */src/**/*.py — lazy
│ ├── philosophy.md # paths: */src/**/*.py — lazy
│ └── uv.md # paths: */pyproject.toml — lazy
```
Rules in nested `.claude/rules/` directories are discovered when Claude's working context includes that subtree. Path-scoped rules within only trigger when matching files are accessed.
### InstructionsLoaded Hook
Track when rules/CLAUDE.md load with the `InstructionsLoaded` event:
```json
{
"InstructionsLoaded": [{
"matcher": "path_glob_match",
"hooks": [{
"type": "command",
"command": "echo 'Rule loaded: $INSTRUCTION_FILE'"
}]
}]
}
```
Load reasons: `session_start`, `nested_traversal`, `path_glob_match`, `include`, `compact`.
---
## 3. Skills Design
### SKILL.md Frontmatter Schema
```yaml
---
name: my-skill # Defaults to directory name
description: What this skill does # Keywords help auto-invocation
argument-hint: [issue-number] # Autocomplete hint for user
paths: # Glob patterns for auto-activation
- "src/api/**/*.ts"
- "tests/**"
user-invocable: true # Show in /menu (default: true)
disable-model-invocation: false # Prevent Claude auto-invoke (default: false)
allowed-tools: # Restrict available tools
- Read
- Grep
- Bash(git:*)
model: claude-sonnet-4-6 # Override session model
effort: medium # Override session effort
context: fork # Run in forked subagent
agent: Explore # Subagent type
shell: bash # bash (default) or powershell
---
```
### Path Scoping
When `paths` is set, the skill activates automatically **only** when working with files matching the patterns:
```yaml
paths:
- "src/api/**/*.ts" # API routes
- "src/handlers/**/*.ts" # Request handlers
```
**Without paths**: Skill applies to all files.
**Monorepo pattern**: Nest `.claude/skills/` per package. Claude auto-discovers from current directory and parents:
```
packages/frontend/.claude/skills/react-patterns/SKILL.md
packages/backend/.claude/skills/api-handler/SKILL.md
```
### Invocation Control Matrix
| `user-invocable` | `disable-model-invocation` | /menu? | Claude auto-invokes? | Use case |
|-------------------|---------------------------|--------|---------------------|----------|
| `true` (default) | `false` (default) | Yes | Yes | Standard skill |
| `true` | `true` | Yes | No | Side-effect workflows (deploy, commit) |
| `false` | `false` | No | Yes | Background knowledge |
| `false` | `true` | No | No | Not useful |
**When `disable-model-invocation: true`:**
- Skill description is **NOT** loaded into context
- Full content loads only when user manually invokes with `/name`
- Use for: deployments, commits, side-effect workflows where timing is critical
**When `user-invocable: false`:**
- Description **IS** always in context (Claude knows about it)
- Does NOT appear in `/` menu
- Claude can invoke automatically when relevant
- Use for: background knowledge, legacy system context, reference material
### Progressive Disclosure
1. **Session start**: Only skill **descriptions** loaded (budget: ~1% of context window, minimum 8000 chars)
2. **On invocation**: Full skill content loaded
3. **Supporting files**: Reference from SKILL.md for on-demand loading
```markdown
For complete API details, see [reference.md](reference.md)
For examples, see [examples.md](examples.md)
```
Descriptions are truncated at 250 chars in listings. Write descriptions that front-load keywords.
### Allowed-Tools Restrictions
Restrict what tools are available when a skill is active:
```yaml
allowed-tools:
- Read
- Grep
- Bash(git:*) # Only git commands
```
Formats:
- Single: `allowed-tools: Read`
- Comma-separated: `allowed-tools: Read, Write, Edit`
- YAML list with patterns: `Bash(npm:*)`, `Bash(docker:*)`
- MCP tools: `mcp__github__search_repositories`
### Dynamic Content in Skills
Inject command output into skill content with `` !`command` ``:
```markdown
Current PR diff: !`gh pr diff`
PR comments: !`gh pr view --comments`
```
**String substitutions:**
| Variable | Description |
|----------|-------------|
| `$ARGUMENTS` | All arguments passed to skill |
| `$0`, `$1`, ... | Specific arguments by index |
| `${CLAUDE_SESSION_ID}` | Current session ID |
| `${CLAUDE_SKILL_DIR}` | Directory containing SKILL.md |
### Skills Interaction with Rules and CLAUDE.md
- CLAUDE.md and rules are in context **before** any skill loads
- When a skill is invoked, its content is **added** to existing context
- Skills cannot override CLAUDE.md or rules — everything is additive
- Skills with `context: fork` run in a subagent that gets its own copy of CLAUDE.md + preloaded skills
- Scope precedence: Enterprise > Personal > Project > Plugin
---
## 4. Hooks as Guardrails
### Hook Events Reference
**Pre-execution:**
- `SessionStart` — Session begins/resumes
- `InstructionsLoaded` — CLAUDE.md or rules loaded
- `UserPromptSubmit` — User submits prompt (before processing)
**Tool lifecycle:**
- `PreToolUse` — Before tool execution (**can block**)
- `PermissionRequest` — Permission dialog about to show
- `PostToolUse` — After tool succeeds
- `PostToolUseFailure` — After tool fails
**Session:**
- `Stop` — Claude finishes responding
- `PreCompact` — Before context compaction
- `PostCompact` — After compaction
**Other:**
- `Notification` — Waiting for input/permission
- `SubagentStart` / `SubagentStop` — Agent lifecycle
- `FileChanged` — Watched file changes
- `ConfigChange` — Settings/skills file changes
### Hook Types — Decision Framework
| Type | Best for | Timeout | Cost |
|------|----------|---------|------|
| `command` | Deterministic shell operations, fast checks | 10-30s | Low (no LLM) |
| `prompt` | Yes/no decisions based on hook input data | 30s | Medium (single LLM call) |
| `agent` | Verification requiring file reads or commands | 60s | High (LLM + tools) |
| `http` | External service logging, team audit | 10s | Network latency |
### PreToolUse — Blocking Dangerous Actions
```json
{
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "bash .claude/hooks/validate-bash.sh",
"timeout": 10
}]
}]
}
```
**Decision responses:**
- `exit 0` or `"permissionDecision": "allow"` — Allow the tool
- `exit 2` or `"permissionDecision": "deny"` — Block with reason
- `"permissionDecision": "ask"` — Show permission prompt normally
**Important**: Hook returning `"allow"` does NOT override permission deny rules. Hooks can tighten restrictions but not loosen past what permission rules allow.
**Rewriting tool input:**
```json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": { "command": "modified-command" }
}
}
```
### PostToolUse — Auto-formatting and Logging
```json
{
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "prettier --write $TOOL_INPUT_FILE_PATH",
"timeout": 30
}]
}]
}
```
Cannot undo the action (already executed), but can inject context or block further work.
### Stop — Task Completion Verification
```json
{
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": ".claude/hooks/check-complete.sh",
"timeout": 45
}]
}]
}
```
**Anti-loop pattern** (critical — Stop fires on every response):
```bash
#!/bin/bash
INPUT=$(cat)
# Check if this hook already triggered to avoid infinite loops
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Allow stop — already verified once
fi
# Your verification logic
if ! all_tasks_done; then
echo "Tasks X and Y still incomplete" >&2
exit 2 # Block stop
fi
exit 0 # Allow stop
```
### PermissionRequest — Auto-approving Safe Patterns
```json
{
"PermissionRequest": [{
"matcher": "Read",
"hooks": [{
"type": "command",
"command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PermissionRequest\",\"decision\":{\"behavior\":\"allow\"}}}'"
}]
}]
}
```
Keep matchers **narrow**. An empty matcher auto-approves everything (dangerous).
### The `if` Field — Argument-Level Filtering
Finer than `matcher` (which only matches tool name). The `if` field matches tool arguments:
```json
{
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"if": "Bash(git *)",
"command": "check-git-policy.sh"
}]
}]
}
```
When `if` doesn't match, the hook process **doesn't spawn** (zero overhead). Uses permission rule syntax: `Bash(git:*)`, `Edit(*.ts)`, etc.
### PreCompact / PostCompact — Preserving Context
```json
{
"PostCompact": [{
"matcher": "auto",
"hooks": [{
"type": "command",
"command": "echo 'Reminder: use uv, not pip. Current task: refactor auth.'"
}]
}]
}
```
PostCompact output goes directly to Claude's context after compaction. Use this to re-inject critical reminders that might be lost.
### Settings Hierarchy for Hooks
Hooks merge across scopes (all matching hooks run):
1. **Managed policy** (highest, cannot override)
2. **Project local** (`.claude/settings.local.json`)
3. **Project shared** (`.claude/settings.json`)
4. **User** (`~/.claude/settings.json`)
5. **Plugin** (`<plugin>/hooks/hooks.json`)
6. **Skill/agent frontmatter** (while active)
When multiple scopes define hooks for the same event, **all hooks run**. For conflicting decisions, most restrictive wins (deny > ask > allow).
### Performance Considerations
**High-frequency hooks** (run often — keep fast):
- `PreToolUse` — fires before every tool call
- `PostToolUse` — fires after every tool call
- Use `command` type + `if` field to minimize overhead
**Low-frequency hooks** (run rarely — can be heavier):
- `SessionStart` — once per session
- `Stop` — once per response
- `PreCompact` / `PostCompact` — on compaction events
**Optimization:**
- Use `if` field to skip hook process on non-matching arguments
- Use `command` type (fast) over `agent` type (slow, uses model tokens)
- Mark expensive hooks `"async": true` to not block
---
## 5. Context Window Management
### What Gets Loaded and When
**At session start** (always in context):
1. System prompt (~4,200 tokens)
2. Auto memory — first 200 lines or 25KB of `MEMORY.md`
3. Environment info (CWD, platform, git status, recent commits)
4. User CLAUDE.md
5. Project CLAUDE.md
6. Unconditional rules (`.claude/rules/` without `paths:`)
7. Skill descriptions only (~1% of context window budget)
8. MCP tool names (schemas loaded on-demand)
**During session** (lazy-loaded):
- Path-scoped rules — when matching files are read
- Subdirectory CLAUDE.md — when accessing files in that directory
- Full skill content — when a skill is invoked
- MCP tool schemas — when Claude considers using a tool
### Token Budget Awareness
| Component | Approximate Cost | When |
|-----------|-----------------|------|
| System prompt | ~4,200 tokens | Always |
| Auto memory | Variable (capped 25KB) | Always |
| Environment | ~280 tokens | Always |
| CLAUDE.md (typical) | 500-2,000 tokens | Always |
| Each unconditional rule | 200-400 tokens | Always |
| Skill descriptions (all) | ~450 tokens total | Always |
| Each path-scoped rule | 200-400 tokens | When triggered |
| Full skill content | Variable | When invoked |
**Strategy**: Move instructions into path-scoped rules to defer their context cost until relevant files are accessed.
### Compaction Behavior
When context fills up, Claude Code compacts:
**Preserved**: System prompt, CLAUDE.md (re-read from disk), auto memory, rules, your intent, key decisions
**Dropped**: Verbatim conversation, full tool outputs, intermediate reasoning
**Lost**: Skill descriptions (only invoked skills survive)
### Strategies for Clean Context
1. **Path-scoped rules** — Instructions only load when relevant files are accessed
2. **Skills with `disable-model-invocation: true`** — No description in context until user invokes
3. **Subagents for exploration** — Heavy file reads happen in a separate context window; only the summary returns
4. **Targeted reads** — Read specific file + line range instead of full files
5. **PostCompact hooks** — Re-inject critical reminders after compaction
6. **CLAUDE.md imports** — Keep main file concise, import details from supporting files
---
## 6. Project Configuration Patterns
### `.claude/settings.json` — Shared Team Config
```json
{
"permissions": {
"allow": [
"Bash(prek *)",
"Bash(uv run pytest *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)"
],
"deny": [
"Read(./.env)",
"Read(./secrets/**)"
]
},
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "ruff format --quiet $TOOL_INPUT_FILE_PATH",
"timeout": 10
}]
}]
},
"env": {
"UV_PYTHON": "3.12"
}
}
```
**Commit this to git.** Team members get shared permissions, hooks, and environment.
### `.claude/settings.local.json` — Personal Overrides
```json
{
"permissions": {
"allow": ["Bash(docker *)"]
},
"model": "claude-opus-4-6"
}
```
**Add to `.gitignore`.** Personal preferences that don't affect the team.
### `.mcp.json` — MCP Server Configuration
```json
{
"mcpServers": {
"github": {
"command": "node",
"args": ["path/to/github-server.js"],
"type": "stdio"
},
"postgres": {
"url": "http://localhost:3000/mcp",
"type": "http"
}
}
}
```
Lives at project root (committed) or `~/.claude.json` (personal).
### Settings Precedence
From highest to lowest:
1. **Managed** (enterprise IT, cannot be overridden)
2. **CLI arguments** (temporary session overrides)
3. **Local project** (`.claude/settings.local.json`)
4. **Shared project** (`.claude/settings.json`)
5. **User** (`~/.claude/settings.json`)
Array settings (like `permissions.allow`) **merge** across scopes (concatenate + deduplicate), not replace.
---
## 7. Real-World Patterns
### Monorepo Setup
```
monorepo/
├── CLAUDE.md # Workspace-wide: build commands, architecture
├── .claude/
│ ├── settings.json # Shared permissions and hooks
│ ├── rules/
│ │ └── commits.md # Unconditional: commit conventions
│ └── skills/
│ └── deploy/SKILL.md # Manual-only deployment skill
├── packages/
│ ├── .claude/
│ │ └── rules/
│ │ ├── patterns.md # paths: */src/**/*.py
│ │ └── uv.md # paths: */pyproject.toml
│ ├── frontend/
│ │ ├── CLAUDE.md # React conventions (lazy-loaded)
│ │ └── .claude/skills/
│ │ └── react-patterns/SKILL.md
│ └── backend/
│ ├── CLAUDE.md # API conventions (lazy-loaded)
│ └── .claude/skills/
│ └── api-handler/SKILL.md
```
**How it works:**
- Root `CLAUDE.md` always in context (workspace build commands)
- `commits.md` always in context (applies to all code)
- `patterns.md` loads only when editing Python source
- `packages/frontend/CLAUDE.md` loads when Claude reads frontend files
- React skill available only when working in frontend package
### CI/CD Quality Gates via Hooks
**`.claude/settings.json`:**
```json
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "ruff check --fix $TOOL_INPUT_FILE_PATH && ruff format $TOOL_INPUT_FILE_PATH",
"timeout": 15
}]
}],
"Stop": [{
"matcher": "",
"hooks": [{
"type": "agent",
"prompt": "Check if code was modified in this session. If so, verify tests pass by running: uv run pytest packages/ -v"
}]
}]
}
}
```
### What to Commit vs. Keep Local
| Commit | Gitignore |
|--------|-----------|
| `.claude/settings.json` | `.claude/settings.local.json` |
| `.claude/rules/` | `CLAUDE.local.md` |
| `.claude/skills/` | Personal MCP configs |
| `.claude/hooks/` (scripts) | API keys / tokens |
| `CLAUDE.md` | `.mcp.json` (if contains secrets) |
| `.mcp.json` (if no secrets) | |
### Onboarding Pattern
A new developer clones the repo and runs `claude`. Automatically:
1. Project `CLAUDE.md` loads — they learn build commands, architecture, coding standards
2. Shared rules load — commit conventions, code style enforced
3. Shared permissions activate — safe commands pre-approved, dangerous ones blocked
4. Hooks engage — auto-formatting on edit, quality checks on stop
5. Skills available — `/deploy`, `/review-pr` ready to use
6. MCP servers connect — project-specific tools available
No manual setup required. Everything is in the committed `.claude/` directory.
---
## Interaction Map
How the pieces compose:
```
┌─────────────────────────────────────────────────────┐
│ Context Window │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ CLAUDE.md │ │ Rules │ │ Skills │ │
│ │ (always) │ │ (lazy/eager) │ │ (on-demand)│ │
│ └─────────────┘ └──────────────┘ └────────────┘ │
│ │
│ All provide context (soft guidance) │
│ None enforce behavior (use hooks/settings for that) │
└─────────────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ settings.json │ │ hooks │
│ (hard enforce) │ │ (hard enforce) │
│ │ │ │
│ • permissions │ │ • PreToolUse │
│ • deny rules │ │ (block actions)│
│ • sandbox │ │ • PostToolUse │
│ │ │ (auto-format) │
│ Cannot be │ │ • Stop │
│ overridden by │ │ (verify tasks) │
│ CLAUDE.md or │ │ │
│ conversation │ │ Exit codes and │
│ │ │ decisions are │
│ │ │ enforced │
└──────────────────┘ └──────────────────┘
```
**Key principle**: CLAUDE.md, rules, and skills are **context** (soft guidance — Claude reads and usually follows, but no guarantee). Settings and hooks are **configuration** (hard enforcement — permissions block tools, hooks can deny actions regardless of Claude's intent).
For critical constraints, don't rely on CLAUDE.md alone. Use `permissions.deny` in settings.json or `PreToolUse` hooks for hard enforcement.
---
## 8. Custom Agents (`.claude/agents/`)
### File Format
Agents are markdown files with YAML frontmatter. Unlike skills (which are directories), agents are single `.md` files:
```
.claude/agents/
├── code-reviewer.md
├── test-writer.md
└── researcher.md
```
### Frontmatter Schema
```yaml
---
name: code-reviewer
description: |
Use this agent when the user asks for code review. Examples:
<example>
Context: User wants feedback on a PR
user: "Review this PR"
assistant: "I'll use the code-reviewer agent."
<commentary>
Code review request triggers this agent.
</commentary>
</example>
model: inherit # sonnet, opus, haiku, inherit, or full model ID
color: blue # red, blue, green, yellow, purple, orange, pink, cyan
tools: ["Read", "Grep", "Glob"] # Restrict available tools (inherit all if omitted)
disallowedTools: ["Write"] # Deny specific tools
permissionMode: default # default, acceptEdits, auto, dontAsk, bypassPermissions, plan
maxTurns: 50 # Max agentic turns before stopping
skills: ["my-skill"] # Skills injected into agent context at startup
mcpServers: {} # MCP servers scoped to this agent
hooks: {} # Hooks during agent lifecycle
memory: project # user, project, or local — persistent memory scope
background: false # Always run as background task
effort: medium # low, medium, high, max
isolation: worktree # Git-isolated execution
initialPrompt: "Start analysis" # Auto-submitted first turn
---
You are a code reviewer. Your core responsibilities:
1. Check for bugs and edge cases
2. Verify test coverage
3. Review naming and documentation
```
The markdown body below the frontmatter becomes the agent's **system prompt**.
### Agents vs Skills
| Aspect | Agent | Skill |
|--------|-------|-------|
| Context | **Separate context window** | Inline in main thread |
| System prompt | Custom per agent | None (injected into session) |
| File format | Single `.md` file | Directory with `SKILL.md` + supporting files |
| Tool restrictions | Per agent via `tools` field | Per skill via `allowed-tools` |
| Worktree isolation | Yes (`isolation: worktree`) | No |
| Path scoping | No | Yes (`paths:` frontmatter) |
| Invocation | Auto-delegation or `/agents` menu | `/skill-name` or auto |
| Supporting files | No — use `skills` field instead | Yes (reference.md, scripts/, etc.) |
### Discovery
- Auto-discovered from `.claude/agents/` at session start
- Scope priority: Managed > CLI > Project > User > Plugin
- Plugin agents use namespace: `plugin-name:agent-name`
- Plugin agents **cannot** define hooks or mcpServers (security restriction)
### When to Use Agents vs Skills
**Use agents when:**
- Task needs a separate context window (heavy exploration, large codebases)
- You want tool restrictions (read-only agent, no-bash agent)
- Task benefits from worktree isolation
- You need a custom system prompt that overrides default behavior
**Use skills when:**
- Task runs inline in the main conversation
- You need path scoping (activate only for certain files)
- You have supporting reference files
- You want progressive disclosure (description → full content)
---
## 9. Commands (`.claude/commands/`)
### Format
Commands are single markdown files — the simpler predecessor to skills:
```
.claude/commands/
├── review-pr.md
└── run-tests.md
```
Same frontmatter as skills (`description`, `allowed-tools`, `disable-model-invocation`, etc.) but **no supporting files** — everything in one `.md` file.
### Commands vs Skills
| Aspect | Command | Skill |
|--------|---------|-------|
| Structure | Single `.md` file | Directory with SKILL.md + supporting files |
| Supporting files | No | Yes (reference.md, scripts/, templates/) |
| Progressive disclosure | No (full content on invoke) | Yes (description → full content) |
| `${CLAUDE_SKILL_DIR}` | Not available | Available |
| Dynamic injection | `` !`command` `` works | `` !`command` `` works |
**Commands are NOT deprecated** but skills are recommended for new work. Both create `/name` shortcuts identically.
---
## 10. Plugin Structure
### Directory Layout
```
my-plugin/
├── .claude-plugin/
│ ├── plugin.json # Manifest (optional but recommended)
│ └── marketplace.json # Multi-plugin marketplace config
├── agents/ # Subagent definitions
│ └── reviewer.md
├── skills/ # Skills with supporting files
│ └── optimize/
│ ├── SKILL.md
│ └── references/
│ └── patterns.md
├── commands/ # Legacy commands
│ └── deploy.md
├── hooks/
│ └── hooks.json # Plugin hook definitions
├── references/ # Shared reference material
│ └── shared/
│ └── conventions.md
├── .mcp.json # Plugin MCP servers
├── bin/ # Executables (added to PATH)
├── output-styles/ # Custom output styles
└── settings.json # Default plugin settings
```
### plugin.json Schema
```json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "What this plugin does",
"author": {"name": "Team", "email": "team@example.com"},
"commands": ["./custom/cmd.md"],
"agents": "./custom/agents/",
"skills": "./custom/skills/",
"hooks": "./hooks.json",
"mcpServers": "./mcp.json",
"outputStyles": "./styles/",
"lspServers": "./.lsp.json",
"userConfig": {
"api_key": {
"description": "Your API key",
"sensitive": true
}
}
}
```
### Plugin Variables
| Variable | Resolves To | Use For |
|----------|-------------|---------|
| `${CLAUDE_PLUGIN_ROOT}` | Plugin installation directory | Ephemeral references (scripts, hooks, config) |
| `${CLAUDE_PLUGIN_DATA}` | `~/.claude/plugins/data/{plugin-id}/` | Persistent data (caches, installed deps) |
| `${CLAUDE_PROJECT_DIR}` | Project root directory | Accessing project files from hooks |
`CLAUDE_PLUGIN_ROOT` changes on plugin updates. `CLAUDE_PLUGIN_DATA` persists across updates.
### Plugin Hooks vs Project Hooks
- Plugin hooks run **only when the plugin is enabled**
- Project hooks **always run**
- Both execute in parallel for the same event
- Most restrictive decision wins (deny > ask > allow)
- Plugin hooks are defined in `hooks/hooks.json` (not in settings.json)
### References in Plugins
Plugins can bundle reference material that agents/skills access via `${CLAUDE_PLUGIN_ROOT}`:
```markdown
<!-- In an agent .md file -->
Read the conventions at ${CLAUDE_PLUGIN_ROOT}/references/shared/conventions.md
```
Reference files are **not auto-loaded** — they're read on-demand when an agent or skill needs them. This keeps them out of context until relevant.
---
## 11. Memory System
### Auto Memory
```
~/.claude/projects/<project-id>/memory/
├── MEMORY.md # Index file (required)
├── debugging.md # Topic files (auto-created)
├── architecture.md
└── decisions.md
```
**Loading**: First 200 lines OR 25KB of `MEMORY.md` loaded at session start. Topic files read on-demand.
**Storage**: Project-scoped by git repo path. All worktrees in same repo share one memory directory. Machine-local (not shared across machines).
**Configuration:**
```json
{
"autoMemoryEnabled": true,
"autoMemoryDirectory": "~/.claude/projects/<id>/memory/"
}
```
Toggle with `/memory` command or `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1`.
### How Memory Interacts with Other Features
- MEMORY.md is **separate from** CLAUDE.md — both load at startup
- CLAUDE.md is deterministic (you control content); memory is Claude-managed
- Both survive compaction (re-read from disk)
- Memory is per-machine; CLAUDE.md is shared via git
### Agent Memory
Agents can have their own persistent memory via the `memory` frontmatter field:
```yaml
---
name: researcher
memory: project # user, project, or local
---
```
Stored in `.claude/agent-memory/<agent-name>/MEMORY.md`.
---
## 12. References & Supporting Files
### In Skills
Skills can bundle arbitrary supporting files alongside SKILL.md:
```
my-skill/
├── SKILL.md # Entry point (required)
├── reference.md # Detailed API docs
├── examples.md # Usage examples
├── scripts/
│ ├── validate.sh # Executable scripts
│ └── helper.py
└── templates/
└── pr-template.md # Templates
```
**Loading behavior**: Supporting files are **NOT auto-loaded**. Claude reads them on-demand when SKILL.md references them:
```markdown
For complete patterns, see [reference.md](reference.md)
Run validation: !`bash ${CLAUDE_SKILL_DIR}/scripts/validate.sh`
```
This is the key progressive disclosure mechanism — SKILL.md is concise, details live in supporting files.
### In Plugins
Plugins use a `references/` directory for shared material accessible by all agents/skills in the plugin:
```
plugin/
├── references/
│ ├── shared/
│ │ ├── conventions.md
│ │ └── pr-preparation.md
│ ├── async/
│ │ └── guide.md
│ └── memory/
│ └── guide.md
├── agents/
│ └── optimizer.md # References: ${CLAUDE_PLUGIN_ROOT}/references/shared/conventions.md
└── skills/
└── optimize/SKILL.md # References: ${CLAUDE_PLUGIN_ROOT}/references/async/guide.md
```
Referenced via `${CLAUDE_PLUGIN_ROOT}/references/...` in agent/skill content. Not auto-loaded — read on-demand.
### In CLAUDE.md (@ Imports)
```markdown
# Project Guide
@docs/architecture.md
@docs/api-reference.md
## Quick Start
...
```
Imported files expand at session start (not lazy). Recursive up to 5 levels.
---
## 13. Other Features
### Handoffs (`.claude/handoffs/`)
Session continuity mechanism:
- `/handoff` saves current session state to `.claude/handoffs/latest.md`
- New session can restore context from handoff
- Gitignored — session-specific, not shared
### .worktreeinclude
Lists gitignored files that should be copied into git worktrees:
```
.env
.env.local
config/secrets.json
```
Syntax follows `.gitignore` patterns. Ensures worktree-isolated agents have access to necessary config files.
### Additional Directories (`--add-dir`)
```bash
claude --add-dir ../shared-lib
```
**What loads from `--add-dir`:**
- `.claude/skills/` — auto-discovered
- Files — read access
**What does NOT load:**
- CLAUDE.md (unless `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1`)
- Agents, commands, hooks, MCP servers, output styles
### Output Styles
Custom response formatting in `~/.claude/output-styles/` or `.claude/output-styles/`:
```yaml
---
description: Concise teaching style
keep-coding-instructions: true
---
Be concise. Lead with code, follow with brief explanation.
Use bullet points. No preamble.
```
Selected via `/config` or `outputStyle` in settings.json.
### Environment Variables in Hooks/Skills
| Variable | Available In | Purpose |
|----------|-------------|---------|
| `${CLAUDE_SESSION_ID}` | Skills, hooks | Current session ID |
| `${CLAUDE_SKILL_DIR}` | Skills only | Skill directory path |
| `${CLAUDE_PROJECT_DIR}` | Hooks, skills | Project root |
| `${CLAUDE_PLUGIN_ROOT}` | Plugin content | Plugin install dir |
| `${CLAUDE_PLUGIN_DATA}` | Plugin content | Persistent plugin data |
| `${CLAUDE_ENV_FILE}` | Hooks only | Write env vars that persist across tool calls |
| `$ARGUMENTS`, `$0`, `$1` | Skills | Skill invocation arguments |
### claudeMdExcludes
Skip specific CLAUDE.md files in monorepos:
```json
{
"claudeMdExcludes": [
"**/node_modules/**/CLAUDE.md",
"vendor/**/CLAUDE.md"
]
}
```
Glob patterns matched against absolute paths. Managed CLAUDE.md cannot be excluded.
---
## Complete Discovery & Loading Sequence
When Claude Code starts a session:
```
1. Load managed settings + managed CLAUDE.md (cannot exclude)
2. Walk up directory tree from CWD:
└─ Load CLAUDE.md + CLAUDE.local.md at each level
3. Discover .claude/rules/*.md:
├─ Unconditional rules → load immediately
└─ Path-scoped rules → register for lazy loading
4. Load auto memory (first 200 lines / 25KB of MEMORY.md)
5. Enumerate skills (descriptions only, ~1% context budget)
6. Enumerate agents (descriptions for delegation)
7. Load MCP server configs (.mcp.json)
8. Register hooks from all scopes
9. Session begins
During session:
├─ Path-scoped rules fire when matching files accessed
├─ Subdirectory CLAUDE.md loads when Claude reads files there
├─ Full skill content loads on invocation
├─ MCP tool schemas load when Claude considers using a tool
├─ Hooks fire on their respective events
└─ Compaction re-reads CLAUDE.md, memory, rules from disk
```
---
## Complete Feature Matrix
| Feature | Location | Load Time | Path Scoping | Auto-discovered |
|---------|----------|-----------|--------------|-----------------|
| CLAUDE.md | Project root, `~/.claude/` | Startup | No | Yes (walk up) |
| CLAUDE.local.md | Project root | Startup | No | Yes |
| Rules (unconditional) | `.claude/rules/` | Startup | No | Yes (recursive) |
| Rules (path-scoped) | `.claude/rules/` | On file access | Yes (`paths:`) | Yes (recursive) |
| Skills | `.claude/skills/*/` | Description: startup; Full: on invoke | Yes (`paths:`) | Yes (nested) |
| Commands | `.claude/commands/` | On invoke | No | Yes |
| Agents | `.claude/agents/` | Description: startup; Full: on delegate | No | Yes |
| Hooks | `settings.json`, plugin | On event | Via `matcher` + `if` | No (configured) |
| Auto memory | `~/.claude/projects/` | Startup (25KB cap) | No | Auto-created |
| MCP servers | `.mcp.json` | Startup | No | Yes |
| Output styles | `.claude/output-styles/` | Startup | No | Yes |
| Plugin refs | `plugin/references/` | On demand | No | No (referenced) |
| Skill refs | `skill-dir/` files | On demand | No | No (referenced) |