# 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** (`/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: Context: User wants feedback on a PR user: "Review this PR" assistant: "I'll use the code-reviewer agent." Code review request triggers this agent. 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 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//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//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//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) |