Skip to main content

Startup & Bootstrap

How Claude Code goes from bun dist/cli.js to a fully interactive REPL — before any conversation begins.

The Big Picture

Think of startup like opening a restaurant before the first customer arrives. You need to turn on the lights (config), check the fridge (auth), set up the menu (tools), and unlock the door (REPL). Only then can you serve anyone.

🚀
Process starts
Quick checks — is this just --version?
⚙️
Load config, auth, plugins, tools, MCP servers
🖥️
Launch the interactive REPL

Fast-Path Routing

The entry point (src/entrypoints/cli.tsx, ~302 lines) is designed to be fast when possible. Before loading the full app, it checks for special flags that need quick responses:

FlagWhat HappensImports Needed
--version / -vPrint version string, exitZero — fastest path
--dump-system-promptRender full system prompt, exitConfig + prompt system
--claude-in-chrome-mcpLaunch Chrome MCP serverMCP subsystem
--computer-use-mcpLaunch computer-use MCP serverMCP subsystem
--daemon-workerStart background daemonDaemon subsystem
remote-control / bridgeLaunch IDE bridgeBridge subsystem
(none of the above)Load full CLIEverything

The key trick is dynamic imports — each path only loads what it needs. The --version path literally imports nothing beyond the entry file itself.

Why dynamic imports matter

If Claude Code loaded all 489 npm packages for every --version check, startup would be painfully slow. Dynamic imports mean you only pay for what you use.

Full Initialization

When no fast-path flag matches, the entry point dynamically imports main.tsx (~4,683 lines), which runs through 9 initialization phases before the REPL is ready:

cli.tsxdynamic import main.tsxinitialization begins
📂
Load Configuration
src/utils/settings/
Read settings from multiple sources and merge them.
~/.claude/settings.json.claude.json (project)Environment variables
🔑
Authenticate
src/utils/auth.ts
Validate API key or stored OAuth tokens. Prompt if missing.
ANTHROPIC_API_KEYOAuth tokensInteractive login
🎛️
Feature Flags
src/services/analytics/
Initialize GrowthBook with 90+ runtime feature flags.
90+ flagsA/B testingGradual rollouts
🔧
Register Tools & Commands
src/tools/ + src/commands/
Load all built-in tools and slash commands into the registry.
45+ tools100+ commandsZod schemas
🔌
Connect MCP Servers
src/services/mcp/
Start configured MCP servers and enumerate their tools.
stdio / SSE / HTTPTool enumerationResource listing
🧩
Load Plugins & Skills
src/plugins/ + src/skills/
Discover plugins and skill files, register their commands and tools.
~/.claude/plugins/~/.claude/skills/Bundled skills
🔒
Build Permission Context
src/utils/permissions/
Merge permission rules from CLI flags, user settings, project config, and enterprise policies.
Permission modeAllow/deny rulesPolicy limits
📊
Initialize Telemetry
src/services/analytics/
Set up OpenTelemetry exporters for traces, metrics, and logs.
TracesMetricsLogs
🖥️
Launch REPL
src/replLauncher.tsx
Render the React/Ink terminal app. Ready for your first message.
React 19 + Ink 6AppState storeKeyboard input active

What init() Actually Wires Up

The early startup work is split between cli.tsx, entrypoints/init.ts, and main.tsx. init.ts does more than simple config loading:

Startup ConcernSource-Backed Behavior
Config safetyEnables config parsing, applies safe env vars first, and installs extra CA certs before any TLS traffic
Managed startup stateCreates loading promises for remote managed settings and policy limits so later systems can wait on them
Network stackConfigures global mTLS, proxy-aware HTTP agents, and a fire-and-forget Anthropic API preconnect
CCR-specific plumbingStarts the upstream proxy relay in remote mode so subprocesses inherit the right proxy env
Cleanup hooksRegisters graceful shutdown plus cleanup for LSP and team state
Scratchpad filesystemCreates the scratchpad directory if that feature is enabled

Split Between init.ts and main.tsx

FileRole
src/entrypoints/init.tsSafe env bootstrapping, network configuration, cleanup registration, remote-settings readiness, scratchpad setup
src/main.tsxTrust flow, settings/UI initialization, plugin/MCP/tool/command loading, LSP manager init, background settings sync upload, and REPL launch

That split matters because some systems need to start before the UI exists. For example, remote managed settings, policy limits, proxy config, and preconnect all happen before the interactive shell is fully rendered.

Session Restoration

When you use --resume or --continue, the bootstrap path changes:

  1. Previous session messages loaded from ~/.claude/sessions/<id>
  2. Conversation state restored to AppState
  3. REPL opens with your history intact — pick up where you left off

When Things Go Wrong

Startup is designed to be resilient — a single failure shouldn't prevent the whole app from launching:

FailureWhat Happens
Missing configFalls back to sensible defaults
Auth failurePrompts for API key or launches OAuth flow in browser
MCP server won't startWarning logged, that server skipped, everything else works
Plugin failureWarning logged, that plugin skipped

What Happens Next?

Once launchRepl() fires, the terminal UI is live and waiting for your first input. From here:

Key Source Files

FileLinesPurpose
src/entrypoints/cli.tsx~302Bootstrap entry, fast-path routing
src/entrypoints/init.ts~300Early init: config safety, network stack, cleanup, remote settings readiness
src/main.tsx~4,683Full CLI initialization
src/replLauncher.tsxREPL rendering setup
src/bootstrap/state.tsSession state initialization