Claude Code 源码深度研究报告(最终版)

基于当前仓库 claude-code 的实际源码结构、入口链、运行时管线和关键注册点整理而成。

分析日期:2026-04-01 清晨(北京时间)

分析方法:

  • 静态源码审读
  • 主入口到主循环的调用链追踪
  • prompt、tool、agent、skill、plugin、hook、MCP 等关键模块交叉核对

需要先说明一个非常重要的分析前提:当前仓库是一个 reverse-engineered 的 Claude Code 工程,不是官方原始仓库;并且 src/entrypoints/cli.tsx 顶部把 feature() polyfill 成了恒为 false。这意味着很多源码中存在的实验路径,在当前仓库默认运行时并不会打开。因此本文会始终区分两层事实:

  1. 当前源码实现了什么。
  2. 当前仓库默认运行时真正启用了什么。

目录

  1. 研究范围与结论总览
  2. 源码结构全景:它为什么更像 Agent Runtime Platform
  3. 系统提示词总装:prompts.ts 的真实地位
  4. Prompt 全量提取与模块级拆解
  5. Agent Prompt 与 built-in agents 深挖
  6. Agent 调度链深挖:从 AgentTool 到 runAgent 再到 query
  7. Skills / Plugins / Hooks / MCP 生态深挖
  8. 权限、Hook、工具执行链深挖
  9. 为什么 Claude Code 这么强:从源码看它真正的护城河
  10. 关键文件索引与后续可继续深挖方向

1. 研究范围与结论总览

1.1 这次到底研究了什么

这次研究不是只看某个 prompt 文件,也不是只做目录级扫视,而是沿着当前仓库的真实执行路径,对下列主线做了系统拆解:

  • Claude Code 当前仓库的整体源码结构
  • 默认 system prompt 如何被构造、缓存和切分
  • 真正有效的 system prompt 如何被选择
  • query.ts 如何组织模型主循环、attachments、工具执行和上下文压缩
  • AgentTool、runAgent、built-in agents 的职责边界
  • Skills、Plugins、Hooks、MCP 如何接入并改变模型可见能力面
  • Permission、Pre/Post hook、Tool execution 如何协同形成可治理 runtime
  • 为什么 Claude Code 的体验明显强于“LLM + 一层 function calling”

1.2 关键确认事实

基于当前仓库代码,可以确认以下事实:

  1. 当前项目是一个多文件 reverse-engineered 工程,主运行入口是 src/entrypoints/cli.tsx,再进入 src/main.tsx 和 src/entrypoints/init.ts。
  2. 当前项目不是一个单纯的 prompt 项目,真正的主会话执行器是 src/query.ts。
  3. src/constants/prompts.ts 负责默认 system prompt 的构造,但最终生效的 prompt 还受到 src/utils/systemPrompt.ts 和 src/utils/attachments.ts 影响。
  4. 默认 system prompt 里确实存在静态 section、动态 section 和明确的 cache boundary:SYSTEM_PROMPT_DYNAMIC_BOUNDARY。
  5. 当前项目的工具执行链并不是“模型决定后直接调函数”,而是经过 schema 校验、工具校验、hook、permission、post-hook、tracing 的完整执行管线。
  6. 当前项目的 Agent 系统不是简单“子任务转发”,而是完整的 orchestration runtime。核心文件至少包括:
    • src/tools/AgentTool/AgentTool.tsx
    • src/tools/AgentTool/runAgent.ts
    • src/tools/AgentTool/prompt.ts
  7. Skills 在当前项目里不是普通说明文档,而是“模型可调用的 prompt-command 抽象”。
  8. Plugins 在当前项目里不是单一扩展点,而是能贡献 commands、skills、agents、hooks、MCP、settings、output styles 的平台级扩展单元。
  9. MCP 在当前项目里既是外部工具与资源接入平面,也是行为说明注入平面。
  10. 当前项目源码中确实存在 Explore / Plan / verification 等 built-in agent 设计,但由于 feature() 在当前仓库默认恒为 false,这些能力不等同于“默认 live capability”。

1.3 最重要的总判断

Claude Code 的强,不是来自某个神秘 system prompt,而是来自一个完整的软件工程系统。这个系统至少包含以下几层:

  • Prompt assembly architecture
  • Prompt cache and context budget management
  • Tool runtime governance
  • Permission model
  • Hook policy layer
  • Agent orchestration
  • Skill packaging
  • Plugin platform
  • MCP integration
  • Conversation lifecycle management

如果只拿走其中一层,例如只拿 system prompt 或只拿文件编辑工具,通常只能做出“能跑”的 coding agent;而 Claude Code 的优势来自这些层被统一在一个产品级 runtime 里。

一句话总结:

Claude Code 的价值,不是一段 prompt,而是一整套把 prompt、context、tool、permission、hook、agent、skill、plugin、MCP、cache 和产品体验统一起来的 Agent Runtime Platform。

2. 源码结构全景:它为什么更像 Agent Runtime Platform

2.1 顶层结构暴露出的系统复杂度

从当前仓库的顶层和 src/ 一级目录来看,Claude Code 至少包含这些关键模块:

  • src/entrypoints/:入口层
  • src/main.tsx:CLI 主编排
  • src/constants/:prompt、querySource、常量、输出样式等
  • src/context.ts:系统上下文和用户上下文构造
  • src/query.ts:主会话循环
  • src/tools/:工具实现层
  • src/services/tools/:工具执行 runtime、hook 适配、流式工具执行器
  • src/tools/AgentTool/:subagent orchestration
  • src/skills/:skills 加载和 bundled skills 注册
  • src/utils/plugins/ 与 src/plugins/:plugin 与 built-in plugin 体系
  • src/services/mcp/:MCP 配置、连接、类型、鉴权、工具抓取
  • src/utils/attachments.ts:attachment 和 system-reminder 注入层
  • src/utils/hooks.ts:广义 hook 运行框架
  • src/services/compact/:上下文压缩系统
  • src/tasks/ 与 src/Task.ts:后台任务和通知体系

这已经足够说明:当前项目不是简单 CLI 包装器,而是一个由会话层、执行层、协作层、扩展层组成的完整 agent runtime。

2.2 入口层说明它是平台,而不是单一交互壳

当前仓库的入口并不只有一个 CLI 文件。关键入口包括:

  • src/entrypoints/cli.tsx
  • src/entrypoints/init.ts
  • src/entrypoints/mcp.ts
  • src/entrypoints/sdk/*

这意味着它从设计上就考虑了多种消费方式:

  • 本地 CLI
  • 初始化和 trust/config bootstrap
  • 作为 MCP server 对外暴露工具
  • SDK / headless 场景的数据结构与运行协议

这种设计不是“一个 REPL 的附加能力”,而是一个平台化 runtime 的常见形态。

2.3 主入口链路的真实结构

当前最可信的主入口链路是:

TEXT
src/entrypoints/cli.tsx
  -> fast path / polyfill / mode dispatch
  -> src/main.tsx
    -> src/entrypoints/init.ts
    -> src/commands.ts
    -> src/tools.ts
    -> src/context.ts
    -> src/query.ts

这里有两个关键事实:

  1. cli.tsx 顶部的 feature() polyfill 会决定大量分支的默认关闭状态。
  2. 真正让 Claude Code 运转起来的不是 prompt 文件,而是 main.tsx + init.ts + query.ts 这一条运行时链路。

2.4 命令系统是产品控制面,而不是边角功能

src/commands.ts 不是简单的 slash command 列表,而是整个产品运行时的重要控制面。当前它至少承担:

  • built-in commands 注册
  • skill commands 注册
  • bundled skills 合并
  • plugin skills / plugin commands 合并
  • dynamic skills 合并
  • availability 和 enable 状态过滤

也就是说,命令系统在这里既是用户交互层,也是 skill / plugin / workflow 的生态入口。

2.5 真正的会话中枢是 query.ts

当前项目最值得强调的一点是:真正的会话执行器不是某个 prompt builder,而是 src/query.ts。

query.ts 负责:

  • 组织 API request 所需的 system prompt、context 和 attachments
  • 执行 auto-compact / microcompact / snip
  • 调度模型流式响应
  • 识别和执行 tool use
  • 调度 stop hooks
  • 生成 tool use summary
  • 管理 turn state、token budget、继续与终止条件

所以从运行架构看,Claude Code 不是“prompt 驱动一切”,而是 “query loop 驱动一切,prompt 是其中一个关键输入”。

3. 系统提示词总装:prompts.ts 的真实地位

3.1 真正的主入口不是单文件,而是三层组合

如果只看 src/constants/prompts.ts,很容易得出“这就是 Claude Code 的主系统提示词入口”的结论。但对当前仓库来说,更准确的说法是:

  • src/constants/prompts.ts:默认 system prompt 构造器
  • src/utils/systemPrompt.ts:有效 system prompt 选择器
  • src/utils/attachments.ts:一部分动态指令和能力清单的增量注入平面

因此 Claude Code 的真实 prompt 架构并不是单层,而是三层叠加:

  1. 默认 prompt blocks
  2. effective prompt selection
  3. attachment-based runtime reminders

3.2 getSystemPrompt() 不是静态文本,而是 section 编排器

src/constants/prompts.ts::getSystemPrompt() 会把 prompt 拆成静态部分和动态部分,并以数组形式返回,而不是直接生成一整块未分层的长字符串。

当前静态主干包括:

  • getSimpleIntroSection()
  • getSimpleSystemSection()
  • getSimpleDoingTasksSection()
  • getActionsSection()
  • getUsingYourToolsSection()
  • getSimpleToneAndStyleSection()
  • getOutputEfficiencySection()

动态部分则通过 section registry 注入,例如:

  • session-specific guidance
  • memory
  • ant model override
  • env info
  • language
  • output style
  • MCP instructions
  • scratchpad
  • function result clearing
  • summarize tool results
  • token budget
  • brief

这说明 Claude Code 的 prompt 不是静态字符串,而是“模块化 system prompt assembly”。

3.3 systemPromptSections.ts 暴露出 prompt 的工程化缓存思路

src/constants/systemPromptSections.ts 里有一套明确的 section registry:

  • systemPromptSection(name, compute)
  • DANGEROUS_uncachedSystemPromptSection(name, compute, reason)
  • resolveSystemPromptSections(sections)

这套机制的价值在于:

  • 常规 section 可缓存
  • 特定 section 可显式声明“每轮重算”
  • /clear 或 /compact 时能统一清理 section state

这不是普通 prompt 拼接能提供的工程收益。它把 prompt section 变成了一个可以单独管理缓存和生命周期的资源。

3.4 buildEffectiveSystemPrompt() 才决定“最终真正发给模型的 prompt”

src/utils/systemPrompt.ts::buildEffectiveSystemPrompt() 负责最后的 prompt 选择逻辑。当前优先级大致是:

  1. overrideSystemPrompt
  2. coordinator system prompt
  3. main-thread agent prompt
  4. custom system prompt
  5. default system prompt
  6. appendSystemPrompt 永远后拼

这说明 prompts.ts 虽然非常重要,但它并不是每次请求都直接决定最终 prompt;它更多是在提供“默认主 prompt 基底”。

3.5 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 是缓存边界,不只是分隔符

当前源码里存在明确的动态边界标记:

  • SYSTEM_PROMPT_DYNAMIC_BOUNDARY

它的作用不是语义分段,而是给 prompt cache split 提供稳定边界。真正消费这个边界的是:

  • src/utils/api.ts::splitSysPromptPrefix()
  • src/services/api/claude.ts::buildSystemPromptBlocks()

在当前实现里,这套逻辑至少支持三种情况:

  1. 有 global cache 且找到 boundary:静态部分单独作为 global cache block,动态部分不缓存。
  2. 因某些条件跳过 global split:system prompt 退回 org-scope cache。
  3. 默认 fallback:system prompt 前缀和其余部分走 org-scope cache。

所以更精确的结论应当是:

SYSTEM_PROMPT_DYNAMIC_BOUNDARY 是 Claude Code prompt cache 经济学中的关键边界标记,但它只是整体 cache strategy 的一部分,而不是全部。

3.6 当前项目里还有一个重要补充:attachment 也承担提示词职责

prompts.ts 并不是唯一的“模型行为说明书”。当前 attachments.ts 会在对话里注入大量 system-reminder,例如:

  • skill_listing
  • agent_listing_delta
  • mcp_instructions_delta
  • critical_system_reminder
  • plan_mode
  • relevant_memories

这说明 Claude Code 的提示词架构是双平面的:

  • system prompt blocks 提供稳定主规则
  • attachment reminders 提供可变、按需、增量的会话规则和能力通告

4. Prompt 全量提取与模块级拆解

4.1 身份与基本定位:getSimpleIntroSection()

这一段的作用不只是“介绍自己”,而是在一开始就完成三件事:

  1. 明确自己是一个 interactive agent。
  2. 明确主要任务是帮助用户做 software engineering work。
  3. 注入 cyber risk instruction,并禁止随意生成或猜测 URL。

这段很短,但它决定了后续行为的起点:Claude Code 不是普通聊天机器人,而是一个工具驱动的工程协作者。

4.2 基础运行现实:getSimpleSystemSection()

这一段定义了 Claude Code 所处的 runtime reality:

  • 所有非工具输出都会直接给用户看到
  • 工具调用运行在用户选定的 permission mode 下
  • 用户拒绝某个 tool call 后,不能原样重试
  • tool result 和 user message 可能带有 system-reminder 等标签
  • 外部结果可能包含 prompt injection
  • 系统存在 hook
  • 对话会自动压缩,并不严格受单次上下文窗口限制

这段的价值很高,因为它把模型从“纯语言模型视角”拉回到了“受治理 runtime 视角”。

4.3 做任务哲学:getSimpleDoingTasksSection()

这一段是 Claude Code 工程行为稳定性的核心来源之一。当前源码明确要求模型:

  • 不要加用户没有要求的功能
  • 不要做额外重构和无关改进
  • 不要滥加 error handling / fallback / validation
  • 不要为一次性操作引入抽象
  • 先读代码再改代码
  • 不要轻易创建新文件
  • 不要给时间估计
  • 失败时先诊断再换策略
  • 注意安全漏洞
  • 不要做无意义的兼容性垃圾
  • 结果必须如实报告,不能虚构验证结果

这部分不是“措辞漂亮”,而是 Anthropic 把 coding agent 的行为制度化写进了 prompt。

4.4 风险动作规范:getActionsSection()

这一节定义了哪些事情属于需要确认的风险动作,包括:

  • destructive operations
  • hard-to-reverse operations
  • 对外可见动作
  • 修改共享状态
  • 向第三方上传内容

而且它不是简单列举风险,而是把 blast radius 思维写进提示词:

  • 不要用 destructive action 当捷径
  • 发现陌生状态先调查
  • merge conflict、lock file 等不要暴力删除

这是 Claude Code 在产品体验上“看起来更稳”的重要来源。

4.5 工具使用规范:getUsingYourToolsSection()

这一段定义了清晰的工具优先级:

  • 读文件优先 Read
  • 改文件优先 Edit
  • 新建文件优先 Write
  • 搜文件优先 Glob
  • 搜内容优先 Grep
  • Bash 只留给真正必须 shell 的任务
  • 有任务工具时优先使用 TodoWrite / TaskCreate
  • 无依赖的多个 tool call 应并行

这说明 Claude Code 的稳,不只是因为“工具很多”,而是因为它有强约束的 tool usage grammar。

4.6 Session-specific guidance:会话态局部规则层

getSessionSpecificGuidanceSection() 是默认 prompt 里非常关键的一段动态 section。它会基于当前 enabled tools 和 feature gate 注入局部规则,例如:

  • 如果有 AskUserQuestion,工具被拒绝后可向用户追问
  • 非交互模式与交互模式的行为差异
  • AgentTool 的使用说明
  • Explore / Plan agent 的使用策略
  • SkillTool 的调用前提
  • DiscoverSkills 的使用 guidance
  • verification contract

这意味着 Claude Code 的 system prompt 并不是“永恒总规则”,而是“稳定主规则 + 会话局部约束”。

4.7 Tone and style:统一输出风格和产品感

getSimpleToneAndStyleSection() 对交互风格做了细粒度约束:

  • 默认不要用 emoji
  • 外部用户分支里要求简洁
  • 引用代码位置时使用 file_path:line_number
  • GitHub issue / PR 用 owner/repo#123
  • tool call 前不要加冒号式废话

这些小规则本身不复杂,但对产品统一感非常重要。

4.8 Output efficiency:用户体验优化,而不是模型能力本身

getOutputEfficiencySection() 明确要求:

  • 先说动作或结论
  • 状态更新要短而关键
  • 不要过度解释
  • 不要把解释塞进大表格
  • 简单问题直接回答

对当前项目而言,这说明 Claude Code 不只关注“能不能做成”,还关注“做事时输出是不是像成熟产品”。

4.9 DEFAULT_AGENT_PROMPT:子 agent 的通用基础人格

当前 prompts.ts 里定义了 DEFAULT_AGENT_PROMPT,其核心意思是:

  • 你是 Claude Code 的 agent
  • 用工具把任务做完
  • 不要半成品,也不要过度设计
  • 完成后给简洁报告

这说明主线程和子 agent 在 prompt 结构上是分层的,不是把主 prompt 原样复制给所有 worker。

5. Agent Prompt 与 built-in agents 深挖

5.1 AgentTool/prompt.ts:Agent 工具的模型侧协议说明书

src/tools/AgentTool/prompt.ts 的价值很高,因为它不是简单的 tool description,而是完整的 agent 使用协议。当前它明确规定了:

  • 可用 agent 列表如何呈现
  • 何时该用 AgentTool
  • 如何写 prompt 给子 agent
  • 前台与后台运行的差异
  • 如何继续已经启动的 agent
  • worktree / remote isolation 的含义
  • 如何并行启动多个 agent

这说明 Claude Code 的多 agent 并不是暗箱,而是明确写给模型看的协议系统。

5.2 built-in agent 的真实清单与启用边界

当前源码里存在的 built-in agents 包括:

  • general-purpose
  • statusline-setup
  • claude-code-guide
  • Explore
  • Plan
  • verification

但必须明确:当前仓库默认 feature() 恒为 false,因此源码中存在,不等于当前默认已启用。结合 src/tools/AgentTool/builtInAgents.ts,当前情况更准确地说是:

  • general-purpose:默认存在
  • statusline-setup:默认存在
  • claude-code-guide:非 SDK 入口下通常存在
  • Explore / Plan:源码存在,但默认由 gate 关闭
  • verification:源码存在,但默认由 gate 关闭

这一点在阅读当前仓库时必须牢记。

5.3 General Purpose Agent:默认工作马

general-purpose 是默认的通用 subagent。它的特点是:

  • tools: ['*']
  • 强调代码搜索、架构分析、多步任务执行
  • 要求优先研究现有代码,不主动制造文档和多余文件

它不是某个超专业角色,而是“默认通用工种”。

5.4 Explore Agent:只读搜索专家

Explore agent 的 system prompt 非常清晰:

  • 严格只读
  • 不能创建、修改、删除、移动文件
  • 不允许用重定向或 heredoc 写文件
  • Bash 仅限只读操作
  • 鼓励高效并行检索
  • 目标是尽快返回搜索结果

这个设计很有价值,因为它把“探索”从“实现”里切开了,让研究型任务不污染主线程上下文,也不带来副作用。

5.5 Plan Agent:纯规划,不做编辑

Plan agent 的 prompt 同样明确:

  • 只读
  • 不改文件
  • 先理解需求和现有架构
  • 输出 step-by-step plan
  • 最后必须给出 Critical Files for Implementation

Plan agent 的价值,不是“帮忙想一想”,而是把 architect / planner 角色从 executor 角色里剥离出来。

5.6 Verification Agent:对抗实现者偏差的独立验证者

verification agent 的 prompt 是当前源码里最强的一组约束之一。它的核心不是“帮忙跑一下测试”,而是:

你的工作不是确认实现看起来能用,而是尽量把它搞坏。

它明确写了两种失败模式:

  • verification avoidance
  • 被前 80% 的表面成功迷惑

并要求:

  • 跑 build
  • 跑 test suite
  • 跑 linter / type-check
  • 根据变更类型做专项验证
  • 做至少一个 adversarial probe
  • 每个 check 都必须有 command 和 output observed
  • 最终必须输出 VERDICT: PASS / FAIL / PARTIAL

这套 prompt 设计非常强,因为它直接针对 LLM 常见的“看代码就当验证完成了”的偷懒倾向做反制。

需要再次提醒的是:当前仓库默认 gate 下,这个 agent 的 runtime 默认并不启用;这里讨论的是其源码设计价值。

5.7 Claude Code Guide Agent:产品知识与文档问答专职 agent

claude-code-guide 不是普通 research agent,而是面向 Claude Code、Claude Agent SDK、Claude API 文档问答的专职 agent。

它的特点包括:

  • 明确分三类知识域:CLI、Agent SDK、Claude API
  • 主动使用 WebFetch / WebSearch
  • 结合本地项目中的 custom skills、custom agents、MCP server 和 user settings 做回答

这说明 built-in agents 不只是“研发过程角色”,也包含面向产品使用场景的专职角色。

5.8 Statusline Setup Agent:高度产品化的专职 agent

statusline-setup 的 prompt 详细规定了如何:

  • 读取 shell 配置文件
  • 解析 PS1
  • 转换 escape sequences
  • 更新 ~/.claude/settings.json
  • 在需要时生成 statusline 脚本

它体现出一种很重要的设计倾向:Claude Code 不只是让 agent “会写代码”,还让 agent 能承担具体产品工作流里的可复用服务角色。

6. Agent 调度链深挖:从 AgentTool 到 runAgent 再到 query

6.1 总体调用链长什么样

当前源码中,subagent 调度主链可以抽象成:

  1. 主模型决定调用 Agent 工具
  2. AgentTool.call() 解析输入参数
  3. 选择 teammate / fork / built-in / custom / background / isolation 路径
  4. 选择 agent definition
  5. 生成 prompt messages 和 system prompt
  6. 组装 agent 工具池和 permission mode
  7. 调用 runAgent()
  8. runAgent() 构造 agent-specific runtime
  9. runAgent() 内部再调用 query()
  10. query() 执行真正的模型循环、工具调用和消息流
  11. 最后写 transcript、metadata,并清理资源

这是一条完整的 subagent runtime pipeline,而不是单次函数跳转。

6.2 AgentTool.call():真正的 orchestration controller

src/tools/AgentTool/AgentTool.tsx 的 call() 做的事情远不止“把 prompt 转给子 agent”。它要处理:

  • description / prompt / subagent_type / model / run_in_background 等参数解析
  • teammate spawn 路径
  • fork 路径与 normal path 分流
  • agent deny 规则过滤
  • MCP requirement 校验
  • agent model 选择
  • background / foreground 生命周期分流
  • remote / worktree isolation
  • agent prompt 和 prompt messages 构造
  • worktree 的创建与清理

也就是说,AgentTool 本质上是一个 agent orchestration controller。

6.3 normal path 与 fork path 的区别

当前源码里 normal path 和 fork path 是两条明显不同的设计路径。

normal path
  • 明确指定 subagent_type,或在 gate 关闭时回落到 general-purpose
  • 依据 agent definition 生成 agent 自己的 system prompt
  • 用简单 user message 作为 prompt message
  • 根据 agent 权限和工具限制组装独立工具池
fork path
  • 只有在 FORK_SUBAGENT 实验开启时才会走
  • 省略 subagent_type 触发
  • 继承父线程的 system prompt 和完整 conversation context
  • 用 buildForkedMessages() 生成 placeholder tool results + directive
  • 尽量保持 cache-identical prefix

这里最关键的一点不是功能,而是目标:fork path 是为“上下文继承 + prompt cache 复用”专门设计的。

6.4 但要明确:fork 是源码里的实验设计,不是当前仓库默认 live path

src/tools/AgentTool/forkSubagent.ts 里的 fork 语义设计很成熟,包括:

  • 避免递归 fork
  • placeholder tool results 保持字节级稳定
  • 强调不要窥探 fork transcript
  • 不要预测 fork 结果
  • fork child 的输出格式和 scope 都被强限制

但当前仓库 feature() 恒为 false,所以这条路径默认不会打开。对当前项目的正确理解应该是:

  • 源码里有非常成熟的 fork 设计。
  • 当前仓库默认运行时并不启用这套实验语义。

6.5 background / foreground / worktree / remote 是不同生命周期模型

AgentTool.call() 当前还处理多种生命周期和隔离模式:

  • foreground sync path
  • async background path
  • worktree isolation
  • remote isolation
  • teammate spawn

其中:

  • background agent 会注册 async task,独立运行,完成后通知主线程
  • foreground agent 会阻塞当前流程并返回结果
  • worktree isolation 会创建临时 git worktree
  • remote isolation 走远程 CCR 路径

这说明 Claude Code 对 subagent 的理解不是“起个线程”,而是有正式生命周期和隔离模型的 worker system。

6.6 runAgent.ts:真正的 subagent runtime constructor

src/tools/AgentTool/runAgent.ts 是当前 agent 子系统最关键的文件之一。它负责:

  • 初始化 agent-specific MCP servers
  • 处理 fork context messages
  • 构造 agent-specific user/system context
  • 生成 agent-specific permission mode
  • 解析 tool pool
  • 执行 SubagentStart hooks
  • 注册 frontmatter hooks
  • 预加载 frontmatter skills
  • 合并 agent MCP tools
  • 构造 subagent ToolUseContext
  • 调 query() 进入主循环
  • 记录 transcript 与 metadata
  • 清理 MCP、hooks、shell tasks 和 tracking

这里非常重要的一个事实是:真正的 agent 执行循环并不在 runAgent.ts 本身,而是在它内部再次调用的 query() 里。

所以分层关系其实很清楚:

  • AgentTool:模式分流与调度
  • runAgent:上下文构造和生命周期管理
  • query:真正的模型循环和 tool-calling 执行器

6.7 agent-specific MCP、hooks、skills 说明 agent 本身也是可配置容器

在 runAgent.ts 中,agent 不是只有一个 system prompt。它还可以带:

  • agent-specific MCP servers
  • frontmatter hooks
  • frontmatter skills
  • memory scope
  • critical system reminder

这说明 agent 在当前代码里本身就是一种“可组合 runtime container”,而不是纯硬编码角色。

7. Skills / Plugins / Hooks / MCP 生态深挖

7.1 Skill:不是文档,而是“模型可调用的 prompt-command 抽象”

当前项目里,Skill 的最佳理解方式不是“一个 markdown 工作流文档”,而是:

一类模型可调用的 prompt-based command 抽象。

它的来源可以包括:

  • bundled skills
  • .claude/skills
  • 旧的 /commands 目录风格 prompt command
  • plugin skills
  • 当前源码里的 MCP skill plumbing

src/tools/SkillTool/prompt.ts 明确要求:

  • 用户任务匹配 skill 时,先调用 Skill tool
  • 不能只提 skill 名字不执行
  • /<something> 形式可视为 skill 调用引用
  • 当前 turn 已经带对应 command tag 时,不要重复调用

这说明 Skill 在 Claude Code 里不是附属功能,而是 first-class primitive。

7.2 Skill 的加载链比想象中更正式

当前 Skill 的加载主链主要经过:

  • src/commands.ts
  • src/skills/loadSkillsDir.ts
  • src/skills/bundledSkills.ts
  • src/tools/SkillTool/SkillTool.ts

Skill frontmatter 支持的能力很丰富,至少包括:

  • allowed-tools
  • arguments
  • argument-hint
  • when_to_use
  • version
  • model
  • effort
  • disable-model-invocation
  • user-invocable
  • hooks
  • context: fork
  • agent
  • shell
  • paths

这意味着 Skill 不只是“文本片段”,而是带有工具许可、参数化、执行上下文和 hook 能力的一类轻量工作流包。

7.3 Skill listing 不是简单罗列,而是 budget-aware 的模型可见性控制

当前 SkillTool prompt 和 attachment 体系对 skill 的模型可见性做了明确工程化控制:

  • SkillTool/prompt.ts 为 skill listing 设置字符预算
  • formatCommandsWithinBudget() 会在预算内裁剪描述
  • attachments.ts 用 skill_listing attachment 按需注入
  • skill-search 开启时,还会做 bundled + MCP 子集过滤

这说明 Claude Code 对“模型知道哪些 skill”和“上下文成本是否可控”这两个问题是一起设计的。

7.4 Plugin:平台级扩展单元,而不是单一 prompt 扩展

当前项目中的 Plugin 能力面明显比普通“CLI 插件”更重。结合 src/types/plugin.ts、src/utils/plugins/schemas.ts、src/utils/plugins/pluginLoader.ts、src/utils/plugins/loadPluginCommands.ts、src/utils/plugins/loadPluginAgents.ts 可以确认,plugin 至少能贡献:

  • commands
  • skills
  • agents
  • output styles
  • hooks
  • MCP servers
  • LSP servers
  • settings
  • commandsMetadata
  • userConfig

并且支持运行时变量替换:

  • ${CLAUDE_PLUGIN_ROOT}
  • ${CLAUDE_PLUGIN_DATA}
  • ${CLAUDE_SKILL_DIR}
  • ${CLAUDE_SESSION_ID}
  • ${user_config.KEY}

这说明 plugin 在当前架构里已经是平台级扩展单元,而不是简单的 prompt 拼接器。

7.5 Plugin 系统有明确的安全边界

一个非常重要但容易漏掉的设计点是:plugin agent 并不能在 agent frontmatter 里任意声明高权限能力。

src/utils/plugins/loadPluginAgents.ts 明确限制了 plugin agent 的这些 frontmatter 字段:

  • permissionMode
  • hooks
  • mcpServers

这些字段会被忽略并警告,因为插件是第三方安装边界,不能让某个隐藏在 plugin 里的 agent 文件悄悄提权。

因此当前项目里的 plugin 系统不是“无限制扩展”,而是带有信任边界设计的扩展平台。

7.6 Hook:不只是 tool hook,而是更宽的运行治理层

如果只看 src/services/tools/toolHooks.ts,会以为 Hook 系统只有:

  • PreToolUse
  • PostToolUse
  • PostToolUseFailure

但当前项目更宽的 Hook 框架在 src/utils/hooks.ts 中,至少还包含:

  • UserPromptSubmit
  • SubagentStart
  • InstructionsLoaded
  • stop 相关 hook

因此更准确的理解是:

  • toolHooks.ts 管的是工具执行链上的 hook。
  • utils/hooks.ts 管的是更大范围的运行时 hook 事件总线。

7.7 MCP:不只是工具桥,也是资源层和行为说明注入层

当前仓库里的 MCP 至少有三层价值:

  1. 动态工具接入:连接到 server 后获得 mcp__server__tool 形式的工具。
  2. 资源接入:通过 ListMcpResourcesTool / ReadMcpResourceTool 访问 MCP resources。
  3. 行为说明接入:connected server 的 instructions 会注入到 prompt 或 attachment。

从 src/services/mcp/types.ts 看,它支持的 transport / config 形态也相当完整:

  • stdio
  • sse
  • http
  • ws
  • sdk
  • claudeai-proxy

这说明 MCP 在当前项目里不是“外接几个函数”的薄层桥,而是完整的连接管理与能力注入平面。

7.8 Claude Code 还能反向作为 MCP server 暴露自身工具

src/entrypoints/mcp.ts 的存在说明 Claude Code 不只是 MCP client,也能作为 MCP server 对外提供工具能力。

这让它在架构上呈现出一个很重要的特征:

  • 向内,它能消费外部 MCP server。
  • 向外,它也能把自己的工具集暴露为 MCP 能力。

8. 权限、Hook、工具执行链深挖

8.1 toolExecution.ts:真正的工具 runtime 主线

当前项目的工具执行绝不是“模型决定 -> 直接调用函数”。在 src/services/tools/toolExecution.ts 里,一次 tool use 至少会经历:

  1. 找到 tool
  2. schema parse
  3. tool.validateInput
  4. Bash speculative classifier check
  5. runPreToolUseHooks
  6. resolveHookPermissionDecision
  7. canUseTool / permission flow
  8. 真正执行 tool.call()
  9. tracing / telemetry / analytics
  10. runPostToolUseHooks
  11. 失败时走 runPostToolUseFailureHooks

这是一条标准的 runtime governance pipeline。

8.2 输入校验:先挡掉低级错误和不可信参数

工具执行前的双层校验包括:

  • Zod schema parse
  • 工具自己的 validateInput

如果不通过,工具不会进入执行层,而是直接返回结构化错误结果。这一步对于降低模型乱填参数带来的运行时污染非常重要。

8.3 PreToolUse hook:最关键的拦截点

runPreToolUseHooks() 允许 hook 在工具执行前输出多种结果:

  • 普通 message
  • hook permission result
  • updatedInput
  • preventContinuation
  • stopReason
  • additional context
  • stop

最重要的三个控制能力是:

  1. updatedInput
    • hook 可以改写输入,而不一定做权限决策。
  2. permissionBehavior
    • hook 可以直接给出 allow / ask / deny。
  3. preventContinuation
    • 即便不 deny,也能阻止后续流程继续。

这使得 hook 真正参与了工具执行控制流,而不是只能做旁路日志。

8.4 resolveHookPermissionDecision():权限语义的关键粘合层

这段逻辑是 Claude Code 可治理性的关键之一。它明确保证:

  • hook allow 不自动绕过 settings 里的 deny / ask 规则
  • 需要用户交互的工具,如果 hook 没有用 updatedInput 满足交互,仍需走统一权限流
  • hook ask 会作为 forceDecision 继续传给 canUseTool
  • hook deny 直接生效

也就是说,Hook 可以参与权限模型,但不能跳出权限模型。

8.5 Post hooks:执行结束后依然可以治理 continuation 和输出

runPostToolUseHooks() 和 runPostToolUseFailureHooks() 说明 Claude Code 并不把 “tool.call 返回” 当成终点。执行完成后 hook 仍然可以:

  • 追加 message
  • 注入 additional context
  • 阻止 continuation
  • 对 MCP tool output 做更新
  • 在失败时补充恢复信息

这就是为什么它比“裸 function calling”更可治理。

8.6 还有一个容易忽略的重要层:工具并发执行语义

Claude Code 不仅治理单个工具调用,还治理多工具并发策略。

当前相关核心包括:

  • src/services/tools/toolOrchestration.ts
  • src/services/tools/StreamingToolExecutor.ts

它们做的事情包括:

  • 根据 tool.isConcurrencySafe() 把工具划分为可并发和不可并发批次
  • read-only / concurrency-safe 工具可并发执行
  • 非并发安全工具串行执行
  • 流式工具执行时保持结果回放顺序
  • 某些兄弟工具出错时,及时取消同批次剩余工具

这说明 Claude Code 的工具层不仅有 permission 和 hook,还有 concurrency semantics governance。

9. 为什么 Claude Code 这么强:从源码看它真正的护城河

9.1 它不是一个 prompt,而是多控制面的统一系统

从当前仓库看,Claude Code 的行为控制不是来自某一段神秘 system prompt,而是至少来自这几层:

  • 默认 system prompt blocks
  • effective system prompt selection
  • system/user context
  • attachment reminders / deltas
  • tool descriptions / agent descriptions / skill descriptions

这说明它的 prompt engineering 本质上是 runtime architecture,而不是单段文案设计。

9.2 它把“好行为”制度化了

Claude Code 最大的优势之一,不是模型天然更聪明,而是:

  • 不要乱加功能
  • 不要过度抽象
  • 不要在用户拒绝后原样重试工具
  • 不要未验证就宣称成功
  • 不要轻易做高风险动作
  • skill 匹配时必须调用 Skill tool
  • verification 必须有命令和输出证据

这些都不是靠模型临场发挥,而是被写进 prompt、tool protocol 和 runtime 规则里。

9.3 它非常懂“上下文是稀缺资源”

当前源码里到处都能看到上下文预算意识:

  • system prompt 动静边界
  • section cache
  • skill listing budget
  • agent listing 从 tool description 挪到 attachment
  • MCP instructions delta attachment
  • memory surfacing
  • auto-compact / microcompact / snip
  • tool use summary

这说明它不是把 token 当成免费空气,而是当成 runtime 预算来设计。

9.4 它把工具执行做成了产品级 runtime,而不是 demo

当前工具执行链里有:

  • schema 校验
  • validateInput
  • hook
  • permission
  • classifier
  • post hook
  • tracing
  • structured error/result handling
  • concurrency control

这类设计会直接体现在“手感”上。普通 agent demo 常常在 happy path 看起来还行,一到复杂场景就崩;Claude Code 的 runtime 治理层正是减少这种崩溃的关键。

9.5 它的生态是“模型可感知”的

很多系统也有插件,也有外部工具,也有 workflow,但模型本身并不知道:

  • 当前有哪些扩展
  • 什么时候应该用
  • 如何调用

Claude Code 不一样。Skill、Agent、MCP 和部分 plugin 能力会通过 prompt、attachment、tool schema 或 command registry 进入模型的可见能力空间。这一点非常关键,因为只有模型感知到了扩展,生态才真正能用起来。

9.6 它不是只关心“能运行”,还关心生命周期完整性

当前仓库里大量代码都在处理:

  • transcript
  • metadata
  • cleanup
  • background task
  • notification
  • resume
  • compaction
  • progress

这说明 Claude Code 的目标不是“做一个能调工具的聊天机器人”,而是“做一个可恢复、可治理、可扩展、可产品化的 agent runtime”。

10. 关键文件索引与后续可继续深挖方向

10.1 核心 Prompt 文件

默认 system prompt 与 section 组织
  • src/constants/prompts.ts
  • src/constants/systemPromptSections.ts
有效 prompt 选择与组合
  • src/utils/systemPrompt.ts
attachment-based 动态提示平面
  • src/utils/attachments.ts

10.2 主会话循环与上下文层

  • src/query.ts
  • src/query/config.ts
  • src/query/deps.ts
  • src/context.ts
  • src/services/api/claude.ts
  • src/utils/api.ts

10.3 核心 Agent 文件

  • src/tools/AgentTool/AgentTool.tsx
  • src/tools/AgentTool/runAgent.ts
  • src/tools/AgentTool/prompt.ts
  • src/tools/AgentTool/loadAgentsDir.ts
  • src/tools/AgentTool/builtInAgents.ts
  • src/tools/AgentTool/forkSubagent.ts
  • src/tools/AgentTool/resumeAgent.ts
built-in agents
  • src/tools/AgentTool/built-in/generalPurposeAgent.ts
  • src/tools/AgentTool/built-in/exploreAgent.ts
  • src/tools/AgentTool/built-in/planAgent.ts
  • src/tools/AgentTool/built-in/verificationAgent.ts
  • src/tools/AgentTool/built-in/claudeCodeGuideAgent.ts
  • src/tools/AgentTool/built-in/statuslineSetup.ts

10.4 核心 Skill / Plugin / Hook / MCP 文件

Skill
  • src/tools/SkillTool/SkillTool.ts
  • src/tools/SkillTool/prompt.ts
  • src/skills/loadSkillsDir.ts
  • src/skills/bundledSkills.ts
  • src/commands.ts
Plugin
  • src/types/plugin.ts
  • src/utils/plugins/schemas.ts
  • src/utils/plugins/pluginLoader.ts
  • src/utils/plugins/loadPluginCommands.ts
  • src/utils/plugins/loadPluginAgents.ts
  • src/plugins/builtinPlugins.ts
Hook
  • src/services/tools/toolHooks.ts
  • src/utils/hooks.ts
Tool execution
  • src/services/tools/toolExecution.ts
  • src/services/tools/toolOrchestration.ts
  • src/services/tools/StreamingToolExecutor.ts
MCP
  • src/services/mcp/types.ts
  • src/services/mcp/config.ts
  • src/services/mcp/client.ts
  • src/services/mcp/utils.ts
  • src/entrypoints/mcp.ts

10.5 如果继续做下一轮,优先建议这样挖

如果要进一步深挖,我建议优先级如下:

  1. src/query.ts
    • 它是 Claude Code 运行真相的核心文件,应当把完整状态机、流式模型调用、工具分支和 compact 路径全部展开。
  2. src/utils/attachments.ts
    • 这决定了很多动态指令和能力清单是如何进入模型上下文的。
  3. src/services/api/claude.ts
    • 这里能看到 prompt cache block、tool schema、betas、provider request 的真实拼装细节。
  4. src/utils/systemPrompt.ts
    • 需要把 default / custom / agent / coordinator prompt 的优先级和组合行为讲透。
  5. src/utils/hooks.ts
    • 这能补齐 tool hooks 之外的更宽事件模型。
  6. src/services/compact/*
    • Claude Code 的上下文治理优势,很大一部分就藏在这里。
  7. src/utils/permissions/*
    • 权限体系不仅决定安全,也决定产品交互体验。

10.6 最终结论

如果只保留一句话总结这份最终版研究报告:

Claude Code 的真正秘密,不是一段 system prompt,而是一套由默认 prompt、effective prompt 选择器、attachment 提示平面、query 主循环、受治理的工具执行链、agent orchestration、skills / plugins / MCP 扩展层、上下文缓存与压缩机制共同组成的产品级 agent runtime。

如果再补一句对当前仓库的限定,那就是:

阅读当前项目时,必须始终区分“源码里存在的设计能力”和“当前仓库默认运行时实际启用的能力”。只有把这两层分开,才能真正看懂 Claude Code 的架构价值。