附录 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.pysrc/permissions/七层权限模型、规则优先级、动态配置
agents.pysrc/core/agent.tsSubAgent 与 TaskAgent 的区分
memory/src/memory/自动记忆提取、向量存储、会话摘要
hooks.pysrc/hooks/26 个事件定义、条件触发、异步执行
skills/src/skills/内置技能包、技能加载器、触发路由
mcp/src/mcp/stdio 与 SSE 双传输、协议协商
ui/src/ui/ink + React 终端渲染、流式输出
cli.pysrc/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 源码推荐阅读路径

  1. 入口 — CLI 启动src/commands/ → 找到 main 函数,看命令如何解析和分发
  2. 查询循环src/core/query.ts → 核心 while 循环,看消息如何流转
  3. 工具注册与分发src/tools/ → 先读 index.ts,再挑一个简单工具看完整实现
  4. 权限检查流src/permissions/ → 看工具执行前如何过权限层
  5. Hook 与事件src/hooks/ → 26 个事件的定义、注册和触发机制
  6. 记忆系统src/memory/ → 会话结束时如何提取和存储记忆

每一步花 30 到 60 分钟即可。不需要读懂每一行——关键是理解数据如何流动:一条用户消息进来后,经过哪些模块、做了哪些变换、最终怎么变成工具调用或文本回复。

💡 三条捷径

  1. 直接搜关键函数名——比如搜 queryLoopexecuteToolcheckPermission,直接跳到核心逻辑
  2. 从测试入手——先读 tests/core/query.test.ts,测试用例告诉你查询循环的输入输出是什么
  3. 跟着报错走——故意触发一个错误(比如执行被拒绝的命令),然后在源码里搜报错信息,反向追踪调用链

核心模块详解

查询循环:一切的起点

src/core/query.ts 是整个 Agent 的心脏。打开这个文件,你会看到一个熟悉的结构——和我们在第 2 章构建的查询循环几乎一样的 while 循环:

  1. 用户输入一条消息,追加到 messages 数组
  2. 调用 Anthropic API,传入完整消息历史和工具定义
  3. 流式接收响应,逐 token 渲染到终端
  4. 如果响应包含 tool_use,执行工具并把结果追加到 messages
  5. 如果响应是纯文本(end_turn),结束本轮,等待下一条用户输入
  6. 如果上下文超出限制,触发压缩(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/ 是安全的核心。打开主文件,你会看到权限检查是一个管道——每个工具调用在执行前要经过七层检查,任何一层拒绝都会阻断执行:

  1. 工具黑名单——某些工具在特定模式下完全禁用
  2. 路径限制——文件操作只能在项目目录内
  3. 命令过滤——Bash 工具有危险命令白名单/黑名单
  4. 用户配置——用户在配置文件中自定义的允许/拒绝规则
  5. 项目配置——项目级 .claude/ 配置中的权限规则
  6. 交互确认——弹窗询问用户是否允许
  7. 会话记忆——记住用户本次会话中已经允许过的操作

这七层的优先级从上到下递减——上层的”拒绝”不可被下层覆盖。这和我们在第 4 章实现的权限模型设计思想一致,但 CC 的实现更细致,比如路径限制会处理符号链接、相对路径、.. 逃逸等边界情况。

源码阅读技巧

最后分享几条实用的大型 TypeScript 项目阅读技巧:

从入口跟调用链

不要试图从目录结构”自上而下”理解项目。找到 package.json 里的 mainbin 字段,那就是入口文件。从入口开始,用编辑器的”Go to Definition”一路跟下去,看一条用户消息从输入到输出经过了哪些函数。

先读类型,后读实现

TypeScript 的 interfacetype 定义就是模块的”合同”——它定义了输入输出的形状,不关心具体怎么实现。先通读一个模块的类型定义,你就知道这个模块对外暴露了什么能力、期望什么输入、返回什么结果。

// 示例 — 先看接口,再看实现

// 先看这个——知道工具长什么样
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.pyquery.tscli.pycommands/
  • 推荐阅读顺序:入口 → 查询循环 → 工具注册 → 权限检查 → Hook 事件 → 记忆系统
  • 源码阅读技巧:从入口跟调用链、先读类型后读实现、用测试理解行为、善用搜索定位
  • 不追求 100% 覆盖——抓住查询循环、工具分发、权限检查三条主线即可
English EN 简体中文 ZH