Claude Code 的架构围绕三个核心概念展开:工具(Tools)提供与环境交互的能力,命令(Commands)为用户提供结构化的操作入口,而会话生命周期(Session Lifecycle)则管理整个对话的状态流转。理解这三个概念及其交互方式,是掌握 Claude Code 架构的关键。本文将深入剖析每个概念的设计哲学、实现机制以及它们如何协同工作,为后续的架构分析奠定坚实基础。
Sources: Tool.ts, command.ts, QueryEngine.ts
工具系统:环境交互的原语
工具是 Claude Code 与外部世界交互的基本单元。每个工具都封装了特定领域的能力,从文件操作(FileReadTool、FileEditTool)到命令执行(BashTool),再到复杂的子代理协作(AgentTool)。工具系统的设计遵循”最小权限原则”——每个工具必须显式声明其权限需求,并通过严格的权限验证流程才能执行。
工具接口设计
工具的核心接口定义在 Tool<Input, Output, P> 泛型类型中,包含以下关键组件:
type Tool<Input, Output, P> = {
name: string // 工具名称
inputSchema: Input // Zod schema 定义输入格式
call(args, context, ...) // 执行逻辑
checkPermissions(input, ctx) // 权限检查
prompt(options) // 生成工具描述供模型理解
renderToolUseMessage(...) // UI 渲染
isConcurrencySafe(input) // 是否支持并发
isReadOnly(input) // 是否只读操作
isDestructive?(input) // 是否破坏性操作
// ... 其他方法
}
工具通过 buildTool() 辅助函数创建,该函数为常用方法提供安全默认值:isEnabled() 默认返回 true,isConcurrencySafe() 默认返回 false(保守假设不支持并发),isReadOnly() 默认返回 false,而 checkPermissions() 默认委托给通用权限系统。这种设计既保证了类型安全,又减少了样板代码。
权限验证流程
工具执行的权限验证采用分层设计:首先调用 validateInput() 验证输入合法性(例如路径是否存在、参数格式是否正确),然后调用 checkPermissions() 进行权限决策。权限结果 PermissionResult 包含 behavior 字段,取值为 'allow'(允许执行)、'deny'(拒绝执行)或 'prompt'(需要用户确认)。工具可以自定义权限逻辑,例如 FileReadTool 会检查路径是否在允许的工作目录范围内。
权限上下文 ToolPermissionContext 包含当前权限模式(PermissionMode)、额外工作目录映射、以及三类规则集:alwaysAllowRules(始终允许)、alwaysDenyRules(始终拒绝)、alwaysAskRules(始终询问)。这些规则可以来自用户配置、项目设置或插件,形成灵活的权限治理体系。
工具类型与示例
Claude Code 内置了 30+ 工具,覆盖文件操作、代码分析、网络请求、任务管理等场景。下表展示了核心工具的分类和职责:
| 工具类别 | 代表工具 | 核心职责 | 并发安全 | 破坏性 |
|---|---|---|---|---|
| 文件操作 | FileReadTool | 读取文件内容、图片、PDF | ✅ | ❌ |
| 文件操作 | FileEditTool | 编辑文件(字符串替换) | ❌ | ✅ |
| 文件操作 | FileWriteTool | 写入新文件或覆盖现有文件 | ❌ | ✅ |
| 命令执行 | BashTool | 执行 shell 命令 | ❌ | 取决于命令 |
| 代码搜索 | GrepTool | 使用 ripgrep 搜索内容 | ✅ | ❌ |
| 代码搜索 | GlobTool | 文件名模式匹配 | ✅ | ❌ |
| 子代理 | AgentTool | 启动子代理执行复杂任务 | ❌ | 取决于子任务 |
| 任务管理 | TaskCreateTool | 创建后台任务 | ✅ | ❌ |
| LSP 集成 | LSPTool | 语言服务器协议查询 | ✅ | ❌ |
| 网络访问 | WebFetchTool | 抓取网页内容 | ✅ | ❌ |
工具注册发生在 src/tools.ts 中,该文件导入所有内置工具并根据特性标志(feature flags)条件加载某些工具。工具集合以 Tools 类型(readonly Tool[])传递给查询引擎和 REPL 界面。
命令系统:用户操作的结构化入口
命令是用户通过斜杠语法(如 /commit、/review)触发的操作单元。与工具不同,命令面向用户交互而非模型调用,它们可以执行本地逻辑、生成提示词、或启动完整的 React UI 界面。命令系统支持别名、可用性限制、动态启用/禁用等高级特性,使其成为扩展 Claude Code 功能的主要途径。
命令类型体系
命令类型 Command 是联合类型,包含三种形态:
graph TD
A[Command] --> B[PromptCommand]
A --> C[LocalCommand]
A --> D[LocalJSXCommand]
B --> E[生成提示词<br/>模型执行]
C --> F[本地函数调用<br/>返回文本/压缩结果]
D --> G[React UI 渲染<br/>交互式界面]
style A fill:#e1f5ff
style B fill:#fff4e6
style C fill:#f0f9ff
style D fill:#fef3f2
PromptCommand 生成发送给模型的提示词,适用于需要模型推理的场景(如 /compact 生成压缩提示)。LocalCommand 直接执行 TypeScript 函数并返回结果(如 /clear 清空消息历史)。LocalJSXCommand 渲染完整的 React 组件树,用于复杂交互界面(如 /config 显示设置面板、/doctor 运行诊断检查)。
命令定义包含基础信息(name、description、aliases)和行为配置(isEnabled()、isHidden()、availability)。availability 字段限制命令在特定认证环境下可用,例如 'claude-ai' 表示仅对 Claude.ai 订阅者可见,'console' 表示仅对直接使用 API Key 的用户可见。isEnabled() 方法支持动态启用逻辑,例如 /voice 命令仅在 VOICE_MODE 特性标志启用时返回 true。
Sources: command.ts, command.ts
命令注册与加载
所有命令在 src/commands.ts 中集中注册。该文件导入内置命令(如 commit、review、compact),并根据特性标志条件加载实验性命令(如 ultraplan、workflows)。命令加载采用延迟策略:LocalCommand 和 LocalJSXCommand 的 load() 方法返回 Promise,只在命令被调用时才加载实现模块,减少启动时间。
命令来源通过 loadedFrom 字段标识:'builtin'(内置)、'bundled'(捆绑技能)、'plugin'(插件提供)、'mcp'(MCP 服务器)、'skills'(技能目录)、'managed'(托管配置)。formatDescriptionWithSource() 函数在用户界面中标注命令来源,帮助用户理解命令的来源和可信度。
// 命令查找示例
export function getCommand(commandName: string, commands: Command[]): Command {
const command = findCommand(commandName, commands)
if (!command) {
throw ReferenceError(
`Command ${commandName} not found. Available commands: ${commands
.map(_ => getCommandName(_))
.sort()
.join(', ')}`
)
}
return command
}
命令查找支持别名匹配,findCommand() 会检查主名称和 aliases 数组。hasCommand() 函数用于检查命令是否存在,常用于权限验证或 UI 条件渲染。
Sources: commands.ts, commands.ts
命令执行流程
命令通过 processUserInput() 函数解析和执行。当用户输入以 / 开头时,解析器提取命令名称和参数,查找对应的 Command 对象,然后根据命令类型分发执行:
- PromptCommand:调用
getPromptForCommand(args, context)生成ContentBlockParam[],这些内容块作为用户消息附加到对话历史中,随后触发模型查询 - LocalCommand:调用
load().then(module => module.call(args, context)),返回的LocalCommandResult可以是文本、压缩结果或跳过标记 - LocalJSXCommand:调用
load().then(module => module.call(onDone, context, args)),返回的 React 节点通过setToolJSX()渲染到界面,命令完成后调用onDone(result, options)通知系统
命令执行结果可以触发后续查询(shouldQuery: true),插入元消息(metaMessages),或自动提交下一条输入(nextInput + submitNextInput)。这种设计支持命令链式调用和自动化工作流。
Sources: command.ts, QueryEngine.ts
会话生命周期:状态持久化与流转管理
会话生命周期管理从用户输入到模型响应的完整流程,包括消息历史的持久化、权限状态的追踪、后台任务的调度以及会话恢复机制。QueryEngine 类是会话生命周期的核心编排者,它封装了查询循环、状态管理和事件流生成,支持 REPL 交互模式和 SDK 无头模式。
QueryEngine 架构
QueryEngine 是有状态的长期对象,每个对话实例对应一个 QueryEngine 实例。构造函数接收 QueryEngineConfig,包含工具列表、命令列表、MCP 客户端、状态访问器(getAppState/setAppState)以及可选的初始消息和自定义配置。引擎内部维护以下状态:
class QueryEngine {
private config: QueryEngineConfig
private mutableMessages: Message[] // 对话历史
private abortController: AbortController // 取消控制
private permissionDenials: SDKPermissionDenial[] // 权限拒绝记录
private totalUsage: NonNullableUsage // Token 使用统计
private readFileState: FileStateCache // 文件读取缓存
private discoveredSkillNames: Set<string> // 技能发现追踪
private loadedNestedMemoryPaths: Set<string> // 嵌套记忆路径
}
submitMessage(prompt, options) 方法是对话的主要入口,它是一个异步生成器,每次 yield 一个 SDKMessage 事件(包括用户消息、助手消息、工具调用、权限拒绝、压缩边界等)。这种流式设计允许调用方实时处理事件,而不需要等待整个查询完成。
Sources: QueryEngine.ts, QueryEngine.ts
消息处理管道
submitMessage() 的执行流程包含以下阶段:
graph TD
A[用户输入 prompt] --> B[processUserInput<br/>解析斜杠命令]
B --> C{命令类型?}
C -->|PromptCommand| D[生成提示词<br/>附加到消息历史]
C -->|LocalCommand| E[执行本地函数<br/>返回结果]
C -->|LocalJSXCommand| F[渲染 React UI<br/>等待交互完成]
D --> G[recordTranscript<br/>持久化到 JSONL]
E --> G
F --> G
G --> H[query 循环<br/>调用 Anthropic API]
H --> I{响应类型?}
I -->|文本块| J[yield assistant message]
I -->|工具调用| K[执行工具<br/>权限验证]
K --> L{权限决策}
L -->|allow| M[tool.call 执行]
L -->|deny| N[yield permission denial]
L -->|prompt| O[显示权限对话框]
O --> P{用户选择}
P -->|允许| M
P -->|拒绝| N
M --> Q[yield tool result]
Q --> H
style A fill:#e8f5e9
style H fill:#fff3e0
style K fill:#fce4ec
style G fill:#f3e5f5
关键步骤说明:
-
用户输入处理:
processUserInput()解析斜杠命令、处理文件附件、注入上下文提示(如 CLAUDE.md 内容)。如果是命令调用,执行对应逻辑;如果是普通文本,构造UserMessage并添加到mutableMessages数组。 -
持久化:在进入查询循环前,调用
recordTranscript(messages)将当前消息历史写入会话 JSONL 文件。这确保即使进程在 API 响应前崩溃(用户点击停止或网络超时),会话仍然可以通过--resume恢复。在--bare模式下,持久化是异步的(fire-and-forget),减少阻塞。 -
查询循环:
query()函数构造 API 请求(包括系统提示、工具定义、消息历史),流式调用 Anthropic Messages API,并处理响应块。文本块直接 yield,工具调用触发执行管道。 -
工具执行管道:对于每个
tool_use块,首先调用canUseTool()进行权限检查(包括钩子预处理、分类器评估、用户确认)。如果权限允许,调用tool.call()执行工具逻辑,生成ToolResult。工具结果通过mapToolResultToToolResultBlockParam()转换为 API 格式,添加到消息历史,并继续查询循环。 -
状态更新:查询循环中持续更新使用统计(
totalUsage)、权限拒绝记录(permissionDenials)、文件缓存(readFileState),并通过setAppState()同步到全局状态供 UI 渲染。
Sources: QueryEngine.ts, QueryEngine.ts
会话持久化与恢复
会话状态通过多个机制持久化:
-
JSONL 转录文件:位于
~/.claude/projects/<project-hash>/<session-id>.jsonl,每行是一个Message对象的 JSON 序列化。recordTranscript()追加新消息,flushSessionStorage()确保数据写入磁盘。 -
会话历史 API:
fetchLatestEvents()和fetchOlderEvents()从远程服务器分页获取历史事件,支持在 Claude.ai 网页界面查看会话记录。这些函数使用anchor_to_latest和before_id游标实现双向分页。 -
全局状态:
src/bootstrap/state.ts维护跨会话的状态,包括累计成本(totalCostUSD)、API 调用时长(totalAPIDuration)、模型使用统计(modelUsage)、会话 ID(sessionId)等。这些状态在进程启动时初始化,在会话期间持续更新。
会话恢复通过 --resume 标志触发。getLastSessionLog() 读取最近的 JSONL 文件,过滤出可恢复的消息(排除队列操作、系统通知等),重构 Message[] 数组并传递给 QueryEngine 构造函数的 initialMessages 参数。恢复的会话从最后一条用户消息继续,保持完整的上下文和状态。
Sources: sessionHistory.ts, bootstrap/state.ts
后台任务与并发管理
用户可以通过双击 Ctrl+B 将当前查询移至后台,这会创建 LocalMainSessionTask 任务。后台任务在独立的事件循环中运行查询,完成后发送系统通知。任务状态通过 registerTask() 注册到全局任务框架,支持通过 /tasks 命令查看和管理。
QueryEngine 支持并发安全控制:如果工具的 isConcurrencySafe() 返回 false,引擎会等待该工具完成后再处理下一个工具调用。interruptBehavior() 方法定义了新消息到达时的行为:'cancel' 立即停止工具执行,'block' 等待工具完成后再处理新消息(默认)。
// LocalMainSessionTask 状态定义
export type LocalMainSessionTaskState = LocalAgentTaskState & {
agentType: 'main-session'
}
export function registerMainSessionTask(
description: string,
setAppState: SetAppState,
mainThreadAgentDefinition?: AgentDefinition,
existingAbortController?: AbortController,
): { taskId: string; abortSignal: AbortSignal }
后台任务的 ID 以 's' 前缀标识(区别于代理任务的 'a' 前缀),状态包括 description、status('pending' | 'running' | 'completed' | 'failed')、startTime、endTime、result 等字段。
Sources: LocalMainSessionTask.ts, Tool.ts
三者协同:从用户输入到环境变更
工具、命令和会话生命周期不是孤立的概念,而是紧密协作的系统。下图展示了从用户输入到环境变更的完整数据流:
graph TD
User[用户输入] --> Input{输入类型}
Input -->|普通文本| Msg[创建 UserMessage]
Input -->|斜杠命令 /| Cmd[查找 Command]
Cmd --> CmdType{命令类型}
CmdType -->|PromptCommand| PromptGen[getPromptForCommand]
CmdType -->|LocalCommand| LocalExec[module.call 执行]
CmdType -->|LocalJSXCommand| JSXRender[渲染 React UI]
PromptGen --> Msg
LocalExec --> Result[返回结果]
JSXRender --> Result
Result --> Msg
Msg --> Persist[recordTranscript<br/>持久化到 JSONL]
Persist --> QueryEngine[QueryEngine.submitMessage]
QueryEngine --> API[调用 Anthropic API]
API --> Response{响应类型}
Response -->|文本| TextMsg[yield assistant message]
Response -->|工具调用| ToolUse[解析 tool_use 块]
ToolUse --> PermCheck{canUseTool<br/>权限检查}
PermCheck -->|allow| ToolExec[tool.call 执行]
PermCheck -->|deny| PermDenial[yield permission denial]
PermCheck -->|prompt| UserPrompt[显示权限对话框]
UserPrompt --> UserChoice{用户选择}
UserChoice -->|允许| ToolExec
UserChoice -->|拒绝| PermDenial
ToolExec --> ToolResult[生成 ToolResult]
ToolResult --> UpdateState[更新 AppState<br/>文件缓存/使用统计]
ToolResult --> AddMsg[添加 tool_result 到消息历史]
AddMsg --> API
TextMsg --> UI[REPL UI 渲染]
PermDenial --> UI
ToolExec --> UI
UpdateState --> EnvChange[环境变更<br/>文件/网络/进程]
style User fill:#e8f5e9
style QueryEngine fill:#e1f5ff
style ToolExec fill:#fff4e6
style EnvChange fill:#fce4ec
这个流程展示了三个核心概念如何协作:
-
命令作为用户入口:用户通过斜杠命令触发特定操作,命令系统解析输入、查找命令定义、执行对应逻辑(生成提示词、执行本地函数或渲染 UI),最终将结果转换为
Message对象 -
QueryEngine 作为编排中心:引擎接收消息后,负责持久化、构造 API 请求、处理响应流、协调工具执行,并通过
AsyncGenerator向上层(REPL 或 SDK)推送事件 -
工具作为环境接口:当模型决定调用工具时,权限系统首先验证操作合法性,然后工具执行产生环境变更(文件修改、命令执行、网络请求),结果返回给模型继续推理
三者通过 ToolUseContext 上下文对象连接:该对象包含工具列表(tools)、命令列表(commands)、状态访问器(getAppState/setAppState)、文件缓存(readFileState)、AbortController 等共享资源。命令执行和工具调用都接收 ToolUseContext,使其能够访问完整的会话状态和配置。
Sources: Tool.ts, QueryEngine.ts
下一步学习路径
掌握工具、命令和会话生命周期这三个核心概念后,您可以按照以下路径深入探索:
架构设计层面
- 整体架构:从 CLI 入口到 QueryEngine:了解
main.tsx如何初始化这三个系统,以及它们在整个架构中的位置 - 状态管理:AppState 与 React Context 体系:深入理解
AppState如何在工具、命令和会话之间共享状态 - 查询引擎:QueryEngine 对话生命周期管理:完整剖析
QueryEngine的实现细节,包括压缩、缓存、错误恢复
工具系统深入
- 工具架构:Tool 接口与权限模型:详细解析工具接口设计、权限验证流程、并发控制机制
- 文件操作工具:读、写、编辑的实现细节:以 FileReadTool、FileEditTool 为例,展示工具实现的最佳实践
- BashTool:命令执行与安全沙箱:了解 BashTool 如何安全地执行 shell 命令,包括沙箱隔离和权限控制
- AgentTool:子代理与多代理协作:探索 AgentTool 如何启动子代理,实现任务分解和并行执行
命令系统实践
- 命令架构:Command 接口与注册机制:学习如何实现自定义命令,包括 PromptCommand、LocalCommand 和 LocalJSXCommand
- 核心命令:commit、review、compact 实现解析:通过实际案例理解命令的设计模式和最佳实践
这三个核心概念是 Claude Code 架构的基石,理解它们的协作方式将为您深入源码、贡献代码或构建自定义扩展奠定坚实基础。建议按照上述路径逐步深入,每个页面都会在前一个基础上展开更详细的实现分析。