@anthropic-ai/sdk、@modelcontextprotocol/sdk)的接法——这些对理解产品外壳怎么搭足够硬。ccsource 可能经社区修补;读思想比「一键 tsc」更重要。参见仓库内 .claude/rules/reverse-engineering.md。事件背景与规模数据:长文复盘(MD)↗ · cli.js.map 说明 ↗
以下从重建树中反复出现的结构归纳——适用于任何「LLM + 工具 + 终端」类产品,而非背诵某个函数名。
QueryEngine 持有 cwd、tools、commands、MCP、agents、canUseTool、会话状态等一整张依赖图;主循环周边功能(压缩、选择器、coordinator、snip)用 Feature 与懒加载挂上去,避免圆形依赖炸包。
除规则表格外,还有面向 shell 的 风险分级解释器(结构化 tool 输出 LOW/MEDIUM/HIGH),以及 auto-mode 下用外部模板 + 标签替换段让用户改 allow/deny/environment 规则——把「黑箱拒绝」变成可编辑策略。
压缩链路里显式出现 token 统计、compact 边界消息、前后 Hook、与 transcript / session 存储的衔接;思想是:压缩不是删聊天记录,是重新立边界并保留可恢复信号。
客户端直接基于官方 @modelcontextprotocol/sdk 多传输(stdio / SSE / Streamable HTTP),工具侧有 MCPTool、资源列出/读取、OAuth 刷新等——外部能力不是「字符串拼进 prompt」,而是协议状态机。
Tool.ts 集中 Progress、Permission、MCP Elicit 等交叉类型,避免每工具各写一套;执行链与权限结果类型在单点导入,降低「工具作者」心智负担。
例如挫败感检测可用关键词/正则而非上情感模型(见站内 map README 与 useFrustrationDetection 一类模块);能省算力与延迟的就不堆 ML——这是产品代码里常见的「克制」。
路径相对于仓库 ccsource/claude-code-main/。
| 路径 | 读什么细节 | 提炼什么思想 |
|---|---|---|
src/QueryEngine.ts |
QueryEngineConfig 字段;对 query、SDK 消息、权限孤儿、压缩 snip 的编排入口 |
单一编排核 + Feature 切片扩展 |
src/Tool.ts |
Tool JSON Schema 形状、CanUseToolFn、各类 Progress 联合类型 |
工具层协议先于具体工具实现 |
src/utils/permissions/yoloClassifier.ts |
auto-mode 规则模板、<user_*_to_replace> 标签解析、内外部权限模板分支 |
策略文本化 + 可导出默认项 |
src/utils/permissions/permissionExplainer.ts |
explain_command 强制 schema、LOW/MEDIUM/HIGH 枚举 |
用结构化模型输出做 UX,而不是裸生成段落 |
src/services/compact/compact.ts |
compact 前后 Hook、边界消息、token 估计、与 session transcript 的衔接 | 压缩 = 带审计边界的上下文重写 |
src/services/mcp/client.ts |
多 Transport、ListTools / CallTool schema、OAuth 与错误类型 | 与 MCP 规范对齐,少造轮子 |
下列摘录来自重建树 ccsource/claude-code-main/,仅作教学引用(少量连续行),便于你对照类型名与函数名;完整版权仍属原作者。
QueryEngine:依赖注入壳,见上文模块表。
MCP:ListToolsResultSchema / CallToolResultSchema,见 services/mcp/client.ts 首部 import。
yoloClassifier + permissionExplainerAutoModeRules三段式 allow / soft_deny / environment 与模板标签 <user_*_to_replace> 对齐,getDefaultExternalAutoModeRules 负责把模板「默认值」解析成数组——CLI 导出默认规则时不必硬编码在 TS 里。
// ccsource/claude-code-main/src/utils/permissions/yoloClassifier.ts(摘录)
export type AutoModeRules = {
allow: string[]
soft_deny: string[]
environment: string[]
}
export function getDefaultExternalAutoModeRules(): AutoModeRules {
return {
allow: extractTaggedBullets('user_allow_rules_to_replace'),
soft_deny: extractTaggedBullets('user_deny_rules_to_replace'),
environment: extractTaggedBullets('user_environment_to_replace'),
}
}
explain_command不是让模型自由写段落,而是强制 tool 形状输出 riskLevel(三档枚举)+ 短 risk,下游 UI 可稳定做颜色/按钮,而不是解析自然语言。
// ccsource/claude-code-main/src/utils/permissions/permissionExplainer.ts(摘录)
const EXPLAIN_COMMAND_TOOL = {
name: 'explain_command',
description: 'Provide an explanation of a shell command',
input_schema: {
type: 'object' as const,
properties: {
explanation: { type: 'string', ... },
reasoning: { type: 'string', ... },
risk: { type: 'string', description: 'What could go wrong, under 15 words' },
riskLevel: {
type: 'string',
enum: ['LOW', 'MEDIUM', 'HIGH'],
...
},
},
required: ['explanation', 'reasoning', 'risk', 'riskLevel'],
},
}
解析侧用 RiskAssessmentSchema(Zod)与 tool 输出对齐,避免「模型多写一句就 JSON 失效」时无处兜底。
yoloClassifier.ts 搜 isUsingExternalPermissions,理清 USER_TYPE 与 GrowthBook 配置如何切换内外模板。permissionExplainer.ts 跟一次「从 toolInput 格式化 → sideQuery → 解析 RiskAssessmentSchema」的调用链。riskLevel 映射到「自动放行 / 弹窗 / 硬拒绝」,映射表应放在权限层还是 bash 工具层?compactConversationcompactConversation 接收完整 messages 与 ToolUseContext,第一步就做 tokenCountWithEstimation(messages),随后进入 compact 专用 SDK 状态(如 setSDKStatus('compacting'))。思想:压缩是和普通对话并列的一等流程,要有独立进度与可取消信号。
// ccsource/claude-code-main/src/services/compact/compact.ts(摘录)
export async function compactConversation(
messages: Message[],
context: ToolUseContext,
cacheSafeParams: CacheSafeParams,
suppressFollowUpQuestions: boolean,
customInstructions?: string,
isAutoCompact: boolean = false,
recompactionInfo?: RecompactionInfo,
): Promise<CompactionResult> {
...
const preCompactTokenCount = tokenCountWithEstimation(messages)
...
context.onCompactProgress?.({ type: 'hooks_start', hookType: 'pre_compact' })
context.setSDKStatus?.('compacting')
const hookResult = await executePreCompactHooks(
{ trigger: isAutoCompact ? 'auto' : 'manual', customInstructions: customInstructions ?? null },
context.abortController.signal,
)
customInstructions = mergeHookInstructions(customInstructions, hookResult.newCustomInstructions)
...
}
executePreCompactHooks 可把额外指令写回 customInstructions(经 mergeHookInstructions),等于给「自动/手动压缩」加了一层用户可插桩策略;与 D 课里「压缩不是悄悄 slice」一致。
compactConversation 往下跟 streamCompactSummary,看摘要请求如何插入消息列表。createCompactBoundaryMessage / SystemCompactBoundaryMessage,理解压缩后历史如何被「切段」以便复盘与重放。
主线课讲概念与伪代码;本专题给你「打开哪几个 ts 文件」。建议顺序:S01 / D01(含 最小 Loop 练习与思考题答案)→ 对照 QueryEngine.ts;S03/D03 → 权限硬读;S05/D05 → Compact 硬读;S07/D07 → services/mcp/client.ts。