Chapter 1 System Design

Entry Points & CLI Bootstrap

How Claude Code boots from zero to interactive REPL in milliseconds

src/entrypoints/cli.tsxLines 170
1
import { feature } from 'bun:bundle';
2
 
3
// Bugfix for corepack auto-pinning, which adds yarnpkg to peoples' package.jsons
4
// eslint-disable-next-line custom-rules/no-top-level-side-effects
5
process.env.COREPACK_ENABLE_AUTO_PIN = '0';
6
 
7
// Set max heap size for child processes in CCR environments (containers have 16GB)
8
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level, custom-rules/safe-env-boolean-check
9
if (process.env.CLAUDE_CODE_REMOTE === 'true') {
10
  // eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
11
  const existing = process.env.NODE_OPTIONS || '';
12
  // eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
13
  process.env.NODE_OPTIONS = existing ? `${existing} --max-old-space-size=8192` : '--max-old-space-size=8192';
14
}
15
 
16
// Harness-science L0 ablation baseline. Inlined here (not init.ts) because
17
// BashTool/AgentTool/PowerShellTool capture DISABLE_BACKGROUND_TASKS into
18
// module-level consts at import time — init() runs too late. feature() gate
19
// DCEs this entire block from external builds.
20
// eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
21
if (feature('ABLATION_BASELINE') && process.env.CLAUDE_CODE_ABLATION_BASELINE) {
22
  for (const k of ['CLAUDE_CODE_SIMPLE', 'CLAUDE_CODE_DISABLE_THINKING', 'DISABLE_INTERLEAVED_THINKING', 'DISABLE_COMPACT', 'DISABLE_AUTO_COMPACT', 'CLAUDE_CODE_DISABLE_AUTO_MEMORY', 'CLAUDE_CODE_DISABLE_BACKGROUND_TASKS']) {
23
    // eslint-disable-next-line custom-rules/no-top-level-side-effects, custom-rules/no-process-env-top-level
24
    process.env[k] ??= '1';
25
  }
26
}
27
 
28
/**
29
 * Bootstrap entrypoint - checks for special flags before loading the full CLI.
30
 * All imports are dynamic to minimize module evaluation for fast paths.
31
 * Fast-path for --version has zero imports beyond this file.
32
 */
33
async function main(): Promise<void> {
34
  const args = process.argv.slice(2);
35
 
36
  // Fast-path for --version/-v: zero module loading needed
37
  if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
38
    // MACRO.VERSION is inlined at build time
39
    // biome-ignore lint/suspicious/noConsole:: intentional console output
40
    console.log(`${MACRO.VERSION} (Claude Code)`);
41
    return;
42
  }
43
 
44
  // For all other paths, load the startup profiler
45
  const {
46
    profileCheckpoint
47
  } = await import('../utils/startupProfiler.js');
48
  profileCheckpoint('cli_entry');
49
 
50
  // Fast-path for --dump-system-prompt: output the rendered system prompt and exit.
51
  // Used by prompt sensitivity evals to extract the system prompt at a specific commit.
52
  // Ant-only: eliminated from external builds via feature flag.
53
  if (feature('DUMP_SYSTEM_PROMPT') && args[0] === '--dump-system-prompt') {
54
    profileCheckpoint('cli_dump_system_prompt_path');
55
    const {
56
      enableConfigs
57
    } = await import('../utils/config.js');
58
    enableConfigs();
59
    const {
60
      getMainLoopModel
61
    } = await import('../utils/model/model.js');
62
    const modelIdx = args.indexOf('--model');
63
    const model = modelIdx !== -1 && args[modelIdx + 1] || getMainLoopModel();
64
    const {
65
      getSystemPrompt
66
    } = await import('../constants/prompts.js');
67
    const prompt = await getSystemPrompt([], model);
68
    // biome-ignore lint/suspicious/noConsole:: intentional console output
69
    console.log(prompt.join('\n'));
70
    return;
Annotations (click the dots)

Every time you run claude, execution begins here at src/entrypoints/cli.tsx. The first goal is brutal simplicity: handle common flags without loading any modules.

🔑Key Insight

The `--version` flag path loads zero modules. `MACRO.VERSION` is a build-time constant inlined by the bundler, making version checks essentially instant.

For everything else, the startup profiler is loaded first. This records exact timestamps at each profileCheckpoint() call, allowing the team to track startup regression down to the millisecond.

ℹ️Info

Notice every import after line 44 is a dynamic `await import()` — this is intentional. Dynamic imports are only evaluated when that code path runs, so unused flags cost zero startup time.

The top-level heap size setting (lines 9–14) is a deliberate side effect. In container environments (CLAUDE_CODE_REMOTE=true), Node.js needs --max-old-space-size=8192 set *before* any memory-intensive modules load.

KEY TAKEAWAYS
  • Fast-path for --version loads ZERO modules — pure inlined constant
  • Heap size for containers is set before any module loads (top-level side effect)
  • The startup profiler tracks import time at each checkpoint
  • All heavy imports are dynamic (async import()) to avoid blocking the fast path
  • MACRO.VERSION is inlined at build time by the bundler — no runtime file reads
AI Assistant

Ask anything about Entry Points & CLI Bootstrap

Powered by Groq · Enter to send, Shift+Enter for newline

1/12Next