CodeDocs Vault

3. Entry Points and Execution Flow

3.1 The fast-path dispatcher (entrypoints/cli.tsx)

entrypoints/cli.tsx exists to keep the claude --version path at zero extra module loads. The file begins with a minimal inline block (no heavy imports) and only pulls in bigger modules after detecting what mode we're in. Every if that dispatches to a special path uses feature(...) from bun:bundle so the build can delete the whole branch in external builds.

Sequence (entrypoints/cli.tsx):

Line Condition What happens
36-41 --version / -v / -V Prints MACRO.VERSION (inlined at build) and returns. No profiler, no config.
47-48 Any other path profileCheckpoint('cli_entry') via a lazy-imported startup profiler.
53-70 --dump-system-prompt (ant-only, feature('DUMP_SYSTEM_PROMPT')) Dynamically imports enableConfigs, getMainLoopModel, getSystemPrompt and emits the rendered prompt. Used by prompt-sensitivity evals.
72-85 --claude-in-chrome-mcp / --chrome-native-host Route to the Chrome integration MCP server / native host.
86-93 --computer-use-mcp (feature('CHICAGO_MCP')) Launches the computer-use MCP server.
100-106 --daemon-worker=<kind> (feature('DAEMON')) Spawned per-worker by the supervisor. Deliberately lean — no enableConfigs(), no analytics.
112-162 claude remote-control / remote / sync / bridge (feature('BRIDGE_MODE')) Full bridge path: auth check → GrowthBook gate → policy-limits check → bridgeMain(). The comment at 132-135 notes auth must come before the gate check, otherwise GrowthBook has no user context and returns a stale default.
165-179 claude daemon Invokes daemonMain with remaining args.
185-209 claude ps|logs|attach|kill or --bg / --background (feature('BG_SESSIONS')) Session-registry management under ~/.claude/sessions/.
212-222 `claude new list
226-233 claude environment-runner (feature('BYOC_ENVIRONMENT_RUNNER')) Headless BYOC (Bring Your Own Cloud) runner.
238-245 claude self-hosted-runner (feature('SELF_HOSTED_RUNNER')) Register + poll against SelfHostedRunnerWorkerService.
247-274 --worktree --tmux Re-exec into tmux inside a fresh worktree before the main CLI loads.
277-279 --update / --upgrade Rewrites process.argv to route to the update subcommand.
283-285 --bare Sets CLAUDE_CODE_SIMPLE=1 early so feature gates fire at module-eval time.
287-298 Default startCapturingEarlyInput() (buffer keystrokes before the TUI mounts), then await import('../main.js') and call cliMain().

This file is small (~302 lines) and could be studied as a template for any CLI whose startup latency matters.

3.2 entrypoints/init.ts — one-time, trust-aware init

Memoized (init.ts:57):

  1. enableConfigs() — validate + enable config system.
  2. Apply safe env vars before the trust dialog; apply NODE_EXTRA_CA_CERTS before first TLS handshake (Bun caches the cert store at boot).
  3. setupGracefulShutdown().
  4. Lazy-init 1P event logging (OpenTelemetry sdk-logs deferred to avoid startup bloat).
  5. Populate OAuth account info, detect JetBrains IDE, detect GitHub repo, kick off managed-settings and policy-limits loading.
  6. Configure global mTLS and HTTP agents; preconnect to Anthropic API (TCP+TLS warm-up overlapping other work).
  7. If CLAUDE_CODE_REMOTE=true, start the upstream proxy relay (gated, fail-open).
  8. Register session-team cleanup, scratchpad init, telemetry init after trust.

3.3 main.tsx — the grand orchestrator

A single 4,683-line file. Broadly:

Its sheer size is a pragmatic sink for per-entrypoint wiring; it is not where the interesting algorithmic logic lives.

3.4 setup.ts — invoked inside the TUI path

setup.ts:56-477 runs before the first render:

3.5 Bridge / Remote / Daemon

Bridge (bridge/bridgeMain.ts): claude remote-control turns this CLI into a long-lived environment server that:

Types live in bridge/types.ts: WorkData, WorkSecret (encrypted bundle containing session_ingress_token, api_base_url, git info, auth tokens, env vars, MCP config), SessionDoneStatus, SpawnMode, BridgeConfig.

Remote sessions (remote/RemoteSessionManager.ts): the other end. When a user opens a session on claude.ai/code/session_…, the browser streams messages over a WebSocket to RemoteSessionManager, which forwards them into a local QueryEngine, and streams SDKMessage / permission requests back out. Callbacks: onMessage, onPermissionRequest, onPermissionCancelled, onConnected, onDisconnected, onReconnecting, onError.

Daemon (daemon/ — referenced from cli.tsx:165-179): long-running supervisor that can host and route multiple worker kinds (--daemon-worker=<kind>).

3.6 Turn lifecycle at the call site

Inside QueryEngine.submitMessage (QueryEngine.ts:209+), each turn:

  1. Clear turn-scoped discoveredSkillNames; set cwd; start timer.
  2. Wrap canUseTool to track denials for SDK reporting (QueryEngine.ts:244-271).
  3. Compute initial main-loop model + thinking config.
  4. fetchSystemPromptParts({ tools, mainLoopModel, additionalWorkingDirectories, mcpClients, customSystemPrompt }) → default prompt, userContext, systemContext.
  5. Prepend a memory-mechanics block when a custom prompt + CLAUDE_COWORK_MEMORY_PATH_OVERRIDE are both set (QueryEngine.ts:310-318).
  6. Register structured-output enforcement hook if a jsonSchema + SyntheticOutputTool is present.
  7. Build a ProcessUserInputContext (messages, setMessages, options, AppState getters, abortController, readFileState, trigger sets).
  8. If an orphaned permission exists (a user approved/denied before the renderer mounted), replay it first (QueryEngine.ts:398-408).
  9. processUserInput({ input, mode:'prompt', context, messages, uuid, isMeta, querySource:'sdk' })messagesFromUserInput, shouldQuery, allowedTools, optional model override, resultText for local commands.
  10. Persist the new user messages into mutableMessages and transcript before entering the query loop, so --resume can recover even if the process is killed mid-API-call.
  11. Yield buildSystemInitMessage (tools, mcpClients, model, permissionMode, commands, agents, skills, plugins, fastMode) — this is the first SDK event seen by callers.
  12. If !shouldQuery (a purely local slash command), emit local-command stdout/stderr and a result message, persist, return.
  13. Otherwise enter the for await (const message of query(...)) loop — everything beyond is described in doc 04.

3.7 Plan / Fast / Auto modes