Claude Code 源码架构总览
先看整体图
如果只从源码目录去看,很容易被大量文件吓住。
但从主干关系看,Claude Code 的架构并不乱,它大致可以抽象成下面这张图:
再看一张更贴近源码目录的分层图
第一层:启动与装配
main.tsx 的职责非常重,它不像普通 CLI 那样只是简单解析参数后执行一个函数。
它会在启动阶段做很多装配工作:
- 预热性能敏感模块
- 加载配置与托管设置
- 初始化认证、遥测、策略限制
- 初始化 MCP、LSP、插件、Skills
- 汇总命令和工具
- 根据模式启动 REPL、非交互流程或远程会话
所以 main.tsx 更像一个系统引导器。
对应源码片段
import { getSystemContext, getUserContext } from './context.js';
import { launchRepl } from './replLauncher.js';
import { getTools } from './tools.js';
import { filterCommandsForRemoteMode, getCommands } from './commands.js';
import { initializeLspServerManager } from './services/lsp/manager.js';
import { initBuiltinPlugins } from './plugins/bundled/index.js';
import { initBundledSkills } from './skills/bundled/index.js';
这段导入列表本身就很有信息量。
它说明入口层不是只拉一个 REPL,而是在同时装配:
- 上下文系统
- 工具系统
- 命令系统
- LSP
- 插件
- Skills
所以 main.tsx 在架构中的真实定位就是“装配根”。
这一层最重要的工程意义
启动层的价值不是“把东西 import 进来”,而是统一决定:
- 当前 session 是什么形态
- 哪些能力要启用
- 哪些状态要预装
- 哪些资源要在进入主循环前准备好
第二层:QueryEngine 主循环
QueryEngine.ts 是 Claude Code 的心脏。
它负责把一次用户任务转化为连续推进的执行过程。
它管理的核心对象包括:
- 消息历史
- 工具可用性
- 权限拒绝记录
- 文件缓存
- token 与成本统计
- 中断控制
- 会话级状态延续
如果没有这一层,Claude Code 就会退化成“带一些工具描述的大模型调用器”。
对应源码片段
export class QueryEngine {
private config: QueryEngineConfig
private mutableMessages: Message[]
private abortController: AbortController
private permissionDenials: SDKPermissionDenial[]
private totalUsage: NonNullableUsage
private readFileState: FileStateCache
constructor(config: QueryEngineConfig) {
this.config = config
this.mutableMessages = config.initialMessages ?? []
this.abortController = config.abortController ?? createAbortController()
this.permissionDenials = []
this.readFileState = config.readFileCache
this.totalUsage = EMPTY_USAGE
}
}
只看这些字段就能知道,QueryEngine 管的不只是“发请求给模型”,还包括:
- 历史消息
- 中断控制
- 权限拒绝
- 文件缓存
- usage 统计
这就是典型的会话级运行时,而不是一次性请求处理器。
第三层:工具系统
Tool.ts 定义工具协议,tools.ts 负责注册和筛选工具。
这层的作用,是把底层能力统一包装成模型可调用的工具接口,例如:
- 读写文件
- Bash / PowerShell
- 搜索与 glob
- MCP 资源读取
- LSP 能力调用
- AskUserQuestion
- Agent / Team / Task 相关工具
你可以把它理解为 Claude Code 的“行动层”。
对应源码片段
export function getAllBaseTools(): Tools {
return [
AgentTool,
TaskOutputTool,
BashTool,
...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
FileReadTool,
FileEditTool,
FileWriteTool,
WebFetchTool,
TodoWriteTool,
WebSearchTool,
AskUserQuestionTool,
SkillTool,
EnterPlanModeTool,
...(isEnvTruthy(process.env.ENABLE_LSP_TOOL) ? [LSPTool] : []),
ListMcpResourcesTool,
ReadMcpResourceTool,
]
}
这段代码直接说明 Claude Code 的能力不是抽象想象,而是明确注册出来的工具集合。
从这里你能非常直观地看到:
- 文件能力
- Shell 能力
- 搜索能力
- 交互能力
- Skill 能力
- LSP 与 MCP 能力
第四层:命令系统
除了模型可调用工具,Claude Code 还有大量显式命令。
commands.ts 聚合了很多斜杠命令,例如:
- 配置类
- 会话类
- 审查类
- 插件类
- MCP 类
- 计划类
- 状态与统计类
命令系统服务的是“用户显式控制”,工具系统服务的是“模型隐式执行”,两者职能不同。
第五层:上下文与状态
这套系统之所以能“像懂项目”,关键不只是工具,还包括上下文与状态:
context.ts负责准备 Git 状态、CLAUDE.md、日期等上下文AppStateStore.ts管理 REPL、任务、通知、远程连接、MCP、插件等 UI 与会话状态
一个负责“给模型看什么”,一个负责“界面和会话当前处于什么状态”。
对应源码片段
export const getSystemContext = memoize(async (): Promise<{ [k: string]: string }> => {
const gitStatus =
isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) ||
!shouldIncludeGitInstructions()
? null
: await getGitStatus()
return {
...(gitStatus && { gitStatus }),
}
})
这里能看出一个关键事实:
Claude Code 会主动把 Git 这样的工程上下文注入到后续对话里,这就是它“看起来懂项目”的重要原因之一。
第六层:扩展能力
从目录结构能看到,Claude Code 早就不是一个封闭工具,而是平台化形态:
services/mcp/*services/lsp/*plugins/*skills/*tools/AgentTool/*remote/*
这些模块意味着 Claude Code 不只是执行内置工具,而是在持续向“可扩展工程智能体平台”演化。
目录体量为什么会这么大
当你看到这个仓库目录很多时,先不要急着认为它“设计很乱”。
更合理的理解是:Claude Code 同时承担了四类系统职责:
- 终端交互应用
- Agent 运行时
- 工具与命令平台
- 外部扩展集成层
把这四类职责叠起来,目录自然不会小。
架构主线与次要分支怎么区分
阅读时,建议把目录分成两类:
主干文件
main.tsxQueryEngine.tsTool.tstools.tscommands.tscontext.tsstate/AppStateStore.ts
延展文件
services/*components/*commands/*tools/*plugins/*skills/*remote/*
先抓住主干,再看延展,效率会高很多。
阅读源码时的正确顺序
如果你直接从海量目录乱翻,很容易迷失。
更推荐的顺序是:
main.tsxQueryEngine.tsTool.tstools.tscommands.tscontext.tsstate/AppStateStore.ts- 再进入
services/mcp、services/lsp、plugins、skills
这条线更接近系统真正的骨架。
小结
Claude Code 的源码架构可以概括成一句话:
用
main.tsx把配置、命令、工具、上下文和扩展能力装配起来,再由QueryEngine驱动整个工程任务循环。
后面几篇文章,我们会沿着这条主干逐步往里拆。