Claude Code v2.1.88 — Decompiled TypeScript Source Analysis

code ZhaiFeiyue-claude-code-source-code
agentclitypescripttool-usemulti-agentmcp

ZhaiFeiyue-claude-code-source-code — L2 #

§1 TL;DR #

Decompiled TypeScript source (~512K LOC) of Anthropic's Claude Code v2.1.88. AsyncGenerator agent loop (QueryEngine→query()→StreamingToolExecutor) with 40+ tools, React/Ink TUI, 4-tier permission engine, 3-strategy context compaction, 5-mode sub-agent spawning, and 108 dead-code-eliminated internal modules.

§2 Project Identity #

FieldValue
Repo
Upstreamsanbuphy/learn-coding-agent (11,910 stars, 19,728 forks)
Primary languageTypeScript (30.3M bytes), JavaScript (27K bytes)
LOC~512,664 across ~1,884 .ts/.tsx files
Largest single filequery.ts (~785KB)
LicenseNone specified (Anthropic copyright disclaimer)
Version analyzed2.1.88 (extracted from npm @anthropic-ai/claude-code)
RuntimeBun-compiled → Node.js ≥ 18 (12MB self-contained bundle)
Stars0 (fork); upstream: 11,910
Date2026-03

§3 Motivation & Core Questions #

One-line pitch: Production-grade agentic coding assistant that wraps a while-true tool-use loop with permissions, streaming, context compression, multi-agent orchestration, and terminal UI.

Q1 痛点:Bare tool-use loops cannot survive production #

The canonical LLM agent pattern — call the model, check for tool_use stop reason, execute tools, append results, loop — breaks down at production scale for several compounding reasons. (1) Unrestricted tool execution on a user's filesystem is a security catastrophe; every tool call needs permission gating with rules, user prompts, and sandboxing. (2) Multi-turn conversations exceed context windows within minutes of real coding work; without compression, the model loses coherent context or the API rejects the request. (3) Complex tasks require decomposition into sub-agents, each needing isolated context to avoid cross-contamination, but sharing file state for coherent edits. (4) Users interact via multiple surfaces — interactive CLI, headless SDK, remote bridge from Claude Desktop — requiring a shared core engine with pluggable I/O. (5) The tool set itself must be extensible (MCP protocol, user plugins, skills) without modifying core loop logic.

Q2 方法:AsyncGenerator agent loop + 12-mechanism production harness #

The architecture factors the agent loop into QueryEngine.submitMessage() which yields SDKMessage via an AsyncGenerator. This generator delegates to query() (the inner while-true loop calling Claude API), which dispatches tool calls through StreamingToolExecutor. Around this core loop, 12 progressive harness mechanisms layer on production concerns:

  1. The Loop (query.ts): while-true calling Claude API, checking stop_reason, executing tools
  2. Tool Dispatch (Tool.ts + tools.ts): buildTool() factory with validateInput→checkPermissions→call lifecycle
  3. Planning (EnterPlanModeTool + TodoWriteTool): list steps before executing
  4. Sub-Agents (AgentTool + forkSubagent.ts): child agents with fresh messages[] but shared file cache
  5. Knowledge On Demand (SkillTool + memdir/): inject context via tool_result, not system prompt; CLAUDE.md lazy loading
  6. Context Compression (services/compact/): autoCompact + snipCompact + contextCollapse
  7. Persistent Tasks (TaskCreate/Update/Get/List): file-based task graph with status tracking
  8. Background Tasks (DreamTask + LocalShellTask): daemon threads with completion notifications
  9. Agent Teams (TeamCreate/Delete + InProcessTeammateTask): persistent teammates with async mailboxes
  10. Team Protocols (SendMessageTool): one request-response pattern for all agent negotiation
  11. Autonomous Agents (coordinator/coordinatorMode.ts): idle cycle + auto-claim
  12. Worktree Isolation (EnterWorktreeTool): tasks manage goals, worktrees manage directories
  13. The permission system enforces a 4-stage pipeline: validateInput() → PreToolUse hooks (user-defined shell scripts) → permission rules (alwaysAllow / alwaysDeny / alwaysAsk from settings, CLI args, session decisions) → interactive prompt (allow once / always / deny) → checkPermissions() (tool-specific path sandboxing) → tool execution.

    核心技术壁垒 #

    The AsyncGenerator-based agent loop design. By making submitMessage() return AsyncGenerator, the engine achieves full-chain streaming from API through tool execution to consumer — every intermediate result (partial text, tool progress, stream events, usage accounting) is yielded as it arrives, not buffered. This single design choice enables three critical properties simultaneously: (a) SDK consumers get real-time streaming without callbacks, (b) the REPL and bridge surfaces compose the same generator with different rendering, and (c) cancellation propagates naturally via AbortController without explicit cleanup at each layer. The alternative — callback-based or observable-based streaming — would require explicit subscription management and error propagation at every intermediate layer, which at 40+ tools and 12 harness mechanisms would be a maintenance disaster.

    Q3 结果 #

    Ships as Anthropic's primary coding CLI. The decompiled source reveals 108 feature-gated modules not present in the published build (daemon, multi-agent coordinator, voice mode, KAIROS autonomous agent, browser automation) — indicating the shipped product exposes roughly 40% of the implemented capability surface. The 12-mechanism harness architecture has proven sufficient to support interactive REPL, headless SDK, remote bridge, and multi-agent swarm modes from a single codebase.

    §4 Architecture & Module Map #

    flowchart TB subgraph ENTRY["Entry Layer"] CLI["cli.tsx
    version / help / daemon"] MAIN["main.tsx
    REPL bootstrap (4.7K LOC)"] SDK["entrypoints/sdk/
    Agent SDK (types, sessions)"] MCP_E["mcp.ts
    MCP server entry"] end subgraph ENGINE["Query Engine"] QE["QueryEngine.ts
    submitMessage() → AsyncGenerator‹SDKMessage›"] QUERY["query.ts (785KB)
    while-true agent loop"] STE["StreamingToolExecutor
    concurrent tool dispatch"] end subgraph TOOLS["Tool System (40+)"] TREG["tools.ts
    registry + presets + filtering"] TFAC["Tool.ts
    buildTool() factory"] subgraph TCAT["Tool Categories"] TFILE["File: Read/Edit/Write/Notebook"] TSEARCH["Search: Glob/Grep/ToolSearch"] TEXEC["Exec: Bash/PowerShell"] TAGENT["Agent: AgentTool/SendMessage/TeamCreate"] TTASK["Task: Create/Update/Get/List/Stop"] TWEB["Web: Fetch/Search"] TMCP_T["MCP: MCPTool/ListResources/ReadResource"] TPLAN["Plan: EnterPlan/ExitPlan/Todo"] TWORK["Worktree: Enter/Exit"] end end subgraph SERVICES["Service Layer"] API["api/claude.ts
    streaming API client"] COMPACT["compact/
    autoCompact + snipCompact + contextCollapse"] MCP_S["mcp/
    MCPConnectionManager"] ANALYTICS["analytics/
    telemetry + GrowthBook"] TOOLEX["tools/
    StreamingToolExecutor + toolOrchestration"] PLUGINS["plugins/
    plugin loader"] end subgraph STATE["State Layer"] APPSTATE["AppState
    permissions / fileHistory / fastMode / speculation"] STORE["AppStateStore.ts
    React Context + hooks"] PERMS["utils/permissions/
    rule engine"] FHIST["fileHistory.ts
    undo/redo snapshots"] FCACHE["fileStateCache.ts
    LRU file cache"] end subgraph TASKS["Task System"] LSHELL["LocalShellTask
    bash execution"] LAGENT["LocalAgentTask
    sub-agent execution"] RAGENT["RemoteAgentTask
    bridge-based remote"] TEAM["InProcessTeammateTask
    in-process teammate"] DREAM["DreamTask
    background thinking"] end subgraph BRIDGE["Bridge Layer"] BMAIN["bridgeMain.ts (115KB)
    session lifecycle"] BREPL["replBridge.ts (100KB)
    REPL bridge controller"] BREMOTE["remoteBridgeCore.ts (39KB)
    remote core"] JWT["jwtUtils.ts
    JWT refresh"] WSEC["workSecret.ts
    auth token mgmt"] end subgraph UI["Terminal UI (React/Ink)"] PRINT["cli/print.ts (212KB)
    output rendering"] COMPS["components/
    40+ component groups"] DESIGN["design-system/
    reusable primitives"] PROMPT["PromptInput/
    input + suggestions"] end subgraph CMDS["Slash Commands (80+)"] direction LR CMDAGENT["agents/"] CMDBRANCH["branch/"] CMDBRIDGE["bridge/ (46KB)"] CMDMCP["mcp/ (56KB)"] CMDMEM["memory/"] CMDPLAN["plan/"] CMDRES["resume/"] CMDREV["review/"] end CLI --> MAIN CLI --> SDK CLI --> MCP_E MAIN --> QE SDK --> QE QE --> QUERY QUERY --> STE STE --> TREG TREG --> TFAC TFAC --> TCAT QE --> API QE --> COMPACT QE --> MCP_S QE --> ANALYTICS QUERY --> APPSTATE APPSTATE --> PERMS APPSTATE --> FHIST APPSTATE --> FCACHE TAGENT --> LAGENT TAGENT --> RAGENT TAGENT --> TEAM TAGENT --> DREAM TAGENT --> LSHELL RAGENT --> BMAIN BMAIN --> BREPL BMAIN --> BREMOTE BMAIN --> JWT BMAIN --> WSEC MAIN --> UI MAIN --> CMDS QE --> TOOLEX

    Top modules by centrality:

    ModulePurpose
    src/query.ts (~785KB)The agent loop: while-true calling Claude API, checking stop_reason, executing tools, managing turns
    src/QueryEngine.tsQuery lifecycle + session state; submitMessage()AsyncGenerator
    src/Tool.tsTool interface definition + buildTool() factory with safe defaults
    src/tools.tsTool registry, presets, filtering — what tools are available per context
    src/main.tsx (4,683 LOC)REPL bootstrap: interactive terminal entry point
    src/bridge/bridgeMain.ts (115KB)Claude Desktop / remote session lifecycle manager
    src/cli/print.ts (212KB)Output rendering engine for terminal display
    src/services/tools/StreamingToolExecutor + toolOrchestration — concurrent tool dispatch
    src/services/compact/Context compression: autoCompact, snipCompact, contextCollapse
    src/utils/permissions/Permission rule engine: allow/deny/ask rules with glob matching
    src/commands.tsSlash command definitions (~80+ commands) with feature-gated imports
    src/bootstrap/state.ts (56KB)Bootstrap state: session ID, persistence, environment config

    §5 Entry Points & API Surface #

    Public Entry Points #

    EntryPathRole
    CLIsrc/entrypoints/cli.tsxVersion, help, daemon launch — primary user-facing entry
    REPLsrc/main.tsxInteractive terminal session (4,683 LOC bootstrap)
    SDKsrc/entrypoints/sdk/Headless/programmatic API via QueryEngine
    MCP Serversrc/entrypoints/mcp.tsExpose Claude Code as an MCP server
    Bridgesrc/bridge/bridgeMain.tsClaude Desktop remote session manager

    SDK API Surface (QueryEngine) #

    The headless API is a single class with one primary method:

    • new QueryEngine(config: QueryEngineConfig) — accepts tools, commands, MCP clients, agent definitions, permission handler, app state, model config, budget limits
    • engine.submitMessage(prompt, options?)AsyncGenerator — yields streaming messages (assistant text, tool use, progress, stream events, result with cost/usage/session_id)

    CLI Configuration (top 10) #

    MechanismExamplesSource
    CLI flags--continue, --resume , --fork-session, --model, --max-turnscli.tsx arg parsing
    Environment varsUSER_TYPE=ant (internal mode), CLAUDE_CODE_* familyconfig.ts, bootstrap/state.ts
    Settings files.claude/settings.json, .claude/settings.local.jsonutils/settings/
    CLAUDE.mdProject-level memory files (lazy-loaded)memdir/memdir.ts
    Feature flagsGrowthBook runtime flags (A/B experiments)services/analytics/
    Compile-time flagsfeature() from Bun — KAIROS, DAEMON, VOICE_MODE, etc.scripts/transform.mjs
    Permission rulesalwaysAllow, alwaysDeny, alwaysAsk per tool + glob patternutils/permissions/
    Slash commands80+ commands (/compact, /plan, /resume, /review, /mcp, etc.)src/commands/
    MCP configstdio/sse/http/ws/sdk transports, OAuth 2.0, API key authservices/mcp/
    HooksPreToolUse / PostToolUse user-defined shell scriptsutils/hooks/

    Extension Points #

    • Tools: buildTool() factory — any new tool implements the Tool interface (validate, check permissions, call, render)
    • MCP: MCPConnectionManager dynamically discovers and registers external tools via mcp____ naming
    • Plugins: services/plugins/ + commands/plugins.ts — runtime-loadable plugin system
    • Skills: skills/loadSkillsDir.ts — directory-based skill discovery, injected via tool_result
    • Hooks: PreToolUse / PostToolUse — user-defined shell scripts executed before/after tool calls
    • Slash commands: Extensible command registry with feature-gated imports
    • Agent definitions: Custom agent definitions for sub-agent spawning
    • Transports: cli/transports/ — SSE, WebSocket, Hybrid, ccrClient for I/O

    §6 Core Data Structures #

    Message — Discriminated Union (src/types/message.ts) #

    • Layout: Discriminated union over type field: assistant (model output with content blocks), user (user input or tool results), system (system prompt parts), stream_event (SSE events), attachment (structured output, max_turns), progress (tool execution progress).
    • Lifecycle: Created per conversation turn. User messages created by processUserInput(), assistant messages yielded by query(), both accumulated in QueryEngine.mutableMessages[]. Persisted to session JSONL on creation.
    • Mutation: Append-only within a session. Messages are never modified after creation — context compression creates new summary messages rather than editing existing ones.
    • Thread safety: Owned by the QueryEngine instance. Single-writer within a conversation turn.

    Tool — Generic Interface (src/Tool.ts) #

    • Layout: Generic type parameterized over input schema (z.ZodType), output type, and progress data. ~30 methods spanning lifecycle (validateInput, checkPermissions, call), capability queries (isEnabled, isConcurrencySafe, isReadOnly, isDestructive), rendering (renderToolUseMessage, renderToolResultMessage — React/Ink nodes), and AI-facing description.
    • Lifecycle: Tools are registered at startup via tools.ts, filtered per context (feature flags, permissions, MCP discovery). Tool instances are stateless — all mutable state lives in ToolUseContext.
    • Mutation: Immutable after buildTool() construction. State flows through ToolUseContext parameter.

    ToolUseContext — Execution Environment (src/Tool.ts) #

    • Layout: 30+ fields capturing the full execution environment: options (commands, debug, model, tools, thinking config, MCP clients), abort controller, file state cache, app state accessors, message history, memory triggers, skill triggers, query tracking, content replacement state, rendered system prompt.
    • Lifecycle: Created fresh per query() invocation, threaded through all tool calls within a turn.
    • Mutation: Mutable — tools and the query loop mutate fields (e.g., pushing to messages, updating readFileState, adding to discoveredSkillNames).

    TaskStateBase — Task Tracking (src/Task.ts) #

    • Layout: id (prefixed: b=bash, a=agent, r=remote, t=team, d=dream + 8 random chars), type (7 variants), status (pending/running/completed/failed/killed), description, timing fields, outputFile path, outputOffset for streaming reads.
    • Lifecycle: Created by createTaskStateBase() on task launch. Status transitions: pending → running → completed/failed/killed. Stored in AppState.tasks.
    • Mutation: Status and timing fields updated by the owning task implementation. Single-writer per task ID.

    AppState — Global Application State (src/state/) #

    • Layout: Contains toolPermissionContext (permission mode, allow/deny/ask rules, bypass availability), fileHistoryState (undo/redo snapshots), tasks (running task map), fastMode, speculation state, and attribution tracking.
    • Lifecycle: Created at session start, persists across turns within a session.
    • Mutation: Updated via setAppState(f: prev => next) pattern (functional update). React integration via useAppState(selector) hook.

    FileStateCache — LRU File Cache (src/utils/fileStateCache.ts) #

    • Layout: LRU cache mapping file paths to their content state. Used by file-reading tools to avoid redundant disk reads within a session.
    • Lifecycle: Created per QueryEngine instance. Shared between parent agent and forked sub-agents.
    • Mutation: Updated on file read/write operations. LRU eviction on capacity limit.

    §7 Critical Path Analysis #

    Hot path: User prompt → streamed response #

    sequenceDiagram participant U as User / SDK Consumer participant QE as QueryEngine participant PUI as processUserInput() participant Q as query() [agent loop] participant API as Claude API participant STE as StreamingToolExecutor participant T as Tool.call() participant PERM as Permission Pipeline participant SESS as Session Storage U->>QE: submitMessage(prompt) QE->>QE: setCwd(), clear skill cache QE->>QE: fetchSystemPromptParts() QE->>PUI: parse slash commands, attachments PUI-->>QE: messagesFromUserInput, shouldQuery QE->>SESS: recordTranscript (fire-and-forget) QE->>QE: load skills + plugins (cache-only) QE-->>U: yield systemInit message loop shouldQuery && turns < maxTurns QE->>Q: messages + systemPrompt + toolUseContext Q->>API: streaming request (messages, tools, model) API-->>Q: SSE stream (text_delta, tool_use blocks) alt stop_reason == "tool_use" Q->>STE: dispatch tool_use blocks (concurrent if safe) loop for each tool_use block STE->>PERM: validateInput → hooks → rules → prompt → checkPermissions alt APPROVED PERM-->>STE: allow STE->>T: tool.call(args, context) T-->>STE: ToolResult else DENIED PERM-->>STE: deny (track in permissionDenials) end end STE-->>Q: tool_result messages Q->>Q: append to messages[], loop else stop_reason == "end_turn" Q-->>QE: final assistant message end QE->>QE: accumulateUsage(), track turns QE->>SESS: recordTranscript (fire-and-forget) QE-->>U: yield normalized SDKMessage end QE-->>U: yield result {cost, usage, session_id}

    Latency breakdown per turn #

    HopBottleneckDominant cost
    processUserInputCPU: slash command parsing, attachment processing~1-5 ms
    fetchSystemPromptPartsCPU: memory file loading, system prompt assembly~5-50 ms (CLAUDE.md discovery)
    Claude API prefillNetwork + GPU: prompt encoding~100 ms–10 s (proportional to context length)
    Claude API decodeNetwork + GPU: token generation~50 ms–60 s (proportional to output length)
    Permission pipeline (per tool call)CPU + user wait: validateInput → hooks → rules → interactive prompt~0 ms (auto-allow) to unbounded (user prompt)
    Tool execution (Bash)CPU/disk: subprocess spawn + execution~50 ms–300 s (command dependent)
    Tool execution (FileRead)Disk I/O~1-10 ms
    Tool execution (GrepTool)CPU: ripgrep subprocess~10-500 ms (codebase size dependent)
    Tool execution (WebFetch)Network~100 ms–10 s
    Session persistenceDisk: JSONL append (fire-and-forget)~1-5 ms (non-blocking)

    The API call dominates wall-clock time. The permission pipeline is the critical human-in-the-loop bottleneck — a single denied tool call with interactive prompt blocks the entire turn until the user responds. Auto-allow rules (alwaysAllow) eliminate this for trusted tool patterns.

    L1 claims vs. code reality #

    The README describes the agent pattern as a simple while-true loop. The actual implementation in query.ts (785KB — the largest file in the codebase) is far more complex: it manages streaming state machines, concurrent tool dispatch, context compaction triggers, sub-agent lifecycle, transcript persistence, turn counting, budget enforcement, model fallback, snip boundaries, and structured output enforcement. The "simple loop" framing understates the complexity by roughly an order of magnitude.

    §8 作者証明 #

    無形式化作者証明 — 仅实证。

    This is a decompiled commercial product, not a research artifact. No formal proofs exist for any component. Correctness assurances come from: (1) TypeScript's static type system with branded types (e.g., SystemPrompt via asSystemPrompt()) preventing certain classes of string/array confusion, (2) Zod schema validation on every tool input, (3) the permission pipeline enforcing security invariants via runtime checks, (4) session persistence providing crash recovery via JSONL replay, (5) the FileHistoryState snapshot system enabling undo/redo for destructive file operations, and (6) presumably extensive internal testing at Anthropic (not included in the decompiled source).

    The key invariant — that the agent loop always terminates — is enforced by maxTurns (turn count limit) and maxBudgetUsd (cost limit), both optional. Without these guards, the loop runs until the model emits stop_reason: "end_turn" or the user aborts via AbortController.

    §9 Concurrency & Memory #

    Concurrency Model #

    MechanismWhereWhy
    Single-threaded event loopNode.js main processAll core logic runs on one thread; I/O is async
    AsyncGenerator streamingQueryEngine → query() → consumerFull-chain streaming without callbacks; backpressure via generator protocol
    AbortControllerEvery tool call, API request, sub-agentCooperative cancellation propagating through the entire call stack
    Concurrent tool dispatchStreamingToolExecutorTools marked isConcurrencySafe execute in parallel within a turn
    Child processesSub-agents (fork mode), BashToolIsolated execution contexts with IPC
    AsyncLocalStoragePer-agent contextContext isolation for sub-agents sharing the same Node.js process
    Fire-and-forget writesrecordTranscript()Non-blocking persistence with ordering guarantee via queue

    Memory Management #

    • Message accumulation: mutableMessages[] grows monotonically within a session. No garbage collection of old messages — this is what drives the need for context compression.
    • Context compression triggers when token count exceeds threshold:
    • autoCompact: summarizes old messages via a dedicated compact API call, replacing verbose history with a concise summary
    • snipCompact: removes zombie messages and stale markers (gated behind HISTORY_SNIP feature flag)
    • contextCollapse: restructures context for efficiency (gated behind CONTEXT_COLLAPSE flag)
    • FileStateCache: LRU cache of file contents. Shared between parent and forked sub-agents to avoid redundant disk reads. Bounded by entry count.
    • FileHistoryState: Snapshot-based undo/redo for file operations. Each destructive file edit stores a before-snapshot. Ring buffer prevents unbounded growth in long sessions.

    Concurrency Concerns #

    1. Single-threaded bottleneck: The core agent loop, permission checks, and tool dispatch all run on the Node.js main thread. A blocking synchronous operation in any tool's validateInput() or checkPermissions() stalls the entire session. This is mitigated by convention (all tool methods are async) rather than enforcement.
    2. mutableMessages[] shared reference: Sub-agents forked in-process via AsyncLocalStorage share the parent's message array reference. The fork mode creates a fresh messages[] copy, but the in-process teammate mode shares state through setAppStateForTasks, creating a subtle mutation-ordering dependency.
    3. Tool concurrency safety: StreamingToolExecutor checks isConcurrencySafe() before parallel dispatch, but the check is per-tool, not per-input. Two concurrent FileEditTool calls to the same file could race despite both being individually "concurrency safe."
    4. Session persistence ordering: recordTranscript() uses fire-and-forget writes with an ordering queue. If the process crashes between a message being yielded and the write completing, the session log loses that message. The REPL path awaits persistence for user messages (crash recovery) but fire-and-forgets assistant messages (performance).
    5. §10 Performance Characteristics #

      Build & Startup #

      MetricValue
      Bundle size~12 MB (single cli.js)
      Cold start~1-3 s (Node.js startup + module init + settings load + MCP discovery)
      Warm start (--continue)~1-3 s + session replay time (proportional to session JSONL size)

      Runtime Bottlenecks #

      The dominant cost in every interaction is the Claude API call (network latency + model inference time). The local TypeScript runtime adds negligible overhead relative to API time. The performance-critical local paths are:

      1. Context assembly: fetchSystemPromptParts() loads CLAUDE.md files, discovers skills, assembles system prompt. Cost scales with the number of memory files and their sizes.
      2. Permission evaluation: For auto-allowed tool patterns, permission checks are sub-millisecond glob matching. For interactive prompts, latency is user-bounded.
      3. File operations: FileReadTool for large files, GrepTool for large codebases — both delegate to system utilities (Node.js fs and rg respectively), so performance matches those tools.
      4. Session persistence: Append-only JSONL writes. For very long sessions, the JSONL file grows without bound. Resume (--continue) replays the entire file.
      5. Scaling Behavior #

        • Context length: Performance degrades linearly with context length (API cost and latency). Context compression mitigates but does not eliminate this — summaries are lossy.
        • Tool count: 40+ built-in tools plus MCP-discovered tools. The tool list is included in every API call as function definitions, consuming context window budget. ToolSearchTool exists specifically to manage this — it defers loading of rarely-used tools.
        • Sub-agent depth: Each sub-agent fork copies the parent's messages and creates a new API conversation. Deep nesting multiplies API calls. No explicit depth limit beyond maxTurns per agent.
        • Concurrent users: Single-process, single-session design. No built-in multi-tenancy. The bridge layer handles one remote session at a time.

        §11 论証鏈 #

        StepClaimEvidenceDepends on
        1A coding assistant must execute tools (file read/write, search, bash) to perform real work, not just generate textFundamental: text-only LLM responses cannot modify files, run tests, or inspect codebases
        2Unrestricted tool execution on a user's filesystem is a security risk requiring multi-layer permission gating4-stage permission pipeline: validateInput → hooks → rules → interactive prompt → checkPermissions, with separate allow/deny/ask rule sets per tool and glob patternStep 1
        3Multi-turn coding conversations exceed context windows within minutes, requiring automatic compressionThree compression strategies (autoCompact, snipCompact, contextCollapse) triggered by token count thresholds; the largest single file in the codebase (query.ts, 785KB) exists partly because context management logic is interleaved with the agent loopStep 1
        4Complex tasks benefit from decomposition into sub-agents with isolated contexts but shared file state5 spawn modes (default, fork, worktree, remote, in-process teammate); fork mode copies messages but shares FileStateCache; worktree mode provides git-level isolationSteps 1, 3
        5Multiple consumer surfaces (interactive CLI, headless SDK, remote bridge) require a shared core engine with pluggable I/OQueryEngine returns AsyncGenerator consumed by REPL (main.tsx), SDK (entrypoints/sdk/), and bridge (bridgeMain.ts) with surface-specific renderingSteps 1-4
        6The tool set must be dynamically extensible without modifying core loop logicMCP protocol integration (mcp____ naming), plugin system (services/plugins/), skill system (skills/), and buildTool() factory all allow tool addition without touching query.tsSteps 1, 2
        7The 12-mechanism harness architecture is sufficient for production deploymentShipped as Anthropic's primary coding CLI; 108 additional feature-gated modules indicate continued capability expansion within the same architectureSteps 1-6

        §12 Tech Debt & Code Quality #

        Known Debts #

        IssueSeverityEvidence
        query.ts at 785KBCriticalSingle file containing the entire agent loop + context management + streaming + tool dispatch interleaved. Refactoring risk is enormous — any change touches the critical path.
        108 dead-code-eliminated modulesHighFeature-gated modules reference types and interfaces that must be stubbed for the decompiled source to type-check. The scripts/stub-modules.mjs auto-generates stubs, but any stub-reality mismatch is silent.
        print.ts at 212KB, bridgeMain.ts at 115KBHighMultiple 100KB+ files suggesting insufficient module decomposition in the rendering and bridge layers.
        bootstrap/state.ts at 56KBMediumBootstrap state conflates session ID generation, persistence config, environment detection, and feature flag resolution into a single module.
        Bun compile-time intrinsics (feature(), MACRO)MediumNot reproducible with standard bundlers. The esbuild-based build in this repo transforms feature()false, losing the ability to test any gated behavior.
        No test suite in decompiled sourceMediumZero test files shipped in the npm package. All testing presumably lives in Anthropic's internal monorepo.
        React/Ink for terminal UILow-MediumFull React component tree with hooks, context, and a design system for a CLI. The abstraction overhead is justified by the complexity of the permission dialogs, progress rendering, and multi-pane layout, but it means debugging the UI requires React mental model.

        Build System #

        • Original: Bun bundler with compile-time feature() intrinsic → single 12MB cli.js
        • Decompiled repo: 4-phase esbuild pipeline (copy → transform → entry → bundle) in scripts/build.mjs
        • prepare-src.mjs: source preparation
        • transform.mjs: feature() → false + MACRO.* replacement
        • stub-modules.mjs: auto-generates stubs for 108 missing modules
        • Gets ~95% functional but requires manual iteration on edge cases
        • Dependencies: esbuild ^0.27.4, TypeScript ^6.0.2 (devDependencies only)

        Dependency Health #

        DependencyRoleRisk
        React + InkTerminal UI renderingInk is niche (terminal React renderer); upstream maintenance varies
        ZodSchema validation for all tool inputsActively maintained, widely adopted
        GrowthBookFeature flags + A/B experimentsExternal service dependency for runtime behavior
        ripgrep (external)GrepTool executionSystem dependency, not bundled
        Node.js ≥ 18RuntimeWell-maintained

        §13 Community Health #

        MetricValue
        Upstream stars11,910 (sanbuphy/learn-coding-agent)
        Upstream forks19,728
        Fork stars (this repo)0
        LicenseNone specified (Anthropic copyright disclaimer)
        MaintainerZhaiFeiyue (fork); sanbuphy (upstream)
        NatureDecompiled proprietary source for research — not a community project
        Upstream activityRepository created 2026-03-31, single snapshot (v2.1.88), no subsequent updates
        Contribution modelNot applicable — this is an extraction, not an open-source project accepting contributions
        Bus factorN/A (Anthropic's internal team maintains the actual product)
        GovernanceAnthropic (commercial entity) owns the product; this repo is an unauthorized decompilation

        This repository exists purely for research and educational analysis. It is not an active open-source project. The upstream sanbuphy/learn-coding-agent repo is a community research effort studying coding agent implementations, with significant community interest (11.9K stars, 19.7K forks) indicating broad demand for understanding agent internals.

        §14 Comparison with Alternatives #

        DimensionClaude CodeCursor (IDE)GitHub Copilot CLIAiderContinue.dev
        ArchitectureTypeScript, React/Ink TUIElectron + VS Code forkGo CLIPython CLITypeScript VS Code ext
        ModelClaude (Anthropic)Multi-model (Claude, GPT, etc.)GPT (OpenAI)Multi-model (GPT, Claude, etc.)Multi-model
        Tool execution40+ built-in tools + MCP + pluginsIDE-integrated toolsShell commandsShell + file editIDE-integrated
        Permission system4-stage pipeline with rules + hooksIDE-native permissionsBasic confirmationYes/no per editIDE-native
        Multi-agent5 spawn modes, team protocols, coordinatorSub-agents (Task tool)NoneNoneNone
        Context management3 compression strategies (auto/snip/collapse)IDE context engineConversation-basedRepo map + chat historyIDE context
        MCP supportFull (stdio/sse/http/ws/sdk, OAuth)MCP clientNoneNoneMCP client
        Session persistenceJSONL with resume/fork/continueIDE historyNoneGit-basedIDE history
        Sub-agent isolationFork (fresh messages), worktree (git isolation)Git worktreeNoneNoneNone
        ExtensibilityMCP + plugins + skills + hooks + slash commandsExtensions + rulesLimitedLimitedExtensions
        Background tasksDreamTask, daemon, cronBackground agentsNoneNoneNone
        UI frameworkReact/Ink (terminal)Electron/VS CodeMinimal CLIMinimal CLIVS Code webview
        Codebase size~512K LOC TypeScriptProprietaryProprietary~30K LOC Python~50K LOC TypeScript
        LicenseProprietary (Anthropic)ProprietaryProprietaryApache-2.0Apache-2.0
        Open sourceNo (decompiled)NoNoYesYes

        Winner by scenario:

        • Maximum tool coverage + multi-agent orchestration for complex coding tasks → Claude Code (40+ tools, 5 spawn modes, team protocols)
        • IDE-integrated experience with multi-model flexibility → Cursor
        • Open-source, self-hosted, model-agnostic coding assistant → Aider (Apache-2.0, any model)
        • Understanding how production agent architecture works (research) → Claude Code (this decompiled source is uniquely detailed)

        §15 実現 Cross-Reference #

        Key file citations #

        ComponentFileRole
        Agent loopsrc/query.ts (~785KB)The while-true agent loop: API calls, tool dispatch, context management
        Query lifecyclesrc/QueryEngine.tssubmitMessage()AsyncGenerator
        Tool interfacesrc/Tool.tsTool definition + buildTool() factory
        Tool registrysrc/tools.tsTool list, presets, filtering
        Task typessrc/Task.ts7 task types, ID generation, status tracking
        REPL bootstrapsrc/main.tsx (4,683 LOC)Interactive terminal entry, React/Ink rendering
        CLI entrysrc/entrypoints/cli.tsxVersion, help, daemon launch
        SDK entrysrc/entrypoints/sdk/Agent SDK types and sessions
        Bridge lifecyclesrc/bridge/bridgeMain.ts (115KB)Claude Desktop / remote session management
        Bridge REPLsrc/bridge/replBridge.ts (100KB)REPL bridge controller
        Output renderingsrc/cli/print.ts (212KB)Terminal output formatting
        Slash commandssrc/commands.ts + src/commands/80+ commands with feature-gated imports
        API clientsrc/services/api/claude.tsStreaming Claude API calls with retry
        Context compressionsrc/services/compact/autoCompact + snipCompact + contextCollapse
        Tool executorsrc/services/tools/StreamingToolExecutor + toolOrchestration
        MCP connectionsrc/services/mcp/MCPConnectionManager (discovery, auth, lifecycle)
        Permission enginesrc/utils/permissions/Rule evaluation: allow/deny/ask per tool + pattern
        Memory systemsrc/memdir/memdir.tsCLAUDE.md discovery and loading
        Skill loadersrc/skills/loadSkillsDir.tsSkill directory discovery
        App statesrc/state/AppStateStore.tsState definition + React provider
        Bootstrapsrc/bootstrap/state.ts (56KB)Session ID, persistence, environment
        File historysrc/utils/fileHistory.tsUndo/redo snapshots
        File cachesrc/utils/fileStateCache.tsLRU file content cache
        Session storagesrc/utils/sessionStorage.tsJSONL session persistence
        Build scriptscripts/build.mjs4-phase esbuild bundler
        Stub generatorscripts/stub-modules.mjsAuto-stub for 108 missing feature-gated modules

        関鍵実装細節 #

        1. AsyncGenerator as the universal streaming primitive: QueryEngine.submitMessage() returns AsyncGenerator, which the REPL, SDK, and bridge all consume through for await...of. This means the entire chain — from API SSE stream through tool execution to UI rendering — uses a single pull-based streaming mechanism. Backpressure is natural: if the consumer is slow (e.g., rendering a large tool output), the generator suspends, which suspends the tool executor, which suspends the API stream reader. No explicit flow control logic is needed anywhere in the pipeline. This is the architectural decision that makes the 40+ tools, 12 harness mechanisms, and 3 consumer surfaces composable without a message bus or event emitter.
          1. buildTool() factory providing safe defaults: Rather than requiring each of 40+ tools to implement the full ~30-method Tool interface, buildTool(def) fills in sensible defaults: maxResultSizeChars = 30000, isReadOnly = () => false, isConcurrencySafe = () => false, renderToolUseMessage using generic formatting, etc. Tool authors only specify the 4-5 methods that differ from defaults. This reduces the per-tool boilerplate from ~200 lines to ~30 lines, which is what makes 40+ tools maintainable. The factory also enforces naming conventions and registers tools with the search index.
            1. Branded types for system prompt safety: The system prompt is not a plain string — it is a branded type SystemPrompt created only via asSystemPrompt(). This prevents a common bug where arbitrary strings are passed as system prompts (which would bypass prompt injection protections). The TypeScript compiler enforces this at build time — no runtime cost.
              1. Fire-and-forget with ordering for transcript persistence: recordTranscript() is non-blocking (fire-and-forget) to avoid adding disk I/O latency to every turn. But it maintains an internal write queue that guarantees ordering — message N is always written before message N+1. User messages use a blocking variant (crash recovery: if the process dies mid-turn, the user's prompt is already persisted and can be replayed on resume). Assistant messages use fire-and-forget (performance: don't block the next turn on disk write).
              2. §16 Verdict & Recommendations #

                When to study this codebase ("yes" regime) #

                • You are building a production agent system and need a reference architecture for the full stack: tool dispatch, permissions, context compression, multi-agent orchestration, session persistence, streaming, and terminal UI
                • You want to understand how Anthropic structures their tool-use loop at production quality — the 12 progressive harness mechanisms represent hard-won engineering lessons
                • You are researching multi-agent coordination patterns — the 5 spawn modes (default, fork, worktree, remote, teammate) and team protocol design are among the most complete implementations available
                • You want to understand the permission system design space — the 4-stage pipeline with hooks, rules, and interactive prompts is significantly more sophisticated than any open-source alternative
                • You are building MCP integrations and need a reference client implementation covering all transports and auth methods

                When NOT to study this codebase ("no" regime) #

                • You want to run or modify Claude Code yourself — the decompiled source is ~95% buildable at best, the 108 missing modules create stubbing headaches, and the Bun compile-time intrinsics cannot be replicated
                • You need an open-source coding assistant to deploy — use Aider (Apache-2.0, 30K LOC Python, model-agnostic) or Continue.dev instead
                • You want a minimal agent loop example — this codebase wraps the core loop in 785KB of production complexity; start with a 50-line tool-use loop first
                • You are looking for test examples — zero tests ship in the decompiled source
                • You are concerned about legal risk — the source is Anthropic's intellectual property, explicitly marked "commercial use strictly prohibited"

                Architectural lessons worth extracting #

                1. AsyncGenerator as universal agent streaming primitive: The pull-based AsyncGenerator design provides backpressure, composability across surfaces, and natural cancellation without explicit flow control. This pattern is directly applicable to any agent framework.
                2. buildTool() factory with safe defaults: Reducing per-tool boilerplate from ~200 lines to ~30 lines makes a 40+ tool inventory maintainable. The factory pattern is more practical than abstract base classes for tool systems where tools vary wildly in complexity.
                3. 4-stage permission pipeline with hooks: Separating validation, user-defined hooks, declarative rules, and tool-specific checks into distinct stages allows independent evolution of each concern. The hook stage (user-defined shell scripts) is particularly clever — it makes the permission system extensible without modifying Claude Code's source.
                4. Context compression as a first-class concern: Treating context management as a service with multiple strategies (auto-compact, snip, collapse) rather than a single heuristic reflects the reality that different conversation patterns need different compression approaches.
                5. Sub-agent context isolation via fork + shared file cache: Fresh messages[] prevents context contamination between parent and child agents, while shared FileStateCache ensures file reads are consistent without redundant I/O. The worktree mode adds git-level isolation for truly independent parallel work.
                6. 108 gated modules: the iceberg beneath the surface #

                  The decompiled source reveals that the shipped product (v2.1.88) exposes roughly 40% of the implemented capability surface. The 108 feature-gated modules include:

                  • KAIROS: Fully autonomous agent with heartbeats, push notifications, PR subscriptions, background PR suggestions — a paradigm shift from interactive CLI to proactive agent
                  • DAEMON: Background daemon process for persistent agent presence
                  • Coordinator/Swarm: Lead agent with autonomous task-claiming teammates
                  • Voice mode: Push-to-talk voice input (implemented, gated)
                  • Browser automation: WebBrowserTool for web interaction
                  • Context collapse: Advanced context restructuring beyond simple compaction

                  These modules indicate the direction of agent evolution: from reactive (user-initiated) to proactive (agent-initiated), from single-session to persistent-daemon, from text to multimodal (voice, browser). The architecture's 12-mechanism harness was designed to accommodate this evolution — the same QueryEngine core supports all these modes through the same AsyncGenerator streaming interface.