Claude Code Boot Sequence: How a CLI Becomes a Runtime Host

3 min readAI Agents

How cli.tsx, main.tsx, and replLauncher.tsx assemble a governed agent session from startup signal handlers to first REPL render.

ai-agentsclaude-codesource-coderuntimetypescript

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:

Files covered in this post5 files
src/
├── cli.tsx
├── main.tsx
├── setup.ts
├── replLauncher.tsx
├── entrypoints/
│   └── init.ts

cli.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. 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. 2

    Memoized init (entrypoints/init.ts)

    src/entrypoints/init.ts

    init() 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. 3

    Session composition (main.tsx action handler)

    src/main.tsx

    The 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. 4

    Interactive setup (setup.ts)

    src/setup.ts

    Only runs on interactive REPL paths. Sets working directory, git worktree, hooks snapshot, file listeners, session memory.

  5. 5

    REPL render (replLauncher.tsx)

    src/replLauncher.tsx

    Dynamically 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.

TypeScript / JavaScriptmain.tsx

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:

  1. Claude Code is optimized around startup responsiveness.
  2. Trust and permissions are designed into the runtime from the first stage of boot.
  3. 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.

Module
Status
FileModuleRoleStatusPriorityPost
src/main.tsxEntrypoints / BootPrimary startup entrypointDeep ReadCritical
src/replLauncher.tsxEntrypoints / BootREPL session launcher and handoffIn ProgressCritical
src/interactiveHelpers.tsxEntrypoints / BootInteractive session helpers and setup screensIndexedHigh
src/setup.tsEntrypoints / BootFirst-run setup and config initializationIndexedMedium
src/commands.tsProduct CommandsCommand assembly and routing layerDeep ReadCritical
src/query.tsCore Agent RuntimeRecoverable streaming turn loopDeep ReadCritical
src/Tool.tsCore Agent RuntimeTool protocol, execution context, permission contextIn ProgressCritical
src/tools.tsCore Agent RuntimeTool registry and orchestration assemblyIn ProgressCritical
src/query/config.tsCore Agent RuntimeImmutable query config snapshotIndexedHigh
src/query/tokenBudget.tsCore Agent RuntimeToken budget tracking and enforcementIndexedHigh
src/services/compact/autoCompact.tsCore Agent RuntimeAutocompact trigger and circuit breakerIndexedHigh
src/services/mcp/config.tsCapability AssemblyMCP server config resolution and deduplicationIndexedHigh
src/utils/processUserInput/processUserInput.tsInput Pipeline3-path input router (text / !bash / /slash)Deep ReadCritical
src/utils/speculation/Input PipelineSpeculative turn pre-executionDeep ReadHigh
src/utils/promptSuggestion/Input PipelineBackground prompt suggestion generationDeep ReadMedium
src/services/SessionMemory/SessionMemory.tsMemory SystemIn-context compaction triggerDeep ReadHigh
src/services/extractMemories/extractMemories.tsMemory SystemTaxonomy-driven extraction and stashing coalescerDeep ReadHigh
src/services/autoDream/autoDream.tsMemory SystemBackground consolidation with 5-gate access controlDeep ReadHigh
src/services/lsp/LSPServerInstance.tsLSP IntegrationLanguage server lifecycle manager (5 states)Deep ReadHigh
src/services/lsp/LSPDiagnosticRegistry.tsLSP IntegrationBounded diagnostic cache with LRU dedupDeep ReadHigh
src/coordinator/coordinatorMode.tsMulti-AgentCoordinator system prompt and synthesize-first strategyDeep ReadCritical
src/utils/swarm/backends/Multi-AgentSwarm backends: TmuxBackend / ITermBackend / InProcessBackendDeep ReadHigh
src/services/analytics/Analytics + ObservabilityDual-sink event emission with PII enforcementDeep ReadHigh
src/utils/telemetry/betaSessionTracing.tsAnalytics + ObservabilityBeta tracer with system-reminder extractionDeep ReadMedium
src/bridge/Bridge + RemoteCCR transport V1/V2 and echo deduplicationDeep ReadMedium

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?