---
title: "Architecture Overview — Claude Code Internals — Vlad Arbatov"
description: "Entry points, data flow, module graph, config, process model, state"
image: "https://arbatov.dev/share-claude.png"
---

# Architecture Overview

## Entry Points & Startup

**Primary Entry**: `entrypoints/cli.tsx` (39KB)
- Bootstrap layer with fast-path checking before full module loading
- Fast paths (zero-module overhead):
  - `--version` / `-v`: prints version instantly
  - `--dump-system-prompt`: renders and outputs system prompt (ant-only)
  - `--daemon-worker=<kind>`: spawns daemon workers for supervisors
  - `--claude-in-chrome-mcp`, `--computer-use-mcp`: special MCP servers
  - `--bridge`, `--remote-control`, `--sync`: bridge mode entry
- Default path: dynamically imports full CLI for standard execution

**Main Module**: `main.tsx` (804KB)
- Real entrypoint after bootstrap checks pass
- Initializes: telemetry, configs, auth, GrowthBook feature gates, MDM raw read, keychain prefetch (in parallel)
- Parses CLI flags, determines operating mode (REPL, server, assistant, coordinator, SDK)
- Loads: plugins, skills, MCPs, agents, commands
- Invokes selected command or launches REPL

**Operating Modes**:
- **REPL** (default): Interactive UI via React + custom terminal (ink)
- **Server** (HTTP API): CCR (Cloud Compute Runtime) environments
- **Assistant** (KAIROS): Asynchronous background agents
- **Coordinator** (COORDINATOR_MODE): Multi-agent swarms
- **SDK/Headless**: Programmatic API via QueryEngine

---

## Core Data Flow

```
User Input → API Call → Tool Execution → Response

1. Input Processing (processUserInput()): Parse user text, build attachments, fetch CLAUDE.md
2. QueryEngine.submitMessage(): Store message, manage file cache, call query()
3. query() generator (query.ts): Main loop that:
   - Fetches system prompt, normalizes messages
   - Calls makeAPICall() for streaming API response
   - Parses streaming events (text blocks, tool_use blocks, usage deltas)
   - Routes tool use blocks to runTools() orchestrator
   - Collects tool results, yields next API turn
   - Stops when stop_reason === 'end_turn'
4. makeAPICall() (services/api/claude.ts): Low-level API interaction:
   - Constructs message array, applies system prompt + betas
   - Handles retries on transient errors, returns Stream of events
5. runTools() (services/tools/toolOrchestration.ts): For each tool:
   - Check permissions (auto-mode classifier or manual prompt)
   - Execute tool (bash, file ops, web fetch, agents, etc.)
   - Normalize output to tool_result message
   - Return for next API iteration
```

**Key Streaming Events Parsed**:
- `content_block_start/delta/stop`: Text content
- `tool_use_start/delta/stop`: Tool definitions + inputs
- `message_delta`: Usage tokens
- `message_stop`: Final stop reason

---

## Key Files at Root

| File | Size | Purpose |
|------|------|---------|
| **main.tsx** | 804KB | CLI initialization, mode selection, command dispatch |
| **query.ts** | 69KB | Generator loop: API streaming, event parsing, tool routing |
| **QueryEngine.ts** | 47KB | High-level state: messages, file cache, usage tracking |
| **Tool.ts** | ~30KB | Tool interface, types, registry lookup by name |
| **tools.ts** | 17KB | Tool discovery, feature-gated loading, presets |
| **commands.ts** | 25KB | Slash commands (/brief, /commit, /context, etc.) |
| **context.ts** | 6.4KB | System context (git status) + user context (CLAUDE.md) |
| **setup.ts** | 21KB | Environment setup and initialization |
| **cost-tracker.ts** | 11KB | Per-model cost tracking and session persistence |

---

## Module Dependency Graph

```
Layers (top → bottom):

┌─────────────────────────────────────────┐
│ CLI Entrypoints (cli.tsx, init.ts)     │
└─────────────────┬─────────────────────┘
                  │
┌─────────────────┴─────────────────────┐
│ Main Application (main.tsx, commands) │
└─────────────────┬─────────────────────┘
                  │
    ┌─────────────┼──────────┬──────────┐
    │             │          │          │
    ▼             ▼          ▼          ▼
┌────────┐  ┌─────────┐  ┌─────────┐  ┌────────────┐
│ UI/REPL│  │QueryEng │  │  Tools  │  │ Commands   │
│REPL.tsx│  │query()  │  │tools.ts │  │commands.ts │
└────┬───┘  └────┬────┘  └────┬────┘  └────────────┘
     │           │             │
     └───────────┼─────────────┘
                 │
    ┌────────────┴──────────────┐
    │ Services Layer            │
    │ - API (claude.ts)         │
    │ - MCP (client.ts)         │
    │ - Analytics (growthbook)  │
    │ - Compact (autoCompact)   │
    └────────────┬──────────────┘
                 │
    ┌────────────┴──────────────────┐
    │                               │
    ▼                               ▼
┌──────────────────────┐  ┌────────────────────┐
│ Utils                │  │ Bootstrap/State    │
│ - messages           │  │ - bootstrap/state  │
│ - permissions        │  │ - AppState.tsx     │
│ - tokens             │  │ - AppStateStore    │
│ - git, config        │  └────────────────────┘
└──────────────────────┘
```

Circular dependency prevention:
- Lazy imports via require() in hot modules
- Centralized type exports (Tool.ts, types/permissions.ts)
- Bootstrap isolation (leaf module, no reverse deps)
- Feature gates (bun:bundle DCE for conditional imports)

---

## Configuration System

**Config Load Priority** (highest → lowest):
1. CLI flags (`--model`, `--tools`, `--add-dir`)
2. Environment variables (CLAUDE_CODE_*, ANTHROPIC_*)
3. Settings.json (~/.claude/settings.json)
4. Managed settings (remote, enterprise-controlled)
5. Defaults (compiled in)

**CLAUDE.md Discovery**:
- `getClaudeMds()` (utils/claudemd.ts) walks project for CLAUDE.md files
- Filtered by .claude/ignore, git root boundaries
- Cached in bootstrap state for system prompt injection
- Content used by auto-mode classifier for permission decisions

**Key Environment Variables**:
- `CLAUDE_CODE_SIMPLE`: Disables advanced features
- `CLAUDE_CODE_REMOTE`: Running in CCR (remote mode)
- `CLAUDE_CODE_DISABLE_THINKING`: Disables extended thinking
- `USER_TYPE`: 'ant' (internal) vs 'external' (public builds)

---

## Process Model

**Default: Single Process**
- REPL/CLI runs in one Node.js process
- Child processes spawned for tools (bash, agents, MCP servers)
- All state in-memory with graceful shutdown flush

**Multi-Process Scenarios**:
1. **Daemon Mode**: Supervisor + worker processes (assistant subsystem, LSP server, etc.)
2. **Agent Swarms** (KAIROS feature): Main thread + subagents as in-process tasks
3. **Bridge Mode**: HTTP server on local machine, syncs state with remote Claude Code instance

**Worktree Support** (EnterWorktreeTool):
- Creates isolated git worktree in `.claude/worktrees/<name>/`
- New branch branched from current HEAD
- CWD changed to worktree root
- State tracked: `isWorktreeModeEnabled()`
- File operations scoped to worktree
- Exit: removes worktree + branch via `git worktree remove`

---

## Tool System Overview

**Tool Discovery**: `getAllBaseTools()` (tools.ts:193)

Base tools (always available):
- **I/O**: FileReadTool, FileWriteTool, FileEditTool, NotebookEditTool
- **Search**: GlobTool, GrepTool, WebFetchTool
- **Execution**: BashTool, PowerShellTool (Windows), REPLTool (ant-only)
- **Assistant**: AgentTool, SkillTool, TaskOutputTool
- **Meta**: ConfigTool, ListMcpResourcesTool, ReadMcpResourceTool, AskUserQuestionTool

Feature-gated tools:
- **SleepTool** (PROACTIVE/KAIROS)
- **WorkflowTool** (WORKFLOW_SCRIPTS)
- **CronTool** family (AGENT_TRIGGERS)
- **WebBrowserTool** (WEB_BROWSER_TOOL)

Dynamic tools:
- **MCP tools**: Loaded from configured MCP servers
- **Plugin tools**: Via plugin system
- **Agent tools**: From `.claude/agents/` directory

**Tool Permission Flow**:
```
Tool invoked → Check canUseTool()
  ├─ Bypass mode? → Allow
  ├─ Auto mode?
  │   └─ yoloClassifier(CLAUDE.md, code) → Allow/Deny
  └─ Manual mode
      └─ PermissionRequest dialog (React) → User confirms/denies
```

---

## State Management

**Global Singleton** (bootstrap/state.ts, 56KB):
- Non-React, process-global state
- Session ID, costs, models, metrics
- Telemetry counters (cost, tokens, LOC)
- Mutable via setters: `setCwd()`, `addToTotalSessionCost()`, etc.

**React Context** (state/AppState.tsx):
- `AppStoreContext`: Provider wraps REPL
- Holds UI-only state (not persisted)

**File State Cache** (utils/fileStateCache.ts):
- In-memory cache of read/edited files
- ~100MB limit (READ_FILE_STATE_CACHE_SIZE)

**Conversation Messages**:
- Held in memory during session
- All user/assistant messages, tool use + results
- Max ~1M tokens before autocompaction

---

## Key Architectural Decisions

1. **Build-time dead code elimination** (feature gates via bun:bundle)
2. **Lazy loading** to minimize startup time
3. **Message-driven tool routing** (streaming event parsing)
4. **Permission-before-execution** (security-first)
5. **In-memory file caching** (performance)
6. **Memoized contexts** (git, CLAUDE.md)
7. **Graceful shutdown** (data integrity)
8. **Prompt cache stability** — tools sorted deterministically to maximize cache hits
