📄

Request My Resume

Thank you for your interest! To receive my resume, please reach out to me through any of the following channels:

Claude Code 源码导读 01|从 `cli.tsx` 到 `main.tsx`,看清系统如何完成启动装配

Digital Strategy Review | 2026

Claude Code 源码导读 01|从 cli.tsxmain.tsx,看清系统如何完成启动装配

文 / 果叔 · 阅读时间 / 8 Min

Claude Code 启动装配主视觉

写在前面

这一篇我想先把启动链路讲透。先把入口和装配过程看清楚,后面再读 query.tsAgentToolREPL.tsx 才不会被局部细节带偏;Claude Code 难读的地方,不在文件多,而在它从入口开始就已经是一套 runtime 的装配过程。

01

cli.tsxmain.tsx:系统是怎么启动的

这一篇只回答一个问题:Claude Code 这套系统是怎么从一个命令启动,逐步装配成完整运行时的。

如果不先建立这个全景图,后面再读 query.tsAgentToolREPL.tsx 很容易陷入局部细节。因为这不是一个“命令解析 -> 执行业务逻辑”的普通 CLI,而是一个有多种运行模式、多个执行后端、复杂能力裁剪逻辑的 agent runtime。

02

一、先看 src/entrypoints/cli.tsx

src/entrypoints/cli.tsx 的角色不是“薄薄一层入口”。它本身就是第一层调度器。

这里最重要的事实是:它没有一上来就导入完整系统,而是先做一轮轻量级、性能敏感的 fast-path 分流。

你从源码里能看出它明显在追求两件事:

01 不需要完整运行时的路径,尽量不要加载完整运行时。

02 不同模式的入口,尽量在最早阶段就分叉。

1. 为什么这里要做 fast-path

cli.tsx 处理了很多可以提前返回的路径,比如:

• --version

• --dump-system-prompt

• --daemon-worker

• remote-control / remote / sync / bridge

• daemon

• ps / logs / attach / kill / --bg

• 模板类命令

• environment-runner

• self-hosted-runner

• --worktree --tmux 相关路径

这意味着 Claude Code 从架构上承认一件事:

“Claude Code”不是一个单一运行形态,而是一套共享核心能力的多入口系统。

也就是说,命令行前台 REPL 只是其中一种入口,后台 worker、bridge、daemon、远程执行环境,本质上也都属于同一体系。

2. 为什么它用动态导入

这里的大量 await import(...) 不是单纯的代码风格,而是很实在的启动控制:

• 只有真正进入某个分支,才加载对应依赖

• 减少普通路径的模块求值成本

• 给后面的 feature gate 留出足够清晰的边界

这和很多 CLI 项目“所有模块先 import,再在逻辑里判断”完全不同。 Claude Code 更像在做一个多模式 runtime launcher。

3. 这一步对后面的 Agent 系统意味着什么

后面我们会看到 agent 有很多执行形态:

• 本地前台

• 本地后台

• 同进程 teammate

• tmux teammate

• worktree agent

• remote agent

如果入口层一开始不把运行模式拆开,后面的系统复杂度会爆炸。 所以 cli.tsx 的核心价值,不是“启动快一点”,而是先把系统边界拆清楚。

03

二、src/main.tsx 不是页面入口,而是运行时装配器

main.tsx 很大,但读法不能按“一个大组件”去读。更准确地说,它是 Claude Code 的runtime assembler。

它负责把下面这些东西装起来:

• config / settings / managed settings

• auth / OAuth / subscription / org policy

• analytics / GrowthBook / feature gates

• model 选择与能力判断

• MCP / plugin / skills

• tools / commands / agents

• store / AppState

• 最终进入某个 REPL 形态

所以它大的原因不是职责混乱,而是它承担了“把一个可运行会话装出来”的总控职责。

Claude Code 启动流程信息图

启动不是单点执行,而是一套分流、装配与能力接线过程。

04

三、启动过程的五个阶段

我建议把 main.tsx 的启动路径理解成 5 个阶段。

阶段一:预热慢路径

文件最开头就做了几件很有经验的事:

• profileCheckpoint(...)

• 提前启动 MDM 读取

• 提前启动 keychain prefetch

这类代码的味道很明显: 作者已经非常清楚哪些操作会拖慢首屏,所以把它们前移并并发化。

这类预热不是“锦上添花”,而是对交互式系统非常关键的优化。因为 Claude Code 启动后要立刻进入高互动状态,不能像后台脚本那样慢慢初始化。

阶段二:初始化环境与策略

在进入主逻辑之前,系统会逐步拉起:

• 配置与环境变量映射

• managed settings

• remote managed settings

• policy limits

• auth / bootstrap 信息

• GrowthBook

• analytics gate

这一步决定的是:当前用户、当前组织、当前环境,到底允许看见什么能力。

这很重要,因为在 Claude Code 里,工具、agent、远程能力、bridge、auto mode 等都不是固定暴露的。 它们必须先经过这层策略和配置判断。

阶段三:组装能力面

接下来会装配:

• commands

• tools

• agent definitions

• MCP clients / resources / commands

• plugins

• skills

这一步的本质不是“注册功能”,而是形成一个当前会话的能力快照。

尤其是 getTools(...) 这一层很关键。 Claude Code 并不把工具当成恒定存在的 SDK 函数,而是把它们看成会被环境、权限、模式、feature gate 裁剪的动态执行面。

阶段四:建立状态仓库

main.tsx 会创建 store,并准备 REPL 所需的初始状态。 这是后面 AppState 能承载任务、通知、MCP、plugin、viewing-agent、permission queue 的前提。

也就是说,这里不只是“初始化 React state”,而是在创建一个会话级运行时状态容器。

阶段五:进入 REPL

最后,系统会根据不同前提进入不同版本的 launchRepl(...) 路径。

这说明 REPL 不是单一页面,而是一个承接多种启动结果的统一交互舞台,比如:

• 正常本地会话

• resume 恢复会话

• remote viewer

• 不同模式的前台控制面

05

四、main.tsx 真正解决的不是“启动”,而是“装配”

如果用一句话概括 main.tsx 的职责,那就是:

在进入第一轮用户交互之前,把当前这次会话该拥有的世界模型装配完整。

这个世界模型包括:

• 当前用户是谁

• 当前模型是什么

• 当前权限模式是什么

• 当前能看到哪些工具

• 当前能加载哪些 agent

• 当前有没有 MCP / plugin / remote bridge

• 当前 REPL 该以什么姿态启动

为什么这件事重要? 因为后面的 query loop、tool orchestration、AgentTool 都假设这些基础条件已经定好了。

也就是说,main.tsx 是整个系统的“前置求值器”。

06

五、从架构角度看,cli.tsx + main.tsx 建立了三层边界

这两份文件合起来,给整套系统建立了三层很重要的边界。

边界一:运行模式边界

cli.tsx 先决定你到底走哪条入口:

• 轻量命令

• daemon/bridge/background

• 还是完整交互运行时

边界二:能力边界

main.tsx 决定这一轮会话到底能看到什么:

• 哪些工具

• 哪些 command

• 哪些 agent

• 哪些 plugin / MCP 能力

边界三:交互边界

launchRepl(...) 决定最终用户如何进入系统:

• 本地前台

• 恢复会话

• 远程观察/控制

所以从源码结构看,这两层不是“启动脚本”,而是把后续复杂系统约束住的第一道防线。

07

六、最值得抓住的阅读重点

如果你打算自己继续深入这两份文件,我建议抓下面这些问题:

01 哪些路径在 cli.tsx 就被提前截断了,为什么?

02 哪些配置必须在 main.tsx 早期完成,否则后面能力暴露会出错?

03 getTools(...)、agent definitions、MCP、plugins 分别在哪个阶段被组装?

04 为什么 main.tsx 里有那么多 launchRepl(...) 调用?

05 为什么它要在 REPL 启动前就把这么多策略和能力判断做完?

这五个问题想清楚了,后面读 query.ts 时就不会把它误解成一个单纯的“聊天循环”。

08

七、一张图看懂启动总流程

Mermaid 图 1

09

八、这一篇的结论

cli.tsxmain.tsx 联手完成的,不是“把程序跑起来”,而是:

• 先在入口层拆分运行模式

• 再在主装配层求值当前会话能力

• 最后把完整运行时交给 REPL

这是后面整套 agent runtime 能成立的前提。 因为你接下来读到的一切复杂度,都是建立在“当前世界已经被装配好了”的假设之上的。

下一篇就该进入这个世界真正的心脏:query.tstools.tstoolOrchestration.ts

Mr. Guo Logo

© 2026 Mr'Guo

Twitter Github WeChat