Architecture Map
Claude Code follows a layered architecture where each layer has a clear responsibility. Understanding this map is the key to navigating the 512K+ line codebase efficiently.
High-Level Architecture
Section titled “High-Level Architecture”graph TB subgraph "CLI Layer" CLI["cli.tsx — Bootstrap Entrypoint"] MAIN["main.tsx — Commander.js CLI"] end
subgraph "Command Layer" CMDS["commands.ts — Command Registry"] SLASH["60+ Slash Commands"] SKILLS["Skills & Plugins"] end
subgraph "Agentic Loop" QE["QueryEngine — Session Manager"] QUERY["query.ts — Turn Orchestrator"] CLAUDE["claude.ts — API Streaming"] end
subgraph "Tool System" TOOLS["tools.ts — Tool Pool Assembly"] TOOL_DEF["Tool.ts — Tool Interface"] STE["StreamingToolExecutor"] BUILTIN["30+ Built-in Tools"] MCP["MCP Tools"] end
subgraph "Infrastructure" RETRY["withRetry.ts — Retry Logic"] SETTINGS["settings.ts — Configuration"] PERMS["permissions.ts — Security"] COMPACT["compact.ts — Context Management"] end
CLI --> MAIN MAIN --> CMDS CMDS --> SLASH CMDS --> SKILLS MAIN --> QE QE --> QUERY QUERY --> CLAUDE CLAUDE --> RETRY QUERY --> STE STE --> BUILTIN STE --> MCP TOOLS --> BUILTIN TOOLS --> MCP QUERY --> COMPACT STE --> PERMS MAIN --> SETTINGSLayer-by-Layer Breakdown
Section titled “Layer-by-Layer Breakdown”Layer 1: CLI Entry (src/entrypoints/)
Section titled “Layer 1: CLI Entry (src/entrypoints/)”The outermost layer handles process bootstrapping and fast-path routing.
Key files:
src/entrypoints/cli.tsx— Bootstrap entrypoint, fast-path routingsrc/entrypoints/init.ts— Configuration, telemetry, and proxy initializationsrc/entrypoints/mcp.ts— MCP server mode entrypoint
The CLI layer implements a fast-path pattern — special flags like --version are handled before any heavy module loading:
async function main(): Promise<void> { const args = process.argv.slice(2)
// Fast-path for --version: zero module loading needed if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) { console.log(`${MACRO.VERSION} (Claude Code)`) return }
// For all other paths, load the startup profiler const { profileCheckpoint } = await import('../utils/startupProfiler.js') profileCheckpoint('cli_entry')
// Route to specialized entrypoints...}The init() function (memoized, runs once) handles:
- Configuration validation and enablement
- Environment variable application (safe subset before trust, full after)
- Graceful shutdown registration
- OAuth account info population
- mTLS and proxy configuration
- Telemetry initialization
Layer 2: CLI Framework (src/main.tsx)
Section titled “Layer 2: CLI Framework (src/main.tsx)”The main module is the largest file in the codebase (~785KB). It wires together Commander.js argument parsing, configuration loading, and the REPL launcher.
Key responsibilities:
- Commander.js command registration with type-safe options
- API key validation and authentication flow
- MCP server initialization
- Tool pool assembly
- REPL/print mode launch
// src/main.tsx — Side effects run at import timeimport { profileCheckpoint } from './utils/startupProfiler.js'profileCheckpoint('main_tsx_entry')
import { startMdmRawRead } from './utils/settings/mdm/rawRead.js'startMdmRawRead() // Fire MDM subprocess in parallel with imports
import { startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js'startKeychainPrefetch() // Fire keychain reads in parallelLayer 3: Command System (src/commands.ts)
Section titled “Layer 3: Command System (src/commands.ts)”The command registry manages 60+ slash commands with a sophisticated loading system:
export async function getCommands(cwd: string): Promise<Command[]> { const allCommands = await loadAllCommands(cwd) const dynamicSkills = getDynamicSkills()
const baseCommands = allCommands.filter( _ => meetsAvailabilityRequirement(_) && isCommandEnabled(_), ) // ... dedupe dynamic skills}Commands come from multiple sources:
- Built-in commands — Hard-coded in
COMMANDS()(memoized) - Bundled skills — Shipped with the binary
- Skill directory commands — User-defined in
.claude/skills/ - Plugin commands — From installed plugins
- Workflow commands — From workflow scripts
- MCP commands — From connected MCP servers
Layer 4: Query Engine (src/QueryEngine.ts)
Section titled “Layer 4: Query Engine (src/QueryEngine.ts)”The QueryEngine class owns the conversation lifecycle:
sequenceDiagram participant User participant QE as QueryEngine participant PUI as processUserInput participant Q as query() participant C as claude.ts
User->>QE: submitMessage(prompt) QE->>PUI: Process slash commands, attachments PUI-->>QE: messages, shouldQuery, allowedTools QE->>Q: query({ messages, systemPrompt, ... }) Q->>C: createStreamedResponse() C-->>Q: Stream events (text, tool_use, ...) Q->>Q: Execute tools via StreamingToolExecutor Q-->>QE: Yield messages (assistant, user, system) QE-->>User: SDKMessage streamKey design: submitMessage() returns an AsyncGenerator<SDKMessage>, allowing the caller to consume messages incrementally:
export class QueryEngine { async *submitMessage( prompt: string | ContentBlockParam[], options?: { uuid?: string; isMeta?: boolean }, ): AsyncGenerator<SDKMessage, void, unknown> { // 1. Process user input (slash commands, attachments) // 2. Build system prompt // 3. Enter query loop for await (const message of query({ messages, systemPrompt, ... })) { // 4. Process each message (record, track usage, yield) } // 5. Yield final result }}Layer 5: Agentic Loop (src/query.ts)
Section titled “Layer 5: Agentic Loop (src/query.ts)”The query() function implements the core agentic loop — the cycle of:
- Send messages to API
- Receive streaming response
- Execute tool calls
- Feed results back
// src/query.ts — Imports reveal the orchestration scopeimport { StreamingToolExecutor } from './services/tools/StreamingToolExecutor.js'import { runTools } from './services/tools/toolOrchestration.js'import { isAutoCompactEnabled } from './services/compact/autoCompact.js'import { FallbackTriggeredError } from './services/api/withRetry.js'The loop handles:
- Auto-compaction: Automatically shrinks context when approaching limits
- Reactive compaction: Retries with compacted context on prompt-too-long errors
- Model fallback: Falls back from Opus to Sonnet on repeated 529 errors
- Tool execution: Parallel execution of concurrent-safe tools via
StreamingToolExecutor
Layer 6: API Streaming (src/services/api/claude.ts)
Section titled “Layer 6: API Streaming (src/services/api/claude.ts)”The deepest layer handles raw API communication:
// src/services/api/claude.ts — Types reveal the protocolimport type { BetaRawMessageStreamEvent, BetaMessageStreamParams,} from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'import type { Stream } from '@anthropic-ai/sdk/streaming.mjs'This layer manages:
- System prompt construction with cache breakpoints
- Message normalization for the API format
- Streaming event processing (message_start, content_block_start, content_block_delta, etc.)
- Beta feature headers (thinking, effort, fast mode, tool search)
- Multi-provider support (Anthropic direct, Bedrock, Vertex)
Layer 7: Tool System (src/Tool.ts, src/tools.ts)
Section titled “Layer 7: Tool System (src/Tool.ts, src/tools.ts)”The tool system is the primary mechanism for Claude to interact with the world:
// src/Tool.ts — Core tool interface (simplified)export type Tool<Input, Output, P> = { name: string inputSchema: Input // Zod schema call(args, context, canUseTool, parentMessage, onProgress?): Promise<ToolResult<Output>> checkPermissions(input, context): Promise<PermissionResult> isReadOnly(input): boolean isDestructive?(input): boolean isConcurrencySafe(input): boolean prompt(options): Promise<string> // ... rendering, validation, progress tracking}Tools are assembled via getTools() with permission-aware filtering:
export const getTools = (permissionContext: ToolPermissionContext): Tools => { if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) { return filterToolsByDenyRules([BashTool, FileReadTool, FileEditTool], permissionContext) } // Full tool pool with deny-rule filtering and REPL mode handling}Module Dependency Flow
Section titled “Module Dependency Flow”graph LR CLI["cli.tsx"] --> INIT["init.ts"] CLI --> MAIN["main.tsx"] MAIN --> SETUP["setup.ts"] MAIN --> QE["QueryEngine.ts"] MAIN --> CMDS["commands.ts"] MAIN --> TOOLS_TS["tools.ts"]
QE --> QUERY["query.ts"] QE --> PUI["processUserInput.ts"]
QUERY --> CLAUDE["claude.ts"] QUERY --> STE["StreamingToolExecutor.ts"] QUERY --> COMPACT["compact.ts"]
CLAUDE --> RETRY["withRetry.ts"] CLAUDE --> ERRORS["errors.ts"]
STE --> EXEC["toolExecution.ts"] EXEC --> PERMS["permissions.ts"] EXEC --> HOOKS["hooks.ts"]
TOOLS_TS --> TOOL["Tool.ts"] TOOL --> BUILTIN["30+ Tool implementations"]Key Abstractions
Section titled “Key Abstractions”QueryEngine
Section titled “QueryEngine”The session-scoped orchestrator. One per conversation. Manages message history, file state cache, usage tracking, and permission denial tracking.
src/QueryEngine.ts (1296 lines)├── submitMessage() — Main entry point, yields SDKMessage stream├── interrupt() — Abort current operation├── getMessages() — Read conversation history└── setModel() — Change model mid-sessionThe universal interface for all capabilities — from reading files to spawning sub-agents. Every tool implements the same interface with buildTool() providing defaults:
src/Tool.ts (793 lines)├── Tool<Input, Output, P> — Full tool interface├── ToolDef<Input, Output, P> — Partial definition (for buildTool)├── buildTool() — Fill defaults, return complete Tool├── ToolUseContext — Runtime context passed to every tool call└── ToolPermissionContext — Permission state for tool filteringCommand
Section titled “Command”The slash command interface supporting three types:
type Command = | { type: 'local'; ... } // Synchronous, local execution | { type: 'local-jsx'; ... } // Renders Ink UI | { type: 'prompt'; ... } // Expands to text for the modelPermission
Section titled “Permission”Multi-source permission rules with pattern matching:
src/types/permissions.ts├── PermissionMode — 'default' | 'plan' | 'auto' | 'bypassPermissions'├── PermissionResult — 'allow' | 'deny' | 'ask' with updatedInput├── ToolPermissionRulesBySource — Rules keyed by setting source└── AdditionalWorkingDirectory — Extra allowed directoriesData Flow: User Input to Response
Section titled “Data Flow: User Input to Response”sequenceDiagram participant U as User participant M as main.tsx participant QE as QueryEngine participant Q as query.ts participant C as claude.ts participant R as withRetry participant API as Anthropic API participant STE as StreamingToolExecutor participant T as Tool.call()
U->>M: Type message + Enter M->>QE: submitMessage(prompt) QE->>QE: processUserInput() → messages QE->>Q: query({ messages, systemPrompt }) Q->>C: createStreamedResponse() C->>R: withRetry(operation) R->>API: POST /v1/messages (streaming) API-->>R: SSE stream events R-->>C: Stream events C-->>Q: Parsed messages
alt Tool use requested Q->>STE: addTool(block, assistantMessage) STE->>T: call(args, context, canUseTool) T-->>STE: ToolResult STE-->>Q: User message with tool_result Q->>C: Next turn with tool results end
Q-->>QE: yield messages QE-->>M: SDKMessage stream M-->>U: Rendered outputDirectory Structure Map
Section titled “Directory Structure Map”src/├── entrypoints/ # Process entry (cli.tsx, init.ts, mcp.ts)├── bootstrap/ # Early state initialization├── commands/ # 60+ slash commands (/help, /config, /model, ...)├── components/ # Ink React components (UI)├── constants/ # System prompts, betas, limits├── context/ # React context providers├── hooks/ # React hooks (permissions, tools, state)├── services/│ ├── api/ # claude.ts, withRetry.ts, errors.ts│ ├── analytics/ # GrowthBook, telemetry│ ├── compact/ # Auto-compaction, reactive compaction│ ├── mcp/ # MCP client, config, server management│ ├── tools/ # StreamingToolExecutor, toolOrchestration│ └── ...├── tools/ # 30+ tool implementations│ ├── AgentTool/│ ├── BashTool/│ ├── FileEditTool/│ ├── FileReadTool/│ ├── GlobTool/│ ├── GrepTool/│ └── ...├── types/ # TypeScript type definitions├── utils/ # Utility modules│ ├── settings/ # Configuration system│ ├── permissions/ # Permission engine│ ├── model/ # Model selection and routing│ ├── plugins/ # Plugin loading│ └── ...├── main.tsx # Commander.js CLI framework├── commands.ts # Command registry├── query.ts # Agentic loop├── QueryEngine.ts # Session lifecycle├── Tool.ts # Tool interface└── tools.ts # Tool pool assembly