CodeDocs Vault

2. Architecture Overview

Component map

                      ┌─────────────────────────────────────────────┐
                      │ entrypoints/cli.tsx   (fast-path dispatcher) │
                      └──────────────┬──────────────────────────────┘
                                     │
          ┌──────────────┬───────────┼─────────────┬─────────────┬──────────────┐
          ▼              ▼           ▼             ▼             ▼              ▼
     --version   claude bridge   claude daemon  claude ps/bg  environment    fall-through
     (MACRO)     bridge/*.ts     daemon/*.ts    cli/bg.ts     -runner         → main.tsx
                                                                                │
                                                                                ▼
                                     ┌──────────────────────────────────────────┐
                                     │ main.tsx (~4.7k LOC)                     │
                                     │  · enable configs, plugins, hooks        │
                                     │  · load commands, tools, agents, MCP     │
                                     │  · decide: interactive | headless        │
                                     └──────────────┬───────────────────────────┘
                                                    │
                  ┌─────────────────────────────────┼────────────────────────┐
                  ▼                                 ▼                        ▼
            renderAndRun()                    runHeadless()           resume path
         (Ink TUI, components/)           (QueryEngine directly)   (sessionStorage)
                  │                                 │
                  │    user prompt / queue          │   programmatic prompt
                  ▼                                 ▼
            ┌──────────────────────────────────────────────────┐
            │ QueryEngine  (QueryEngine.ts)                    │
            │ per-conversation state: messages, usage,         │
            │ permissions, file cache, discovered skills       │
            └──────────────┬───────────────────────────────────┘
                           │  submitMessage() → async generator of SDKMessage
                           ▼
            ┌──────────────────────────────────────────────────┐
            │ query()  (query.ts)   — the agent loop            │
            │ while(true):                                      │
            │   • call model (stream)                           │
            │   • dispatch tool_use blocks in parallel          │
            │   • execute tools via Tool.call()                 │
            │   • feed tool_results back                        │
            │   • maybe compact / escalate / fallback           │
            └──────────────┬───────────────┬──────────┬─────────┘
                           │               │          │
                           ▼               ▼          ▼
             ┌──────────────────┐  ┌──────────┐  ┌─────────────────┐
             │ services/api/     │  │ tools/*  │  │ services/compact │
             │   claude.ts       │  │ 40+ tools│  │   autoCompact    │
             │ (streaming,       │  │ MCP      │  │   compact.ts     │
             │  retry, thinking, │  │ plugins  │  │   summarize      │
             │  caching)         │  │ skills   │  │   tool state     │
             └──────────────────┘  └──────────┘  └─────────────────┘

Layered structure

The tree is flat by folder but layered by import direction:

Layer Folders Responsibility
Leaf / pure constants/, types/, schemas/ Pure data, no side effects. constants/ is explicitly leaf-of-DAG at module-load time (constants/product.ts:60-63).
Utilities utils/ (329 files) File I/O, git, bash AST, fileHistory, caches, crypto, terminal, sandbox, analytics, plugin loader, sinks, ...
Core services services/api, services/compact, services/mcp, services/SessionMemory, services/extractMemories, services/policyLimits Non-UI capabilities that outlive any one turn.
Tool implementations tools/* One folder per tool (Bash, Read, Edit, Agent, Skill, …) + tools.ts registry.
Agent loop query.ts, Tool.ts, QueryEngine.ts, Task.ts, tools.ts, commands.ts The heart: composes tools + model + history into a generator.
State state/, bootstrap/state.ts, context/ AppState store + React context providers + module-level bootstrap singletons.
UI (Ink/React) components/, hooks/, ink/, screens/, keybindings/, vim/ Interactive rendering, keyboard, dialogs.
Entry orchestration entrypoints/, main.tsx, setup.ts, cli/ Boot, dispatch, handler wiring.
Remote / external bridge/, remote/, upstreamproxy/, server/, plugins/, native-ts/ Remote sessions, proxy, MCP servers shipped alongside.

Key abstractions

ToolTool.ts:362

The canonical extension point. Every capability Claude can take is a Tool<Input, Output> with:

Commandtypes/command.ts:205

Three flavors: 'prompt' (injects text; invoked via SkillTool or AgentTool), 'local' (runs client-side without the model), and 'local-jsx' (renders Ink UI). Slash commands and skills share the same type — a skill IS a command with type: 'prompt'.

AgentDefinitiontools/AgentTool/loadAgentsDir.ts:162

Union of BuiltInAgentDefinition | CustomAgentDefinition | PluginAgentDefinition. Each has agentType, whenToUse, tools allowlist, optional disallowedTools, optional mcpServers, model, effort, and getSystemPrompt(). Loaded via getAgentDefinitionsWithOverrides() (loadAgentsDir.ts:296).

QueryEngineQueryEngine.ts:184

"One QueryEngine per conversation. Each submitMessage() call starts a new turn within the same conversation."

It owns mutableMessages, abortController, permissionDenials, totalUsage, readFileState, discoveredSkillNames, loadedNestedMemoryPaths. Wraps query() for headless / SDK callers.

ToolUseContextTool.ts (referenced widely)

A bag passed into every tool.call() containing: options (model, tools, mcpClients, agentDefinitions, thinkingConfig), AppState getters/setters, abortController, readFileState, permission context callbacks, file-history / attribution updaters, and skill/memory trigger sets.

AppStatestate/AppStateStore.ts

A large immutable struct managed via a tiny singleton store (state/store.ts). Covers permission context, fastMode, fileHistory, attribution, speculation state, remote-session status, repl-bridge status, thinking state, and much more. React consumers use useSyncExternalStore subscription.

Data flow for a single turn

┌─ user input (keyboard / stdin / websocket) ─┐
│                                             │
▼                                             │
processUserInput() ── slash cmd? ── local cmd ─┤── setMessages, maybe exit
        │                                     │
        ▼                                     │
  new UserMessage(s) pushed to mutableMessages │
        │                                     │
        ▼                                     │
  recordTranscript()  → sessionStorage JSONL   │
        │                                     │
        ▼                                     │
┌─ query() generator ─────────────────────────┘
│    • compose system prompt (fetchSystemPromptParts)
│    • compose user/system context (getUserContext, getSystemContext)
│    • call claude.ts streaming
│         yields assistant blocks (text, thinking, tool_use)
│    • parallel execute tool_use via Tool.call()
│         yields tool_result blocks
│    • if max_output / fallback / 529 → recovery branch
│    • if threshold hit → auto-compact → continue
│    • if no tool_use → stop hooks → maybe retry → exit
└→  each yielded SDKMessage flows back to the UI or SDK consumer

Dependency direction

Broadly one-way:

entrypoints ─▶ main.tsx ─▶ QueryEngine ─▶ query ─▶ services/api + tools/* + services/compact
                    │                                   │
                    ├─▶ components/UI ◀── hooks/* ───────┤
                    │                                   │
                    ▼                                   ▼
                state/ + context/                  utils/*
                    ▲                                   ▲
                    └─── constants/ + types/ (leaf) ────┘

Circular imports are avoided with lazy require() patterns and explicit comments — e.g. constants/product.ts:62-73 lazy-requires bridge/sessionIdCompat.ts to preserve "constants/ as leaf-of-DAG at module-load time." Likewise bootstrap/state.ts exists to hold module-level singletons without pulling in heavier modules.

What "one conversation" looks like as state

The critical running state for a session:

Field Kind Lives in
mutableMessages: Message[] Array of user/assistant/system/attachment messages QueryEngine.mutableMessages or AppState.messages
toolPermissionContext Permission mode + rules AppState.toolPermissionContext
readFileState: FileStateCache Hashes of every file read this turn (gates edits) QueryEngine.readFileState
fileHistoryState Snapshot-able history for /rewind AppState.fileHistory
attributionState Tracks co-author attribution for commits AppState.attribution
totalUsage: NonNullableUsage Token accounting across all turns QueryEngine.totalUsage
permissionDenials Tool denials for SDK result.permission_denials QueryEngine.permissionDenials
discoveredSkillNames Which skills the model has discovered this turn QueryEngine.discoveredSkillNames
loadedNestedMemoryPaths Nested CLAUDE.md files loaded as attachments QueryEngine.loadedNestedMemoryPaths
sessionId, parentSessionId Session lineage (plan → implement) bootstrap/state.ts module-level
cwd, originalCwd, projectRoot Working-directory tracking bootstrap/state.ts module-level
lastAPIRequest Last params (for /share bug reports) bootstrap/state.ts

The transcript is appended to ~/.claude/sessions/<sessionId>/transcript.jsonl as it goes, so --resume can reconstruct everything.