Claude Code Boot Sequence: How a CLI Becomes a Runtime Host
How cli.tsx, main.tsx, and replLauncher.tsx assemble a governed agent session from startup signal handlers to first REPL render.
Claude Code is not a CLI that wraps an API call. It is a runtime host that happens to present as a CLI. The distinction shows up immediately in how it boots.
The boot file hierarchy
The startup chain spans five files:
src/
├── cli.tsx
├── main.tsx
├── setup.ts
├── replLauncher.tsx
├── entrypoints/
│ └── init.tscli.tsx is the real entry point, not main.tsx. It implements waterfall fast-paths: --version returns before a single module is loaded. Each subcommand (daemon, bridge, ps, logs) loads only the modules it needs.
main.tsx at 4,683 lines handles everything else: auth strategies, permission init, MCP config, plugin loading, AppState creation, and Commander CLI definitions.
The annotated startup timeline
Side-effect hoisting is the most non-obvious optimization in the boot sequence. Two reads — MDM (managed device policy) and keychain (API key) — fire between import statements, running in parallel with the ~135ms of module evaluation time:
T+0ms cli.tsx entry point
T+2ms startMdmRawRead() and startKeychainPrefetch() fire (between imports)
T+137ms module evaluation complete — MDM and keychain reads already done
T+150ms API preconnection HEAD request fires (puts TCP+TLS into keep-alive pool)
T+250ms REPL renders, buffered stdin input consumed
By the time preAction executes, the prefetches have already completed. The API preconnection saves 100–200ms on the first real model call by placing a connection into Bun's keep-alive pool before it is needed.
Stdin buffering at T+0ms means keystrokes typed during startup are not lost — they are consumed when the REPL mounts.
Boot pipeline — five stages
From process entry to first REPL render.
- 1
Waterfall fast-paths (cli.tsx)
src/cli.tsx--version returns with zero module loads. Subcommands load only what they need. MDM and keychain prefetches fire between import statements.
- 2
Memoized init (entrypoints/init.ts)
src/entrypoints/init.tsinit() runs exactly once via lodash memoize. Distinguishes safe environment variables (applied before trust dialog) from complete environment variables (applied after). API preconnection HEAD request fires here.
- 3
Session composition (main.tsx action handler)
src/main.tsxThe large .action() block assembles the concrete session: permission mode, tool context, MCP config, custom agents, worktree mode, teammate identity, system prompt overrides, model choice.
- 4
Interactive setup (setup.ts)
src/setup.tsOnly runs on interactive REPL paths. Sets working directory, git worktree, hooks snapshot, file listeners, session memory.
- 5
REPL render (replLauncher.tsx)
src/replLauncher.tsxDynamically imports <App> and <REPL> at the last moment to defer heavy UI module loading. Renders via Ink. Buffered stdin input is consumed on mount.
Trust is a boot problem, not just a tool problem
Trust checks precede most runtime assembly. Specifically:
- git-backed system context is only prefetched when the directory is already trusted or the session is non-interactive
- assistant mode is refused in untrusted directories
- some setup work is deferred until after setup screens and trust checks complete
The consequence: a user in an untrusted directory gets a constrained session, not a full one with tools enabled. Trust governs which session gets assembled, not just which tools execute within it.
Special launch modes normalize into the common runtime
Launch modes like cc:// deep links, claude assistant, and claude ssh <host> are rewritten in argv early in cli.tsx. Most land inside the full interactive runtime with its approval dialogs, streaming UX, and session resume.
This is a deliberate product choice over isolated per-mode utilities. One runtime surface is maintained and tested; edge cases do not drift into separate code paths.
The action handler is the composition root
The main .action(...) block in main.tsx is where a generic session becomes a specific one. Everything assembled here — permission mode, MCP config, agent definitions, worktree mode, remote and Chrome integrations — is passed to replLauncher as props. The REPL does not independently discover its capabilities; they are handed to it at mount.
Same concept in three languages
Boot Pipeline Skeleton
The JavaScript version stays closest to the source repo. Python and Go are teaching rewrites of the same startup idea: normalize launch mode early, initialize global state once, then compose a concrete session.
Closest to the real codebase structure.
async function main() {
installSignalHandlers();
normalizeSpecialLaunchArgs(process.argv);
const isNonInteractive =
hasPrintFlag(process.argv) ||
hasInitOnlyFlag(process.argv) ||
hasSdkUrl(process.argv) ||
!process.stdout.isTTY;
setIsInteractive(!isNonInteractive);
initializeEntrypoint(isNonInteractive);
eagerLoadSettings();
await init();
runMigrations();
const program = buildCommanderProgram();
await program.parseAsync(process.argv);
}
program.action(async (prompt, options) => {
const permissionMode = resolvePermissionMode(options);
const toolContext = await initializeToolPermissionContext(options);
const commands = await getCommands(getCwd());
const agents = await getAgentDefinitionsWithOverrides(getCwd());
await maybeShowSetupScreens();
await launchRepl({ prompt, options, permissionMode, toolContext, commands, agents });
});What this file proves
After this first pass, main.tsx proves three architecture points:
- Claude Code is optimized around startup responsiveness.
- Trust and permissions are designed into the runtime from the first stage of boot.
- The CLI is a session host that composes capabilities before entering the main agent loop.
Source file inventory: Entrypoints and Boot
The files below form the boot layer. These are the ones worth reading before anything else.
Entrypoints / Boot layer
Files that own the startup pipeline, session assembly, and mode normalization. Read these before diving into the query loop or tool system.
| File | Module | Role | Status | Priority | Post |
|---|---|---|---|---|---|
src/main.tsx | Entrypoints / Boot | Primary startup entrypoint | Deep Read | Critical | This post |
src/replLauncher.tsx | Entrypoints / Boot | REPL session launcher and handoff | In Progress | Critical | — |
src/interactiveHelpers.tsx | Entrypoints / Boot | Interactive session helpers and setup screens | Indexed | High | — |
src/setup.ts | Entrypoints / Boot | First-run setup and config initialization | Indexed | Medium | — |
src/commands.ts | Product Commands | Command assembly and routing layer | Deep Read | Critical | Command System post |
src/query.ts | Core Agent Runtime | Recoverable streaming turn loop | Deep Read | Critical | Query Loop post |
src/Tool.ts | Core Agent Runtime | Tool protocol, execution context, permission context | In Progress | Critical | Tool Runtime post |
src/tools.ts | Core Agent Runtime | Tool registry and orchestration assembly | In Progress | Critical | Tool Runtime post |
src/query/config.ts | Core Agent Runtime | Immutable query config snapshot | Indexed | High | — |
src/query/tokenBudget.ts | Core Agent Runtime | Token budget tracking and enforcement | Indexed | High | — |
src/services/compact/autoCompact.ts | Core Agent Runtime | Autocompact trigger and circuit breaker | Indexed | High | — |
src/services/mcp/config.ts | Capability Assembly | MCP server config resolution and deduplication | Indexed | High | MCP Assembly post |
src/utils/processUserInput/processUserInput.ts | Input Pipeline | 3-path input router (text / !bash / /slash) | Deep Read | Critical | Input Pipeline post |
src/utils/speculation/ | Input Pipeline | Speculative turn pre-execution | Deep Read | High | Input Pipeline post |
src/utils/promptSuggestion/ | Input Pipeline | Background prompt suggestion generation | Deep Read | Medium | Input Pipeline post |
src/services/SessionMemory/SessionMemory.ts | Memory System | In-context compaction trigger | Deep Read | High | Memory System post |
src/services/extractMemories/extractMemories.ts | Memory System | Taxonomy-driven extraction and stashing coalescer | Deep Read | High | Memory System post |
src/services/autoDream/autoDream.ts | Memory System | Background consolidation with 5-gate access control | Deep Read | High | Memory System post |
src/services/lsp/LSPServerInstance.ts | LSP Integration | Language server lifecycle manager (5 states) | Deep Read | High | LSP Integration post |
src/services/lsp/LSPDiagnosticRegistry.ts | LSP Integration | Bounded diagnostic cache with LRU dedup | Deep Read | High | LSP Integration post |
src/coordinator/coordinatorMode.ts | Multi-Agent | Coordinator system prompt and synthesize-first strategy | Deep Read | Critical | Swarm/Multi-Agent post |
src/utils/swarm/backends/ | Multi-Agent | Swarm backends: TmuxBackend / ITermBackend / InProcessBackend | Deep Read | High | Swarm/Multi-Agent post |
src/services/analytics/ | Analytics + Observability | Dual-sink event emission with PII enforcement | Deep Read | High | Analytics post |
src/utils/telemetry/betaSessionTracing.ts | Analytics + Observability | Beta tracer with system-reminder extraction | Deep Read | Medium | Analytics post |
src/bridge/ | Bridge + Remote | CCR transport V1/V2 and echo deduplication | Deep Read | Medium | — |
Showing 25 of 25 files
The question is no longer:
- how does Claude Code call the model?
It becomes:
- how does Claude Code build a safe, resumable, interactive execution runtime before the model even takes the first turn?