Claude Code Wiki
首页 入门指南

核心概念:工具、命令与会话生命周期

中级

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() 默认返回 trueisConcurrencySafe() 默认返回 false(保守假设不支持并发),isReadOnly() 默认返回 false,而 checkPermissions() 默认委托给通用权限系统。这种设计既保证了类型安全,又减少了样板代码。

Sources: Tool.ts, Tool.ts

权限验证流程

工具执行的权限验证采用分层设计:首先调用 validateInput() 验证输入合法性(例如路径是否存在、参数格式是否正确),然后调用 checkPermissions() 进行权限决策。权限结果 PermissionResult 包含 behavior 字段,取值为 'allow'(允许执行)、'deny'(拒绝执行)或 'prompt'(需要用户确认)。工具可以自定义权限逻辑,例如 FileReadTool 会检查路径是否在允许的工作目录范围内。

权限上下文 ToolPermissionContext 包含当前权限模式(PermissionMode)、额外工作目录映射、以及三类规则集:alwaysAllowRules(始终允许)、alwaysDenyRules(始终拒绝)、alwaysAskRules(始终询问)。这些规则可以来自用户配置、项目设置或插件,形成灵活的权限治理体系。

Sources: Tool.ts, Tool.ts

工具类型与示例

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 界面。

Sources: tools.ts, Tool.ts

命令系统:用户操作的结构化入口

命令是用户通过斜杠语法(如 /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 运行诊断检查)。

命令定义包含基础信息(namedescriptionaliases)和行为配置(isEnabled()isHidden()availability)。availability 字段限制命令在特定认证环境下可用,例如 'claude-ai' 表示仅对 Claude.ai 订阅者可见,'console' 表示仅对直接使用 API Key 的用户可见。isEnabled() 方法支持动态启用逻辑,例如 /voice 命令仅在 VOICE_MODE 特性标志启用时返回 true

Sources: command.ts, command.ts

命令注册与加载

所有命令在 src/commands.ts 中集中注册。该文件导入内置命令(如 commitreviewcompact),并根据特性标志条件加载实验性命令(如 ultraplanworkflows)。命令加载采用延迟策略:LocalCommandLocalJSXCommandload() 方法返回 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

关键步骤说明:

  1. 用户输入处理processUserInput() 解析斜杠命令、处理文件附件、注入上下文提示(如 CLAUDE.md 内容)。如果是命令调用,执行对应逻辑;如果是普通文本,构造 UserMessage 并添加到 mutableMessages 数组。

  2. 持久化:在进入查询循环前,调用 recordTranscript(messages) 将当前消息历史写入会话 JSONL 文件。这确保即使进程在 API 响应前崩溃(用户点击停止或网络超时),会话仍然可以通过 --resume 恢复。在 --bare 模式下,持久化是异步的(fire-and-forget),减少阻塞。

  3. 查询循环query() 函数构造 API 请求(包括系统提示、工具定义、消息历史),流式调用 Anthropic Messages API,并处理响应块。文本块直接 yield,工具调用触发执行管道。

  4. 工具执行管道:对于每个 tool_use 块,首先调用 canUseTool() 进行权限检查(包括钩子预处理、分类器评估、用户确认)。如果权限允许,调用 tool.call() 执行工具逻辑,生成 ToolResult。工具结果通过 mapToolResultToToolResultBlockParam() 转换为 API 格式,添加到消息历史,并继续查询循环。

  5. 状态更新:查询循环中持续更新使用统计(totalUsage)、权限拒绝记录(permissionDenials)、文件缓存(readFileState),并通过 setAppState() 同步到全局状态供 UI 渲染。

Sources: QueryEngine.ts, QueryEngine.ts

会话持久化与恢复

会话状态通过多个机制持久化:

  • JSONL 转录文件:位于 ~/.claude/projects/<project-hash>/<session-id>.jsonl,每行是一个 Message 对象的 JSON 序列化。recordTranscript() 追加新消息,flushSessionStorage() 确保数据写入磁盘。

  • 会话历史 APIfetchLatestEvents()fetchOlderEvents() 从远程服务器分页获取历史事件,支持在 Claude.ai 网页界面查看会话记录。这些函数使用 anchor_to_latestbefore_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' 前缀),状态包括 descriptionstatus'pending' | 'running' | 'completed' | 'failed')、startTimeendTimeresult 等字段。

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

这个流程展示了三个核心概念如何协作:

  1. 命令作为用户入口:用户通过斜杠命令触发特定操作,命令系统解析输入、查找命令定义、执行对应逻辑(生成提示词、执行本地函数或渲染 UI),最终将结果转换为 Message 对象

  2. QueryEngine 作为编排中心:引擎接收消息后,负责持久化、构造 API 请求、处理响应流、协调工具执行,并通过 AsyncGenerator 向上层(REPL 或 SDK)推送事件

  3. 工具作为环境接口:当模型决定调用工具时,权限系统首先验证操作合法性,然后工具执行产生环境变更(文件修改、命令执行、网络请求),结果返回给模型继续推理

三者通过 ToolUseContext 上下文对象连接:该对象包含工具列表(tools)、命令列表(commands)、状态访问器(getAppState/setAppState)、文件缓存(readFileState)、AbortController 等共享资源。命令执行和工具调用都接收 ToolUseContext,使其能够访问完整的会话状态和配置。

Sources: Tool.ts, QueryEngine.ts

下一步学习路径

掌握工具、命令和会话生命周期这三个核心概念后,您可以按照以下路径深入探索:

架构设计层面

工具系统深入

命令系统实践

这三个核心概念是 Claude Code 架构的基石,理解它们的协作方式将为您深入源码、贡献代码或构建自定义扩展奠定坚实基础。建议按照上述路径逐步深入,每个页面都会在前一个基础上展开更详细的实现分析。