Entrypoint System

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.tsrunHeadless() 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.tsstartMCPServer() 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:

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:

  1. Set working directorysetCwd(cwd) — MUST happen before hooks
  2. Capture hooks configuration snapshot
  3. Initialize FileChanged hook watcher
  4. Worktree creation (if --worktree flag) — resolves to main repo root, optionally creates tmux session
  5. Background jobs:
  6. initSessionMemory() — synchronous
  7. initContextCollapse() (feature-gated)
  8. lockCurrentVersion()
  9. Prefetch system (unless --bare):
  10. Load available commands
  11. Load plugin hooks
  12. Register attribution hooks
  13. Register session file access hooks
  14. Start team memory watcher
  15. Check for release notes
  16. Get recent activity
  17. Permission mode validation — check for dangerous root + --dangerously-skip-permissions
  18. Emit telemetrylogEvent('tengu_started', {})

Phase 3: Init (src/entrypoints/init.ts, lines 57-341)

Memoized — called once per session. Handles external integrations:

  1. Config enablement — load .claude/settings.json
  2. Safe environment variables + CA certificates (before first TLS connection)
  3. Graceful shutdown setup
  4. Parallel async initialization:
  5. 1P event logging
  6. GrowthBook refresh
  7. OAuth account info
  8. JetBrains IDE detection
  9. GitHub repo detection
  10. Remote settings promises (if eligible):
  11. initializeRemoteManagedSettingsLoadingPromise()
  12. initializePolicyLimitsLoadingPromise()
  13. Network configuration:
  14. mTLS configuration
  15. HTTP proxy agents
  16. API TCP+TLS warmup (preconnectAnthropicApi())
  17. 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:

  1. Determine session mode: isNonInteractive = hasPrintFlag || hasInitOnlyFlag || hasSdkUrl || !process.stdout.isTTY
  2. Determine client type: cli, sdk-typescript, sdk-python, claude-vscode, claude-desktop, remote, etc.
  3. Main command action handler (line 1006) — the massive handler that:
  4. Loads trust dialog (interactive only)
  5. Resolves initial model
  6. Applies environment variables
  7. Loads agents, tools, MCP servers
  8. Initializes permission context
  9. Loads plugins
  10. Validates policy limits
  11. Creates initial AppState
  12. Diverges: calls either launchRepl() (interactive) or runHeadless() (print/SDK)
  13. 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:

What depends on it

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

Relations

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