Bootstrap 是 Claude Code 的启动临界路径, 从进程入口到 REPL 渲染第一帧. 整体设计遵循
"fast-path 短路 + lazy import + parallel prefetch" 三大原则, 确保 --version 零
依赖输出, 交互启动 <300ms 首帧渲染.
process.argv
|
v
+--------------------------------------------+
| cli.tsx (entrypoint) |
| Top-level side-effects: |
| COREPACK_ENABLE_AUTO_PIN=0 |
| CCR max-heap 8192 |
| Ablation baseline env vars |
| |
| Fast-path dispatch: |
| --version/-v/-V --> console.log, exit |
| --daemon-worker --> workerRegistry |
| bridge/remote --> bridgeMain |
| daemon --> daemonMain |
| ps/logs/attach/kill/--bg --> bg.js |
| new/list/reply --> templateJobs |
| environment-runner --> envRunnerMain |
| self-hosted-runner --> shRunnerMain |
| --worktree+--tmux --> execIntoTmux |
| |
| Normal path: |
| startCapturingEarlyInput() |
| dynamic import('../main.js') |
| await cliMain() |
+--------------------------------------------+
|
v
+--------------------------------------------+
| main.tsx (full init) |
| Module-eval side-effects: |
| profileCheckpoint('main_tsx_entry') |
| startMdmRawRead() // MDM prefetch |
| startKeychainPrefetch() // macOS |
| |
| main() function: |
| warning handler + SIGINT |
| deep-link URI dispatch |
| assistant/ssh argv rewriting |
| detect interactive/non-interactive |
| eagerLoadSettings() |
| run() --> Commander program |
+--------------------------------------------+
|
v
+--------------------------------------------+
| Commander preAction hook |
| ensureMdmSettingsLoaded() |
| ensureKeychainPrefetchCompleted() |
| init() (memoized, once) |
| initSinks() |
| runMigrations() |
| loadRemoteManagedSettings() |
| loadPolicyLimits() |
+--------------------------------------------+
|
v
+--------------------------------------------+
| Default command .action() handler |
| setup() + parallel cmd/agent loading |
| MCP config loading |
| showSetupScreens() (trust/auth/onboard) |
| model resolution + tool permission init |
| session resume (--continue/--resume) |
| launchRepl() |
+--------------------------------------------+
File: src/entrypoints/cli.tsx
在 main() 函数执行之前, 三个 side-effect 在模块求值阶段执行:
// 1. 阻止 corepack 修改 package.json
process.env.COREPACK_ENABLE_AUTO_PIN = '0'
// 2. CCR 环境设置 8GB 堆上限
if (process.env.CLAUDE_CODE_REMOTE === 'true') {
process.env.NODE_OPTIONS = existing
? `${existing} --max-old-space-size=8192`
: '--max-old-space-size=8192'
}
// 3. Ablation baseline (ant-only, feature-gated DCE)
if (feature('ABLATION_BASELINE') && process.env.CLAUDE_CODE_ABLATION_BASELINE) {
// 设置 CLAUDE_CODE_SIMPLE, DISABLE_THINKING, DISABLE_COMPACT 等
}所有 import 均为 dynamic import, 保证未匹配路径零模块加载开销:
| 优先级 | 匹配条件 | 目标模块 | 需要 enableConfigs | Profiler Checkpoint |
|---|---|---|---|---|
| 1 | --version, -v, -V |
无 (MACRO.VERSION 编译时内联) | 否 | 无 |
| 2 | --dump-system-prompt |
constants/prompts.js |
是 | cli_dump_system_prompt_path |
| 3 | --claude-in-chrome-mcp |
claudeInChrome/mcpServer.js |
否 | cli_claude_in_chrome_mcp_path |
| 4 | --chrome-native-host |
claudeInChrome/chromeNativeHost.js |
否 | cli_chrome_native_host_path |
| 5 | --computer-use-mcp |
computerUse/mcpServer.js |
否 | cli_computer_use_mcp_path |
| 6 | --daemon-worker |
daemon/workerRegistry.js |
否 | 无 |
| 7 | remote-control/rc/bridge/sync |
bridge/bridgeMain.js |
是 | cli_bridge_path |
| 8 | daemon |
daemon/main.js |
是 | cli_daemon_path |
| 9 | ps/logs/attach/kill/--bg |
cli/bg.js |
是 | cli_bg_path |
| 10 | new/list/reply |
cli/handlers/templateJobs.js |
否 | cli_templates_path |
| 11 | environment-runner |
environment-runner/main.js |
否 | cli_environment_runner_path |
| 12 | self-hosted-runner |
self-hosted-runner/main.js |
否 | cli_self_hosted_runner_path |
| 13 | --worktree + --tmux |
utils/worktree.js |
是 | cli_tmux_worktree_fast_path |
// 零 import, 零 module load
if (args.length === 1 && (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
console.log(`${MACRO.VERSION} (Claude Code)`)
return
}MACRO.VERSION 在构建时由 Bun 内联为字符串常量, 该路径不触发任何 import().
if (feature('DAEMON') && args[0] === '--daemon-worker') {
const { runDaemonWorker } = await import('../daemon/workerRegistry.js')
await runDaemonWorker(args[1]) // args[1] = worker kind
return
}daemon-worker 是 supervisor 内部 fork 出的子进程, 不调用 enableConfigs(), 不初始化
analytics sinks -- worker 是精简进程. 如果 worker kind 需要 config/auth (如 assistant
worker), 由 worker 自身的 run() 函数内部调用.
Bridge 路径有完整的认证和策略检查链:
enableConfigs() --> getClaudeAIOAuthTokens() 检查 accessToken
--> getBridgeDisabledReason() (等待 GrowthBook 初始化)
--> checkBridgeMinVersion() 版本兼容性
--> waitForPolicyLimitsToLoad() + isPolicyAllowed('allow_remote_control')
--> bridgeMain(args.slice(1))
// ps/logs/attach/kill 子命令, 或 --bg/--background flag
if (feature('BG_SESSIONS') && (args[0] === 'ps' || ...)) {
enableConfigs()
const bg = await import('../cli/bg.js')
switch (args[0]) {
case 'ps': await bg.psHandler(args.slice(1)); break
case 'logs': await bg.logsHandler(args[1]); break
case 'attach': await bg.attachHandler(args[1]); break
case 'kill': await bg.killHandler(args[1]); break
default: await bg.handleBgFlag(args) // --bg/--background
}
}未匹配任何 fast-path 后, 进入完整 CLI 启动:
// 1. 开始捕获用户早期输入 (防止丢失 REPL 渲染前的按键)
const { startCapturingEarlyInput } = await import('../utils/earlyInput.js')
startCapturingEarlyInput()
// 2. 动态导入 main.tsx (触发大量模块求值)
profileCheckpoint('cli_before_main_import')
const { main: cliMain } = await import('../main.js')
profileCheckpoint('cli_after_main_import')
// 3. 执行主流程
await cliMain()
profileCheckpoint('cli_after_main_complete')File: src/main.tsx
main.tsx 的前 20 行在模块求值期间触发三个关键 side-effect, 利用 import 加载时间做 并行预取:
// Side-effect 1: 标记入口时间点
import { profileCheckpoint } from './utils/startupProfiler.js'
profileCheckpoint('main_tsx_entry')
// Side-effect 2: 启动 MDM 子进程读取 (plutil/reg query)
import { startMdmRawRead } from './utils/settings/mdm/rawRead.js'
startMdmRawRead()
// 与后续 ~135ms 的 import 并行执行
// Side-effect 3: 启动 macOS keychain 并行读取
import { startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js'
startKeychainPrefetch()
// OAuth token (~32ms) + legacy API key (~33ms) 并行 vs 串行 ~65ms在所有 static import 完成后:
profileCheckpoint('main_tsx_imports_loaded')export async function main() {
profileCheckpoint('main_function_start')
// 1. Windows 安全: 阻止从当前目录执行命令
process.env.NoDefaultCurrentDirectoryInExePath = '1'
// 2. 初始化 warning handler + SIGINT 处理
initializeWarningHandler()
process.on('exit', () => resetCursor())
process.on('SIGINT', () => { /* print 模式由 print.ts 处理 */ })
// 3. Deep-link URI 处理 (LODESTONE feature gate)
// --handle-uri <uri> --> handleDeepLinkUri() --> process.exit()
// macOS LaunchServices (__CFBundleIdentifier) --> handleUrlSchemeLaunch()
// 4. Assistant 模式 argv 重写
// `claude assistant [sessionId]` --> 提取参数, 重写 argv
// 5. SSH 远程 argv 重写
// `claude ssh <host> [dir]` --> 提取 host/cwd/flags, 重写 argv
// 6. 检测交互/非交互模式
const isNonInteractive = hasPrintFlag || hasInitOnlyFlag || hasSdkUrl || !process.stdout.isTTY
setIsInteractive(!isNonInteractive)
// 7. 确定 entrypoint 类型
initializeEntrypoint(isNonInteractive) // 设置 CLAUDE_CODE_ENTRYPOINT
// 8. 确定 clientType
// github-action | sdk-typescript | sdk-python | sdk-cli
// claude-vscode | local-agent | claude-desktop | remote | cli
setClientType(clientType)
// 9. 提前加载 --settings 和 --setting-sources
eagerLoadSettings()
// 10. 进入 Commander 命令解析
await run()
}const clientType = (() => {
if (isEnvTruthy(process.env.GITHUB_ACTIONS)) return 'github-action'
if (process.env.CLAUDE_CODE_ENTRYPOINT === 'sdk-ts') return 'sdk-typescript'
if (process.env.CLAUDE_CODE_ENTRYPOINT === 'sdk-py') return 'sdk-python'
if (process.env.CLAUDE_CODE_ENTRYPOINT === 'sdk-cli') return 'sdk-cli'
if (process.env.CLAUDE_CODE_ENTRYPOINT === 'claude-vscode') return 'claude-vscode'
if (process.env.CLAUDE_CODE_ENTRYPOINT === 'local-agent') return 'local-agent'
if (process.env.CLAUDE_CODE_ENTRYPOINT === 'claude-desktop') return 'claude-desktop'
if (hasSessionIngressToken) return 'remote'
return 'cli'
})()所有子命令共享的初始化逻辑:
program.hook('preAction', async (thisCommand) => {
// 1. 等待预取完成 (几乎零成本 - 子进程在 import 期间已完成)
await Promise.all([
ensureMdmSettingsLoaded(),
ensureKeychainPrefetchCompleted()
])
// 2. 核心初始化 (memoized, 仅执行一次)
await init() // --> src/entrypoints/init.ts
// 3. 设置终端标题
process.title = 'claude'
// 4. 初始化日志 sinks
initSinks()
// 5. 处理 --plugin-dir
setInlinePlugins(pluginDir)
// 6. 执行迁移
runMigrations() // CURRENT_MIGRATION_VERSION = 11
// 7. 加载远程托管配置 (非阻塞)
void loadRemoteManagedSettings()
void loadPolicyLimits()
})action_handler_start
|
v
kairos/assistant 检测 & 初始化
|
v
解析所有 CLI options
|
v
工具权限上下文构建 (permissionMode, autoMode, toolPermissionContext)
|
v
assertMinVersion() + MCP config 预加载
|
v
action_before_setup
|
v
setup() + 并行加载 commands + agents [~28ms]
|
v
action_after_setup
|
v
GrowthBook 初始化 + analytics 事件注册
|
v
showSetupScreens() (trust dialog / OAuth / onboarding)
|
v
LSP manager 初始化 (trust 建立后)
|
v
模型解析 + thinking config
|
v
session resume (--continue / --resume / --from-pr)
|
v
launchRepl()
File: src/bootstrap/state.ts
Bootstrap state 是整个应用的全局单例状态, 通过闭包模块模式封装, 仅暴露 getter/setter 函数. 设计原则: bootstrap 模块是 import DAG 的叶节点, 不得引入业务模块依赖.
type State = {
// === 路径状态 ===
originalCwd: string // 启动时的工作目录 (symlink resolved, NFC normalized)
projectRoot: string // 稳定项目根 - 启动时设置一次, 不受 EnterWorktreeTool 影响
cwd: string // 当前工作目录 (可随 worktree/chdir 变化)
// === 成本与性能追踪 ===
totalCostUSD: number
totalAPIDuration: number
totalAPIDurationWithoutRetries: number
totalToolDuration: number
turnHookDurationMs: number
turnToolDurationMs: number
turnClassifierDurationMs: number
turnToolCount: number
turnHookCount: number
turnClassifierCount: number
startTime: number // Date.now() at init
lastInteractionTime: number
// === 代码变更追踪 ===
totalLinesAdded: number
totalLinesRemoved: number
// === 模型状态 ===
modelUsage: { [modelName: string]: ModelUsage }
mainLoopModelOverride: ModelSetting | undefined
initialMainLoopModel: ModelSetting // CLI/SDK 指定的模型
modelStrings: ModelStrings | null
hasUnknownModelCost: boolean
// === 会话状态 ===
isInteractive: boolean
kairosActive: boolean // assistant 模式激活
strictToolResultPairing: boolean // HFI: mismatch 时抛异常而非修复
userMsgOptIn: boolean
clientType: string // 'cli' | 'sdk-typescript' | 'remote' | ...
sessionSource: string | undefined
sessionId: SessionId // UUID, randomUUID() 初始化
parentSessionId: SessionId | undefined
// === SDK 状态 ===
sdkAgentProgressSummariesEnabled: boolean
questionPreviewFormat: 'markdown' | 'html' | undefined
initJsonSchema: Record<string, unknown> | null
registeredHooks: Partial<Record<HookEvent, RegisteredHookMatcher[]>> | null
sdkBetas: string[] | undefined
// === 配置来源 ===
flagSettingsPath: string | undefined
flagSettingsInline: Record<string, unknown> | null
allowedSettingSources: SettingSource[]
sessionIngressToken: string | null | undefined
oauthTokenFromFd: string | null | undefined
apiKeyFromFd: string | null | undefined
// === Telemetry (OTel) ===
meter: Meter | null
sessionCounter: AttributedCounter | null
locCounter: AttributedCounter | null
prCounter: AttributedCounter | null
commitCounter: AttributedCounter | null
costCounter: AttributedCounter | null
tokenCounter: AttributedCounter | null
codeEditToolDecisionCounter: AttributedCounter | null
activeTimeCounter: AttributedCounter | null
statsStore: { observe(name: string, value: number): void } | null
loggerProvider: LoggerProvider | null
eventLogger: ReturnType<typeof logs.getLogger> | null
meterProvider: MeterProvider | null
tracerProvider: BasicTracerProvider | null
// === Agent 状态 ===
agentColorMap: Map<string, AgentColorName>
agentColorIndex: number
mainThreadAgentType: string | undefined
// === API 调试 ===
lastAPIRequest: Omit<BetaMessageStreamParams, 'messages'> | null
lastAPIRequestMessages: BetaMessageStreamParams['messages'] | null
lastClassifierRequests: unknown[] | null
cachedClaudeMdContent: string | null
inMemoryErrorLog: Array<{ error: string; timestamp: string }>
// === Plugin/Skill 状态 ===
inlinePlugins: Array<string> // --plugin-dir
chromeFlagOverride: boolean | undefined
useCoworkPlugins: boolean
invokedSkills: Map<string, { skillName: string; skillPath: string; content: string; ... }>
// === 权限状态 ===
sessionBypassPermissionsMode: boolean
sessionTrustAccepted: boolean
// === Session 持久化 ===
sessionPersistenceDisabled: boolean
sessionProjectDir: string | null
// === Plan Mode ===
hasExitedPlanMode: boolean
needsPlanModeExitAttachment: boolean
planSlugCache: Map<string, string>
// === Teleport ===
teleportedSessionInfo: { isTeleported: boolean; hasLoggedFirstMessage: boolean; sessionId: string | null } | null
// === Cache/Latch 状态 ===
promptCache1hAllowlist: string[] | null
promptCache1hEligible: boolean | null
afkModeHeaderLatched: boolean | null
fastModeHeaderLatched: boolean | null
cacheEditingHeaderLatched: boolean | null
thinkingClearLatched: boolean | null
promptId: string | null
lastMainRequestId: string | undefined
lastApiCompletionTimestamp: number | null
pendingPostCompaction: boolean
// === Channel/Remote ===
allowedChannels: ChannelEntry[]
hasDevChannels: boolean
directConnectServerUrl: string | undefined
isRemoteMode: boolean
additionalDirectoriesForClaudeMd: string[]
// === 其他 ===
systemPromptSectionCache: Map<string, string | null>
lastEmittedDate: string | null
scheduledTasksEnabled: boolean
sessionCronTasks: SessionCronTask[]
sessionCreatedTeams: Set<string>
lspRecommendationShownThisSession: boolean
slowOperations: Array<{ operation: string; durationMs: number; timestamp: number }>
needsAutoModeExitAttachment: boolean
}function getInitialState(): State {
// Resolve symlinks to match shell.ts setCwd behavior
let resolvedCwd = ''
const rawCwd = cwd()
try {
resolvedCwd = realpathSync(rawCwd).normalize('NFC')
} catch {
resolvedCwd = rawCwd.normalize('NFC') // EPERM on CloudStorage mounts
}
return {
originalCwd: resolvedCwd,
projectRoot: resolvedCwd,
cwd: resolvedCwd,
sessionId: randomUUID() as SessionId,
startTime: Date.now(),
lastInteractionTime: Date.now(),
clientType: 'cli',
allowedSettingSources: ['userSettings', 'projectSettings', 'localSettings',
'flagSettings', 'policySettings'],
// ... (所有字段初始为零值/null/空集合)
}
}
const STATE: State = getInitialState()// Session 管理
export function getSessionId(): SessionId
export function regenerateSessionId(options?: { setCurrentAsParent?: boolean }): SessionId
export function switchSession(sessionId: SessionId, projectDir?: string | null): void
export function getParentSessionId(): SessionId | undefined
export function getSessionProjectDir(): string | null
export const onSessionSwitch: (cb: (id: SessionId) => void) => () => void
// 路径管理
export function getOriginalCwd(): string
export function setOriginalCwd(cwd: string): void // NFC normalize
export function getProjectRoot(): string
export function setProjectRoot(cwd: string): void // 仅 --worktree startup
export function getCwdState(): string
export function setCwdState(cwd: string): void
// 成本/性能
export function addToTotalCostState(cost: number, usage: ModelUsage, model: string): void
export function getTotalCostUSD(): number
export function getTotalAPIDuration(): number
export function addToToolDuration(duration: number): void
export function addToTurnHookDuration(duration: number): void
// Token 统计
export function getTotalInputTokens(): number // sumBy(modelUsage, 'inputTokens')
export function getTotalOutputTokens(): number
export function getTotalCacheReadInputTokens(): number
export function getTotalCacheCreationInputTokens(): number
// 交互时间 (懒刷新, 批量化 Date.now())
export function updateLastInteractionTime(immediate?: boolean): void
export function flushInteractionTime(): void // Ink render 前调用switchSession() 是 session ID 和 project dir 的原子切换:
export function switchSession(
sessionId: SessionId,
projectDir: string | null = null
): void {
STATE.planSlugCache.delete(STATE.sessionId) // 清理旧 session 的 plan slug
STATE.sessionId = sessionId
STATE.sessionProjectDir = projectDir
sessionSwitched.emit(sessionId) // Signal 通知订阅者 (如 concurrentSessions)
}regenerateSessionId() 用于 plan mode -> implementation 切换:
export function regenerateSessionId(
options: { setCurrentAsParent?: boolean } = {}
): SessionId {
if (options.setCurrentAsParent) {
STATE.parentSessionId = STATE.sessionId // 保留父子关系
}
STATE.planSlugCache.delete(STATE.sessionId)
STATE.sessionId = randomUUID() as SessionId
STATE.sessionProjectDir = null
return STATE.sessionId
}export type ChannelEntry =
| { kind: 'plugin'; name: string; marketplace: string; dev?: boolean }
| { kind: 'server'; name: string; dev?: boolean }kind: 'plugin' 通过 marketplace 验证 + allowlist; kind: 'server' 的 allowlist 始终
失败 (schema 仅限 plugin).
File: src/utils/startupProfiler.ts
const DETAILED_PROFILING = isEnvTruthy(process.env.CLAUDE_CODE_PROFILE_STARTUP)
const STATSIG_SAMPLE_RATE = 0.005 // 0.5% 外部用户
const STATSIG_LOGGING_SAMPLED =
process.env.USER_TYPE === 'ant' || Math.random() < STATSIG_SAMPLE_RATE
const SHOULD_PROFILE = DETAILED_PROFILING || STATSIG_LOGGING_SAMPLED两种模式:
- 采样日志: 100% ant 用户, 0.5% 外部用户, 各阶段时间上报 Statsig
- 详细 profiling:
CLAUDE_CODE_PROFILE_STARTUP=1, 带内存快照的完整报告
profiler_initialized
cli_entry
main_tsx_entry
main_tsx_imports_loaded // ~135ms from main_tsx_entry
main_function_start
main_warning_handler_initialized
main_client_type_determined
eagerLoadSettings_start
eagerLoadSettings_end
main_before_run
run_function_start
run_commander_initialized
preAction_start
preAction_after_mdm
preAction_after_init
preAction_after_sinks
preAction_after_migrations
preAction_after_remote_settings
preAction_after_settings_sync
action_handler_start
action_before_setup
action_after_setup
action_after_input_prompt
cli_after_main_complete
const PHASE_DEFINITIONS = {
import_time: ['cli_entry', 'main_tsx_imports_loaded'],
init_time: ['init_function_start', 'init_function_end'],
settings_time: ['eagerLoadSettings_start', 'eagerLoadSettings_end'],
total_time: ['cli_entry', 'main_after_run'],
}export function profileCheckpoint(name: string): void {
if (!SHOULD_PROFILE) return
const perf = getPerformance()
perf.mark(name)
if (DETAILED_PROFILING) {
memorySnapshots.push(process.memoryUsage())
}
}File: src/utils/settings/mdm/rawRead.ts
在 main.tsx 模块求值阶段, startMdmRawRead() 启动 MDM 设置子进程:
macOS: plutil -convert json -o - /path/to/plist (可能多个 plist)
Windows: reg query HKLM\...\Anthropic /v Settings + HKCU\...
子进程在 ~135ms 的 import 期间并行运行, 结果通过 getMdmRawReadPromise() 在
ensureMdmSettingsLoaded() (preAction hook) 中消费.
export type RawReadResult = {
plistStdouts: Array<{ stdout: string; label: string }> | null
hklmStdout: string | null
hkcuStdout: string | null
}File: src/utils/secureStorage/keychainPrefetch.ts
macOS 专用. applySafeConfigEnvironmentVariables() 需要读取两个 keychain entry:
"Claude Code-credentials"(OAuth tokens) ~32ms"Claude Code"(legacy API key) ~33ms
串行读取需要 ~65ms. 通过 startKeychainPrefetch() 在 import 阶段并行启动两个
security find-generic-password 子进程:
function spawnSecurity(serviceName: string): Promise<SpawnResult> {
return new Promise(resolve => {
execFile('security',
['find-generic-password', '-a', getUsername(), '-w', '-s', serviceName],
{ encoding: 'utf-8', timeout: KEYCHAIN_PREFETCH_TIMEOUT_MS },
...)
})
}--bare 模式跳过 keychain prefetch (仅使用 ANTHROPIC_API_KEY 或 apiKeyHelper).
MCP 配置加载与 setup() 并行启动:
// 仅读取本地文件, 无执行 -- 安全的预加载
const mcpConfigPromise = strictMcpConfig || isBareMode()
? Promise.resolve({ servers: {} })
: getClaudeCodeMcpConfigs(dynamicMcpConfig)
// claude.ai 代理服务器 fetch (-p 模式, 非 enterprise)
const claudeaiConfigPromise = isNonInteractiveSession && !strictMcpConfig
? fetchClaudeAIMcpConfigsIfEligible()
: Promise.resolve({})MCP 资源的实际预取 (prefetchAllMcpResources) 延迟到 trust dialog 之后.
REPL 首次渲染后启动 (startDeferredPrefetches):
export function startDeferredPrefetches(): void {
// --bare 模式跳过所有 prefetch
if (isBareMode()) return
void initUser() // 用户信息
void getUserContext() // CLAUDE.md + 上下文
prefetchSystemContextIfSafe() // git status (需要 trust)
void getRelevantTips() // 提示信息
void prefetchAwsCredentialsAndBedRockInfoIfSafe() // Bedrock
void prefetchGcpCredentialsIfSafe() // Vertex
void countFilesRoundedRg(getCwd(), ...) // ripgrep 文件计数
void initializeAnalyticsGates()
void prefetchOfficialMcpUrls()
void refreshModelCapabilities()
void settingsChangeDetector.initialize()
void skillChangeDetector.initialize()
}File: src/entrypoints/init.ts
init() 是 memoized 函数, 保证全生命周期仅执行一次:
export const init = memoize(async (): Promise<void> => {
// Phase 1: 配置系统
enableConfigs()
applySafeConfigEnvironmentVariables() // 安全的 env vars (trust 前)
applyExtraCACertsFromConfig() // TLS 证书 (必须在首次 TLS 前)
// Phase 2: 退出清理
setupGracefulShutdown()
// Phase 3: 日志/分析 (异步)
void Promise.all([
import('firstPartyEventLogger.js'),
import('growthbook.js')
]).then(([fp, gb]) => {
fp.initialize1PEventLogging()
gb.onGrowthBookRefresh(() => fp.reinitialize1PEventLoggingIfConfigChanged())
})
// Phase 4: Auth
void populateOAuthAccountInfoIfNeeded()
// Phase 5: IDE 检测
void initJetBrainsDetection()
void detectCurrentRepository()
// Phase 6: 远程设置加载 Promise 初始化
if (isEligibleForRemoteManagedSettings()) {
initializeRemoteManagedSettingsLoadingPromise()
}
if (isPolicyLimitsEligible()) {
initializePolicyLimitsLoadingPromise()
}
// Phase 7: 首次启动时间记录
recordFirstStartTime()
// Phase 8: 网络配置
configureGlobalMTLS() // mTLS
configureGlobalAgents() // proxy
preconnectAnthropicApi() // TCP+TLS 握手预热 (~100-200ms)
// Phase 9: CCR upstream proxy (CLAUDE_CODE_REMOTE only)
if (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE)) {
await initUpstreamProxy()
}
// Phase 10: 平台
setShellIfWindows() // git-bash
registerCleanup(shutdownLspServerManager)
registerCleanup(cleanupSessionTeams)
// Phase 11: Scratchpad
if (isScratchpadEnabled()) {
await ensureScratchpadDir()
}
})export function initializeTelemetryAfterTrust(): void {
if (isEligibleForRemoteManagedSettings()) {
// 等待远程设置加载后再初始化 telemetry
void waitForRemoteManagedSettingsToLoad().then(async () => {
applyConfigEnvironmentVariables() // 包含远程设置
await doInitializeTelemetry()
})
} else {
void doInitializeTelemetry()
}
}
async function doInitializeTelemetry(): Promise<void> {
if (telemetryInitialized) return
telemetryInitialized = true
// Lazy-load: ~400KB OpenTelemetry + protobuf
const { initializeTelemetry } = await import('instrumentation.js')
const meter = await initializeTelemetry()
setMeter(meter, createAttributedCounter)
getSessionCounter()?.add(1)
}// 1. 注册 bundled skills/plugins (纯内存, <1ms)
initBuiltinPlugins()
initBundledSkills()
// 2. 并行启动 setup + commands + agents
const setupPromise = setup(preSetupCwd, permissionMode, ...)
const commandsPromise = worktreeEnabled ? null : getCommands(preSetupCwd)
const agentDefsPromise = worktreeEnabled ? null : getAgentDefinitionsWithOverrides(preSetupCwd)
await setupPromise
// setup() ~28ms, 主要是 startUdsMessaging (socket bind ~20ms)在 setup() 之后, 首帧渲染前:
const onboardingShown = await showSetupScreens(
root,
permissionMode,
allowDangerouslySkipPermissions,
commands,
enableClaudeInChrome,
devChannels
)showSetupScreens 包含:
- Trust dialog (首次在项目目录运行)
- OAuth 登录流程
- Onboarding 引导
- Permission mode 选择
File: src/replLauncher.tsx
export async function launchRepl(
root: Root,
appProps: {
getFpsMetrics: () => FpsMetrics | undefined
stats?: StatsStore
initialState: AppState
},
replProps: REPLProps,
renderAndRun: (root: Root, element: React.ReactNode) => Promise<void>
): Promise<void> {
// 延迟加载 App + REPL 组件
const { App } = await import('./components/App.js')
const { REPL } = await import('./screens/REPL.js')
await renderAndRun(root,
<App {...appProps}>
<REPL {...replProps} />
</App>
)
}三种 launchRepl 调用场景:
- --continue: 加载最近会话后启动 (带 initialMessages)
- Direct Connect: 连接远程服务器后启动 (带 directConnectConfig)
- Normal: 全新会话或交互式 resume 后启动
if (options.continue) {
const { clearSessionCaches } = await import('./commands/clear/caches.js')
clearSessionCaches() // 清除过期缓存
// 加载最近的会话 (当前目录)
const result = await loadConversationForResume(undefined, undefined)
if (!result) {
return await exitWithError(root, 'No conversation found to continue')
}
// 处理恢复的会话
const loaded = await processResumedConversation(result, {
forkSession: !!options.forkSession,
includeAttribution: true,
transcriptPath: result.fullPath
}, resumeContext)
await launchRepl(root, { ... }, {
...sessionConfig,
initialMessages: loaded.messages,
initialFileHistorySnapshots: loaded.fileHistorySnapshots,
initialContentReplacements: loaded.contentReplacements,
initialAgentName: loaded.agentName,
initialAgentColor: loaded.agentColor,
mainThreadAgentDefinition: loaded.restoredAgentDef ?? mainThreadAgentDefinition
}, renderAndRun)
}File: src/utils/sessionRestore.ts
export type ProcessedResume = {
messages?: Message[]
fileHistorySnapshots?: FileHistorySnapshot[]
attributionSnapshots?: AttributionSnapshotMessage[]
contextCollapseCommits?: ContextCollapseCommitEntry[]
contextCollapseSnapshot?: ContextCollapseSnapshotEntry
}恢复流程:
loadConversationForResume()-- 从 transcript 文件加载原始日志- 反序列化 message chain, 处理 message normalization
- 过滤孤立 thinking-only messages / 空白 assistant messages
- 恢复 file history snapshots (用于 diff 追踪)
- 恢复 attribution snapshots (用于 commit 归因)
switchSession()切换到恢复的 session ID- 如果
forkSession=true, 使用新 session ID (保留 parent 关系) - 恢复 cost state, worktree state, todo list
- 运行 SessionStart hooks (
processSessionStartHooks)
Session 支持 chain 结构 -- 一个 session 可以 fork 自另一个:
// sessionStorage.ts
buildConversationChain() // 构建完整的消息链
checkResumeConsistency() // 验证链的完整性if (options.resume) {
if (typeof resume === 'string') {
// UUID -- 直接恢复指定 session
} else {
// true -- 打开交互式选择器
await launchResumeChooser(root, ...)
}
}File: src/entrypoints/agentSdkTypes.ts
公共 SDK 的类型入口, 重新导出:
sdk/coreTypes.ts-- 可序列化类型 (messages, configs)sdk/runtimeTypes.ts-- 不可序列化类型 (callbacks, interfaces)sdk/controlTypes.ts-- 控制协议类型 (@alpha)sdk/settingsTypes.generated.js-- 设置类型sdk/toolTypes.js-- 工具类型 (@internal)
关键函数:
// 自定义工具定义
export function tool<Schema extends AnyZodRawShape>(
name: string,
description: string,
inputSchema: Schema,
handler: (args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>,
extras?: { annotations?: ToolAnnotations; searchHint?: string; alwaysLoad?: boolean }
): SdkMcpToolDefinition<Schema>
// 创建进程内 MCP server
export function createSdkMcpServer(options: {
name: string
version?: string
tools?: Array<SdkMcpToolDefinition<any>>
}): McpSdkServerConfigWithInstanceexport const HOOK_EVENTS = [
'PreToolUse', 'PostToolUse', 'PostToolUseFailure',
'Notification', 'UserPromptSubmit',
'SessionStart', 'SessionEnd',
'Stop', 'StopFailure',
'SubagentStart', 'SubagentStop',
'PreCompact', 'PostCompact',
'PermissionRequest', 'PermissionDenied',
'Setup', 'TeammateIdle',
'TaskCreated', 'TaskCompleted',
'Elicitation', 'ElicitationResult',
'ConfigChange',
'WorktreeCreate', 'WorktreeRemove',
'InstructionsLoaded', 'CwdChanged', 'FileChanged',
] as constFile: src/entrypoints/mcp.ts
将 Claude Code 暴露为 MCP server (通过 stdio transport):
export async function startMCPServer(
cwd: string,
debug: boolean,
verbose: boolean
): Promise<void>实现:
ListToolsRequestSchemahandler: 导出所有内置工具 (inputSchema -> zodToJsonSchema)CallToolRequestSchemahandler: 执行工具调用, 带权限检查- 额外暴露
reviewcommand 作为 MCP tool
File: src/main.tsx -- runMigrations()
const CURRENT_MIGRATION_VERSION = 11
function runMigrations(): void {
if (getGlobalConfig().migrationVersion !== CURRENT_MIGRATION_VERSION) {
migrateAutoUpdatesToSettings()
migrateBypassPermissionsAcceptedToSettings()
migrateEnableAllProjectMcpServersToSettings()
resetProToOpusDefault()
migrateSonnet1mToSonnet45()
migrateLegacyOpusToCurrent()
migrateSonnet45ToSonnet46()
migrateOpusToOpus1m()
migrateReplBridgeEnabledToRemoteControlAtStartup()
resetAutoModeOptInForDefaultOffer() // feature('TRANSCRIPT_CLASSIFIER')
migrateFennecToOpus() // ant-only
saveGlobalConfig(prev => ({
...prev,
migrationVersion: CURRENT_MIGRATION_VERSION
}))
}
// 异步迁移 (非阻塞)
migrateChangelogFromConfig().catch(() => {})
}T=0ms process start
T=0ms cli.tsx module eval (COREPACK, CCR heap, ablation)
T=0ms main() -> args check
T=~1ms profileCheckpoint('cli_entry')
T=~1ms dynamic import('../main.js') starts
T=~1ms main.tsx module-eval: profileCheckpoint('main_tsx_entry')
T=~1ms startMdmRawRead() --> plutil/reg query subprocess
T=~2ms startKeychainPrefetch() --> 2x security subprocess (macOS)
T=~2ms ... 135ms of static imports ...
T=~137ms profileCheckpoint('main_tsx_imports_loaded')
T=~138ms main() -> initializeWarningHandler, SIGINT
T=~139ms detect interactive, set clientType
T=~140ms eagerLoadSettings (--settings, --setting-sources)
T=~141ms run() -> Commander init
T=~142ms preAction hook fires:
T=~142ms ensureMdmSettingsLoaded() // ~free (subprocess done)
T=~142ms ensureKeychainPrefetchCompleted() // ~free
T=~143ms init() starts
T=~143ms enableConfigs()
T=~144ms applySafeConfigEnvironmentVariables()
T=~145ms setupGracefulShutdown()
T=~146ms mTLS + proxy config
T=~147ms preconnectAnthropicApi() // TCP+TLS overlap
T=~150ms init() ends
T=~151ms initSinks()
T=~152ms runMigrations()
T=~153ms loadRemoteManagedSettings() (async)
T=~155ms action handler starts
T=~160ms setup() + parallel cmds/agents
T=~188ms setup() done (~28ms)
T=~190ms showSetupScreens() (may block for user input)
T=???ms trust accepted
T=???ms model resolution
T=???ms launchRepl()
T=???ms REPL first render
T=???ms startDeferredPrefetches()
| 文件路径 | 职责 |
|---|---|
src/entrypoints/cli.tsx |
CLI bootstrap, fast-path 分发 |
src/main.tsx |
完整初始化流程, Commander 配置, REPL 启动 |
src/bootstrap/state.ts |
全局状态单例, getter/setter 导出 |
src/entrypoints/init.ts |
init() memoized 初始化, telemetry |
src/entrypoints/mcp.ts |
MCP server 入口 |
src/entrypoints/agentSdkTypes.ts |
Agent SDK 公共类型 |
src/entrypoints/sdk/coreTypes.ts |
SDK 核心类型 (从 Zod schema 生成) |
src/entrypoints/sdk/controlSchemas.ts |
SDK 控制协议 schema |
src/utils/startupProfiler.ts |
启动性能 profiling |
src/utils/settings/mdm/rawRead.ts |
MDM 设置预读取 |
src/utils/secureStorage/keychainPrefetch.ts |
macOS keychain 预读取 |
src/replLauncher.tsx |
REPL 组件懒加载 + 渲染 |
src/utils/conversationRecovery.ts |
会话恢复逻辑 |
src/utils/sessionRestore.ts |
会话状态恢复 |
src/setup.ts |
setup() 函数 (worktree, cwd, UDS) |
src/interactiveHelpers.ts |
showSetupScreens, renderAndRun |