Skip to main content

Startup Flow: From CLI to REPL

Startup Chain Overview

Step 1: entrypoints/cli.tsx -- Thin Bootstrap Layer

cli.tsx is the true process entry point. Its design principle is to load as few modules as possible, providing zero-dependency responses for fast paths.

Fast Path (Zero Module Loading)

if (args[0] === '--version' || args[0] === '-v') {
console.log(`${MACRO.VERSION} (Claude Code)`);
return;
}

Special Mode Dispatch

Before loading the full CLI, cli.tsx checks process.argv and dispatches to various special modes:

ArgumentModeDescription
--claude-in-chrome-mcpChrome MCPChrome extension MCP server
--computer-use-mcpComputer Use MCPComputer control MCP (feature-gated)
--daemon-workerDaemon WorkerBackground worker process (KAIROS)
--bridgeBridge ModeIDE bridge (VS Code/JetBrains)
--templateTemplate ModeProject template generation
defaultFull CLIimport('../main.js').main()

Key file: src/entrypoints/cli.tsx

Step 2: main.tsx -- Full CLI Entry

main.tsx is the core entry (~4600+ lines), handling Commander CLI configuration, initialization, tool registration, and REPL launch.

Parallel Prefetch (Performance Critical)

The first lines of main.tsx leverage ES module side effects to prefetch expensive operations in parallel:

profileCheckpoint('main_tsx_entry');
startMdmRawRead(); // Start MDM subprocess (plutil/reg query)
startKeychainPrefetch(); // Start macOS Keychain reads (OAuth + API Key, two concurrent reads)

This lets expensive I/O run in parallel with the subsequent ~135ms of module loading.

Commander CLI Configuration

const program = new CommanderCommand('claude')
.option('-p, --print <prompt>', 'non-interactive mode')
.option('--model <model>', 'specify model')
.option('--permission-mode <mode>', 'permission mode')
.option('--allowedTools <tools...>', 'allowed tool list')
.option('--max-turns <n>', 'max loop turns')
// ... more options

Initialization Flow

Key file: src/main.tsx

Step 3: entrypoints/init.ts -- One-Time Initialization

init() handles process-level one-time setup, called from main():

ItemDescription
enableConfigs()Enable configuration system
applySafeConfigEnvironmentVariables()Safely apply environment variables
Repo detectionFind .git, set working directory
OAuth initSet up auth context
Policy loadingloadPolicyLimits() fetches from server
Remote settingsloadRemoteManagedSettings()
TelemetryRegister OpenTelemetry sink
Shutdown hooksprocess.on('exit') cleanup

Key file: src/entrypoints/init.ts

Step 4: bootstrap/state.ts -- Global State

bootstrap/state.ts provides "judicious global state" (original code comment): session ID, telemetry sinks, channel info, settings cache, model overrides, and atomic operations for token budgets and cost tracking.

Key file: src/bootstrap/state.ts

Step 5: replLauncher.tsx -- REPL Launch

replLauncher.tsx bridges from main.tsx to the actual UI: dynamically loads App and REPL components, configures Ink render options, and mounts the React component tree to the terminal.

Key file: src/replLauncher.tsx

Step 6: screens/REPL.tsx -- Interactive Interface

REPL.tsx is the orchestration center (~3000+ lines) managing session/query state, message lists, tool permission queues, modal dialogs, component tree assembly, keybindings, and the bridge to the query() loop.

See 08-terminal-ui.md for details.

Key file: src/screens/REPL.tsx

Two Run Modes

Interactive Mode (REPL)

cli.tsx -> main.tsx -> init() -> launchRepl() -> REPL.tsx -> query()

Headless Mode (--print / SDK)

cli.tsx -> main.tsx -> init() -> runHeadlessStreaming() -> QueryEngine -> query()

Both modes share the same query() core; they differ only in how user input becomes messages and how yielded events are rendered or serialized.

Next

Go to 03-core-loop.md to dive into the most important part of Claude Code -- the agent loop.

Hands-on Experiment

This chapter has a corresponding Python experiment:

Lab 02 — Startup Flow

Covers: CLI dispatch, parallel prefetch, lazy imports

cd experiments && python -m exp_02_startup_flow.main --mock