first commit

This commit is contained in:
H
2026-04-03 13:01:19 +08:00
commit 538eced414
2575 changed files with 645911 additions and 0 deletions

View File

@@ -0,0 +1,813 @@
# 核心模块详解
本文件说明:详细分析 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 源码的基础。