Files
claude-code-mirror/claude-code-中文Wiki/02-核心模块详解.md
2026-04-03 13:01:19 +08:00

814 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 核心模块详解
本文件说明:详细分析 Claude Code 的核心源文件,理解其实现原理。
## 1. main.tsx - CLI 入口点
### 1.1 功能概述
`main.tsx` 是 Claude Code 应用程序的入口点,负责:
- CLI 参数解析
- 应用程序初始化
- REPLRead-Eval-Print Loop启动
- 各种启动前检查和配置
### 1.2 启动流程
```typescript
// 1. 启动性能分析
profileCheckpoint('main_tsx_entry')
// 2. MDM 设置预读取(并行)
startMdmRawRead()
// 3. Keychain 凭据预读取(并行)
startKeychainPrefetch()
// 4. 初始化遥测
initializeTelemetryAfterTrust()
// 5. 获取引导数据
const bootstrapData = await fetchBootstrapData()
// 6. 启动 REPL
launchRepl()
```
### 1.3 关键导入
```typescript
import { Command as CommanderCommand } from '@commander-js/extra-typings'
import React from 'react'
import chalk from 'chalk'
import { getTools } from './tools.js'
import { getCommands, filterCommandsForRemoteMode } from './commands.js'
import { getSystemContext, getUserContext } from './context.js'
```
### 1.4 核心配置初始化
```typescript
// 权限模式设置
const initialPermissionMode = await initialPermissionModeFromCLI()
// 工具权限上下文初始化
initializeToolPermissionContext({
mode: initialPermissionMode,
additionalWorkingDirectories: new Map(),
alwaysAllowRules: {},
alwaysDenyRules: {},
alwaysAskRules: {},
isBypassPermissionsModeAvailable: false,
})
// 获取工具列表
const tools = getTools(appState.toolPermissionContext)
// 获取命令列表
const commands = await getCommands(cwd)
```
## 2. QueryEngine.ts - LLM 查询引擎核心
### 2.1 功能概述
`QueryEngine` 是 Claude Code 的核心查询处理类,负责:
- 管理对话生命周期(每个对话一个 QueryEngine 实例)
- 处理用户消息提交
- 执行 API 调用循环
- 处理流式响应
- 编排工具执行
- 管理会话状态消息、Token 使用等)
### 2.2 类结构
```typescript
export class QueryEngine {
private config: QueryEngineConfig
private mutableMessages: Message[] // 对话消息列表
private abortController: AbortController // 中断控制器
private permissionDenials: SDKPermissionDenial[] // 权限拒绝记录
private totalUsage: NonNullableUsage // 累计 Token 使用
private hasHandledOrphanedPermission = false
private readFileState: FileStateCache // 文件读取缓存
private discoveredSkillNames = new Set<string>()
private loadedNestedMemoryPaths = new Set<string>()
}
```
### 2.3 核心方法submitMessage()
这是处理用户输入的主要方法:
```typescript
async *submitMessage(
prompt: string | ContentBlockParam[],
options?: { uuid?: string; isMeta?: boolean },
): AsyncGenerator<SDKMessage, void, unknown>
```
**执行流程**
1. **初始化阶段**
```typescript
this.discoveredSkillNames.clear()
setCwd(cwd)
// 获取系统提示词
const { defaultSystemPrompt, userContext, systemContext } =
await fetchSystemPromptParts({ tools, mainLoopModel, ... })
```
2. **处理用户输入**
```typescript
const { messages, shouldQuery, allowedTools, model, resultText } =
await processUserInput({
input: prompt,
mode: 'prompt',
context: processUserInputContext,
})
// 更新权限规则
setAppState(prev => ({
...prev,
toolPermissionContext: {
...prev.toolPermissionContext,
alwaysAllowRules: { command: allowedTools }
}
}))
```
3. **构建系统提示**
```typescript
const systemPrompt = asSystemPrompt([
...(customPrompt ?? defaultSystemPrompt),
...(memoryMechanicsPrompt ?? []), // 记忆机制提示
...(appendSystemPrompt ?? [])
])
```
4. **查询循环**
```typescript
for await (const message of query({
messages,
systemPrompt,
userContext,
systemContext,
canUseTool: wrappedCanUseTool,
})) {
// 处理各种消息类型
switch (message.type) {
case 'assistant': /* 记录助手消息 */ break
case 'progress': /* 进度更新 */ break
case 'user': /* 用户消息 */ break
case 'stream_event': /* 流式事件 */ break
case 'attachment': /* 附件处理 */ break
}
}
```
### 2.4 流式响应处理
QueryEngine 处理多种流式事件:
```typescript
if (message.type === 'stream_event') {
// message_start: 新消息开始
if (message.event.type === 'message_start') {
currentMessageUsage = EMPTY_USAGE
currentMessageUsage = updateUsage(currentMessageUsage, message.event.message.usage)
}
// message_delta: 消息增量更新
if (message.event.type === 'message_delta') {
currentMessageUsage = updateUsage(currentMessageUsage, message.event.usage)
if (message.event.delta.stop_reason != null) {
lastStopReason = message.event.delta.stop_reason
}
}
// message_stop: 消息结束
if (message.event.type === 'message_stop') {
this.totalUsage = accumulateUsage(this.totalUsage, currentMessageUsage)
}
}
```
### 2.5 思考模式 (Thinking Mode)
Claude Code 支持 Claude 的思考模式:
```typescript
const initialThinkingConfig: ThinkingConfig = thinkingConfig
? thinkingConfig
: shouldEnableThinkingByDefault() !== false
? { type: 'adaptive' } // 自适应思考
: { type: 'disabled' } // 禁用
```
思考规则(注释中的说明):
1. 包含思考或编辑块的消息必须在 `max_thinking_length > 0` 的查询中
2. 思考块不能是消息块中的最后一个
3. 思考块必须在整个助手轨迹中保留
### 2.6 错误恢复机制
```typescript
// USD 预算超限检查
if (maxBudgetUsd !== undefined && getTotalCost() >= maxBudgetUsd) {
yield {
type: 'result',
subtype: 'error_max_budget_usd',
...
}
return
}
// 最大轮次检查
if (maxTurns && nextTurnCount > maxTurns) {
yield { type: 'result', subtype: 'error_max_turns', ... }
return
}
// 结构化输出重试限制
if (callsThisQuery >= maxRetries) {
yield { type: 'result', subtype: 'error_max_structured_output_retries', ... }
return
}
```
## 3. Tool.ts - 工具基类和接口定义
### 3.1 工具接口定义
```typescript
export type Tool<
Input extends AnyObject = AnyObject,
Output = unknown,
P extends ToolProgressData = ToolProgressData,
> = {
// 工具名称
readonly name: string
// 可选别名(用于工具重命名后的向后兼容)
aliases?: string[]
// 工具搜索提示词
searchHint?: string
// 核心执行方法
call(
args: z.infer<Input>,
context: ToolUseContext,
canUseTool: CanUseToolFn,
parentMessage: AssistantMessage,
onProgress?: ToolCallProgress<P>,
): Promise<ToolResult<Output>>
// 获取工具描述
description(
input: z.infer<Input>,
options: {...}
): Promise<string>
// 输入 Schema
readonly inputSchema: Input
// 输入 JSON Schema用于 MCP 工具)
readonly inputJSONSchema?: ToolInputJSONSchema
// 输出 Schema
outputSchema?: z.ZodType<unknown>
}
```
### 3.2 工具权限方法
```typescript
// 验证输入是否有效
validateInput?(
input: z.infer<Input>,
context: ToolUseContext,
): Promise<ValidationResult>
// 检查权限
checkPermissions(
input: z.infer<Input>,
context: ToolUseContext,
): Promise<PermissionResult>
// 准备权限匹配器
preparePermissionMatcher?(
input: z.infer<Input>,
): Promise<(pattern: string) => boolean>
```
### 3.3 工具渲染方法
```typescript
// 渲染工具使用消息
renderToolUseMessage(
input: Partial<z.infer<Input>>,
options: { theme: ThemeName; verbose: boolean; commands?: Command[] }
): React.ReactNode
// 渲染工具结果消息
renderToolResultMessage?(
content: Output,
progressMessagesForMessage: ProgressMessage<P>[],
options: {...}
): React.ReactNode
// 渲染工具使用标签
renderToolUseTag?(input: Partial<z.infer<Input>>): React.ReactNode
// 渲染进度消息
renderToolUseProgressMessage?(
progressMessagesForMessage: ProgressMessage<P>[],
options: {...}
): React.ReactNode
```
### 3.4 buildTool 工厂函数
```typescript
const TOOL_DEFAULTS = {
isEnabled: () => true,
isConcurrencySafe: (_input?: unknown) => false,
isReadOnly: (_input?: unknown) => false,
isDestructive: (_input?: unknown) => false,
checkPermissions: async () => ({ behavior: 'allow', updatedInput: input }),
toAutoClassifierInput: (_input?: unknown) => '',
userFacingName: (_input?: unknown) => '',
}
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
return {
...TOOL_DEFAULTS,
userFacingName: () => def.name,
...def,
} as BuiltTool<D>
}
```
### 3.5 工具权限上下文
```typescript
export type ToolPermissionContext = {
mode: PermissionMode // 'default' | 'auto' | 'bypass' | 'plan'
additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>
alwaysAllowRules: ToolPermissionRulesBySource
alwaysDenyRules: ToolPermissionRulesBySource
alwaysAskRules: ToolPermissionRulesBySource
isBypassPermissionsModeAvailable: boolean
isAutoModeAvailable?: boolean
strippedDangerousRules?: ToolPermissionRulesBySource
shouldAvoidPermissionPrompts?: boolean
awaitAutomatedChecksBeforeDialog?: boolean
prePlanMode?: PermissionMode
}
```
## 4. commands.ts - 命令注册中心
### 4.1 功能概述
`commands.ts` 是 Claude Code 的命令注册中心,管理所有 slash commands以 `/` 开头的命令)。
### 4.2 命令类型
```typescript
type CommandType =
| 'prompt' // 展开为提示发送给模型
| 'local' // 本地执行,返回文本
| 'local-jsx' // 本地执行,渲染 Ink UI
```
### 4.3 命令定义结构
```typescript
interface Command {
type: CommandType
name: string
aliases?: string[]
description: string
contentLength: number
source: 'builtin' | 'plugin' | 'bundled' | 'mcp' | ...
availability?: ('claude-ai' | 'console')[]
// ...
}
```
### 4.4 命令加载流程
```typescript
// 1. 获取技能目录命令
const skillDirCommands = await getSkillDirCommands(cwd)
// 2. 获取插件命令
const pluginCommands = await getPluginCommands()
// 3. 获取内置命令
const builtinCommands = COMMANDS()
// 4. 合并并过滤
const allCommands = [
...bundledSkills,
...builtinPluginSkills,
...skillDirCommands,
...workflowCommands,
...pluginCommands,
...pluginSkills,
...COMMANDS(),
]
// 5. 检查可用性要求
const filteredCommands = allCommands.filter(cmd =>
meetsAvailabilityRequirement(cmd)
)
// 6. 检查是否启用
.filter(cmd => isCommandEnabled(cmd))
```
### 4.5 远程安全命令
```typescript
export const REMOTE_SAFE_COMMANDS: Set<Command> = new Set([
session, exit, clear, help, theme, color, vim,
cost, usage, copy, btw, feedback, plan,
keybindings, statusline, stickers, mobile,
])
```
### 4.6 命令查找
```typescript
export function findCommand(
commandName: string,
commands: Command[],
): Command | undefined {
return commands.find(
_ =>
_.name === commandName ||
getCommandName(_) === commandName ||
_.aliases?.includes(commandName)
)
}
```
## 5. context.ts - 系统/用户上下文收集
### 5.1 功能概述
`context.ts` 负责收集系统上下文和用户上下文,这些上下文会被预置到每个对话的开头。
### 5.2 Git 状态收集
```typescript
export const getGitStatus = memoize(async (): Promise<string | null> => {
// 检查是否是 Git 仓库
const isGit = await getIsGit()
if (!isGit) return null
// 并行获取多个 Git 信息
const [branch, mainBranch, status, log, userName] = await Promise.all([
getBranch(),
getDefaultBranch(),
execFileNoThrow(gitExe(), ['status', '--short'], ...),
execFileNoThrow(gitExe(), ['log', '--oneline', '-n', '5'], ...),
execFileNoThrow(gitExe(), ['config', 'user.name'], ...),
])
return [
`Current branch: ${branch}`,
`Main branch: ${mainBranch}`,
`Git user: ${userName}`,
`Status:\n${truncatedStatus}`,
`Recent commits:\n${log}`,
].join('\n\n')
})
```
### 5.3 系统上下文
```typescript
export const getSystemContext = memoize(async () => {
const gitStatus = await getGitStatus()
// 缓存破坏注入(仅用于调试)
const injection = feature('BREAK_CACHE_COMMAND')
? getSystemPromptInjection()
: null
return {
...(gitStatus && { gitStatus }),
...(injection && { cacheBreaker: `[CACHE_BREAKER: ${injection}]` }),
}
})
```
### 5.4 用户上下文
```typescript
export const getUserContext = memoize(async () => {
// 获取 CLAUDE.md 文件内容
const shouldDisableClaudeMd = isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CLAUDE_MDS)
|| (isBareMode() && getAdditionalDirectoriesForClaudeMd().length === 0)
const claudeMd = shouldDisableClaudeMd
? null
: getClaudeMds(filterInjectedMemoryFiles(await getMemoryFiles()))
// 缓存 CLAUDE.md 内容供自动模式分类器使用
setCachedClaudeMdContent(claudeMd || null)
return {
...(claudeMd && { claudeMd }),
currentDate: `Today's date is ${getLocalISODate()}.`,
}
})
```
### 5.5 上下文缓存
所有上下文函数都使用 `memoize` 进行缓存,确保在一次对话中只计算一次:
```typescript
export const getSystemContext = memoize(async () => {...})
export const getUserContext = memoize(async () => {...})
export const getGitStatus = memoize(async () => {...})
```
## 6. cost-tracker.ts - Token 成本追踪
### 6.1 功能概述
`cost-tracker.ts` 负责追踪和计算 API 调用的 Token 使用量和成本。
### 6.2 核心数据结构
```typescript
type StoredCostState = {
totalCostUSD: number
totalAPIDuration: number
totalAPIDurationWithoutRetries: number
totalToolDuration: number
totalLinesAdded: number
totalLinesRemoved: number
lastDuration: number | undefined
modelUsage: { [modelName: string]: ModelUsage }
}
type ModelUsage = {
inputTokens: number
outputTokens: number
cacheReadInputTokens: number
cacheCreationInputTokens: number
webSearchRequests: number
costUSD: number
contextWindow: number
maxOutputTokens: number
}
```
### 6.3 成本计算
```typescript
export function addToTotalSessionCost(
cost: number,
usage: Usage,
model: string,
): number {
const modelUsage = addToTotalModelUsage(cost, usage, model)
addToTotalCostState(cost, modelUsage, model)
// 发送到 Statsig 计数器
getCostCounter()?.add(cost, { model })
getTokenCounter()?.add(usage.input_tokens, { ...attrs, type: 'input' })
getTokenCounter()?.add(usage.output_tokens, { ...attrs, type: 'output' })
// 处理 Advisor 使用量
for (const advisorUsage of getAdvisorUsage(usage)) {
const advisorCost = calculateUSDCost(advisorUsage.model, advisorUsage)
totalCost += addToTotalSessionCost(advisorCost, advisorUsage, advisorUsage.model)
}
return totalCost
}
```
### 6.4 成本格式化
```typescript
export function formatTotalCost(): string {
const costDisplay = formatCost(getTotalCostUSD())
return chalk.dim(`
Total cost: ${costDisplay}
Total duration (API): ${formatDuration(getTotalAPIDuration())}
Total duration (wall): ${formatDuration(getTotalDuration())}
Total code changes: ${linesAdded} lines added, ${linesRemoved} lines removed
${formatModelUsage()}
`)
}
```
### 6.5 会话成本保存/恢复
```typescript
// 保存当前会话成本到项目配置
export function saveCurrentSessionCosts(fpsMetrics?: FpsMetrics): void {
saveCurrentProjectConfig(current => ({
...current,
lastCost: getTotalCostUSD(),
lastAPIDuration: getTotalAPIDuration(),
lastLinesAdded: getTotalLinesAdded(),
lastLinesRemoved: getTotalLinesRemoved(),
lastModelUsage: Object.fromEntries(...),
lastSessionId: getSessionId(),
}))
}
// 恢复会话成本
export function restoreCostStateForSession(sessionId: string): boolean {
const data = getStoredSessionCosts(sessionId)
if (!data) return false
setCostStateForRestore(data)
return true
}
```
## 7. query.ts - 查询管道
### 7.1 功能概述
`query.ts` 是核心的查询处理管道,负责:
- 消息流式 API 调用
- 工具调用编排
- 自动压缩
- 错误恢复
### 7.2 主查询循环
```typescript
export async function* query(
params: QueryParams,
): AsyncGenerator<...> {
const consumedCommandUuids: string[] = []
const terminal = yield* queryLoop(params, consumedCommandUuids)
return terminal
}
async function* queryLoop(
params: QueryParams,
consumedCommandUuids: string[],
): AsyncGenerator<...> {
let state: State = {
messages: params.messages,
toolUseContext: params.toolUseContext,
autoCompactTracking: undefined,
maxOutputTokensRecoveryCount: 0,
// ...
}
while (true) {
// 1. 上下文压缩
const { compactionResult } = await deps.autocompact(...)
// 2. API 调用循环
for await (const message of deps.callModel({...})) {
if (message.type === 'assistant') {
assistantMessages.push(message)
// 处理工具调用块
}
}
// 3. 工具执行
if (needsFollowUp) {
const toolUpdates = runTools(toolUseBlocks, ...)
for await (const update of toolUpdates) {
// 处理工具结果
}
}
// 4. 递归继续
state = { ...state, messages: [...messages, ...assistantMessages, ...toolResults] }
}
}
```
### 7.3 工具执行编排
```typescript
// 两种工具执行模式
if (streamingToolExecutor) {
// 流式工具执行:在模型流式输出的同时执行工具
logEvent('tengu_streaming_tool_execution_used', {...})
} else {
// 批量工具执行:等待模型完全响应后执行
logEvent('tengu_streaming_tool_execution_not_used', {...})
}
const toolUpdates = streamingToolExecutor
? streamingToolExecutor.getRemainingResults()
: runTools(toolUseBlocks, assistantMessages, canUseTool, toolUseContext)
```
### 7.4 错误恢复机制
```typescript
// 1. 模型回退
if (innerError instanceof FallbackTriggeredError && fallbackModel) {
currentModel = fallbackModel
attemptWithFallback = true
continue
}
// 2. Prompt Too Long 恢复
if (isWithheld413) {
// 尝试排空上下文折叠
const drained = contextCollapse.recoverFromOverflow(...)
if (drained.committed > 0) {
state = { ...state, messages: drained.messages }
continue
}
// 尝试响应式压缩
const compacted = await reactiveCompact.tryReactiveCompact(...)
if (compacted) {
// ...
}
}
// 3. Max Output Tokens 恢复
if (maxOutputTokensRecoveryCount < MAX_OUTPUT_TOKENS_RECOVERY_LIMIT) {
const recoveryMessage = createUserMessage({
content: `Output token limit hit. Resume directly...`
})
state = { ...state, messages: [...messages, recoveryMessage] }
continue
}
```
### 7.5 上下文压缩
```typescript
// Snip去除压缩
if (feature('HISTORY_SNIP')) {
const snipResult = snipModule!.snipCompactIfNeeded(messagesForQuery)
messagesForQuery = snipResult.messages
snipTokensFreed = snipResult.tokensFreed
}
// 微压缩
const microcompactResult = await deps.microcompact(messagesForQuery, ...)
messagesForQuery = microcompactResult.messages
// 自动压缩
const { compactionResult } = await deps.autocompact(
messagesForQuery,
toolUseContext,
{ systemPrompt, userContext, systemContext, toolUseContext },
querySource,
tracking,
snipTokensFreed,
)
```
### 7.6 工具使用摘要
```typescript
// 为耗时的 Haiku 调用生成工具使用摘要
if (config.gates.emitToolUseSummaries && toolUseBlocks.length > 0) {
const summary = await generateToolUseSummary({
tools: toolInfoForSummary,
signal: toolUseContext.abortController.signal,
lastAssistantText,
})
// 异步生成,不阻塞下一个 API 调用
nextPendingToolUseSummary = summary.then(s =>
s ? createToolUseSummaryMessage(summary, toolUseIds) : null
)
}
```
## 8. 总结
这七个核心文件共同构成了 Claude Code 的核心架构:
| 文件 | 职责 |
|------|------|
| `main.tsx` | CLI 入口,应用程序初始化 |
| `QueryEngine.ts` | 对话生命周期管理,消息处理 |
| `Tool.ts` | 工具基类和接口定义 |
| `commands.ts` | Slash 命令注册和管理 |
| `context.ts` | 系统/用户上下文收集 |
| `cost-tracker.ts` | Token 成本追踪 |
| `query.ts` | API 调用管道,工具编排 |
理解这些核心模块是深入研究 Claude Code 源码的基础。