附录 A:Claude Code 源码导航地图
带你走一遍 Claude Code 的核心源码——知道在哪找什么
为什么要读源码
学技术有三种方式:看文档、写代码、读源码。前两种让你”会用”,第三种让你”理解”。
Claude Code 是目前最成熟的开源 AI Agent 运行时之一,用 TypeScript 编写,总计 51.2 万行代码,分布在约 1900 个文件中。它不是实验项目——每天有大量开发者在生产环境中使用它。读懂它的源码,你能看到一个生产级 AI Agent 在工程上要解决哪些问题、做了哪些取舍。
更重要的是:本书构建的 Harness 与 Claude Code 的架构一一对应。你在前面章节里手搓的每一个模块——查询循环、工具注册、权限系统、Hook 机制、技能包、记忆系统——在 Claude Code 源码中都有对应实现。读 CC 源码就像看你自己代码的”生产版本”,能帮你理解:
- 你的简化方案省略了什么,为什么生产环境需要补回来
- 同样的设计思想在 TypeScript 里是怎么表达的
- 当规模从几百行扩展到几万行时,架构如何演进
本附录不是逐行讲解源码,而是一张地图——告诉你哪个模块在哪、从哪读起、重点看什么。拿着这张地图,打开编辑器,自己走一遍。
项目结构总览
克隆 Claude Code 仓库后,你会看到这样的顶层结构:
claude-code/ — 顶层目录
├── src/
│ ├── commands/ — CLI 命令定义与路由
│ ├── core/ — 查询循环、流式处理、上下文管理
│ ├── permissions/ — 七层权限模型
│ ├── tools/ — 45+ 工具实现
│ ├── memory/ — 记忆系统(提取、存储、加载)
│ ├── hooks/ — 生命周期钩子
│ ├── skills/ — 内置技能包
│ ├── mcp/ — MCP 协议集成
│ └── ui/ — ink + React 终端界面
├── tests/ — 测试套件
├── package.json — 依赖与脚本
└── tsconfig.json — TypeScript 配置
核心代码全在 src/ 下。每个子目录对应一个独立的功能模块,职责边界清晰。这种”按功能分目录”的组织方式在中大型 TypeScript 项目中非常常见——和我们在 Harness 中用 Python 包做的事情一样。
tests/ 目录的结构与 src/ 镜像,每个模块都有对应的测试文件。读源码时,测试文件往往比实现文件更容易理解——因为测试用例就是”这个模块该怎么用”的活文档。
十大模块定位
下面这张表是本附录的核心——把我们在 Harness 中构建的每个模块,映射到 Claude Code 源码中的对应位置:
| 我们的模块 | CC 对应目录 | 关键看点 |
|---|---|---|
engine.py 查询循环 | src/core/query.ts | 核心 while 循环、流式 SSE 解析、上下文压缩 |
tools/ 工具注册 | src/tools/ | 45+ 工具文件、统一接口、参数校验 |
permissions.py | src/permissions/ | 七层权限模型、规则优先级、动态配置 |
agents.py | src/core/agent.ts | SubAgent 与 TaskAgent 的区分 |
memory/ | src/memory/ | 自动记忆提取、向量存储、会话摘要 |
hooks.py | src/hooks/ | 26 个事件定义、条件触发、异步执行 |
skills/ | src/skills/ | 内置技能包、技能加载器、触发路由 |
mcp/ | src/mcp/ | stdio 与 SSE 双传输、协议协商 |
ui/ | src/ui/ | ink + React 终端渲染、流式输出 |
cli.py | src/commands/ | 命令注册、参数解析(commander.js) |
几个值得注意的差异:
-
规模差异——我们的
tools/目录可能只有几个工具文件,CC 的src/tools/下有 45 个以上。但工具注册和分发的模式是一样的:每个工具实现一个统一接口,由中央注册表管理。 -
语言差异——CC 用 TypeScript,我们用 Python。类型系统的表达方式不同(interface vs dataclass / TypedDict),但设计意图相同。读 CC 源码时,重点看接口定义(
.d.ts文件或 interface 声明),这比读具体实现更快抓住模块职责。 -
UI 差异——CC 用 ink(基于 React 的终端 UI 框架)做了丰富的交互界面。我们的 Harness 用简单的文本输出。UI 部分可以跳过或浏览即可,不影响理解核心架构。
推荐阅读路径
源码量大,不能从头到尾逐行读。按下面的顺序走,从入口开始,逐步深入:
图 A-1:Claude Code 源码推荐阅读路径
- 入口 — CLI 启动 →
src/commands/→ 找到 main 函数,看命令如何解析和分发 - 查询循环 →
src/core/query.ts→ 核心 while 循环,看消息如何流转 - 工具注册与分发 →
src/tools/→ 先读 index.ts,再挑一个简单工具看完整实现 - 权限检查流 →
src/permissions/→ 看工具执行前如何过权限层 - Hook 与事件 →
src/hooks/→ 26 个事件的定义、注册和触发机制 - 记忆系统 →
src/memory/→ 会话结束时如何提取和存储记忆
每一步花 30 到 60 分钟即可。不需要读懂每一行——关键是理解数据如何流动:一条用户消息进来后,经过哪些模块、做了哪些变换、最终怎么变成工具调用或文本回复。
💡 三条捷径
- 直接搜关键函数名——比如搜
queryLoop、executeTool、checkPermission,直接跳到核心逻辑- 从测试入手——先读
tests/core/query.test.ts,测试用例告诉你查询循环的输入输出是什么- 跟着报错走——故意触发一个错误(比如执行被拒绝的命令),然后在源码里搜报错信息,反向追踪调用链
核心模块详解
查询循环:一切的起点
src/core/query.ts 是整个 Agent 的心脏。打开这个文件,你会看到一个熟悉的结构——和我们在第 2 章构建的查询循环几乎一样的 while 循环:
- 用户输入一条消息,追加到
messages数组 - 调用 Anthropic API,传入完整消息历史和工具定义
- 流式接收响应,逐 token 渲染到终端
- 如果响应包含
tool_use,执行工具并把结果追加到messages - 如果响应是纯文本(
end_turn),结束本轮,等待下一条用户输入 - 如果上下文超出限制,触发压缩(compact)
CC 的查询循环比我们的复杂在三个地方:流式解析用的是 Server-Sent Events(SSE)协议,需要处理各种边界情况;上下文管理不是简单截断而是用 LLM 做摘要压缩;错误恢复包括 API 超时重试、模型降级、速率限制退避。
工具系统:45 个文件的统一接口
src/tools/ 目录下每个文件都实现了同一个接口。打开任意一个工具文件(比如 bash.ts),你会看到:
- 一个
definition对象——描述工具的名称、参数 schema、描述信息(传给 LLM) - 一个
execute函数——接收参数、执行操作、返回结果 - 参数校验逻辑——在执行前检查参数类型和合法性
这和我们在第 3 章构建的 @tool 装饰器做的事一模一样,只是 CC 用 TypeScript 的类型系统做了更严格的约束。
重点看的文件:
| 文件 | 看点 |
|---|---|
bash.ts | 最复杂的工具——沙箱执行、超时控制、输出截断 |
file-write.ts | 文件写入——diff 展示、权限检查、自动备份 |
file-read.ts | 文件读取——大文件分页、编码检测 |
grep.ts | 搜索工具——性能优化、结果限制 |
index.ts | 工具注册表——所有工具的统一入口 |
权限系统:七层检查
src/permissions/ 是安全的核心。打开主文件,你会看到权限检查是一个管道——每个工具调用在执行前要经过七层检查,任何一层拒绝都会阻断执行:
- 工具黑名单——某些工具在特定模式下完全禁用
- 路径限制——文件操作只能在项目目录内
- 命令过滤——Bash 工具有危险命令白名单/黑名单
- 用户配置——用户在配置文件中自定义的允许/拒绝规则
- 项目配置——项目级
.claude/配置中的权限规则 - 交互确认——弹窗询问用户是否允许
- 会话记忆——记住用户本次会话中已经允许过的操作
这七层的优先级从上到下递减——上层的”拒绝”不可被下层覆盖。这和我们在第 4 章实现的权限模型设计思想一致,但 CC 的实现更细致,比如路径限制会处理符号链接、相对路径、.. 逃逸等边界情况。
源码阅读技巧
最后分享几条实用的大型 TypeScript 项目阅读技巧:
从入口跟调用链
不要试图从目录结构”自上而下”理解项目。找到 package.json 里的 main 或 bin 字段,那就是入口文件。从入口开始,用编辑器的”Go to Definition”一路跟下去,看一条用户消息从输入到输出经过了哪些函数。
先读类型,后读实现
TypeScript 的 interface 和 type 定义就是模块的”合同”——它定义了输入输出的形状,不关心具体怎么实现。先通读一个模块的类型定义,你就知道这个模块对外暴露了什么能力、期望什么输入、返回什么结果。
// 示例 — 先看接口,再看实现
// 先看这个——知道工具长什么样
interface Tool {
name: string;
description: string;
parameters: JSONSchema;
execute(params: unknown): Promise<ToolResult>;
}
// 再看这个——知道具体怎么做
class BashTool implements Tool {
async execute(params: unknown) {
// 500 行实现...
}
}
用测试理解行为
当你看不懂一个函数在做什么时,去 tests/ 目录找对应的测试文件。测试用例会告诉你:
- 这个函数的正常输入和输出是什么
- 边界情况有哪些(空输入、超大输入、非法参数)
- 错误情况该返回什么
测试就是可执行的文档。
善用搜索
以下几种搜索在读源码时特别有用:
| 搜索内容 | 目的 |
|---|---|
| 错误消息字符串 | 从用户可见的报错反向追踪到源码位置 |
TODO / HACK / FIXME | 发现作者自己认为不完美的地方——这些往往是最好的学习点 |
事件名称(如 PreToolUse) | 追踪一个事件从定义到触发到处理的完整链路 |
import.*from | 理解模块间的依赖关系 |
接口名(如 interface Tool) | 找到某个抽象的所有实现 |
不要追求 100%
51 万行代码不需要全读完。理解了核心的查询循环、工具分发、权限检查这三条主线,你就掌握了 Claude Code 80% 的架构。剩下的模块(UI、MCP、具体工具实现)按需查阅即可。
💡 核心观点:源码是最好的老师
文档告诉你”应该怎么用”,源码告诉你”实际怎么做”。
本书构建的 Harness 是一个精简的教学实现——它帮你建立了正确的心智模型。但当你遇到生产环境的问题(性能、安全、边界情况),答案往往在 Claude Code 的源码里。
把这个附录当作你的导航地图。每次遇到具体问题,先在地图上定位到对应模块,然后打开源码,顺着调用链找答案。读得越多,你对 AI Agent 架构的理解就越深。
附录小结
- Claude Code 是开源的 TypeScript AI Agent 运行时,51.2 万行代码,与本书 Harness 架构一一对应
- 项目按功能分目录:
core/(查询循环)、tools/(工具)、permissions/(权限)、hooks/(钩子)、memory/(记忆)、skills/(技能) - 十大模块各有对应:从
engine.py→query.ts到cli.py→commands/ - 推荐阅读顺序:入口 → 查询循环 → 工具注册 → 权限检查 → Hook 事件 → 记忆系统
- 源码阅读技巧:从入口跟调用链、先读类型后读实现、用测试理解行为、善用搜索定位
- 不追求 100% 覆盖——抓住查询循环、工具分发、权限检查三条主线即可