1205 lines
38 KiB
Markdown
1205 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) |
|