入口与 Bootstrap
理解 Claude Code 的 bootstrap 过程是理解一切的基础。启动序列跨越四个文件,各自承担不同职责:快速路由、核心初始化、CLI 框架以及环境配置。
Bootstrap 序列概览
Section titled “Bootstrap 序列概览”sequenceDiagram
participant OS as Operating System
participant CLI as cli.tsx
participant INIT as init.ts
participant MAIN as main.tsx
participant SETUP as setup.ts
participant REPL as REPL.tsx
OS->>CLI: bun run cli.tsx
CLI->>CLI: Fast-path checks (--version, --bridge, etc.)
CLI->>INIT: init()
INIT->>INIT: enableConfigs()
INIT->>INIT: applySafeConfigEnvironmentVariables()
INIT->>INIT: setupGracefulShutdown()
INIT->>INIT: configureGlobalAgents() (proxy/mTLS)
CLI->>MAIN: Dynamic import main.tsx
MAIN->>MAIN: Side effects: profiler, MDM, keychain
MAIN->>MAIN: Commander.js argument parsing
MAIN->>MAIN: API key validation
MAIN->>MAIN: MCP server initialization
MAIN->>SETUP: setup(cwd, permissionMode, ...)
SETUP->>SETUP: setCwd(), captureHooksConfigSnapshot()
SETUP->>SETUP: Worktree creation (if --worktree)
SETUP->>SETUP: Background jobs, telemetry
MAIN->>REPL: launchRepl() or print mode
第一阶段:快速路由(src/entrypoints/cli.tsx)
Section titled “第一阶段:快速路由(src/entrypoints/cli.tsx)”bootstrap entrypoint 采用零导入快速路径设计。某些操作应在不加载完整应用程序的情况下完成:
async function main(): Promise<void> { const args = process.argv.slice(2)
// Fast-path for --version/-v: zero module loading needed if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) { console.log(`${MACRO.VERSION} (Claude Code)`) return }
// For all other paths, load the startup profiler const { profileCheckpoint } = await import('../utils/startupProfiler.js') profileCheckpoint('cli_entry')快速路径模式支持多种专用模式:
| Flag/子命令 | 行为 | 导入量 |
|---|---|---|
--version | 打印版本并退出 | 零 |
--dump-system-prompt | 输出系统 prompt(仅内部) | 最少——config + 模型 |
--claude-in-chrome-mcp | Chrome 扩展的 MCP server | Chrome MCP 模块 |
--daemon-worker | 守护进程 worker(仅内部) | 仅 worker 注册表 |
remote-control | 远程会话的 bridge 模式 | Bridge 模块 |
关键设计:每个 import 都是 await import()(动态导入),按需加载。
cli.tsx 中的环境配置
Section titled “cli.tsx 中的环境配置”在主 CLI 加载之前,cli.tsx 处理两项关键的环境调整:
// src/entrypoints/cli.tsx — Top-level side effects// Prevent corepack auto-pinning (modifies package.json)process.env.COREPACK_ENABLE_AUTO_PIN = '0'
// Set max heap size for CCR (Claude Code Remote) containersif (process.env.CLAUDE_CODE_REMOTE === 'true') { const existing = process.env.NODE_OPTIONS || '' process.env.NODE_OPTIONS = existing ? `${existing} --max-old-space-size=8192` : '--max-old-space-size=8192'}第二阶段:核心初始化(src/entrypoints/init.ts)
Section titled “第二阶段:核心初始化(src/entrypoints/init.ts)”init() 函数是**记忆化(memoized)**的——无论被调用多少次,都只执行一次:
export const init = memoize(async (): Promise<void> => { const initStartTime = Date.now() logForDiagnosticsNoPII('info', 'init_started') profileCheckpoint('init_function_start')
// 1. Enable configuration system enableConfigs()
// 2. Apply SAFE environment variables (before trust dialog) applySafeConfigEnvironmentVariables()
// 3. Apply extra CA certificates from settings applyExtraCACertsFromConfig()
// 4. Register graceful shutdown handlers setupGracefulShutdown()
// 5. Configure mTLS configureGlobalMTLS()
// 6. Configure proxy (HTTP agents) configureGlobalAgents()
// 7. Pre-connect to Anthropic API preconnectAnthropicApi() // ...})Init 序列分解
Section titled “Init 序列分解”init 序列精心排列操作顺序,在遵守依赖关系的同时最大化并行性:
graph TB
A["enableConfigs()"] --> B["applySafeConfigEnvironmentVariables()"]
B --> C["applyExtraCACertsFromConfig()"]
C --> D["setupGracefulShutdown()"]
D --> E["configureGlobalMTLS()"]
E --> F["configureGlobalAgents()"]
B --> G["populateOAuthAccountInfoIfNeeded() (async, void)"]
B --> H["initJetBrainsDetection() (async, void)"]
B --> I["detectCurrentRepository() (async, void)"]
B --> J["initializeRemoteManagedSettingsLoadingPromise()"]
F --> K["preconnectAnthropicApi()"]
关键顺序:
enableConfigs()必须最先运行——所有内容都从 config 中读取applySafeConfigEnvironmentVariables()应用信任前安全的环境变量(无危险密钥)applyExtraCACertsFromConfig()必须在任何 TLS 连接之前执行(Bun 在启动时缓存证书存储)configureGlobalMTLS()在configureGlobalAgents()之前——mTLS 设置会注入到代理 agent 中
信任后初始化
Section titled “信任后初始化”用户接受信任对话框后,还会应用额外的敏感 config:
// Called later in the startup sequence, after trust is establishedexport async function initializeTelemetryAfterTrust(): Promise<void> { // Now safe to apply ALL environment variables (including sensitive ones) applyConfigEnvironmentVariables() // Initialize telemetry, analytics, etc.}第三阶段:CLI 框架(src/main.tsx)
Section titled “第三阶段:CLI 框架(src/main.tsx)”主模块是代码库中最大的文件。它注册 commander.js 命令、解析参数并启动相应模式。
导入时的副作用
Section titled “导入时的副作用”main.tsx 在任何 import 完成求值之前触发三个并行操作:
// src/main.tsx — Lines 1-20import { profileCheckpoint } from './utils/startupProfiler.js'profileCheckpoint('main_tsx_entry') // Mark entry timestamp
import { startMdmRawRead } from './utils/settings/mdm/rawRead.js'startMdmRawRead() // Fire MDM subprocesses (plutil/reg query) in parallel
import { startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js'startKeychainPrefetch() // Fire macOS keychain reads in parallel这种并行预取模式通过将 keychain 读取与剩余约 135ms 的模块导入重叠,在 macOS 上节省约 65ms。
Commander.js 注册
Section titled “Commander.js 注册”CLI 使用 @commander-js/extra-typings 进行类型安全的参数解析:
// src/main.tsx (conceptual)import { Command as CommanderCommand, Option } from '@commander-js/extra-typings'
const program = new CommanderCommand() .name('claude') .option('-p, --print', 'Print mode (non-interactive)') .option('--model <model>', 'Model to use') .option('--permission-mode <mode>', 'Permission mode') .option('--dangerously-skip-permissions', 'Skip all permission checks') .option('--resume <session>', 'Resume a previous session') .option('--worktree', 'Create a git worktree for this session') // ... 40+ more optionsmain.tsx 中的启动流程
Section titled “main.tsx 中的启动流程”参数解析后,启动流程继续:
- Config 验证 — 检查无效 settings,必要时显示对话框
- 认证流程 — 验证 API key 或 OAuth token
- GrowthBook 初始化 — 来自 Anthropic 实验平台的 feature flag
- MCP server 启动 — 并行连接已配置的 MCP server
- tool 组装 — 根据权限和 config 构建 tool 池
- agent 定义 — 从
.claude/agents/加载 agent 定义 - 模式分发 — 启动 REPL(交互式)或 print 模式(无头)
第四阶段:环境配置(src/setup.ts)
Section titled “第四阶段:环境配置(src/setup.ts)”setup() 函数处理初始化后的环境配置:
export async function setup( cwd: string, permissionMode: PermissionMode, allowDangerouslySkipPermissions: boolean, worktreeEnabled: boolean, worktreeName: string | undefined, tmuxEnabled: boolean, customSessionId?: string | null, worktreePRNumber?: number, messagingSocketPath?: string,): Promise<void> {Setup 序列
Section titled “Setup 序列”graph TB
A["setCwd(cwd)"] --> B["captureHooksConfigSnapshot()"]
B --> C["initializeFileChangedWatcher(cwd)"]
C --> D{worktreeEnabled?}
D -->|Yes| E["createWorktreeForSession()"]
E --> F["process.chdir(worktreePath)"]
D -->|No| G["Background jobs"]
F --> G
G --> H["initSessionMemory()"]
G --> I["lockCurrentVersion()"]
G --> J["getCommands() prefetch"]
G --> K["loadPluginHooks() prefetch"]
K --> L["checkForReleaseNotes()"]
L --> M["Permission mode validation"]
Claude Code 为进程终止信号注册处理程序:
// src/utils/gracefulShutdown.ts (called from init.ts)export function setupGracefulShutdown(): void { // Register cleanup for SIGINT, SIGTERM, SIGHUP // Ensures: // - Terminal state is restored (cursor, alternate screen) // - Session data is flushed to disk // - Child processes are killed // - Telemetry is flushed}gracefulShutdownSync() 函数为无法使用异步清理的场景提供同步退出路径:
// Used in signal handlers where async is not availablegracefulShutdownSync()// Restores terminal, flushes sync-safe writesWorktree 创建
Section titled “Worktree 创建”传入 --worktree 时,setup.ts 会创建一个隔离的 git worktree:
// src/setup.ts — Worktree handlingif (worktreeEnabled) { const hasHook = hasWorktreeCreateHook() const inGit = await getIsGit() if (!hasHook && !inGit) { process.stderr.write(chalk.red( `Error: Can only use --worktree in a git repository` )) process.exit(1) }
const slug = worktreePRNumber ? `pr-${worktreePRNumber}` : (worktreeName ?? getPlanSlug())
worktreeSession = await createWorktreeForSession(getSessionId(), slug, ...)
process.chdir(worktreeSession.worktreePath) setCwd(worktreeSession.worktreePath) setOriginalCwd(getCwd()) setProjectRoot(getCwd())}Permission mode 验证
Section titled “Permission mode 验证”对于 --dangerously-skip-permissions,setup.ts 强制执行安全检查:
// src/setup.ts — Security validationif (permissionMode === 'bypassPermissions' || allowDangerouslySkipPermissions) { // Block root/sudo on Unix if (process.platform !== 'win32' && process.getuid?.() === 0 && process.env.IS_SANDBOX !== '1') { console.error('--dangerously-skip-permissions cannot be used with root/sudo') process.exit(1) }
// External users: require Docker/sandbox + no internet if (process.env.USER_TYPE === 'ant') { const [isDocker, hasInternet] = await Promise.all([ envDynamic.getIsDocker(), env.hasInternetAccess(), ]) if (!isSandboxed || hasInternet) { console.error('--dangerously-skip-permissions requires sandbox with no internet') process.exit(1) } }}进程生命周期
Section titled “进程生命周期”stateDiagram-v2
[*] --> CliEntry: bun run cli.tsx
CliEntry --> FastPath: --version, --bridge, etc.
FastPath --> [*]: Exit immediately
CliEntry --> Init: Normal path
Init --> MainTsx: Dynamic import
MainTsx --> ArgParse: Commander.js
ArgParse --> AuthCheck: Validate API key
AuthCheck --> McpInit: Start MCP servers
McpInit --> Setup: setup()
Setup --> ReplMode: Interactive
Setup --> PrintMode: --print flag
ReplMode --> Running: REPL loop
PrintMode --> Running: Single query
Running --> Shutdown: SIGINT/SIGTERM/exit
Shutdown --> Cleanup: Flush sessions, restore terminal
Cleanup --> [*]
bootstrap 序列包含若干关键性能优化:
- 并行预取 — MDM 读取、keychain 读取和 API 预连接与模块导入并行进行
- 动态导入 — 快速路径加载零模块;主路径使用
await import() - 记忆化 init —
init()被memoize()防止重复初始化 - 后台任务 — 非关键工作(attribution hook、session 文件访问)通过
void import().then()运行 - 启动分析器 — 整个启动路径中的
profileCheckpoint()调用允许测量每个阶段
// Profiling checkpoints in the startup pathprofileCheckpoint('cli_entry')profileCheckpoint('init_function_start')profileCheckpoint('init_configs_enabled')profileCheckpoint('init_safe_env_vars_applied')profileCheckpoint('init_after_graceful_shutdown')profileCheckpoint('init_after_1p_event_logging')profileCheckpoint('main_tsx_entry')profileCheckpoint('setup_before_prefetch')profileCheckpoint('setup_after_prefetch')