导语:本文是对 Claude Code (Anthropic 官方 CLI 工具) 的源码还原与深度分析。该项目使用 TypeScript + React + Ink 构建,共计 1,884 个 TS/TSX 文件,运行在 Bun 运行时之上。本文将从整体架构、启动流程、运行模式以及核心数据流四个维度,带你硬核拆解这款强大的 AI CLI 工具。
参考仓库(源码地址):https://github.com/ChinaSiro/claude-code-sourcemap
1.1 目录结构
项目的核心逻辑集中在 src/ 目录下,各模块职责划分非常清晰:
src/ ├── main.tsx # 主入口 (~800KB),CLI 解析 + 启动流程 ├── entrypoints/ # 多种启动模式 (CLI/SDK/Sandbox) ├── state/ # Zustand 风格全局状态管理 (AppStateStore) ├── tools/ # 43 个工具实现 (Bash, FileEdit, Grep, Agent 等) ├── commands/ # 101 个 CLI 命令 (commit, review, config 等) ├── components/ # 144 个 React 终端 UI 组件 ├── ink/ # 定制增强版 Ink 终端 UI 框架 ├── services/ # 36 个服务模块 (MCP, OAuth, API, LSP 等) ├── hooks/ # React hooks + AI 行为钩子系统 ├── tasks/ # 后台任务管理 (Shell/Agent/Teammate/Workflow 等) ├── utils/ # 329 个工具函数/模块 ├── bridge/ # 桌面端集成 (Desktop/远程控制) ├── skills/ # AI 技能系统 ├── vim/ # Vim 模式支持 ├── voice/ # 语音模式 └── coordinator/ # 多 Agent 编排
1.2 核心设计
feature() 做编译期死代码消除。
UI 框架
React + 深度定制
Ink,支持焦点管理、动画、点击与滚动。
状态管理 不可变
AppState (DeepImmutable),Zustand 风格 store。
工具系统 统一
Tool 接口,内置/MCP/LSP 工具共用同一抽象层。
权限控制 完整的权限检查流程,支持适配符规则和逐工具控制。
功能门控 大量 Feature Flag (如
KAIROS,
COORDINATOR_MODE,
VOICE_MODE 等)。
渲染优化 源码中可见 React Compiler 编译器优化标记,大幅提升终端渲染性能。
1.3 关键文件速览
main.tsx- 主入口,负责启动编排。Tool.ts- 工具基础接口定义。tools.ts- 工具注册表。query.ts- 查询引擎(整个系统的心脏),API 调用核心,约 1700 行。QueryEngine.ts- 查询执行引擎 (SDK/headless 封装)。commands.ts- 命令注册。context.ts- 系统与用户上下文构建。
整个启动过程像流水线一样依次执行,主要分为 5 个阶段:
2.1 第一阶段:模块加载期的并行预热 (main.tsx:1-20)
profileCheckpoint(‘main_tsx_entry’)- 记录时间戳startMdmRawRead()- 并行读取 MDM 企业配置 (plutil/reg)startKeychainPrefetch()- 并行读取 macOS 钥匙串 (OAuth token)
💡 设计思路:利用 JS
import语句求值的 ~135ms,提前启动耗时的子进程。这些进程在后台跑,等后面需要结果时再await,这是一种非常巧妙的启动性能优化手段。
2.2 第二阶段:main() 函数 (main.tsx:585-854)
主要做两件事:
- 安全设置:防止 Windows PATH 劫持、注册 SIGINT/exit 处理。
- 特殊启动模式分流:根据
argv判断是否走特殊路径:cc:// URL- Direct Connect 模式–handle-uri- Deep Link 协议处理claude assistant- Kairos 助手模式claude ssh- SSH 远程模式- 如果都不是,则进入标准的
run()流程。
2.3 第三阶段:run() - Commander CLI 解析 (main.tsx:884-1006)
Commander 初始化并注册 preAction hook (每个命令执行前运行):
awaitMDM + Keychain 预取完成await init()- 核心初始化- 加载 analytics sinks
- 运行数据迁移 (migrations)
- 加载远程设置 + 策略限制 (非阻塞)
其中 init() 在 entrypoints/init.ts 中,完成了配置系统启用、安全环境变量应用、优雅退出清理、mTLS 配置、网络代理配置,以及提前建立 Anthropic API 的 TCP+TLS 连接 (又一个并行优化)。
2.4 第四阶段:setup() + 并行加载 (main.tsx:1903-2030)
并行执行以下三项任务:
setup()- 初始化工作目录、worktree、Session、权限模式。getCommands()- 加载所有 CLI 命令。getAgentDefinitions()- 加载 agent 定义。
2.5 第五阶段:分流到 REPL 或 Print 模式
根据是否有 -p/–print 参数进入不同运行模式(详见第三章)。
2.6 启动流程总结图
用户输入 `claude “修个 bug”` │ ▼ [模块加载] MDM/Keychain 并行预热 │ ▼ [main()] 安全设置 + 特殊模式分流 │ ▼ [run()] Commander 解析命令行参数 │ ▼ [preAction] init() -> 配置/网络/安全/API预连接 │ ▼ [setup()] 工作目录/Session/权限 + 并行加载 commands/agents │ ▼ [分流] ── -p ──> Print 模式 (QueryEngine 单次执行) │ └── 交互模式 ──> launchRepl() ->
REPL = Read-Eval-Print Loop (术语源自 Lisp):读取 -> 执行 -> 输出 -> 循环。
3.1 模式对比
claude
claude -p “修个bug”
交互方式 持续对话,等待用户输入 执行一次就退出
UI 渲染 Ink 渲染终端 UI (React 组件树) 无 UI,直接输出文本/JSON
状态管理 React AppState (Zustand store)
QueryEngine 类内部
核心入口
launchRepl() ->
QueryEngine.submitMessage()
3.2 代码中的分流点
在 main.tsx 中,核心判断逻辑如下:
if (isNonInteractiveSession) { // Print 模式:用 QueryEngine 单次执行 QueryEngine.submitMessage(prompt) } else { // REPL 模式:启动 Ink 渲染循环 await launchRepl(root, appProps, sessionConfig, renderAndRun) }
replLauncher.tsx 非常简洁(仅 22 行),其核心就是挂载 React 组件:
整条数据流的核心是一个 AsyncGenerator 循环。用户每发一条消息,就触发一次 query() 调用,它通过 yield 不断向外吐出事件(流式消息、工具结果等),调用方消费这些事件来更新 UI。
4.1 三个核心文件的职责
context.ts 构建上下文 (git status, CLAUDE.md, 日期)
食材准备
query.ts 单次对话循环:API调用 -> 工具执行 -> 继续/停止
厨师做菜
QueryEngine.ts 会话级状态管理,封装
query() 给 SDK/headless 用
餐厅前台
4.2 上下文构建 (context.ts)
每次对话开始前,系统构建两种上下文(均使用 memoize 缓存,整个会话只算一次):
getSystemContext()-> 包含 git 状态 (分支、最近提交、status)getUserContext()-> 包含CLAUDE.md文件内容 + 当前日期
4.3 查询循环 (query.ts) - 系统的心脏
query() 是一个 AsyncGenerator,核心结构是一个 while(true) 循环。下面用伪代码展示其核心分支逻辑:
while (true)
// ③ 关键分支点 if (!needsFollowUp) {
// 模型没有请求工具 -> 任务完成 return { reason: 'completed' } // <- 唯一的正常退出
}
// ④ 模型请求了工具 -> 执行工具 for await (update of runTools(…)) {
yield update.message // 工具结果给 UI
}
// ⑤ 拼接消息,准备下一轮 state = {
messages: [...之前的消息, ...assistant回复, ...工具结果], turnCount: nextTurnCount, transition: { reason: 'next_turn' },
} // 没有 break/return -> 自动 continue 回到 while(true) 顶部 }
循环退出条件:
- 只有
return才会跳出循环。只要模型还在调工具,循环就不会停。 - 一个典型的 Agent 任务可能循环几十次:
读文件 -> 思考 -> 改文件 -> 运行测试 -> 再改 -> … -> 最终给出文本回复 -> 退出。
退出状态枚举:
completed: 正常退出,模型不再调用工具。aborted_streaming: 用户按了 Ctrl+C。max_turns: 达到–max-turns限制。prompt_too_long: 上下文太长且压缩失败。hook_stopped: Hook 阻止了继续。
4.4 工具执行 (toolOrchestration.ts)
工具不是逐个执行的,而是通过 partitionToolCalls 分批并行或串行:
- 批次1:
[GrepTool, GlobTool]->isConcurrencySafe=true-> 并行执行 - 批次2:
[FileEditTool]->isConcurrencySafe=false-> 串行执行 - 批次3:
[GrepTool]->isConcurrencySafe=true-> 并行执行
注:只读工具 (Grep, Read 等) 可以并行,写操作 (Edit, Bash 等) 必须串行。默认最大并发数为 10。
4.5 QueryEngine (QueryEngine.ts)
QueryEngine 是对 query() 的类封装,管理跨轮次的状态:
class QueryEngine { mutableMessages: Message[] // 整个会话的消息历史 totalUsage: Usage // 累计 token 用量
async *submitMessage(prompt) {
// 1. 构建 systemPrompt, userContext, systemContext // 2. 创建 toolUseContext (权限、工具列表等) // 3. 调用 query(params) 并 yield 结果 // 4. 累积消息到 mutableMessages
} }
(注:REPL 模式不走 QueryEngine,而是直接调用 query(),状态由 React AppState 管理)
4.6 完整数据流图
用户输入 “帮我修 src/app.ts 的 bug” │ ▼ ┌─ context.ts ──────────────────────────────────┐ │ systemContext = { gitStatus } │ │ userContext = { claudeMd, date } │ └───────────────────────────────────────────────┘ │ ▼ ┌─ query.ts ── while(true) 循环 ────────────────┐ │ │ │ [预处理] 压缩/裁剪消息 -> 确保不超 context window │ │ │ │ │ ▼ │ │ [API 调用] callModel(messages, systemPrompt, tools) │ │ - 流式返回 │ │ ▼ │ │ [模型回复] “我来看看代码” + tool_use: FileReadTool │ │ │ │ │ ▼ │ │ [工具执行] runTools() -> 读取 src/app.ts -> 返回文件内容│ │ │ │ │ ▼ │ │ [拼接消息] messages += [assistant回复, tool_result]│ │ │ │ │ ▼ - continue, 回到循环顶部 │ │ │ │ │ [API 调用] callModel(更长的 messages) │ │ │ │ │ ▼ │ │ [模型回复] “找到 bug 了” + tool_use: FileEditTool │ │ │ │ │ ▼ │ │ [工具执行] runTools() -> 编辑文件 -> 返回 diff │ │ │ │ │ ▼ - continue │ │ │ │ │ [API 调用] callModel(…) │ │ │ │ │ ▼ │ │ [模型回复] “已修复!” (无 tool_use) │ │ │ │ │ ▼ │ │ [stop hooks] -> return { reason: ‘completed’ }│ └───────────────────────────────────────────────┘ │ ▼ UI 渲染每个 yield 出来的消息
结语:Claude Code 的源码展示了极高的工程素养,无论是从启动时的性能压榨,还是对大模型上下文的精准控制,都非常值得前端和 Node.js 开发者学习。如果你觉得这篇源码解析对你有帮助,欢迎点赞、收藏、评论!
后面我将继续分享Claude code源码中《上下文管理》和《SKILL调用》的内容,感兴趣的同学们可以继续关注!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/256421.html