Entrypoint System
- Entity ID:
ent-20260410-13d0d22b73f8 - Type:
concept - Scope:
shared - Status:
active - Aliases: startup sequence, initialization, boot sequence
Description
The initialization and startup orchestration for Claude Code. Four entrypoints serve different operating modes: interactive CLI (REPL), headless/print mode, MCP server mode, and SDK embedding. The startup sequence is carefully ordered to minimize time-to-first-prompt while initializing network, auth, telemetry, and configuration in parallel where possible.
The four operating modes
1. Interactive CLI (REPL)
Condition: No -p/--print flag, TTY available
What it does: Full terminal UI with interactive prompting — the default Claude Code experience. Uses Ink (React for CLIs) to render the TUI. Enters a REPL loop: user input → API call → tool execution → display result.
2. Headless/print mode (-p/--print)
Condition: -p/--print flag set, or !process.stdout.isTTY
Handler: src/cli/print.ts — runHeadless()
What it does: Non-interactive, single-shot execution. Processes one prompt (from argument or stdin), executes tools with permissions (no dialog — uses --permission-prompt-tool), outputs result, exits.
Output formats: text (human-readable), json (single result object), stream-json (line-delimited streaming events)
3. MCP server mode (mcp serve)
Handler: src/entrypoints/mcp.ts — startMCPServer()
What it does: Runs Claude Code as an MCP server over stdio. Exposes all tools via the MCP protocol (ListTools + CallTool). No interactive TUI, no full init sequence — just setup() then serve. Minimal AppState with empty permission context. Other MCP clients can call Claude Code's tools directly.
4. SDK mode (--sdk-url)
Condition: --sdk-url with --input-format=stream-json + --output-format=stream-json
What it does: Treated as non-interactive. Same initialization as print mode but connects via WebSocket to a remote server. Stream-JSON I/O for structured protocol communication. Full init() and tool loading, but no TUI.
Startup sequence
Phase 1: CLI bootstrap (src/entrypoints/cli.tsx)
The very first code to run. Handles fast-path commands that exit immediately without heavy imports:
--version/-v— print version, exit--dump-system-prompt— output rendered prompt (internal only)--claude-in-chrome-mcp— Chrome integration MCP server--daemon-worker=<kind>— internal daemon workerremote-control/rc— bridge modedaemon— long-running supervisorps|logs|attach|kill/--bg— session management
Parallel prefetching starts immediately:
- startMdmRawRead() — MDM (mobile device management) settings subprocess on macOS/Windows
- startKeychainPrefetch() — parallel keychain reads for OAuth + legacy API keys
Phase 2: Setup (src/setup.ts, lines 56-477)
Per-session initialization, called from main.tsx:
- Set working directory —
setCwd(cwd)— MUST happen before hooks - Capture hooks configuration snapshot
- Initialize FileChanged hook watcher
- Worktree creation (if
--worktreeflag) — resolves to main repo root, optionally creates tmux session - Background jobs:
initSessionMemory()— synchronousinitContextCollapse()(feature-gated)lockCurrentVersion()- Prefetch system (unless
--bare): - Load available commands
- Load plugin hooks
- Register attribution hooks
- Register session file access hooks
- Start team memory watcher
- Check for release notes
- Get recent activity
- Permission mode validation — check for dangerous root +
--dangerously-skip-permissions - Emit telemetry —
logEvent('tengu_started', {})
Phase 3: Init (src/entrypoints/init.ts, lines 57-341)
Memoized — called once per session. Handles external integrations:
- Config enablement — load
.claude/settings.json - Safe environment variables + CA certificates (before first TLS connection)
- Graceful shutdown setup
- Parallel async initialization:
- 1P event logging
- GrowthBook refresh
- OAuth account info
- JetBrains IDE detection
- GitHub repo detection
- Remote settings promises (if eligible):
initializeRemoteManagedSettingsLoadingPromise()initializePolicyLimitsLoadingPromise()- Network configuration:
- mTLS configuration
- HTTP proxy agents
- API TCP+TLS warmup (
preconnectAnthropicApi()) - Platform-specific: Windows shell setup, LSP manager cleanup, team cleanup
Phase 4: CLI parsing (src/main.tsx, lines 884-4400)
Commander.js parses process.argv and routes to the appropriate handler:
- Determine session mode:
isNonInteractive = hasPrintFlag || hasInitOnlyFlag || hasSdkUrl || !process.stdout.isTTY - Determine client type:
cli,sdk-typescript,sdk-python,claude-vscode,claude-desktop,remote, etc. - Main command action handler (line 1006) — the massive handler that:
- Loads trust dialog (interactive only)
- Resolves initial model
- Applies environment variables
- Loads agents, tools, MCP servers
- Initializes permission context
- Loads plugins
- Validates policy limits
- Creates initial AppState
- Diverges: calls either
launchRepl()(interactive) orrunHeadless()(print/SDK) - Other commands:
mcp serve,auth login|status|logout,plugin install|uninstall|...,agents,doctor, etc.
Bootstrap state
src/bootstrap/state.ts manages global state set during initialization:
| State | Purpose |
|---|---|
originalCwd |
Session start directory (never changes) |
projectRoot |
Stable project root (set at startup, not updated by EnterWorktree) |
cwd |
Current working directory (can change) |
isInteractive |
Boolean mode flag |
clientType |
'cli', 'sdk-typescript', 'remote', etc. |
sessionId |
UUID for session tracking |
telemetry |
Meter, counters, logger provider |
modelUsage |
Per-model token/cost tracking |
kairosActive |
Assistant mode active |
registeredHooks |
SDK callbacks + plugin hooks |
Feature gates
Many entrypoint branches are feature-gated with feature('FEATURE_NAME'), enabling dead code elimination at build time:
DAEMON— daemon worker supportBRIDGE_MODE— remote control bridgeDIRECT_CONNECT— direct server connections (cc:// URLs)SSH_REMOTE— SSH session modeKAIROS— assistant modeCONTEXT_COLLAPSE— context optimizationBG_SESSIONS— background session managementCHICAGO_MCP— computer use MCP serverLODESTONE— deep link protocol
What depends on it
- Every other system — the entrypoint initializes all services, tools, and state before anything else runs
- Service layer — services are initialized in
init.tsin a specific order (network before API calls, config before feature flags) - Agent lifecycle — agents inherit the initialization state from the parent session
- Permission system — permission mode is validated during setup, before any tools execute
Design trade-offs
| Decision | Trade-off |
|---|---|
| Parallel prefetching (keychain, MDM, commands) | Faster startup, but startup failures are harder to debug (race conditions) |
Memoized init() |
Called-once guarantee simplifies reasoning, but means re-init after config change requires session restart |
| Fast-path commands in cli.tsx | Sub-millisecond response for --version, but complex branching early in the code |
| MCP server skips full init | Faster MCP startup, but MCP mode lacks some features available in CLI mode |
| Commander.js for CLI parsing | Battle-tested library, but the main action handler is 3000+ lines — a complexity magnet |
--bare flag skips all background jobs |
Fast for scripted usage, but no hooks, no memory, no attribution |
Key claims
- Four entrypoints serve distinct operating modes from a single codebase
- Parallel prefetching (keychain, MDM, network warmup) minimizes time-to-first-prompt
- Feature gates enable dead code elimination — unused modes don't ship in the binary
- MCP server mode provides tool access without the full CLI initialization overhead
Relations
rel-entrypoint-services: entrypoint-system --[initializes]--> service-layerrel-entrypoint-agent: entrypoint-system --[launches]--> agent-lifecyclerel-entrypoint-tools: entrypoint-system --[registers]--> tool-system
Sources
Source code at src/entrypoints/cli.tsx, src/main.tsx, src/setup.ts, src/entrypoints/init.ts, src/entrypoints/mcp.ts, src/bootstrap/state.ts