From 14c46df881c0262e68d1cb3274efd6a4b98c2a2e Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Sun, 12 Apr 2026 11:38:58 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B8=85=E7=90=86=E5=9E=83=E5=9C=BE?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/REVISION-PLAN.md | 128 -- docs/feature-exploration-plan.md | 457 ---- docs/feature-flags-audit-complete.md | 2002 ----------------- docs/openai-task-tools.md | 190 -- docs/plans/openai-compatibility.md | 425 ---- docs/projects-collection.md | 35 - docs/test-plans/01-tool-system.md | 147 -- docs/test-plans/02-utils-pure-functions.md | 416 ---- docs/test-plans/03-context-building.md | 134 -- docs/test-plans/04-permission-system.md | 104 - docs/test-plans/05-model-routing.md | 113 - docs/test-plans/06-message-handling.md | 165 -- docs/test-plans/07-cron.md | 112 - docs/test-plans/08-git-utils.md | 106 - docs/test-plans/09-config-settings.md | 161 -- docs/test-plans/10-fix-weak-tests.md | 361 --- .../11-strengthen-acceptable-tests.md | 177 -- docs/test-plans/12-mock-reliability.md | 145 -- docs/test-plans/13-cjk-truncate-tests.md | 71 - docs/test-plans/14-integration-tests.md | 191 -- docs/test-plans/15-cli-coverage-baseline.md | 67 - .../phase-16-zero-dep-pure-functions.md | 188 -- docs/test-plans/phase-17-tool-submodules.md | 203 -- docs/test-plans/phase-18-weak-fixes.md | 110 - docs/test-plans/phase19-batch1-micro-utils.md | 435 ---- .../phase19-batch2-utils-state-commands.md | 287 --- .../phase19-batch3-tool-submodules.md | 258 --- docs/test-plans/phase19-batch4-services.md | 215 -- docs/test-plans/phase19-batch5-mcp-config.md | 200 -- docs/testing-spec.md | 296 --- docs/ultraplan-implementation.md | 444 ---- 31 files changed, 8343 deletions(-) delete mode 100644 docs/REVISION-PLAN.md delete mode 100644 docs/feature-exploration-plan.md delete mode 100644 docs/feature-flags-audit-complete.md delete mode 100644 docs/openai-task-tools.md delete mode 100644 docs/plans/openai-compatibility.md delete mode 100644 docs/projects-collection.md delete mode 100644 docs/test-plans/01-tool-system.md delete mode 100644 docs/test-plans/02-utils-pure-functions.md delete mode 100644 docs/test-plans/03-context-building.md delete mode 100644 docs/test-plans/04-permission-system.md delete mode 100644 docs/test-plans/05-model-routing.md delete mode 100644 docs/test-plans/06-message-handling.md delete mode 100644 docs/test-plans/07-cron.md delete mode 100644 docs/test-plans/08-git-utils.md delete mode 100644 docs/test-plans/09-config-settings.md delete mode 100644 docs/test-plans/10-fix-weak-tests.md delete mode 100644 docs/test-plans/11-strengthen-acceptable-tests.md delete mode 100644 docs/test-plans/12-mock-reliability.md delete mode 100644 docs/test-plans/13-cjk-truncate-tests.md delete mode 100644 docs/test-plans/14-integration-tests.md delete mode 100644 docs/test-plans/15-cli-coverage-baseline.md delete mode 100644 docs/test-plans/phase-16-zero-dep-pure-functions.md delete mode 100644 docs/test-plans/phase-17-tool-submodules.md delete mode 100644 docs/test-plans/phase-18-weak-fixes.md delete mode 100644 docs/test-plans/phase19-batch1-micro-utils.md delete mode 100644 docs/test-plans/phase19-batch2-utils-state-commands.md delete mode 100644 docs/test-plans/phase19-batch3-tool-submodules.md delete mode 100644 docs/test-plans/phase19-batch4-services.md delete mode 100644 docs/test-plans/phase19-batch5-mcp-config.md delete mode 100644 docs/testing-spec.md delete mode 100644 docs/ultraplan-implementation.md diff --git a/docs/REVISION-PLAN.md b/docs/REVISION-PLAN.md deleted file mode 100644 index 931875bb8..000000000 --- a/docs/REVISION-PLAN.md +++ /dev/null @@ -1,128 +0,0 @@ -# 文档修正计划 - -> 目标:补充源码级洞察,让每篇文档从"概念科普"升级为"逆向工程白皮书"水准。 - ---- - -## 第一梯队:空壳页,需要大幅重写 - -### 1. `safety/sandbox.mdx` — 沙箱机制 ✅ DONE - -**现状**:35 行,只列了"文件系统/网络/进程/时间"四个维度,没有任何实现细节。 - -**修正方向**: -- 补充 macOS `sandbox-exec` 的实际调用方式,展示沙箱 profile 的关键片段 -- 说明 `getSandboxConfig()` 的判定逻辑:哪些命令走沙箱、哪些跳过 -- 补充 `dangerouslyDisableSandbox` 参数的设计权衡 -- 加入 Linux 平台的沙箱差异对比(seatbelt vs namespace) -- 展示一次命令执行从权限检查→沙箱包裹→实际执行的完整链路 - ---- - -### 2. `introduction/what-is-claude-code.mdx` — 什么是 Claude Code ✅ DONE - -**现状**:39 行,纯营销文案,和"普通聊天 AI"的对比表太低级。 - -**修正方向**: -- 砍掉"能做什么"的泛泛列表,改为一个具体的端到端示例(从用户输入→系统处理→最终输出) -- 用一张简化架构图替代文字描述,让读者 30 秒建立直觉 -- 补充 Claude Code 的技术定位:不是 IDE 插件、不是 Web Chat,而是 terminal-native agentic system -- 加入与 Cursor / Copilot / Aider 等工具的定位差异(架构层面而非功能清单) - ---- - -### 3. `introduction/why-this-whitepaper.mdx` — 为什么写这份白皮书 ✅ DONE - -**现状**:40 行,全是空话,四张 Card 只是后续章节标题的预告。 - -**修正方向**: -- 明确定位:这是对 Anthropic 官方 CLI 的逆向工程分析,不是官方文档 -- 列出逆向过程中发现的 3-5 个最意外/最精妙的设计决策(吊住读者胃口) -- 说明白皮书的阅读路线图:推荐的阅读顺序和每个章节解决什么问题 -- 补充"这份白皮书不是什么"——不是使用教程,不是 API 文档 - ---- - -### 4. `safety/why-safety-matters.mdx` — 为什么安全至关重要 ✅ DONE - -**现状**:40 行,只列了显而易见的风险,"安全 vs 效率的平衡"只有 3 个 bullet。 - -**修正方向**: -- 从源码角度展示安全体系的全景图:权限规则 → 沙箱 → Plan Mode → 预算上限 → Hooks 的纵深防御链 -- 补充 Claude 自身 System Prompt 中的安全指令("执行前确认"、"优先可逆操作"等),展示 AI 端的安全约束 -- 用真实场景说明"安全 vs 效率"的工程权衡:比如 Read 工具为什么免审批、Bash 工具为什么要逐条确认 -- 加入 Prompt Injection 防御的简要说明(tool result 中的恶意内容如何被系统标记) - ---- - -## 第二梯队:有骨架但太浅,需要补肉 - -### 5. `conversation/streaming.mdx` — 流式响应 ✅ DONE - -**现状**:43 行,只说了"流式好"和 3 行 provider 表。 - -**修正方向**: -- 补充 `BetaRawMessageStreamEvent` 的核心事件类型及其含义 -- 展示文本 chunk 和 tool_use block 交织的状态机流转 -- 说明流式中的错误处理:网络断开、API 限流、token 超限时的重试/降级策略 -- 补充 `processStreamEvents()` 的核心逻辑:如何从事件流中分离出文本、工具调用、usage 统计 - ---- - -### 6. `tools/search-and-navigation.mdx` — 搜索与导航 ✅ DONE - -**现状**:43 行,只说 Glob 和 Grep 存在。 - -**修正方向**: -- 补充 ripgrep 二进制的内嵌方式(vendor 目录、平台适配) -- 说明搜索结果的 head_limit 默认 250 的设计原因(token 预算) -- 展示 ToolSearch 的实现:如何用语义匹配在 50+ 工具(含 MCP)中找到最相关的 -- 补充 Glob 按修改时间排序的意义:最近修改的文件最可能与当前任务相关 - ---- - -### 7. `tools/task-management.mdx` — 任务管理 ✅ DONE - -**现状**:50 行,只有流程 Steps 和状态展示的 4 个 bullet。 - -**修正方向**: -- 补充任务的数据模型:id / subject / description / status / blockedBy / blocks / owner -- 说明依赖管理的实现:blockedBy 如何阻止任务被认领、完成一个任务后如何自动解锁下游 -- 展示任务与 Agent 工具的联动:子 Agent 如何认领任务、报告进度 -- 补充 activeForm 字段的 UX 设计:进行中任务的 spinner 动画文案 - ---- - -### 8. `context/token-budget.mdx` — Token 预算管理 ✅ DONE - -**现状**:55 行,预算控制只有 3 张 Card 各一句话。 - -**修正方向**: -- 补充 `contextWindowTokens` 和 `maxOutputTokens` 的动态计算逻辑 -- 说明缓存 breakpoint 的放置策略:System Prompt 中不变内容在前、变化内容在后的原因 -- 展示工具输出截断的具体机制:超长结果如何被 truncate、何时触发 micro-compact -- 补充 token 计数的实现:`countTokens` 的调用时机和近似 vs 精确计数的权衡 - ---- - -### 9. `agent/worktree-isolation.mdx` — Worktree 隔离 ✅ DONE - -**现状**:55 行,只描述了 git worktree 的概念。 - -**修正方向**: -- 展示 `.claude/worktrees/` 的目录结构和分支命名规则 -- 说明 worktree 的生命周期:创建时机(`isolation: "worktree"`)→ 子 Agent 执行 → 完成/放弃 → 自动清理 -- 补充 worktree 与子 Agent 的绑定关系:Agent 结束时如何判断 keep or remove -- 加入 EnterWorktree / ExitWorktree 工具的交互设计 - ---- - -### 10. `extensibility/custom-agents.mdx` — 自定义 Agent ✅ DONE - -**现状**:56 行,只有配置表和示例表。 - -**修正方向**: -- 展示 agent markdown 文件的完整 frontmatter 格式(name / description / model / allowedTools 等) -- 说明 agent 如何被加载和注入 System Prompt:`loadAgentDefinitions()` 的发现和合并逻辑 -- 展示工具限制的实现:allowedTools 如何过滤工具列表 -- 补充 agent 与 subagent_type 参数的关联:Agent 工具如何指定使用自定义 Agent diff --git a/docs/feature-exploration-plan.md b/docs/feature-exploration-plan.md deleted file mode 100644 index 62e46f03b..000000000 --- a/docs/feature-exploration-plan.md +++ /dev/null @@ -1,457 +0,0 @@ -# Feature 探索计划书 - -> 生成日期:2026-04-02 -> 代码库中已识别 89 个 feature flag,本文档按实现完整度和探索价值分级,制定探索优先级和路线图。 -> -> **已完成**:BUDDY(✅ 2026-04-02)、TRANSCRIPT_CLASSIFIER / Auto Mode(✅ 2026-04-02) - ---- - -## 一、总览 - -### 按实现状态分类 - -| 状态 | 数量 | 说明 | -|------|------|------| -| 已实现/可用 | 11 | 代码完整,开启 feature 后可运行(可能需要 OAuth 等外部依赖) | -| 部分实现 | 8 | 核心逻辑存在但关键模块为 stub,需要补全 | -| 纯 Stub | 15 | 所有函数/工具返回空值,需要从零实现 | -| N/A | 55+ | 内部基础设施、低引用量辅助功能,或反编译丢失过多 | - -### 启用方式 - -所有 feature 通过环境变量启用: - -```bash -# 单个 feature -FEATURE_BUDDY=1 bun run dev - -# 多个 feature 组合 -FEATURE_KAIROS=1 FEATURE_PROACTIVE=1 FEATURE_FORK_SUBAGENT=1 bun run dev -``` - ---- - -## 二、Tier 1 — 已实现/可用(优先探索) - -### 2.1 KAIROS(常驻助手模式)⭐ 最高优先级 - -- **引用数**:154(全库最大) -- **功能**:将 CLI 变为常驻后台助手,支持: - - 持久化 bridge 会话(跨重启复用 session) - - 后台执行任务(用户离开终端时继续工作) - - 推送通知到移动端(任务完成/需要输入时) - - 每日记忆日志 + `/dream` 知识蒸馏 - - 外部频道消息接入(Slack/Discord/Telegram) -- **子 Feature**: - -| 子 Feature | 引用 | 功能 | -|-----------|------|------| -| `KAIROS_BRIEF` | 39 | Brief 工具(`SendUserMessage`),结构化消息输出 | -| `KAIROS_CHANNELS` | 19 | 外部频道消息接入 | -| `KAIROS_PUSH_NOTIFICATION` | 4 | 移动端推送通知 | -| `KAIROS_GITHUB_WEBHOOKS` | 3 | GitHub PR webhook 订阅 | -| `KAIROS_DREAM` | 1 | 夜间记忆蒸馏 | - -- **关键文件**:`src/assistant/`、`src/tools/BriefTool/`、`src/services/mcp/channelNotification.ts`、`src/memdir/memdir.ts` -- **外部依赖**:Anthropic OAuth(claude.ai 订阅)、GrowthBook 特性门控 -- **探索命令**:`FEATURE_KAIROS=1 FEATURE_KAIROS_BRIEF=1 FEATURE_PROACTIVE=1 bun run dev` - -**探索步骤**: -1. 开启 feature,观察启动行为变化 -2. 测试 `/assistant`、`/brief` 命令 -3. 验证 BriefTool 输出模式 -4. 尝试频道消息接入 -5. 测试 `/dream` 记忆蒸馏 - ---- - -### ~~2.2 TRANSCRIPT_CLASSIFIER(Auto Mode 分类器)~~ ✅ 已完成 - -- **引用数**:108 -- **功能**:使用 LLM 对用户意图进行分类,实现 auto mode(自动决定工具权限) -- **状态**:✅ prompt 模板已重建,功能完整可用(2026-04-02 完成) - ---- - -### 2.3 VOICE_MODE(语音输入) - -- **引用数**:46 -- **功能**:按键说话(Push-to-Talk),音频流式传输到 Anthropic STT 端点(Nova 3),实时转录显示 -- **当前状态**:**完整实现**,包括录音、WebSocket 流、转录插入 -- **关键文件**:`src/voice/voiceModeEnabled.ts`、`src/hooks/useVoice.ts`、`src/services/voiceStreamSTT.ts` -- **外部依赖**:Anthropic OAuth(非 API key)、macOS 原生音频或 SoX -- **探索命令**:`FEATURE_VOICE_MODE=1 bun run dev` -- **默认快捷键**:长按空格键录音 - -**探索步骤**: -1. 确认 OAuth token 可用 -2. 测试按住空格录音 → 释放后转录 -3. 验证实时中间转录显示 -4. 测试 `/voice` 命令切换 - ---- - -### 2.4 TEAMMEM(团队共享记忆) - -- **引用数**:51 -- **功能**:基于 GitHub 仓库的团队共享记忆系统,`memory/team/` 目录双向同步到 Anthropic 服务器 -- **当前状态**:**完整实现**,包括增量同步、冲突解决、密钥扫描、路径穿越防护 -- **关键文件**:`src/services/teamMemorySync/`(index、watcher、secretScanner)、`src/memdir/teamMemPaths.ts` -- **外部依赖**:Anthropic OAuth + GitHub remote(`getGithubRepo()`) -- **探索命令**:`FEATURE_TEAMMEM=1 bun run dev` - -**探索步骤**: -1. 确认项目有 GitHub remote -2. 开启后观察 `memory/team/` 目录创建 -3. 测试团队记忆写入和同步 -4. 验证密钥扫描防护 - ---- - -### 2.5 COORDINATOR_MODE(多 Agent 编排) - -- **引用数**:32 -- **功能**:CLI 变为编排者,通过 AgentTool 派发任务给多个 worker 并行执行 -- **当前状态**:核心逻辑实现,worker agent 模块为 stub -- **关键文件**:`src/coordinator/coordinatorMode.ts`(系统 prompt 完整)、`src/coordinator/workerAgent.ts`(stub) -- **限制**:编排者只能使用 AgentTool/TaskStop/SendMessage,不能直接操作文件 -- **探索命令**:`FEATURE_COORDINATOR_MODE=1 CLAUDE_CODE_COORDINATOR_MODE=1 bun run dev` - -**探索步骤**: -1. 补全 `workerAgent.ts` stub -2. 测试多 worker 并行任务派发 -3. 验证 worker 结果汇总 - ---- - -### 2.6 BRIDGE_MODE(远程控制) - -- **引用数**:28 -- **功能**:本地 CLI 注册为 bridge 环境,可从 claude.ai 或其他控制面远程驱动 -- **当前状态**:v1(env-based)和 v2(env-less)实现均存在 -- **关键文件**:`src/bridge/bridgeEnabled.ts`、`src/bridge/replBridge.ts`(v1)、`src/bridge/remoteBridgeCore.ts`(v2) -- **外部依赖**:claude.ai OAuth、GrowthBook 门控 `tengu_ccr_bridge` -- **探索命令**:`FEATURE_BRIDGE_MODE=1 bun run dev` - ---- - -### 2.7 FORK_SUBAGENT(上下文继承子 Agent) - -- **引用数**:4 -- **功能**:AgentTool 生成 fork 子 agent,继承父级完整对话上下文,优化 prompt cache -- **当前状态**:**完整实现**(`forkSubagent.ts`),支持 worktree 隔离通知、递归防护 -- **关键文件**:`src/tools/AgentTool/forkSubagent.ts` -- **探索命令**:`FEATURE_FORK_SUBAGENT=1 bun run dev` - ---- - -### 2.8 TOKEN_BUDGET(Token 预算控制) - -- **引用数**:9 -- **功能**:解析用户指定的 token 预算(如 "spend 2M tokens"),自动持续工作直到达到目标 -- **当前状态**:解析器**完整实现**,支持简写和详细语法;QueryEngine 中的周转逻辑已连接 -- **关键文件**:`src/utils/tokenBudget.ts`、`src/QueryEngine.ts` -- **探索命令**:`FEATURE_TOKEN_BUDGET=1 bun run dev` - ---- - -### 2.9 MCP_SKILLS(MCP 技能发现) - -- **引用数**:9 -- **功能**:将 MCP 服务器提供的 prompt 类型命令筛选为可调用技能 -- **当前状态**:**功能性实现**(config 门控筛选器) -- **关键文件**:`src/commands.ts`(`getMcpSkillCommands()`) -- **探索命令**:`FEATURE_MCP_SKILLS=1 bun run dev` - ---- - -### 2.10 TREE_SITTER_BASH(Bash AST 解析) - -- **引用数**:3 -- **功能**:纯 TypeScript bash 命令 AST 解析器,用于 fail-closed 权限匹配 -- **当前状态**:**完整实现**(`bashParser.ts` ~2000行 + `ast.ts` ~400行) -- **关键文件**:`src/utils/vendor/tree-sitter-bash/` -- **探索命令**:`FEATURE_TREE_SITTER_BASH=1 bun run dev` - ---- - -### ~~2.11 BUDDY(虚拟伙伴)~~ ✅ 已完成 - -- **引用数**:16 -- **功能**:`/buddy` 命令,支持 hatch/rehatch/pet/mute/unmute -- **状态**:✅ 已合入,功能完整可用(2026-04-02 完成) - ---- - -## 三、Tier 2 — 部分实现(需要补全) - -### 3.1 PROACTIVE(主动模式) - -- **引用数**:37 -- **功能**:Tick 驱动的自主代理,定时唤醒执行工作,配合 SleepTool 控制节奏 -- **当前状态**:核心模块 `src/proactive/index.ts` **全部 stub**(activate/deactivate/pause 返回 false 或空操作) -- **依赖**:与 KAIROS 强绑定(所有检查都是 `feature('PROACTIVE') || feature('KAIROS')`) -- **补全工作量**:中等 — 需要实现 tick 生成、SleepTool 集成、暂停/恢复逻辑 - -### 3.2 BASH_CLASSIFIER(Bash 命令分类器) - -- **引用数**:45 -- **功能**:LLM 驱动的 bash 命令意图分类(允许/拒绝/询问) -- **当前状态**:`bashClassifier.ts` **全部 stub**(`matches: false`) -- **补全工作量**:大 — 需要 LLM 调用实现、prompt 设计 - -### 3.3 ULTRAPLAN(增强规划) - -- **引用数**:10 -- **功能**:关键字触发增强计划模式,输入 "ultraplan" 自动转为 plan -- **当前状态**:关键字检测**完整实现**,`/ultraplan` 命令**为 stub** -- **补全工作量**:小 — 只需实现命令处理逻辑 - -### 3.4 EXPERIMENTAL_SKILL_SEARCH(技能语义搜索) - -- **引用数**:21 -- **功能**:DiscoverSkills 工具,根据当前任务语义搜索可用技能 -- **当前状态**:布线完整,核心搜索逻辑 stub -- **补全工作量**:中等 — 需要实现搜索引擎和索引 - -### 3.5 CONTEXT_COLLAPSE(上下文折叠) - -- **引用数**:20 -- **功能**:CtxInspectTool 让模型内省上下文窗口大小,优化压缩决策 -- **当前状态**:工具 stub,HISTORY_SNIP 子功能也 stub -- **补全工作量**:中等 - -### 3.6 WORKFLOW_SCRIPTS(工作流自动化) - -- **引用数**:10 -- **功能**:基于文件的自动化工作流 + `/workflows` 命令 -- **当前状态**:WorkflowTool、命令、加载器全部 stub -- **补全工作量**:大 — 需要从零设计工作流 DSL - -### 3.7 WEB_BROWSER_TOOL(浏览器工具) - -- **引用数**:4 -- **功能**:模型可调用浏览器工具导航和交互网页 -- **当前状态**:工具注册存在,实现 stub -- **补全工作量**:大 - -### 3.8 DAEMON(后台守护进程) - -- **引用数**:3 -- **功能**:后台守护进程 + 远程控制服务器 -- **当前状态**:只有条件导入布线,无实现 -- **补全工作量**:极大 - ---- - -## 四、Tier 3 — 纯 Stub / N/A(低优先级) - -| Feature | 引用 | 状态 | 说明 | -|---------|------|------|------| -| CHICAGO_MCP | 16 | N/A | Anthropic 内部 MCP 基础设施 | -| UDS_INBOX | 17 | Experimental | 本机 UDS 消息层 + 本机 named-pipe 协调层 | -| MONITOR_TOOL | 13 | Stub | 文件/进程监控工具 | -| BG_SESSIONS | 11 | Stub | 后台会话管理 | -| SHOT_STATS | 10 | 无实现 | 逐 prompt 统计 | -| EXTRACT_MEMORIES | 7 | 无实现 | 自动记忆提取 | -| TEMPLATES | 6 | Stub | 项目/提示模板 | -| LODESTONE | 6 | N/A | 内部基础设施 | -| STREAMLINED_OUTPUT | 1 | — | 精简输出模式 | -| HOOK_PROMPTS | 1 | — | Hook 提示词 | -| CCR_AUTO_CONNECT | 3 | — | CCR 自动连接 | -| CCR_MIRROR | 4 | — | CCR 镜像模式 | -| CCR_REMOTE_SETUP | 1 | — | CCR 远程设置 | -| NATIVE_CLIPBOARD_IMAGE | 2 | — | 原生剪贴板图片 | -| CONNECTOR_TEXT | 7 | — | 连接器文本 | - -以及其余 40+ 个低引用量 feature。 - ---- - -## 五、探索路线图 - -### Phase 1:快速验证(无外部依赖) - -> 目标:确认代码可以正常运行,体验基本功能 - -| 优先级 | Feature | 命令 | 预期效果 | -|--------|---------|------|----------| -| 1 | BUDDY | `FEATURE_BUDDY=1 bun run dev` | `/buddy hatch` 生成伙伴 | -| 2 | FORK_SUBAGENT | `FEATURE_FORK_SUBAGENT=1 bun run dev` | Agent 可生成上下文继承的子任务 | -| 3 | TOKEN_BUDGET | `FEATURE_TOKEN_BUDGET=1 bun run dev` | 输入 "spend 500k tokens" 测试自动持续 | -| 4 | TREE_SITTER_BASH | `FEATURE_TREE_SITTER_BASH=1 bun run dev` | 更精确的 bash 权限匹配 | -| 5 | MCP_SKILLS | `FEATURE_MCP_SKILLS=1 bun run dev` | MCP 服务器 prompt 提升为技能 | - -### Phase 2:核心功能探索(需要 OAuth) - -> 目标:体验 KAIROS 全套能力 - -| 优先级 | Feature | 命令 | 预期效果 | -|--------|---------|------|----------| -| 1 | TRANSCRIPT_CLASSIFIER | `FEATURE_TRANSCRIPT_CLASSIFIER=1 bun run dev` | Auto mode 自动激活 | -| 2 | KAIROS 全套 | `FEATURE_KAIROS=1 FEATURE_KAIROS_BRIEF=1 FEATURE_KAIROS_CHANNELS=1 FEATURE_PROACTIVE=1 bun run dev` | 常驻助手 + Brief 输出 + 频道消息 | -| 3 | VOICE_MODE | `FEATURE_VOICE_MODE=1 bun run dev` | 按空格说话 | -| 4 | TEAMMEM | `FEATURE_TEAMMEM=1 bun run dev` | 团队记忆同步 | -| 5 | COORDINATOR_MODE | `FEATURE_COORDINATOR_MODE=1 CLAUDE_CODE_COORDINATOR_MODE=1 bun run dev` | 多 agent 编排 | - -### Phase 3:Stub 补全开发 - -> 目标:将高价值 stub 实现为可用功能 - -| 优先级 | Feature | 补全难度 | 价值 | -|--------|---------|----------|------| -| 1 | PROACTIVE | 中 | 自主工作能力 | -| 2 | ULTRAPLAN | 小 | 增强规划 | -| 3 | CONTEXT_COLLAPSE | 中 | 长对话优化 | -| 4 | EXPERIMENTAL_SKILL_SEARCH | 中 | 技能发现 | -| 5 | BASH_CLASSIFIER | 大 | 安全增强 | - ---- - -## 六、推荐组合方案 - -### "全功能助手"组合 - -```bash -FEATURE_KAIROS=1 \ -FEATURE_KAIROS_BRIEF=1 \ -FEATURE_KAIROS_CHANNELS=1 \ -FEATURE_KAIROS_PUSH_NOTIFICATION=1 \ -FEATURE_PROACTIVE=1 \ -FEATURE_FORK_SUBAGENT=1 \ -FEATURE_TOKEN_BUDGET=1 \ -FEATURE_TRANSCRIPT_CLASSIFIER=1 \ -FEATURE_BUDDY=1 \ -bun run dev -``` - -### "多 Agent 协作"组合 - -```bash -FEATURE_COORDINATOR_MODE=1 \ -FEATURE_FORK_SUBAGENT=1 \ -FEATURE_BRIDGE_MODE=1 \ -FEATURE_BG_SESSIONS=1 \ -CLAUDE_CODE_COORDINATOR_MODE=1 \ -bun run dev -``` - -### "开发者增强"组合 - -```bash -FEATURE_TRANSCRIPT_CLASSIFIER=1 \ -FEATURE_TREE_SITTER_BASH=1 \ -FEATURE_TOKEN_BUDGET=1 \ -FEATURE_MCP_SKILLS=1 \ -FEATURE_CONTEXT_COLLAPSE=1 \ -bun run dev -``` - ---- - -## 七、风险与注意事项 - -1. **OAuth 依赖**:KAIROS、VOICE_MODE、TEAMMEM、BRIDGE_MODE 需要 Anthropic OAuth 认证(claude.ai 订阅),API key 用户无法使用 -2. **GrowthBook 门控**:部分功能(VOICE_MODE 的 `tengu_cobalt_frost`、TEAMMEM 的 `tengu_herring_clock`)即使 feature flag 开启,还需要服务端 GrowthBook 开关 -3. **反编译不完整**:所有"已实现"功能均为反编译产物,可能存在运行时错误,需要逐个验证 -4. **Proactive stub**:KAIROS 的自主工作能力依赖 PROACTIVE,但 PROACTIVE 核心是 stub,需先补全 -5. **tsc 错误**:代码库有 ~1341 个 TypeScript 编译错误(来自反编译),不影响 Bun 运行时但在 IDE 中会有大量红线 - ---- - -## 附录:Feature Flag 完整列表 - -共 89 个 feature flag(按引用数降序): - -| Feature | 引用 | Tier | -|---------|------|------| -| KAIROS | 154 | 1 | -| TRANSCRIPT_CLASSIFIER | 108 | 1 | -| TEAMMEM | 51 | 1 | -| VOICE_MODE | 46 | 1 | -| BASH_CLASSIFIER | 45 | 2 | -| KAIROS_BRIEF | 39 | 1 | -| PROACTIVE | 37 | 2 | -| COORDINATOR_MODE | 32 | 1 | -| BRIDGE_MODE | 28 | 1 | -| EXPERIMENTAL_SKILL_SEARCH | 21 | 2 | -| CONTEXT_COLLAPSE | 20 | 2 | -| KAIROS_CHANNELS | 19 | 1 | -| UDS_INBOX | 17 | 3 | -| CHICAGO_MCP | 16 | 3 | -| BUDDY | 16 | 1 | -| HISTORY_SNIP | 15 | 2 | -| MONITOR_TOOL | 13 | 3 | -| COMMIT_ATTRIBUTION | 12 | — | -| CACHED_MICROCOMPACT | 12 | — | -| BG_SESSIONS | 11 | 3 | -| WORKFLOW_SCRIPTS | 10 | 2 | -| ULTRAPLAN | 10 | 2 | -| SHOT_STATS | 10 | 3 | -| TOKEN_BUDGET | 9 | 1 | -| PROMPT_CACHE_BREAK_DETECTION | 9 | — | -| MCP_SKILLS | 9 | 1 | -| EXTRACT_MEMORIES | 7 | 3 | -| CONNECTOR_TEXT | 7 | — | -| TEMPLATES | 6 | 3 | -| LODESTONE | 6 | 3 | -| TREE_SITTER_BASH_SHADOW | 5 | — | -| QUICK_SEARCH | 5 | — | -| MESSAGE_ACTIONS | 5 | — | -| DOWNLOAD_USER_SETTINGS | 5 | — | -| DIRECT_CONNECT | 5 | — | -| WEB_BROWSER_TOOL | 4 | 2 | -| VERIFICATION_AGENT | 4 | — | -| TERMINAL_PANEL | 4 | — | -| SSH_REMOTE | 4 | — | -| REVIEW_ARTIFACT | 4 | — | -| REACTIVE_COMPACT | 4 | — | -| KAIROS_PUSH_NOTIFICATION | 4 | 1 | -| HISTORY_PICKER | 4 | — | -| FORK_SUBAGENT | 4 | 1 | -| CCR_MIRROR | 4 | — | -| TREE_SITTER_BASH | 3 | 1 | -| MEMORY_SHAPE_TELEMETRY | 3 | — | -| MCP_RICH_OUTPUT | 3 | — | -| KAIROS_GITHUB_WEBHOOKS | 3 | 1 | -| FILE_PERSISTENCE | 3 | — | -| DAEMON | 3 | 2 | -| CCR_AUTO_CONNECT | 3 | — | -| UPLOAD_USER_SETTINGS | 2 | — | -| POWERSHELL_AUTO_MODE | 2 | — | -| OVERFLOW_TEST_TOOL | 2 | — | -| NEW_INIT | 2 | — | -| NATIVE_CLIPBOARD_IMAGE | 2 | — | -| HARD_FAIL | 2 | — | -| ENHANCED_TELEMETRY_BETA | 2 | — | -| COWORKER_TYPE_TELEMETRY | 2 | — | -| BREAK_CACHE_COMMAND | 2 | — | -| AWAY_SUMMARY | 2 | — | -| AUTO_THEME | 2 | — | -| ALLOW_TEST_VERSIONS | 2 | — | -| AGENT_TRIGGERS_REMOTE | 2 | — | -| AGENT_MEMORY_SNAPSHOT | 2 | — | -| UNATTENDED_RETRY | 1 | — | -| ULTRATHINK | 1 | — | -| TORCH | 1 | — | -| STREAMLINED_OUTPUT | 1 | — | -| SLOW_OPERATION_LOGGING | 1 | — | -| SKILL_IMPROVEMENT | 1 | — | -| SELF_HOSTED_RUNNER | 1 | — | -| RUN_SKILL_GENERATOR | 1 | — | -| PERFETTO_TRACING | 1 | — | -| NATIVE_CLIENT_ATTESTATION | 1 | — | -| KAIROS_DREAM | 1 | 1 | -| IS_LIBC_MUSL | 1 | — | -| IS_LIBC_GLIBC | 1 | — | -| HOOK_PROMPTS | 1 | — | -| DUMP_SYSTEM_PROMPT | 1 | — | -| COMPACTION_REMINDERS | 1 | — | -| CCR_REMOTE_SETUP | 1 | — | -| BYOC_ENVIRONMENT_RUNNER | 1 | — | -| BUILTIN_EXPLORE_PLAN_AGENTS | 1 | — | -| BUILDING_CLAUDE_APPS | 1 | — | -| ANTI_DISTILLATION_CC | 1 | — | -| AGENT_TRIGGERS | 1 | — | -| ABLATION_BASELINE | 1 | — | diff --git a/docs/feature-flags-audit-complete.md b/docs/feature-flags-audit-complete.md deleted file mode 100644 index 898fa3689..000000000 --- a/docs/feature-flags-audit-complete.md +++ /dev/null @@ -1,2002 +0,0 @@ -# Claude Code 编译时特性标志(Feature Flags)完整审计报告 - -> 审计日期: 2026-04-05 -> 代码库: Claude Code CLI -> 总计特性标志数: 92 个 -> 编译时门控机制: `feature('FLAG_NAME')` — 来自 `bun:bundle` 的编译时常量 -> 运行时门控机制: `USER_TYPE` 环境变量 + GrowthBook 远程开关(`tengu_*` 前缀) - ---- - -## 门控机制概述 - -Claude Code 使用三层门控系统: - -1. **编译时标志** (`feature('...')` from `bun:bundle`): 在构建时决定代码是否包含在最终产物中。当 `feature('X')` 为 `false` 时,Bun 的死代码消除(DCE)会移除整个 `if` 分支,最终产物中完全不包含该功能的代码。 -2. **运行时用户类型** (`USER_TYPE`): 通过环境变量区分用户类型(如 `internal`, `external`, `enterprise`),在运行时决定功能是否可用。 -3. **远程开关** (GrowthBook SDK, `tengu_*` 前缀): 通过 Anthropic 的 GrowthBook 实例进行远程 A/B 测试和功能开关控制,可在不重新部署的情况下开启/关闭功能。 - -本文档审计的是第一层——编译时标志。所有 92 个标志均以 `feature('FLAG_NAME')` 的形式出现在源代码中。 - ---- - -## 分类标准 - -- **COMPLETE(完整实现)**: 核心功能代码完整,所有引用文件存在且有实质性内容。只需在构建配置中将该标志设为 `true` 即可启用。 -- **PARTIAL(部分实现)**: 有实质性的功能代码,但存在缺失的文件(命令入口、组件等)或关键模块仅有空壳。启用后可能报错或功能不完整。 -- **STUB(纯桩/最小实现)**: 仅有 1-2 处引用,没有或几乎没有实际功能代码。代码只是为该标志预留了位置。 - ---- - -## 统计摘要 - -| 分类 | 数量 | 标志名称 | -|------|------|----------| -| COMPLETE | 22 | BRIDGE_MODE, COORDINATOR_MODE, CONTEXT_COLLAPSE, VOICE_MODE, TEAMMEM, COMMIT_ATTRIBUTION, ULTRAPLAN, BASH_CLASSIFIER, TRANSCRIPT_CLASSIFIER, EXTRACT_MEMORIES, CACHED_MICROCOMPACT, TOKEN_BUDGET, AGENT_TRIGGERS, REACTIVE_COMPACT, KAIROS_BRIEF, CCR_REMOTE_SETUP, SHOT_STATS, BG_SESSIONS, PROACTIVE, CHICAGO_MCP, VERIFICATION_AGENT, PROMPT_CACHE_BREAK_DETECTION | -| PARTIAL | 19 | KAIROS, BUDDY, MONITOR_TOOL, HISTORY_SNIP, WORKFLOW_SCRIPTS, UDS_INBOX, KAIROS_CHANNELS, FORK_SUBAGENT, EXPERIMENTAL_SKILL_SEARCH, WEB_BROWSER_TOOL, MCP_SKILLS, REVIEW_ARTIFACT, KAIROS_GITHUB_WEBHOOKS, CONNECTOR_TEXT, TEMPLATES, LODESTONE, HISTORY_PICKER, MESSAGE_ACTIONS, TERMINAL_PANEL | -| STUB | 51 | TORCH, KAIROS_DREAM, KAIROS_PUSH_NOTIFICATION, DAEMON, DIRECT_CONNECT, SSH_REMOTE, STREAMLINED_OUTPUT, ANTI_DISTILLATION_CC, NATIVE_CLIENT_ATTESTATION, ABLATION_BASELINE, AGENT_MEMORY_SNAPSHOT, AGENT_TRIGGERS_REMOTE, ALLOW_TEST_VERSIONS, AUTO_THEME, AWAY_SUMMARY, BREAK_CACHE_COMMAND, BUILDING_CLAUDE_APPS, BUILTIN_EXPLORE_PLAN_AGENTS, BYOC_ENVIRONMENT_RUNNER, CCR_AUTO_CONNECT, CCR_MIRROR, COMPACTION_REMINDERS, COWORKER_TYPE_TELEMETRY, DOWNLOAD_USER_SETTINGS, DUMP_SYSTEM_PROMPT, ENHANCED_TELEMETRY_BETA, FILE_PERSISTENCE, HARD_FAIL, HOOK_PROMPTS, IS_LIBC_GLIBC, IS_LIBC_MUSL, MCP_RICH_OUTPUT, MEMORY_SHAPE_TELEMETRY, NATIVE_CLIPBOARD_IMAGE, NEW_INIT, OVERFLOW_TEST_TOOL, PERFETTO_TRACING, POWERSHELL_AUTO_MODE, QUICK_SEARCH, RUN_SKILL_GENERATOR, SELF_HOSTED_RUNNER, SKILL_IMPROVEMENT, SLOW_OPERATION_LOGGING, TREE_SITTER_BASH, TREE_SITTER_BASH_SHADOW, ULTRATHINK, UNATTENDED_RETRY, UPLOAD_USER_SETTINGS, SKIP_DETECTION_WHEN_AUTOUPDATES_DISABLED | - ---- - -## 当前启用状态 (2026-04-05) - -> 经 Codex CLI 独立复核验证,详见 `feature-flags-codex-review.md` - -| 标志 | build.ts | dev.ts | 实际验证状态 | 备注 | -|------|:--------:|:------:|:----------:|------| -| AGENT_TRIGGERS_REMOTE | **ON** | **ON** | compile-only | 环境标记,原始即启用 | -| CHICAGO_MCP | **ON** | **ON** | compile-only | Computer Use,原始即启用 | -| VOICE_MODE | **ON** | **ON** | compile-only | 语音模式,原始即启用 | -| SHOT_STATS | **ON** | **ON** | compile-only, 已验证 | 本轮新增,纯本地统计 | -| PROMPT_CACHE_BREAK_DETECTION | **ON** | **ON** | compile-only, 已验证 | 本轮新增,内部诊断 | -| TOKEN_BUDGET | **ON** | **ON** | compile-only, 已验证 | 本轮新增,支持 `+500k` 语法 | -| BUDDY | off | **ON** | compile+GrowthBook | 仅 dev 模式 | -| TRANSCRIPT_CLASSIFIER | off | **ON** | compile+GrowthBook | 仅 dev 模式 | -| BRIDGE_MODE | off | **ON** | compile+remote | 仅 dev 模式,需 claude.ai 订阅 | - ---- - -# 一、COMPLETE(完整实现)— 共 22 个 - -以下标志的功能代码完整,所有引用的文件均存在且有实质性内容。只需在构建配置中将对应标志设为 `true` 即可启用该功能。 - ---- - -## 1. BRIDGE_MODE `[dev: ON]` - -**编译时引用次数**: 29(单引号 28 + 双引号 1) -**功能描述**: 远程桥接模式。允许 Claude Code CLI 通过 WebSocket 连接到远程服务端(如 claude.ai Web 端),实现远程控制、会话转发、权限代理、附件传输等功能。这是 Claude Code 最大的子系统之一。 -**分类**: COMPLETE -**启用条件**: 将 `BRIDGE_MODE` 编译标志设为 `true` - -**核心实现文件(src/bridge/ 目录,共 32 个文件,12,619 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/bridge/bridgeMain.ts | 2,999 行 | 桥接主入口,管理整个远程桥接生命周期 | -| src/bridge/replBridge.ts | 2,406 行 | REPL 桥接核心,处理消息路由和会话管理 | -| src/bridge/remoteBridgeCore.ts | 1,008 行 | 远程桥接核心连接逻辑 | -| src/bridge/initReplBridge.ts | 569 行 | REPL 桥接初始化 | -| src/bridge/sessionRunner.ts | 550 行 | 会话运行器,管理远程会话执行 | -| src/bridge/bridgeApi.ts | 539 行 | 桥接 API 封装 | -| src/bridge/bridgeUI.ts | 530 行 | 桥接模式 UI 组件 | -| src/bridge/bridgeMessaging.ts | 461 行 | 桥接消息协议 | -| src/bridge/createSession.ts | 384 行 | 远程会话创建逻辑 | -| src/bridge/replBridgeTransport.ts | 370 行 | REPL 桥接传输层 | -| src/bridge/types.ts | 262 行 | 桥接相关类型定义 | -| src/bridge/jwtUtils.ts | 256 行 | JWT 令牌工具 | -| src/bridge/trustedDevice.ts | 210 行 | 可信设备管理 | -| src/bridge/bridgePointer.ts | 210 行 | 桥接指针管理 | -| src/bridge/bridgeEnabled.ts | 202 行 | 桥接模式启用检测 | -| src/bridge/inboundAttachments.ts | 175 行 | 入站附件处理 | -| src/bridge/envLessBridgeConfig.ts | 165 行 | 无环境变量桥接配置 | -| src/bridge/bridgeStatusUtil.ts | 163 行 | 桥接状态工具 | -| src/bridge/debugUtils.ts | 141 行 | 桥接调试工具 | -| src/bridge/bridgeDebug.ts | 135 行 | 桥接调试模块 | -| src/bridge/workSecret.ts | 127 行 | 工作密钥管理 | -| src/bridge/pollConfig.ts | 110 行 | 轮询配置 | -| src/bridge/pollConfigDefaults.ts | 82 行 | 轮询配置默认值 | -| src/bridge/inboundMessages.ts | 80 行 | 入站消息处理 | -| src/bridge/capacityWake.ts | 56 行 | 容量唤醒 | -| src/bridge/sessionIdCompat.ts | 57 行 | 会话 ID 兼容层 | -| src/bridge/codeSessionApi.ts | 168 行 | 代码会话 API | -| src/bridge/bridgeConfig.ts | 48 行 | 桥接配置 | -| src/bridge/bridgePermissionCallbacks.ts | 43 行 | 桥接权限回调 | -| src/bridge/replBridgeHandle.ts | 36 行 | REPL 桥接句柄 | -| src/bridge/flushGate.ts | 71 行 | 刷新门控 | -| src/bridge/webhookSanitizer.ts | 3 行 | Webhook 清理 | -| src/bridge/peerSessions.ts | 3 行 | 对等会话(桩) | - -**引用该标志的文件(13 个)**: -1. src/bridge/bridgeEnabled.ts — 检测桥接模式是否编译启用 -2. src/commands.ts — 条件注册 `/bridge` 命令和 `/remoteControlServer` 命令 -3. src/commands/bridge/index.ts — 桥接命令入口(604 行) -4. src/components/PromptInput/PromptInputFooter.tsx — 桥接模式下的页脚 UI -5. src/components/Settings/Config.tsx — 设置面板中的桥接选项 -6. src/entrypoints/cli.tsx — CLI 入口中的桥接模式初始化 -7. src/hooks/useCanUseTool.tsx — 桥接模式下的工具权限 -8. src/hooks/useReplBridge.tsx — REPL 桥接 Hook -9. src/main.tsx — 主入口中的桥接模式启动 -10. src/screens/REPL.tsx — REPL 屏幕中的桥接集成 -11. src/tools/BriefTool/attachments.ts — Brief 工具附件处理 -12. src/tools/BriefTool/upload.ts — Brief 工具上传 -13. src/tools/ConfigTool/supportedSettings.ts — 配置工具中的桥接设置 - -**启用所需操作**: 仅需将编译标志 `BRIDGE_MODE` 设为 `true`。所有代码完整,命令入口 `src/commands/bridge/index.ts`(604 行)和 `src/commands/bridge/bridge.tsx`(46,907 行)均存在。 - ---- - -## 2. COORDINATOR_MODE - -**编译时引用次数**: 32 -**功能描述**: 协调器模式。允许 Claude Code 作为"领导者"协调多个"工作者"代理并行执行任务。工作者可以在同一进程内运行(in-process),也可以通过 tmux/iTerm2 面板运行。支持权限同步、重连、团队管理等。 -**分类**: COMPLETE -**启用条件**: 将 `COORDINATOR_MODE` 编译标志设为 `true` - -**核心实现文件(src/coordinator/ 目录,370 行 + src/utils/swarm/ 目录,7,620 行 = 共 7,990 行)**: - -src/coordinator/ 目录(2 个文件): - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/coordinator/coordinatorMode.ts | 369 行 | 协调器模式核心逻辑,管理领导者/工作者角色 | -| src/coordinator/workerAgent.ts | 1 行 | 工作者代理(桩文件,实际逻辑在 swarm 中) | - -src/utils/swarm/ 目录(22 个文件): - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/utils/swarm/inProcessRunner.ts | 1,552 行 | 进程内工作者运行器 | -| src/utils/swarm/permissionSync.ts | 928 行 | 权限同步机制 | -| src/utils/swarm/backends/TmuxBackend.ts | 764 行 | Tmux 后端执行器 | -| src/utils/swarm/teamHelpers.ts | 683 行 | 团队辅助函数 | -| src/utils/swarm/It2SetupPrompt.tsx | 379 行 | iTerm2 设置提示 UI | -| src/utils/swarm/backends/ITermBackend.ts | 370 行 | iTerm2 后端执行器 | -| src/utils/swarm/backends/PaneBackendExecutor.ts | 354 行 | 面板后端执行器 | -| src/utils/swarm/backends/InProcessBackend.ts | 339 行 | 进程内后端 | -| src/utils/swarm/spawnInProcess.ts | 328 行 | 进程内 spawn 逻辑 | -| src/utils/swarm/backends/types.ts | 311 行 | 后端类型定义 | -| src/utils/swarm/backends/registry.ts | 464 行 | 后端注册表 | -| src/utils/swarm/backends/it2Setup.ts | 245 行 | iTerm2 设置逻辑 | -| src/utils/swarm/spawnUtils.ts | 146 行 | Spawn 工具函数 | -| src/utils/swarm/teammateInit.ts | 129 行 | 队友初始化 | -| src/utils/swarm/reconnection.ts | 119 行 | 重连逻辑 | -| src/utils/swarm/teammateLayoutManager.ts | 107 行 | 队友布局管理 | -| src/utils/swarm/backends/teammateModeSnapshot.ts | 87 行 | 队友模式快照 | -| src/utils/swarm/backends/detection.ts | 128 行 | 后端检测 | -| src/utils/swarm/leaderPermissionBridge.ts | 54 行 | 领导者权限桥接 | -| src/utils/swarm/constants.ts | 33 行 | 常量定义 | -| src/utils/swarm/teammatePromptAddendum.ts | 18 行 | 队友提示附加内容 | -| src/utils/swarm/teammateModel.ts | 10 行 | 队友模型配置 | - -**引用该标志的文件(15 个)**: -1. src/QueryEngine.ts — 查询引擎中的协调器模式分支 -2. src/cli/print.ts — CLI 输出中的协调器模式处理 -3. src/commands/clear/conversation.ts — 清除对话时的协调器状态处理 -4. src/components/PromptInput/PromptInputFooterLeftSide.tsx — 协调器模式下的页脚左侧 UI -5. src/coordinator/coordinatorMode.ts — 协调器模式核心逻辑 -6. src/main.tsx — 主入口中的协调器模式启动 -7. src/screens/REPL.tsx — REPL 屏幕中的协调器集成 -8. src/screens/ResumeConversation.tsx — 恢复对话时的协调器处理 -9. src/tools.ts — 工具注册中的协调器工具 -10. src/tools/AgentTool/AgentTool.tsx — Agent 工具中的协调器模式分支 -11. src/tools/AgentTool/builtInAgents.ts — 内置代理定义 -12. src/utils/processUserInput/processSlashCommand.tsx — 斜杠命令处理中的协调器 -13. src/utils/sessionRestore.ts — 会话恢复中的协调器状态 -14. src/utils/systemPrompt.ts — 系统提示中的协调器指令 -15. src/utils/toolPool.ts — 工具池中的协调器工具 - -**启用所需操作**: 仅需将编译标志 `COORDINATOR_MODE` 设为 `true`。所有 7,990 行代码完整。 - ---- - -## 3. CONTEXT_COLLAPSE - -**编译时引用次数**: 23(单引号 20 + 双引号 3) -**功能描述**: 上下文折叠/分析功能。提供对话上下文的可视化分析,包括 token 使用量统计、上下文窗口利用率、自动压缩触发等。 -**分类**: COMPLETE -**启用条件**: 将 `CONTEXT_COLLAPSE` 编译标志设为 `true` - -**核心实现文件(共 2,258 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/utils/analyzeContext.ts | 1,382 行 | 上下文分析核心逻辑 | -| src/components/ContextVisualization.tsx | 488 行 | 上下文可视化 UI 组件 | -| src/commands/context/context-noninteractive.ts | 325 行 | 非交互式上下文命令 | -| src/commands/context/context.tsx | 63 行 | 交互式上下文命令入口 | - -**引用该标志的文件(13 个)**: -1. src/commands/context/context-noninteractive.ts — 非交互式上下文分析命令 -2. src/commands/context/context.tsx — 上下文命令入口 -3. src/components/ContextVisualization.tsx — 上下文可视化组件 -4. src/components/TokenWarning.tsx — Token 警告组件中的上下文折叠检测 -5. src/query.ts — 查询中的上下文折叠处理 -6. src/screens/REPL.tsx — REPL 中的上下文折叠集成 -7. src/screens/ResumeConversation.tsx — 恢复对话中的上下文折叠 -8. src/services/compact/autoCompact.ts — 自动压缩中的上下文折叠触发 -9. src/services/compact/postCompactCleanup.ts — 压缩后清理 -10. src/setup.ts — 初始化设置中的上下文折叠 -11. src/tools.ts — 工具注册 -12. src/utils/analyzeContext.ts — 上下文分析核心 -13. src/utils/sessionRestore.ts — 会话恢复 - -**启用所需操作**: 仅需将编译标志 `CONTEXT_COLLAPSE` 设为 `true`。 - ---- - -## 4. VOICE_MODE `[build: ON] [dev: ON]` - -**编译时引用次数**: 49(单引号 46 + 双引号 3) -**功能描述**: 语音模式。集成语音转文字(STT)功能,用户可以通过麦克风输入语音,实时转换为文本发送给 AI。包括语音指示器 UI、语音流处理、键绑定等。 -**分类**: COMPLETE -**启用条件**: 将 `VOICE_MODE` 编译标志设为 `true` - -**核心实现文件(共 1,410 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/hooks/useVoiceIntegration.tsx | 676 行 | 语音集成 React Hook | -| src/services/voiceStreamSTT.ts | 544 行 | 语音流式 STT(语音转文字)服务 | -| src/components/PromptInput/VoiceIndicator.tsx | 136 行 | 语音指示器 UI 组件 | -| src/voice/voiceModeEnabled.ts | 54 行 | 语音模式启用检测 | - -**引用该标志的文件(16 个)**: -1. src/commands.ts — 条件注册语音相关命令 -2. src/components/LogoV2/VoiceModeNotice.tsx — 语音模式通知 UI -3. src/components/PromptInput/Notifications.tsx — 提示输入通知中的语音状态 -4. src/components/PromptInput/PromptInputFooterLeftSide.tsx — 页脚左侧语音按钮 -5. src/components/PromptInput/VoiceIndicator.tsx — 语音指示器组件 -6. src/components/TextInput.tsx — 文本输入中的语音模式处理 -7. src/hooks/useVoiceIntegration.tsx — 语音集成 Hook -8. src/keybindings/defaultBindings.ts — 语音模式键绑定 -9. src/screens/REPL.tsx — REPL 中的语音模式集成 -10. src/services/voiceStreamSTT.ts — STT 服务 -11. src/state/AppState.tsx — 应用状态中的语音状态 -12. src/tools/ConfigTool/ConfigTool.ts — 配置工具中的语音设置 -13. src/tools/ConfigTool/prompt.ts — 配置工具提示 -14. src/tools/ConfigTool/supportedSettings.ts — 支持的设置项 -15. src/utils/settings/types.ts — 设置类型定义 -16. src/voice/voiceModeEnabled.ts — 语音模式启用逻辑 - -**启用所需操作**: 仅需将编译标志 `VOICE_MODE` 设为 `true`。 - ---- - -## 5. TEAMMEM - -**编译时引用次数**: 53(单引号 51 + 双引号 2) -**功能描述**: 团队记忆功能。允许团队成员之间共享和同步记忆文件(CLAUDE.md),包括记忆提取、秘密过滤、文件选择器、折叠显示等。 -**分类**: COMPLETE -**启用条件**: 将 `TEAMMEM` 编译标志设为 `true` - -**核心实现文件(共 1,026 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/components/memory/MemoryFileSelector.tsx | 437 行 | 记忆文件选择器 UI | -| src/services/teamMemorySync/watcher.ts | 387 行 | 团队记忆文件监视器 | -| src/components/messages/teamMemCollapsed.tsx | 139 行 | 团队记忆折叠显示组件 | -| src/services/teamMemorySync/teamMemSecretGuard.ts | 44 行 | 团队记忆秘密过滤器 | -| src/components/messages/teamMemSaved.ts | 19 行 | 团队记忆保存状态 | - -**引用该标志的文件(17 个)**: -1. src/components/memory/MemoryFileSelector.tsx — 记忆文件选择器 -2. src/components/messages/CollapsedReadSearchContent.tsx — 折叠的读取/搜索内容 -3. src/components/messages/SystemTextMessage.tsx — 系统消息中的团队记忆显示 -4. src/components/messages/teamMemCollapsed.tsx — 团队记忆折叠组件 -5. src/components/messages/teamMemSaved.ts — 保存状态 -6. src/memdir/memdir.ts — 记忆目录操作 -7. src/services/extractMemories/extractMemories.ts — 记忆提取中的团队记忆 -8. src/services/extractMemories/prompts.ts — 记忆提取提示 -9. src/services/teamMemorySync/teamMemSecretGuard.ts — 秘密过滤 -10. src/services/teamMemorySync/watcher.ts — 文件监视 -11. src/setup.ts — 初始化中的团队记忆设置 -12. src/utils/claudemd.ts — CLAUDE.md 处理 -13. src/utils/collapseReadSearch.ts — 折叠读取/搜索 -14. src/utils/config.ts — 配置中的团队记忆 -15. src/utils/memory/types.ts — 记忆类型定义 -16. src/utils/memoryFileDetection.ts — 记忆文件检测 -17. src/utils/sessionFileAccessHooks.ts — 会话文件访问钩子 - -**启用所需操作**: 仅需将编译标志 `TEAMMEM` 设为 `true`。 - ---- - -## 6. COMMIT_ATTRIBUTION - -**编译时引用次数**: 12 -**功能描述**: 提交归属功能。在 git 提交中标记哪些代码是由 AI 生成的,包括 git trailer、统计信息、提交后处理等。 -**分类**: COMPLETE -**启用条件**: 将 `COMMIT_ATTRIBUTION` 编译标志设为 `true` - -**核心实现文件(共 1,354 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/utils/commitAttribution.ts | 961 行 | 提交归属核心逻辑 | -| src/utils/attribution.ts | 393 行 | 归属计算与标记 | - -**引用该标志的文件(9 个)**: -1. src/cli/print.ts — CLI 输出中的归属信息 -2. src/commands/clear/caches.ts — 清除缓存中的归属数据 -3. src/screens/REPL.tsx — REPL 中的归属集成 -4. src/services/compact/postCompactCleanup.ts — 压缩后的归属清理 -5. src/setup.ts — 初始化中的归属设置 -6. src/utils/attribution.ts — 归属核心 -7. src/utils/sessionRestore.ts — 会话恢复中的归属 -8. src/utils/shell/bashProvider.ts — Bash 提供者中的归属钩子(255 行) -9. src/utils/worktree.ts — 工作树中的归属处理(1,519 行) - -**启用所需操作**: 仅需将编译标志 `COMMIT_ATTRIBUTION` 设为 `true`。 - ---- - -## 7. ULTRAPLAN - -**编译时引用次数**: 10 -**功能描述**: 超级计划模式。提供增强版的计划功能,允许用户创建更详细、更结构化的执行计划。 -**分类**: COMPLETE -**启用条件**: 将 `ULTRAPLAN` 编译标志设为 `true` - -**核心实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/commands/ultraplan.tsx | 470 行 | 超级计划命令完整实现 | - -**引用该标志的文件(5 个)**: -1. src/commands.ts — 条件注册 `/ultraplan` 命令 -2. src/components/PromptInput/PromptInput.tsx — 提示输入中的超级计划处理 -3. src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx — 退出计划模式权限 -4. src/screens/REPL.tsx — REPL 中的超级计划集成 -5. src/utils/processUserInput/processUserInput.ts — 用户输入处理 - -**启用所需操作**: 仅需将编译标志 `ULTRAPLAN` 设为 `true`。 - ---- - -## 8. BASH_CLASSIFIER - -**编译时引用次数**: 49(单引号 45 + 双引号 4) -**功能描述**: Bash 命令分类器。对用户请求执行的 Bash 命令进行安全分类,决定是否需要用户确认。支持自动模式(YOLO mode)下的智能权限判断。 -**分类**: COMPLETE -**启用条件**: 将 `BASH_CLASSIFIER` 编译标志设为 `true` - -**实现分布**: 该功能的代码分布在权限系统、工具系统和 UI 组件的 19 个文件中,与现有权限架构深度集成。 - -**引用该标志的文件(20 个)**: -1. src/cli/structuredIO.ts — 结构化 IO 中的分类器输出 -2. src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx — 工具成功消息中的分类器信息 -3. src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx — Bash 权限请求 UI -4. src/components/permissions/PermissionDecisionDebugInfo.tsx — 权限决策调试信息 -5. src/components/permissions/PermissionRuleExplanation.tsx — 权限规则解释 -6. src/components/permissions/hooks.ts — 权限 Hooks -7. src/hooks/toolPermission/PermissionContext.ts — 权限上下文 -8. src/hooks/toolPermission/handlers/coordinatorHandler.ts — 协调器权限处理 -9. src/hooks/toolPermission/handlers/interactiveHandler.ts — 交互式权限处理 -10. src/hooks/toolPermission/handlers/swarmWorkerHandler.ts — Swarm 工作者权限处理 -11. src/hooks/toolPermission/permissionLogging.ts — 权限日志 -12. src/hooks/useCanUseTool.tsx — 工具可用性检查 -13. src/services/api/withRetry.ts — API 重试中的分类器 -14. src/tools/BashTool/bashPermissions.ts — Bash 权限逻辑 -15. src/tools/BashTool/pathValidation.ts — 路径验证 -16. src/utils/classifierApprovals.ts — 分类器审批记录 -17. src/utils/messages.ts — 消息处理 -18. src/utils/permissions/permissions.ts — 权限核心 -19. src/utils/permissions/yoloClassifier.ts — YOLO 模式分类器 -20. src/utils/swarm/inProcessRunner.ts — 进程内运行器中的分类器 - -**启用所需操作**: 仅需将编译标志 `BASH_CLASSIFIER` 设为 `true`。 - ---- - -## 9. TRANSCRIPT_CLASSIFIER `[dev: ON]` - -**编译时引用次数**: 110(单引号 107 + 双引号 3) -**功能描述**: 转录分类器。这是引用次数第二多的标志,与自动模式(Auto Mode)权限系统深度集成。对整个对话转录进行分析,判断 AI 请求的工具调用是否安全。 -**分类**: COMPLETE -**启用条件**: 将 `TRANSCRIPT_CLASSIFIER` 编译标志设为 `true` - -**实现分布**: 该功能的代码分布在 44 个文件中,是除 KAIROS 外集成最广泛的功能。 - -**引用该标志的文件(44 个)**: -1. src/cli/print.ts — CLI 输出 -2. src/cli/structuredIO.ts — 结构化 IO -3. src/commands/login/login.tsx — 登录命令 -4. src/components/PromptInput/PromptInput.tsx — 提示输入 -5. src/components/Settings/Config.tsx — 设置配置 -6. src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx — 工具错误消息 -7. src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx — 工具成功消息 -8. src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx — 退出计划模式权限 -9. src/components/permissions/PermissionDecisionDebugInfo.tsx — 权限决策调试 -10. src/components/permissions/PermissionRuleExplanation.tsx — 权限规则解释 -11. src/components/permissions/hooks.ts — 权限 Hooks -12. src/constants/betas.ts — Beta 常量 -13. src/hooks/notifs/useAutoModeUnavailableNotification.ts — 自动模式不可用通知 -14. src/hooks/toolPermission/PermissionContext.ts — 权限上下文 -15. src/hooks/toolPermission/handlers/interactiveHandler.ts — 交互式处理 -16. src/hooks/toolPermission/permissionLogging.ts — 权限日志 -17. src/hooks/useCanUseTool.tsx — 工具可用性 -18. src/hooks/useReplBridge.tsx — REPL 桥接 -19. src/interactiveHelpers.tsx — 交互帮助函数 -20. src/main.tsx — 主入口 -21. src/migrations/resetAutoModeOptInForDefaultOffer.ts — 迁移脚本 -22. src/screens/REPL.tsx — REPL 屏幕 -23. src/services/api/claude.ts — Claude API 服务 -24. src/services/tools/toolExecution.ts — 工具执行 -25. src/tools/AgentTool/AgentTool.tsx — Agent 工具 -26. src/tools/AgentTool/agentToolUtils.ts — Agent 工具工具函数 -27. src/tools/AgentTool/runAgent.ts — 运行 Agent -28. src/tools/BashTool/bashPermissions.ts — Bash 权限 -29. src/tools/ConfigTool/supportedSettings.ts — 支持的设置 -30. src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts — 退出计划模式工具 -31. src/tools/NotebookEditTool/NotebookEditTool.ts — Notebook 编辑工具 -32. src/types/permissions.ts — 权限类型 -33. src/utils/attachments.ts — 附件处理 -34. src/utils/autoModeDenials.ts — 自动模式拒绝 -35. src/utils/betas.ts — Beta 工具 -36. src/utils/classifierApprovals.ts — 分类器审批 -37. src/utils/permissions/PermissionMode.ts — 权限模式 -38. src/utils/permissions/autoModeState.ts — 自动模式状态 -39. src/utils/permissions/bypassPermissionsKillswitch.ts — 绕过权限 Kill Switch -40. src/utils/permissions/getNextPermissionMode.ts — 获取下一个权限模式 -41. src/utils/permissions/permissionSetup.ts — 权限设置 -42. src/utils/permissions/permissions.ts — 权限核心 -43. src/utils/permissions/yoloClassifier.ts — YOLO 分类器 -44. src/utils/settings/settings.ts — 设置 -45. src/utils/settings/types.ts — 设置类型 -46. src/utils/toolResultStorage.ts — 工具结果存储 - -**启用所需操作**: 仅需将编译标志 `TRANSCRIPT_CLASSIFIER` 设为 `true`。 - ---- - -## 10. EXTRACT_MEMORIES - -**编译时引用次数**: 7 -**功能描述**: 记忆提取功能。从对话中自动提取有用的记忆信息并保存到记忆文件中。 -**分类**: COMPLETE -**启用条件**: 将 `EXTRACT_MEMORIES` 编译标志设为 `true` - -**核心实现文件(共 769 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/services/extractMemories/extractMemories.ts | 615 行 | 记忆提取核心算法 | -| src/services/extractMemories/prompts.ts | 154 行 | 记忆提取的 AI 提示词 | - -**引用该标志的文件(4 个)**: -1. src/cli/print.ts — CLI 输出中的记忆提取信息 -2. src/memdir/paths.ts — 记忆目录路径 -3. src/query/stopHooks.ts — 查询停止钩子中触发记忆提取 -4. src/utils/backgroundHousekeeping.ts — 后台维护中的记忆提取 - -**启用所需操作**: 仅需将编译标志 `EXTRACT_MEMORIES` 设为 `true`。 - ---- - -## 11. CACHED_MICROCOMPACT - -**编译时引用次数**: 12 -**功能描述**: 缓存微压缩功能。在对话压缩时使用缓存策略优化性能。 -**分类**: COMPLETE -**启用条件**: 将 `CACHED_MICROCOMPACT` 编译标志设为 `true` - -**实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/services/compact/microCompact.ts | 530 行 | 微压缩核心实现 | - -**引用该标志的文件(5 个)**: -1. src/constants/prompts.ts — 提示词常量 -2. src/query.ts — 查询引擎 -3. src/services/api/claude.ts — Claude API 服务 -4. src/services/api/logging.ts — API 日志 -5. src/services/compact/microCompact.ts — 微压缩核心 - -**启用所需操作**: 仅需将编译标志 `CACHED_MICROCOMPACT` 设为 `true`。 - ---- - -## 12. TOKEN_BUDGET `[build: ON] [dev: ON]` *NEW* - -**编译时引用次数**: 9 -**功能描述**: Token 预算管理。允许设置和跟踪 token 使用预算,在接近限制时提供警告。 -**分类**: COMPLETE -**启用条件**: 将 `TOKEN_BUDGET` 编译标志设为 `true` - -**核心实现文件(共 166 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/utils/tokenBudget.ts | 73 行 | Token 预算核心逻辑 | -| src/query/tokenBudget.ts | 93 行 | 查询层的 Token 预算管理 | - -**引用该标志的文件(6 个)**: -1. src/components/PromptInput/PromptInput.tsx — 提示输入中的预算显示 -2. src/components/Spinner.tsx — 加载指示器中的预算信息 -3. src/constants/prompts.ts — 提示词中的预算指令 -4. src/query.ts — 查询引擎中的预算检查 -5. src/screens/REPL.tsx — REPL 中的预算集成 -6. src/utils/attachments.ts — 附件处理中的预算计算 - -**启用所需操作**: 仅需将编译标志 `TOKEN_BUDGET` 设为 `true`。 - ---- - -## 13. AGENT_TRIGGERS - -**编译时引用次数**: 11 -**功能描述**: 代理触发器/定时任务。允许 AI 创建、管理和执行 cron 定时任务。 -**分类**: COMPLETE -**启用条件**: 将 `AGENT_TRIGGERS` 编译标志设为 `true` - -**核心实现文件(共 543 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/tools/ScheduleCronTool/CronCreateTool.ts | 157 行 | Cron 创建工具 | -| src/tools/ScheduleCronTool/prompt.ts | 135 行 | Cron 工具提示词 | -| src/tools/ScheduleCronTool/CronListTool.ts | 97 行 | Cron 列表工具 | -| src/tools/ScheduleCronTool/CronDeleteTool.ts | 95 行 | Cron 删除工具 | -| src/tools/ScheduleCronTool/UI.tsx | 59 行 | Cron UI 组件 | - -**引用该标志的文件(6 个)**: -1. src/cli/print.ts — CLI 输出 -2. src/constants/tools.ts — 工具常量 -3. src/screens/REPL.tsx — REPL 集成 -4. src/skills/bundled/index.ts — 内置技能 -5. src/tools.ts — 工具注册 -6. src/tools/ScheduleCronTool/prompt.ts — Cron 提示词 - -**启用所需操作**: 仅需将编译标志 `AGENT_TRIGGERS` 设为 `true`。 - ---- - -## 14. REACTIVE_COMPACT - -**编译时引用次数**: 5(单引号 4 + 双引号 1) -**功能描述**: 响应式压缩。根据上下文使用情况动态触发对话压缩。 -**分类**: COMPLETE -**启用条件**: 将 `REACTIVE_COMPACT` 编译标志设为 `true` - -**实现文件(压缩服务已完整,共 2,586 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/services/compact/compact.ts | 1,705 行 | 压缩核心逻辑 | -| src/services/compact/microCompact.ts | 530 行 | 微压缩 | -| src/services/compact/autoCompact.ts | 351 行 | 自动压缩触发 | - -**引用该标志的文件(5 个)**: -1. src/commands/compact/compact.ts — 压缩命令 -2. src/components/TokenWarning.tsx — Token 警告 -3. src/query.ts — 查询引擎 -4. src/services/compact/autoCompact.ts — 自动压缩 -5. src/utils/analyzeContext.ts — 上下文分析 - -**启用所需操作**: 仅需将编译标志 `REACTIVE_COMPACT` 设为 `true`。 - ---- - -## 15. KAIROS_BRIEF - -**编译时引用次数**: 39 -**功能描述**: Kairos Brief 功能。提供简报工具,允许 AI 生成和管理项目简报。 -**分类**: COMPLETE -**启用条件**: 将 `KAIROS_BRIEF` 编译标志设为 `true` - -**核心实现文件(共 334 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/tools/BriefTool/BriefTool.ts | 204 行 | Brief 工具核心 | -| src/commands/brief.ts | 130 行 | Brief 命令实现 | - -**引用该标志的文件(20 个)**: -1. src/commands.ts — 命令注册 -2. src/commands/brief.ts — Brief 命令 -3. src/components/Messages.tsx — 消息组件 -4. src/components/PromptInput/Notifications.tsx — 通知 -5. src/components/PromptInput/PromptInput.tsx — 提示输入 -6. src/components/PromptInput/PromptInputQueuedCommands.tsx — 排队命令 -7. src/components/Settings/Config.tsx — 设置 -8. src/components/Spinner.tsx — 加载指示器 -9. src/components/messages/UserPromptMessage.tsx — 用户提示消息 -10. src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx — 工具成功消息 -11. src/constants/prompts.ts — 提示词 -12. src/hooks/useGlobalKeybindings.tsx — 全局键绑定 -13. src/keybindings/defaultBindings.ts — 默认键绑定 -14. src/main.tsx — 主入口 -15. src/tools/BriefTool/BriefTool.ts — Brief 工具 -16. src/tools/ToolSearchTool/prompt.ts — 工具搜索提示 -17. src/utils/attachments.ts — 附件 -18. src/utils/conversationRecovery.ts — 对话恢复 -19. src/utils/permissions/permissionRuleParser.ts — 权限规则解析 -20. src/utils/settings/types.ts — 设置类型 - -**启用所需操作**: 仅需将编译标志 `KAIROS_BRIEF` 设为 `true`。 - ---- - -## 16. CCR_REMOTE_SETUP - -**编译时引用次数**: 1 -**功能描述**: CCR(Claude Code Remote)远程设置命令。 -**分类**: COMPLETE -**启用条件**: 将 `CCR_REMOTE_SETUP` 编译标志设为 `true` - -**引用该标志的文件(1 个)**: -1. src/commands.ts — 条件注册远程设置命令 - -**启用所需操作**: 仅需将编译标志 `CCR_REMOTE_SETUP` 设为 `true`。命令文件通过条件 require 加载。 - ---- - -## 17. SHOT_STATS `[build: ON] [dev: ON]` *NEW* - -**编译时引用次数**: 10 -**功能描述**: 统计功能。提供详细的会话统计信息,包括 token 使用、工具调用、时间统计等,带有完整的 UI 面板。 -**分类**: COMPLETE -**启用条件**: 将 `SHOT_STATS` 编译标志设为 `true` - -**核心实现文件(共 2,722 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/components/Stats.tsx | 1,227 行 | 统计 UI 组件 | -| src/utils/stats.ts | 1,061 行 | 统计核心逻辑 | -| src/utils/statsCache.ts | 434 行 | 统计缓存 | - -**引用该标志的文件(3 个)**: -1. src/components/Stats.tsx — 统计 UI -2. src/utils/stats.ts — 统计核心 -3. src/utils/statsCache.ts — 统计缓存 - -**启用所需操作**: 仅需将编译标志 `SHOT_STATS` 设为 `true`。 - ---- - -## 18. BG_SESSIONS - -**编译时引用次数**: 11 -**功能描述**: 后台会话功能。支持对话恢复和并发会话管理,允许会话在后台继续运行。 -**分类**: COMPLETE -**启用条件**: 将 `BG_SESSIONS` 编译标志设为 `true` - -**核心实现文件(共 801 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/utils/conversationRecovery.ts | 597 行 | 对话恢复逻辑 | -| src/utils/concurrentSessions.ts | 204 行 | 并发会话管理 | - -**引用该标志的文件(7 个)**: -1. src/commands/exit/exit.tsx — 退出命令中的后台会话处理 -2. src/entrypoints/cli.tsx — CLI 入口中的后台会话 -3. src/main.tsx — 主入口 -4. src/query.ts — 查询引擎 -5. src/screens/REPL.tsx — REPL 集成 -6. src/utils/concurrentSessions.ts — 并发会话 -7. src/utils/conversationRecovery.ts — 对话恢复 - -**启用所需操作**: 仅需将编译标志 `BG_SESSIONS` 设为 `true`。 - ---- - -## 19. PROACTIVE - -**编译时引用次数**: 37 -**功能描述**: 主动模式。AI 可以在没有用户输入的情况下主动发起操作或建议。 -**分类**: COMPLETE -**启用条件**: 将 `PROACTIVE` 编译标志设为 `true` - -**核心实现文件(共 63 行,注意:大部分逻辑与 KAIROS 共享,通过 `feature('PROACTIVE') || feature('KAIROS')` 模式门控)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/proactive/index.ts | 57 行 | 主动模式入口 | -| src/proactive/useProactive.ts | 6 行 | 主动模式 Hook | - -**引用该标志的文件(15 个)**: -1. src/cli/print.ts — CLI 输出 -2. src/commands.ts — 命令注册(`feature('PROACTIVE') || feature('KAIROS')`) -3. src/commands/clear/conversation.ts — 清除对话 -4. src/components/Messages.tsx — 消息组件 -5. src/components/PromptInput/PromptInputFooterLeftSide.tsx — 页脚 -6. src/components/PromptInput/usePromptInputPlaceholder.ts — 输入占位符 -7. src/constants/prompts.ts — 提示词 -8. src/main.tsx — 主入口 -9. src/screens/REPL.tsx — REPL(多处引用,通过 require 加载 proactive 模块) -10. src/services/compact/prompt.ts — 压缩提示 -11. src/tools.ts — 工具注册 -12. src/tools/AgentTool/AgentTool.tsx — Agent 工具 -13. src/utils/sessionStorage.ts — 会话存储 -14. src/utils/settings/types.ts — 设置类型 -15. src/utils/systemPrompt.ts — 系统提示 - -**特殊说明**: PROACTIVE 在代码中几乎总是与 KAIROS 一起使用(`feature('PROACTIVE') || feature('KAIROS')`),意味着启用 KAIROS 也会启用主动功能。PROACTIVE 模块文件(src/proactive/)存在且有内容。 - -**启用所需操作**: 仅需将编译标志 `PROACTIVE` 设为 `true`。 - ---- - -## 20. CHICAGO_MCP `[build: ON] [dev: ON]` - -**编译时引用次数**: 16 -**功能描述**: Chicago MCP(Computer Use 计算机使用)。集成计算机使用功能,允许 AI 控制桌面应用程序。 -**分类**: COMPLETE -**启用条件**: 将 `CHICAGO_MCP` 编译标志设为 `true` - -**核心实现文件(共 421 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/utils/computerUse/wrapper.tsx | 335 行 | 计算机使用包装器 | -| src/utils/computerUse/cleanup.ts | 86 行 | 计算机使用清理 | - -**引用该标志的文件(10 个)**: -1. src/entrypoints/cli.tsx — CLI 入口 -2. src/main.tsx — 主入口 -3. src/query.ts — 查询引擎 -4. src/query/stopHooks.ts — 停止钩子 -5. src/services/analytics/metadata.ts — 分析元数据 -6. src/services/mcp/client.ts — MCP 客户端 -7. src/services/mcp/config.ts — MCP 配置 -8. src/state/AppStateStore.ts — 应用状态 -9. src/utils/computerUse/cleanup.ts — 清理 -10. src/utils/computerUse/wrapper.tsx — 包装器 - -**启用所需操作**: 仅需将编译标志 `CHICAGO_MCP` 设为 `true`。 - ---- - -## 21. VERIFICATION_AGENT - -**编译时引用次数**: 4 -**功能描述**: 验证代理。内置代理类型,用于验证任务执行结果的正确性。 -**分类**: COMPLETE -**启用条件**: 将 `VERIFICATION_AGENT` 编译标志设为 `true` - -**核心实现文件(共 478 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/tools/TaskUpdateTool/TaskUpdateTool.ts | 406 行 | 任务更新工具 | -| src/tools/AgentTool/builtInAgents.ts | 72 行 | 内置代理定义 | - -**引用该标志的文件(4 个)**: -1. src/constants/prompts.ts — 提示词 -2. src/tools/AgentTool/builtInAgents.ts — 内置代理 -3. src/tools/TaskUpdateTool/TaskUpdateTool.ts — 任务更新工具 -4. src/tools/TodoWriteTool/TodoWriteTool.ts — TodoWrite 工具 - -**启用所需操作**: 仅需将编译标志 `VERIFICATION_AGENT` 设为 `true`。 - ---- - -## 22. PROMPT_CACHE_BREAK_DETECTION `[build: ON] [dev: ON]` *NEW* - -**编译时引用次数**: 9 -**功能描述**: 提示缓存中断检测。检测提示缓存是否被意外破坏,并在压缩时考虑缓存状态。 -**分类**: COMPLETE -**启用条件**: 将 `PROMPT_CACHE_BREAK_DETECTION` 编译标志设为 `true` - -**引用该标志的文件(6 个)**: -1. src/commands/compact/compact.ts — 压缩命令 -2. src/services/api/claude.ts — Claude API 服务 -3. src/services/compact/autoCompact.ts — 自动压缩 -4. src/services/compact/compact.ts — 压缩核心 -5. src/services/compact/microCompact.ts — 微压缩 -6. src/tools/AgentTool/runAgent.ts — 运行 Agent - -**启用所需操作**: 仅需将编译标志 `PROMPT_CACHE_BREAK_DETECTION` 设为 `true`。 - ---- - -# 二、PARTIAL(部分实现)— 共 19 个 - -以下标志有实质性的功能代码,但存在缺失的文件(命令入口、组件等)或关键模块仅有空壳。启用后可能报错或功能不完整。 - ---- - -## 23. KAIROS - -**编译时引用次数**: 156(单引号 154 + 双引号 2) -**功能描述**: Kairos 是 Claude Code 最大的功能集合。它是一个综合性平台功能,涵盖频道通知、主动模式、简报、GitHub Webhook、推送通知等多个子系统。几乎贯穿整个代码库。 -**分类**: PARTIAL -**缺失原因**: `src/commands/assistant/` 目录完全缺失(包括 `index.ts` 和 `gate.ts`),但 `src/commands.ts` 中通过条件 require 引用了 `commands/assistant/index.js` - -**引用该标志的文件(59 个)**: -1. src/bridge/bridgeMain.ts -2. src/bridge/initReplBridge.ts -3. src/cli/print.ts -4. src/commands.ts -5. src/commands/bridge/bridge.tsx -6. src/commands/brief.ts -7. src/commands/clear/conversation.ts -8. src/components/LogoV2/ChannelsNotice.tsx -9. src/components/LogoV2/LogoV2.tsx -10. src/components/Messages.tsx -11. src/components/PromptInput/Notifications.tsx -12. src/components/PromptInput/PromptInput.tsx -13. src/components/PromptInput/PromptInputFooterLeftSide.tsx -14. src/components/PromptInput/PromptInputQueuedCommands.tsx -15. src/components/PromptInput/usePromptInputPlaceholder.ts -16. src/components/Settings/Config.tsx -17. src/components/Spinner.tsx -18. src/components/StatusLine.tsx -19. src/components/messages/UserPromptMessage.tsx -20. src/components/messages/UserTextMessage.tsx -21. src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx -22. src/constants/prompts.ts -23. src/hooks/toolPermission/handlers/interactiveHandler.ts -24. src/hooks/useAssistantHistory.ts -25. src/hooks/useCanUseTool.tsx -26. src/hooks/useGlobalKeybindings.tsx -27. src/hooks/useReplBridge.tsx -28. src/interactiveHelpers.tsx -29. src/keybindings/defaultBindings.ts -30. src/main.tsx -31. src/memdir/memdir.ts -32. src/memdir/paths.ts -33. src/screens/REPL.tsx -34. src/services/analytics/metadata.ts -35. src/services/compact/compact.ts -36. src/services/compact/prompt.ts -37. src/services/mcp/channelNotification.ts -38. src/services/mcp/useManageMCPConnections.ts -39. src/skills/bundled/index.ts -40. src/tools.ts -41. src/tools/AgentTool/AgentTool.tsx -42. src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx -43. src/tools/BashTool/BashTool.tsx -44. src/tools/BriefTool/BriefTool.ts -45. src/tools/ConfigTool/supportedSettings.ts -46. src/tools/EnterPlanModeTool/EnterPlanModeTool.ts -47. src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts -48. src/tools/PowerShellTool/PowerShellTool.tsx -49. src/tools/ScheduleCronTool/prompt.ts -50. src/tools/ToolSearchTool/prompt.ts -51. src/utils/attachments.ts -52. src/utils/conversationRecovery.ts -53. src/utils/messageQueueManager.ts -54. src/utils/messages.ts -55. src/utils/permissions/permissionRuleParser.ts -56. src/utils/processUserInput/processSlashCommand.tsx -57. src/utils/sessionStorage.ts -58. src/utils/settings/types.ts -59. src/utils/systemPrompt.ts - -**缺失文件**: -- src/commands/assistant/index.ts — 完全缺失(src/commands.ts 第 69 行引用了 `commands/assistant/index.js`) -- src/commands/assistant/gate.ts — 完全缺失 - -**启用所需修复**: 需要创建 `src/commands/assistant/` 目录及其 `index.ts` 和 `gate.ts` 文件。 - ---- - -## 24. BUDDY `[dev: ON]` - -**编译时引用次数**: 18(单引号 16 + 双引号 2) -**功能描述**: 伙伴精灵功能。在 CLI 中显示一个可爱的像素精灵角色作为 AI 助手的化身,有动画、表情、通知等。 -**分类**: PARTIAL -**缺失原因**: `src/commands/buddy/index.ts` 命令入口文件缺失,但 `src/buddy/` 目录下有完整的 1,298 行实现代码 - -**核心实现文件(src/buddy/ 目录,共 1,298 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/buddy/sprites.ts | 514 行 | 精灵图形定义 | -| src/buddy/CompanionSprite.tsx | 370 行 | 精灵 React 组件 | -| src/buddy/types.ts | 148 行 | 类型定义 | -| src/buddy/companion.ts | 133 行 | 伙伴核心逻辑 | -| src/buddy/useBuddyNotification.tsx | 97 行 | 伙伴通知 Hook | -| src/buddy/prompt.ts | 36 行 | 伙伴提示词 | - -**引用该标志的文件(8 个)**: -1. src/buddy/CompanionSprite.tsx — 精灵组件 -2. src/buddy/prompt.ts — 提示词 -3. src/buddy/useBuddyNotification.tsx — 通知 -4. src/commands.ts — 条件注册 `/buddy` 命令(引用 `commands/buddy/index.js`) -5. src/components/PromptInput/PromptInput.tsx — 提示输入 -6. src/screens/REPL.tsx — REPL 集成 -7. src/utils/attachments.ts — 附件 - -**缺失文件**: -- src/commands/buddy/index.ts — 命令入口缺失 - -**启用所需修复**: 需要创建 `src/commands/buddy/index.ts` 命令入口文件。 - ---- - -## 25. MONITOR_TOOL - -**编译时引用次数**: 13 -**功能描述**: 监控工具。允许 AI 在后台启动长时间运行的 shell 任务并监控其输出。 -**分类**: PARTIAL -**缺失原因**: MonitorMcpDetailDialog 和 MonitorPermissionRequest 文件虽然存在但仅有 3 行空壳 - -**核心实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/tasks/LocalShellTask/LocalShellTask.tsx | 522 行 | 本地 Shell 任务完整实现 | -| src/tools/MonitorTool/MonitorTool.ts | 1 行 | 监控工具(桩) | -| src/tasks/MonitorMcpTask/MonitorMcpTask.ts | 5 行 | MCP 监控任务(桩) | -| src/components/tasks/MonitorMcpDetailDialog.tsx | 3 行 | MCP 详情对话框(桩) | -| src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.tsx | 3 行 | 监控权限请求(桩) | - -**引用该标志的文件(9 个)**: -1. src/components/permissions/PermissionRequest.tsx — 权限请求 -2. src/components/tasks/BackgroundTasksDialog.tsx — 后台任务对话框 -3. src/tasks.ts — 任务注册 -4. src/tasks/LocalShellTask/LocalShellTask.tsx — Shell 任务 -5. src/tools.ts — 工具注册 -6. src/tools/AgentTool/runAgent.ts — Agent 运行 -7. src/tools/BashTool/BashTool.tsx — Bash 工具 -8. src/tools/BashTool/prompt.ts — Bash 提示 -9. src/tools/PowerShellTool/PowerShellTool.tsx — PowerShell 工具 - -**启用所需修复**: 需要实现 `src/tools/MonitorTool/MonitorTool.ts`、`src/tasks/MonitorMcpTask/MonitorMcpTask.ts`、`src/components/tasks/MonitorMcpDetailDialog.tsx` 和 `src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.tsx`。 - ---- - -## 26. HISTORY_SNIP - -**编译时引用次数**: 16(单引号 15 + 双引号 1) -**功能描述**: 历史剪辑。允许从对话历史中剪切特定片段。 -**分类**: PARTIAL -**缺失原因**: `src/commands/force-snip.ts` 命令文件缺失 - -**引用该标志的文件(8 个)**: -1. src/QueryEngine.ts — 查询引擎 -2. src/commands.ts — 命令注册(引用 `commands/force-snip.js`) -3. src/components/Message.tsx — 消息组件 -4. src/query.ts — 查询 -5. src/tools.ts — 工具注册 -6. src/utils/attachments.ts — 附件 -7. src/utils/collapseReadSearch.ts — 折叠读取搜索 -8. src/utils/messages.ts — 消息处理 - -**缺失文件**: -- src/commands/force-snip.ts — 命令文件缺失 - -**启用所需修复**: 需要创建 `src/commands/force-snip.ts`。 - ---- - -## 27. WORKFLOW_SCRIPTS - -**编译时引用次数**: 10 -**功能描述**: 工作流脚本。允许定义和执行自定义工作流。 -**分类**: PARTIAL -**缺失原因**: 多个核心文件仅有 1-5 行空壳,命令入口目录缺失 - -**实现文件(大部分为空壳)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/components/WorkflowMultiselectDialog.tsx | 127 行 | 工作流多选对话框(有内容) | -| src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts | 5 行 | 本地工作流任务(桩) | -| src/components/tasks/WorkflowDetailDialog.tsx | 3 行 | 工作流详情对话框(桩) | -| src/tools/WorkflowTool/WorkflowPermissionRequest.tsx | 3 行 | 工作流权限请求(桩) | -| src/tools/WorkflowTool/createWorkflowCommand.ts | 3 行 | 创建工作流命令(桩) | -| src/tools/WorkflowTool/WorkflowTool.ts | 1 行 | 工作流工具(桩) | -| src/tools/WorkflowTool/constants.ts | 1 行 | 常量(桩) | - -**引用该标志的文件(7 个)**: -1. src/commands.ts — 命令注册(引用 `commands/workflows/index.js`) -2. src/components/permissions/PermissionRequest.tsx — 权限请求 -3. src/components/tasks/BackgroundTasksDialog.tsx — 后台任务 -4. src/constants/tools.ts — 工具常量 -5. src/tasks.ts — 任务注册 -6. src/tools.ts — 工具注册 -7. src/utils/permissions/classifierDecision.ts — 分类器决策 - -**缺失文件**: -- src/commands/workflows/index.ts — 命令入口目录缺失 - -**启用所需修复**: 需要实现所有空壳文件并创建命令入口。 - ---- - -## 28. UDS_INBOX - -**编译时引用次数**: 18(历史快照) -**功能描述**: 本机进程间通信能力。当前由两层组成: -1. `udsMessaging` / `udsClient`:通用 UDS 消息层,供 `SendMessageTool` 与 `/peers` 使用。 -2. `pipeTransport` / `pipeRegistry`:会话级 named-pipe 协调层,供 `/pipes`、`/attach`、`/detach`、`/send`、`/pipe-status`、`/history`、`/claim-main` 使用。 - -**当前分类**: IMPLEMENTED / EXPERIMENTAL - -**当前事实**: -- `src/utils/udsMessaging.ts` 与 `src/utils/udsClient.ts` 已实现,不再是空壳。 -- `src/utils/pipeTransport.ts` 使用本机 named pipe / Unix socket;`localIp` / `hostname` / `machineId` 仅用于注册表展示与身份判定,不是已上线的局域网传输层。 -- `src/screens/REPL.tsx` 内联承载当前有效的 pipe 控制平面;早期 hook 试验路径已清理。 - -**核心实现文件**: - -| 文件路径 | 功能说明 | -|----------|----------| -| src/utils/udsMessaging.ts | 通用 UDS server / inbox | -| src/utils/udsClient.ts | 通用 peer 发现、探活、发送 | -| src/utils/pipeTransport.ts | named-pipe server/client、探活、AppState 扩展 | -| src/utils/pipeRegistry.ts | main/sub 注册表、machineId、claim-main | -| src/commands/peers/peers.ts | UDS peer 可达性检查 | -| src/commands/pipes/pipes.ts | pipe 注册表检查与选择器入口 | -| src/commands/attach/attach.ts | master -> slave attach | -| src/screens/REPL.tsx | 当前生效的 REPL pipe bootstrap 与心跳 | - -**备注**: 如需真实局域网通信,需要单独引入 TCP/WebSocket 传输、认证与发现机制;现有代码尚未实现该层。详见 `docs/features/uds-inbox.md`。 - ---- - -## 29. KAIROS_CHANNELS - -**编译时引用次数**: 21(单引号 19 + 双引号 2) -**功能描述**: Kairos 频道功能。MCP 频道通知系统。 -**分类**: PARTIAL -**缺失原因**: 依赖 KAIROS 的 assistant/gate.ts 模块 - -**核心实现文件(共 581 行)**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/services/mcp/channelNotification.ts | 316 行 | 频道通知服务 | -| src/components/LogoV2/ChannelsNotice.tsx | 265 行 | 频道通知 UI | - -**引用该标志的文件(15 个)**: -1. src/cli/print.ts -2. src/components/LogoV2/ChannelsNotice.tsx -3. src/components/LogoV2/LogoV2.tsx -4. src/components/messages/UserTextMessage.tsx -5. src/hooks/toolPermission/handlers/interactiveHandler.ts -6. src/hooks/useCanUseTool.tsx -7. src/interactiveHelpers.tsx -8. src/main.tsx -9. src/services/mcp/channelNotification.ts -10. src/services/mcp/useManageMCPConnections.ts -11. src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx -12. src/tools/EnterPlanModeTool/EnterPlanModeTool.ts -13. src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts -14. src/utils/messageQueueManager.ts -15. src/utils/messages.ts - -**启用所需修复**: 需先修复 KAIROS 的缺失文件。 - ---- - -## 30. FORK_SUBAGENT - -**编译时引用次数**: 5(单引号 4 + 双引号 1) -**功能描述**: 分叉子代理。允许从当前会话分叉出独立的子代理进程。 -**分类**: PARTIAL -**缺失原因**: `src/commands/fork/index.ts` 命令入口缺失(注意:代码中引用的是 `commands/branch/index.js`,而 `src/commands/branch/index.ts` 存在) - -**核心实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/tools/AgentTool/forkSubagent.ts | 210 行 | 分叉子代理核心逻辑 | - -**引用该标志的文件(5 个)**: -1. src/commands.ts — 命令注册 -2. src/commands/branch/index.ts — 分支命令入口 -3. src/components/messages/UserTextMessage.tsx — 用户消息 -4. src/tools/AgentTool/forkSubagent.ts — 分叉逻辑 -5. src/tools/ToolSearchTool/prompt.ts — 工具搜索提示 - -**缺失文件**: -- src/commands/fork/index.ts — 命令入口缺失(但 branch/index.ts 存在,可能是重命名) - -**启用所需修复**: 需确认命令入口路径是否正确。 - ---- - -## 31. EXPERIMENTAL_SKILL_SEARCH - -**编译时引用次数**: 21 -**功能描述**: 实验性技能搜索。本地技能搜索功能。 -**分类**: PARTIAL -**缺失原因**: 核心搜索逻辑可能不完整(SkillTool.ts 有 1,108 行但 localSearch 功能可能缺失) - -**引用该标志的文件(9 个)**: -1. src/commands.ts — 命令注册 -2. src/components/messages/AttachmentMessage.tsx — 附件消息 -3. src/constants/prompts.ts — 提示词 -4. src/query.ts — 查询 -5. src/services/compact/compact.ts — 压缩 -6. src/services/mcp/useManageMCPConnections.ts — MCP 连接管理 -7. src/tools/SkillTool/SkillTool.ts — 技能工具(1,108 行) -8. src/utils/attachments.ts — 附件 -9. src/utils/messages.ts — 消息 - ---- - -## 32. WEB_BROWSER_TOOL - -**编译时引用次数**: 4 -**功能描述**: Web 浏览器工具。允许 AI 在面板中打开和操作网页。 -**分类**: PARTIAL -**缺失原因**: `src/tools/WebBrowserTool/WebBrowserPanel.tsx` 仅 3 行,返回 `null` - -**实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/tools/WebBrowserTool/WebBrowserPanel.tsx | 3 行 | `export function WebBrowserPanel() { return null }` | - -**引用该标志的文件(3 个)**: -1. src/main.tsx — 主入口 -2. src/screens/REPL.tsx — REPL -3. src/tools.ts — 工具注册 - -**启用所需修复**: 需要实现 `WebBrowserPanel.tsx`。 - ---- - -## 33. MCP_SKILLS - -**编译时引用次数**: 9 -**功能描述**: MCP 技能系统。通过 MCP 协议加载和运行技能。 -**分类**: PARTIAL - -**实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/skills/mcpSkillBuilders.ts | 44 行 | MCP 技能构建器 | -| src/skills/mcpSkills.ts | 3 行 | MCP 技能(桩) | - -**引用该标志的文件(3 个)**: -1. src/commands.ts — 命令注册 -2. src/services/mcp/client.ts — MCP 客户端 -3. src/services/mcp/useManageMCPConnections.ts — MCP 连接管理 - ---- - -## 34. REVIEW_ARTIFACT - -**编译时引用次数**: 4 -**功能描述**: 审查工件。允许 AI 审查和标注工件(代码片段、文档等)。 -**分类**: PARTIAL -**缺失原因**: ReviewArtifactTool.ts 仅 1 行,ReviewArtifactPermissionRequest.tsx 仅 3 行 - -**实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/tools/ReviewArtifactTool/ReviewArtifactTool.ts | 1 行 | 审查工件工具(桩) | -| src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.tsx | 3 行 | 权限请求(桩) | - -**引用该标志的文件(2 个)**: -1. src/components/permissions/PermissionRequest.tsx — 权限请求 -2. src/skills/bundled/index.ts — 内置技能 - ---- - -## 35. KAIROS_GITHUB_WEBHOOKS - -**编译时引用次数**: 4(单引号 3 + 双引号 1) -**功能描述**: Kairos GitHub Webhooks。订阅 GitHub PR 活动的 Webhook。 -**分类**: PARTIAL -**缺失原因**: `src/commands/subscribe-pr.ts` 命令文件缺失 - -**引用该标志的文件(4 个)**: -1. src/commands.ts — 命令注册(引用 `commands/subscribe-pr.js`) -2. src/components/messages/UserTextMessage.tsx — 用户消息 -3. src/hooks/useReplBridge.tsx — REPL 桥接 -4. src/tools.ts — 工具注册 - -**缺失文件**: -- src/commands/subscribe-pr.ts — 命令文件缺失 - ---- - -## 36. CONNECTOR_TEXT - -**编译时引用次数**: 8(单引号 7 + 双引号 1) -**功能描述**: 连接器文本。控制消息中的连接器文本显示方式。 -**分类**: PARTIAL - -**引用该标志的文件(5 个)**: -1. src/components/Message.tsx — 消息组件 -2. src/constants/betas.ts — Beta 常量 -3. src/services/api/claude.ts — Claude API -4. src/services/api/logging.ts — API 日志 -5. src/utils/messages.ts — 消息处理 - ---- - -## 37. TEMPLATES - -**编译时引用次数**: 6 -**功能描述**: 模板系统。支持从 Markdown 配置文件加载模板。 -**分类**: PARTIAL - -**实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/utils/markdownConfigLoader.ts | 600 行 | Markdown 配置加载器 | -| src/keybindings/template.ts | 52 行 | 模板键绑定 | - -**引用该标志的文件(5 个)**: -1. src/entrypoints/cli.tsx — CLI 入口 -2. src/query.ts — 查询 -3. src/query/stopHooks.ts — 停止钩子 -4. src/utils/markdownConfigLoader.ts — 配置加载器 -5. src/utils/permissions/filesystem.ts — 文件系统权限 - ---- - -## 38. LODESTONE - -**编译时引用次数**: 6 -**功能描述**: Lodestone 功能。具体功能不明确,可能与导航或指引相关。 -**分类**: PARTIAL - -**引用该标志的文件(4 个)**: -1. src/interactiveHelpers.tsx — 交互帮助 -2. src/main.tsx — 主入口 -3. src/utils/backgroundHousekeeping.ts — 后台维护 -4. src/utils/settings/types.ts — 设置类型 - -**说明**: 没有专属实现文件,代码散布在 4 个文件中。 - ---- - -## 39. HISTORY_PICKER - -**编译时引用次数**: 4 -**功能描述**: 历史选择器。交互式历史搜索和选择。 -**分类**: PARTIAL - -**实现文件**: - -| 文件路径 | 行数 | 功能说明 | -|----------|------|----------| -| src/hooks/useHistorySearch.ts | 303 行 | 历史搜索 Hook | - -**引用该标志的文件(2 个)**: -1. src/components/PromptInput/PromptInput.tsx — 提示输入 -2. src/hooks/useHistorySearch.ts — 历史搜索 - ---- - -## 40. MESSAGE_ACTIONS - -**编译时引用次数**: 5 -**功能描述**: 消息操作。对消息执行操作(如复制、编辑、重试等)。 -**分类**: PARTIAL - -**引用该标志的文件(2 个)**: -1. src/keybindings/defaultBindings.ts — 默认键绑定 -2. src/screens/REPL.tsx — REPL - ---- - -## 41. TERMINAL_PANEL - -**编译时引用次数**: 5(单引号 4 + 双引号 1) -**功能描述**: 终端面板。在 UI 中显示内嵌终端面板。 -**分类**: PARTIAL - -**引用该标志的文件(5 个)**: -1. src/components/PromptInput/PromptInputHelpMenu.tsx — 帮助菜单 -2. src/hooks/useGlobalKeybindings.tsx — 全局键绑定 -3. src/keybindings/defaultBindings.ts — 默认键绑定 -4. src/tools.ts — 工具注册 -5. src/utils/permissions/classifierDecision.ts — 分类器决策 - ---- - -# 三、STUB(纯桩/最小实现)— 共 51 个 - -以下标志仅有极少的引用(通常 1-3 处),没有或几乎没有实际功能代码。代码只是为该标志预留了位置。 - ---- - -## 42. TORCH - -**编译时引用次数**: 1 -**功能描述**: Torch 功能(具体不明)。 -**分类**: STUB -**引用文件**: src/commands.ts — 条件注册 `/torch` 命令(引用 `commands/torch.js`) -**缺失文件**: src/commands/torch.ts — 命令文件完全不存在 -**代码量**: 0 行专属代码 -**说明**: 纯占位符,没有任何实现。 - ---- - -## 43. KAIROS_DREAM - -**编译时引用次数**: 1 -**功能描述**: Kairos Dream(具体不明)。 -**分类**: STUB -**引用文件**: src/skills/bundled/index.ts — 内置技能注册 -**代码量**: 0 行专属代码 - ---- - -## 44. KAIROS_PUSH_NOTIFICATION - -**编译时引用次数**: 4 -**功能描述**: Kairos 推送通知。 -**分类**: STUB -**引用文件**: -1. src/components/Settings/Config.tsx — 设置 -2. src/tools.ts — 工具注册 -3. src/tools/ConfigTool/supportedSettings.ts — 支持的设置 -**代码量**: 0 行专属代码,仅在设置中预留了开关位 - ---- - -## 45. DAEMON - -**编译时引用次数**: 3 -**功能描述**: 守护进程模式。 -**分类**: STUB -**引用文件**: -1. src/commands.ts — 条件注册命令(与 BRIDGE_MODE 组合) -2. src/entrypoints/cli.tsx — CLI 入口 -**代码量**: 0 行专属代码 -**说明**: 在 commands.ts 中,`DAEMON` 与 `BRIDGE_MODE` 一起用于条件加载 `commands/remoteControlServer/index.js`,该文件不存在。 - ---- - -## 46. DIRECT_CONNECT - -**编译时引用次数**: 5 -**功能描述**: 直连模式。 -**分类**: STUB -**引用文件**: src/main.tsx — 主入口 -**代码量**: 0 行专属代码 - ---- - -## 47. SSH_REMOTE - -**编译时引用次数**: 4 -**功能描述**: SSH 远程连接。 -**分类**: STUB -**引用文件**: src/main.tsx — 主入口 -**代码量**: 0 行专属代码 - ---- - -## 48. STREAMLINED_OUTPUT - -**编译时引用次数**: 1 -**功能描述**: 精简输出模式。 -**分类**: STUB -**引用文件**: src/cli/print.ts — CLI 输出 -**代码量**: 0 行专属代码 - ---- - -## 49. ANTI_DISTILLATION_CC - -**编译时引用次数**: 1 -**功能描述**: 反蒸馏(防止模型蒸馏攻击)。 -**分类**: STUB -**引用文件**: src/services/api/claude.ts — Claude API 服务 -**代码量**: 0 行专属代码 - ---- - -## 50. NATIVE_CLIENT_ATTESTATION - -**编译时引用次数**: 1 -**功能描述**: 原生客户端认证。 -**分类**: STUB -**引用文件**: src/constants/system.ts — 系统常量 -**代码量**: 0 行专属代码 - ---- - -## 51. ABLATION_BASELINE - -**编译时引用次数**: 1 -**功能描述**: 消融基线测试。 -**分类**: STUB -**引用文件**: src/entrypoints/cli.tsx — CLI 入口 -**代码量**: 0 行专属代码 - ---- - -## 52. AGENT_MEMORY_SNAPSHOT - -**编译时引用次数**: 2 -**功能描述**: 代理记忆快照。 -**分类**: STUB -**引用文件**: -1. src/main.tsx — 主入口 -2. src/tools/AgentTool/loadAgentsDir.ts — 加载代理目录 -**代码量**: 0 行专属代码 - ---- - -## 53. AGENT_TRIGGERS_REMOTE `[build: ON] [dev: ON]` - -**编译时引用次数**: 2 -**功能描述**: 远程代理触发器。 -**分类**: STUB -**引用文件**: -1. src/skills/bundled/index.ts — 内置技能 -2. src/tools.ts — 工具注册 -**代码量**: 0 行专属代码 - ---- - -## 54. ALLOW_TEST_VERSIONS - -**编译时引用次数**: 2 -**功能描述**: 允许测试版本。 -**分类**: STUB -**引用文件**: src/utils/nativeInstaller/download.ts — 原生安装器下载(523 行,但标志仅用于一处条件判断) -**代码量**: 0 行专属代码 - ---- - -## 55. AUTO_THEME - -**编译时引用次数**: 3(单引号 2 + 双引号 1) -**功能描述**: 自动主题切换。 -**分类**: STUB -**引用文件**: -1. src/components/ThemePicker.tsx — 主题选择器 -2. src/components/design-system/ThemeProvider.tsx — 主题提供者 -3. src/tools/ConfigTool/supportedSettings.ts — 支持的设置 -**代码量**: 0 行专属代码 - ---- - -## 56. AWAY_SUMMARY - -**编译时引用次数**: 2 -**功能描述**: 离开摘要。用户离开时生成会话摘要。 -**分类**: STUB -**引用文件**: -1. src/hooks/useAwaySummary.ts — 离开摘要 Hook(125 行,但功能可能不完整) -2. src/screens/REPL.tsx — REPL -**代码量**: 约 125 行(useAwaySummary.ts) - ---- - -## 57. BREAK_CACHE_COMMAND - -**编译时引用次数**: 2 -**功能描述**: 缓存中断命令。 -**分类**: STUB -**引用文件**: src/context.ts — 上下文 -**代码量**: 0 行专属代码 - ---- - -## 58. BUILDING_CLAUDE_APPS - -**编译时引用次数**: 1 -**功能描述**: 构建 Claude 应用程序。 -**分类**: STUB -**引用文件**: src/skills/bundled/index.ts — 内置技能 -**代码量**: 0 行专属代码 - ---- - -## 59. BUILTIN_EXPLORE_PLAN_AGENTS - -**编译时引用次数**: 1 -**功能描述**: 内置探索和计划代理。 -**分类**: STUB -**引用文件**: src/tools/AgentTool/builtInAgents.ts — 内置代理定义 -**代码量**: 0 行专属代码 - ---- - -## 60. BYOC_ENVIRONMENT_RUNNER - -**编译时引用次数**: 1 -**功能描述**: BYOC(Bring Your Own Cloud)环境运行器。 -**分类**: STUB -**引用文件**: src/entrypoints/cli.tsx — CLI 入口 -**代码量**: 0 行专属代码 - ---- - -## 61. CCR_AUTO_CONNECT - -**编译时引用次数**: 3 -**功能描述**: CCR 自动连接。 -**分类**: STUB -**引用文件**: -1. src/bridge/bridgeEnabled.ts — 桥接启用检测 -2. src/utils/config.ts — 配置 -**代码量**: 0 行专属代码 - ---- - -## 62. CCR_MIRROR - -**编译时引用次数**: 4 -**功能描述**: CCR 镜像模式。 -**分类**: STUB -**引用文件**: -1. src/bridge/bridgeEnabled.ts — 桥接启用检测 -2. src/bridge/remoteBridgeCore.ts — 远程桥接核心 -3. src/main.tsx — 主入口 -**代码量**: 0 行专属代码 - ---- - -## 63. COMPACTION_REMINDERS - -**编译时引用次数**: 1 -**功能描述**: 压缩提醒。 -**分类**: STUB -**引用文件**: src/utils/attachments.ts — 附件处理 -**代码量**: 0 行专属代码 - ---- - -## 64. COWORKER_TYPE_TELEMETRY - -**编译时引用次数**: 2 -**功能描述**: 共同工作者类型遥测。 -**分类**: STUB -**引用文件**: src/services/analytics/metadata.ts — 分析元数据 -**代码量**: 0 行专属代码 - ---- - -## 65. DOWNLOAD_USER_SETTINGS - -**编译时引用次数**: 5 -**功能描述**: 下载用户设置(从远程同步)。 -**分类**: STUB -**引用文件**: -1. src/cli/print.ts — CLI 输出 -2. src/commands/reload-plugins/reload-plugins.ts — 重载插件 -3. src/services/settingsSync/index.ts — 设置同步 -**代码量**: 0 行专属代码 - ---- - -## 66. DUMP_SYSTEM_PROMPT - -**编译时引用次数**: 1 -**功能描述**: 转储系统提示(调试用)。 -**分类**: STUB -**引用文件**: src/entrypoints/cli.tsx — CLI 入口 -**代码量**: 0 行专属代码 - ---- - -## 67. ENHANCED_TELEMETRY_BETA - -**编译时引用次数**: 2 -**功能描述**: 增强遥测 Beta。 -**分类**: STUB -**引用文件**: src/utils/telemetry/sessionTracing.ts — 会话追踪(927 行,但标志仅用于一处条件) -**代码量**: 0 行专属代码 - ---- - -## 68. FILE_PERSISTENCE - -**编译时引用次数**: 3 -**功能描述**: 文件持久化。 -**分类**: STUB -**引用文件**: -1. src/cli/print.ts — CLI 输出 -2. src/utils/filePersistence/filePersistence.ts — 文件持久化(287 行) -**代码量**: 约 287 行(filePersistence.ts),但仅 3 处引用 - ---- - -## 69. HARD_FAIL - -**编译时引用次数**: 2 -**功能描述**: 硬失败模式(遇到错误时立即退出而非优雅降级)。 -**分类**: STUB -**引用文件**: -1. src/main.tsx — 主入口 -2. src/utils/log.ts — 日志工具 -**代码量**: 0 行专属代码 - ---- - -## 70. HOOK_PROMPTS - -**编译时引用次数**: 1 -**功能描述**: 钩子提示。 -**分类**: STUB -**引用文件**: src/screens/REPL.tsx — REPL -**代码量**: 0 行专属代码 - ---- - -## 71. IS_LIBC_GLIBC - -**编译时引用次数**: 1 -**功能描述**: 检测 libc 是否为 glibc。 -**分类**: STUB -**引用文件**: src/utils/envDynamic.ts — 动态环境检测(151 行) -**代码量**: 0 行专属代码(标志用于条件编译) - ---- - -## 72. IS_LIBC_MUSL - -**编译时引用次数**: 1 -**功能描述**: 检测 libc 是否为 musl。 -**分类**: STUB -**引用文件**: src/utils/envDynamic.ts — 动态环境检测(151 行) -**代码量**: 0 行专属代码(标志用于条件编译) - ---- - -## 73. MCP_RICH_OUTPUT - -**编译时引用次数**: 3 -**功能描述**: MCP 富文本输出。 -**分类**: STUB -**引用文件**: src/tools/MCPTool/UI.tsx — MCP 工具 UI -**代码量**: 0 行专属代码 - ---- - -## 74. MEMORY_SHAPE_TELEMETRY - -**编译时引用次数**: 3 -**功能描述**: 记忆形状遥测。 -**分类**: STUB -**引用文件**: -1. src/memdir/findRelevantMemories.ts — 查找相关记忆 -2. src/utils/sessionFileAccessHooks.ts — 会话文件访问钩子 -**代码量**: 0 行专属代码 - ---- - -## 75. NATIVE_CLIPBOARD_IMAGE - -**编译时引用次数**: 2 -**功能描述**: 原生剪贴板图片支持。 -**分类**: STUB -**引用文件**: src/utils/imagePaste.ts — 图片粘贴(416 行,但标志仅用于一处条件) -**代码量**: 0 行专属代码 - ---- - -## 76. NEW_INIT - -**编译时引用次数**: 2 -**功能描述**: 新的初始化流程。 -**分类**: STUB -**引用文件**: src/commands/init.ts — 初始化命令 -**代码量**: 0 行专属代码 - ---- - -## 77. OVERFLOW_TEST_TOOL - -**编译时引用次数**: 2 -**功能描述**: 溢出测试工具(内部测试用)。 -**分类**: STUB -**引用文件**: -1. src/tools.ts — 工具注册 -2. src/utils/permissions/classifierDecision.ts — 分类器决策 -**代码量**: 0 行专属代码 - ---- - -## 78. PERFETTO_TRACING - -**编译时引用次数**: 1 -**功能描述**: Perfetto 追踪(性能追踪工具)。 -**分类**: STUB -**引用文件**: src/utils/telemetry/perfettoTracing.ts — Perfetto 追踪(1,120 行,但标志仅用于一处) -**代码量**: 约 1,120 行(perfettoTracing.ts)存在,但仅 1 处引用 - ---- - -## 79. POWERSHELL_AUTO_MODE - -**编译时引用次数**: 2 -**功能描述**: PowerShell 自动模式。 -**分类**: STUB -**引用文件**: -1. src/utils/permissions/permissions.ts — 权限 -2. src/utils/permissions/yoloClassifier.ts — YOLO 分类器 -**代码量**: 0 行专属代码 - ---- - -## 80. QUICK_SEARCH - -**编译时引用次数**: 5 -**功能描述**: 快速搜索。 -**分类**: STUB -**引用文件**: -1. src/components/PromptInput/PromptInput.tsx — 提示输入 -2. src/keybindings/defaultBindings.ts — 默认键绑定 -**代码量**: 0 行专属代码 - ---- - -## 81. RUN_SKILL_GENERATOR - -**编译时引用次数**: 1 -**功能描述**: 运行技能生成器。 -**分类**: STUB -**引用文件**: src/skills/bundled/index.ts — 内置技能 -**代码量**: 0 行专属代码 - ---- - -## 82. SELF_HOSTED_RUNNER - -**编译时引用次数**: 1 -**功能描述**: 自托管运行器。 -**分类**: STUB -**引用文件**: src/entrypoints/cli.tsx — CLI 入口 -**代码量**: 0 行专属代码 - ---- - -## 83. SKILL_IMPROVEMENT - -**编译时引用次数**: 1 -**功能描述**: 技能改进。 -**分类**: STUB -**引用文件**: src/utils/hooks/skillImprovement.ts — 技能改进(267 行,但标志仅 1 处引用) -**代码量**: 约 267 行(skillImprovement.ts) - ---- - -## 84. SLOW_OPERATION_LOGGING - -**编译时引用次数**: 1 -**功能描述**: 慢操作日志记录。 -**分类**: STUB -**引用文件**: src/utils/slowOperations.ts — 慢操作(286 行,但标志仅 1 处引用) -**代码量**: 约 286 行(slowOperations.ts) - ---- - -## 85. TREE_SITTER_BASH - -**编译时引用次数**: 3 -**功能描述**: Tree-sitter Bash 解析器。 -**分类**: STUB -**引用文件**: src/utils/bash/parser.ts — Bash 解析器 -**代码量**: 0 行专属代码 - ---- - -## 86. TREE_SITTER_BASH_SHADOW - -**编译时引用次数**: 5 -**功能描述**: Tree-sitter Bash 影子模式(并行运行 tree-sitter 和传统解析器进行对比)。 -**分类**: STUB -**引用文件**: -1. src/tools/BashTool/bashPermissions.ts — Bash 权限 -2. src/utils/bash/parser.ts — Bash 解析器 -**代码量**: 0 行专属代码 - ---- - -## 87. ULTRATHINK - -**编译时引用次数**: 1 -**功能描述**: 超级思考模式。 -**分类**: STUB -**引用文件**: src/utils/thinking.ts — 思考工具(162 行,但标志仅 1 处引用) -**代码量**: 0 行专属代码 - ---- - -## 88. UNATTENDED_RETRY - -**编译时引用次数**: 1 -**功能描述**: 无人值守重试。 -**分类**: STUB -**引用文件**: src/services/api/withRetry.ts — API 重试 -**代码量**: 0 行专属代码 - ---- - -## 89. UPLOAD_USER_SETTINGS - -**编译时引用次数**: 2 -**功能描述**: 上传用户设置(同步到远程)。 -**分类**: STUB -**引用文件**: -1. src/main.tsx — 主入口 -2. src/services/settingsSync/index.ts — 设置同步 -**代码量**: 0 行专属代码 - ---- - -## 90. SKIP_DETECTION_WHEN_AUTOUPDATES_DISABLED - -**编译时引用次数**: 1(仅双引号形式) -**功能描述**: 当自动更新禁用时跳过检测。 -**分类**: STUB -**引用文件**: src/components/AutoUpdaterWrapper.tsx — 自动更新包装器 -**代码量**: 0 行专属代码 - ---- - -## 91. QUICK_SEARCH(已在 #80 列出) - -注:QUICK_SEARCH 已在 #80 列出。总计为 92 个独立标志(含 SKIP_DETECTION_WHEN_AUTOUPDATES_DISABLED)。 - ---- - -# 四、缺失文件汇总 - -以下是 `src/commands.ts` 中通过 `feature()` 条件 require 引用的文件,但在源代码中不存在: - -| 标志 | 引用路径 | 状态 | -|------|----------|------| -| TORCH | commands/torch.js | 文件完全不存在,无 .ts 版本 | -| PROACTIVE(与 KAIROS 共用) | commands/assistant/index.js | 整个 commands/assistant/ 目录不存在 | -| KAIROS | commands/assistant/index.js | 同上 | -| DAEMON + BRIDGE_MODE | commands/remoteControlServer/index.js | 文件不存在 | -| HISTORY_SNIP | commands/force-snip.js | 文件完全不存在,无 .ts 版本 | -| WORKFLOW_SCRIPTS | commands/workflows/index.js | 整个 commands/workflows/ 目录不存在 | -| KAIROS_GITHUB_WEBHOOKS | commands/subscribe-pr.js | 文件完全不存在,无 .ts 版本 | -| UDS_INBOX | commands/peers/index.js | 整个 commands/peers/ 目录不存在 | -| BUDDY | commands/buddy/index.js | 整个 commands/buddy/ 目录不存在(但 src/buddy/ 有 1,298 行实现) | - -以下是源代码中通过条件 require 引用但内容为空壳(1-5 行)的文件: - -| 文件路径 | 行数 | 所属标志 | -|----------|------|----------| -| src/tools/MonitorTool/MonitorTool.ts | 1 行 | MONITOR_TOOL | -| src/tools/WorkflowTool/WorkflowTool.ts | 1 行 | WORKFLOW_SCRIPTS | -| src/tools/WorkflowTool/constants.ts | 1 行 | WORKFLOW_SCRIPTS | -| src/tools/ReviewArtifactTool/ReviewArtifactTool.ts | 1 行 | REVIEW_ARTIFACT | -| src/utils/udsMessaging.ts | 1 行 | UDS_INBOX | -| src/utils/udsClient.ts | 3 行 | UDS_INBOX | -| src/skills/mcpSkills.ts | 3 行 | MCP_SKILLS | -| src/tools/WebBrowserTool/WebBrowserPanel.tsx | 3 行 | WEB_BROWSER_TOOL | -| src/tools/WorkflowTool/createWorkflowCommand.ts | 3 行 | WORKFLOW_SCRIPTS | -| src/tools/WorkflowTool/WorkflowPermissionRequest.tsx | 3 行 | WORKFLOW_SCRIPTS | -| src/components/tasks/WorkflowDetailDialog.tsx | 3 行 | WORKFLOW_SCRIPTS | -| src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.tsx | 3 行 | MONITOR_TOOL | -| src/components/tasks/MonitorMcpDetailDialog.tsx | 3 行 | MONITOR_TOOL | -| src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.tsx | 3 行 | REVIEW_ARTIFACT | -| src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts | 5 行 | WORKFLOW_SCRIPTS | -| src/tasks/MonitorMcpTask/MonitorMcpTask.ts | 5 行 | MONITOR_TOOL | -| src/coordinator/workerAgent.ts | 1 行 | COORDINATOR_MODE | -| src/bridge/webhookSanitizer.ts | 3 行 | BRIDGE_MODE | -| src/bridge/peerSessions.ts | 3 行 | BRIDGE_MODE | - ---- - -# 五、按引用次数排序的完整列表 - -| 排名 | 标志名称 | 引用次数 | 分类 | -|------|----------|----------|------| -| 1 | KAIROS | 156 | PARTIAL | -| 2 | TRANSCRIPT_CLASSIFIER | 110 | COMPLETE | -| 3 | TEAMMEM | 53 | COMPLETE | -| 4 | VOICE_MODE | 49 | COMPLETE | -| 5 | BASH_CLASSIFIER | 49 | COMPLETE | -| 6 | KAIROS_BRIEF | 39 | COMPLETE | -| 7 | PROACTIVE | 37 | COMPLETE | -| 8 | COORDINATOR_MODE | 32 | COMPLETE | -| 9 | BRIDGE_MODE | 29 | COMPLETE | -| 10 | CONTEXT_COLLAPSE | 23 | COMPLETE | -| 11 | EXPERIMENTAL_SKILL_SEARCH | 21 | PARTIAL | -| 12 | KAIROS_CHANNELS | 21 | PARTIAL | -| 13 | UDS_INBOX | 18 | PARTIAL | -| 14 | CHICAGO_MCP | 16 | COMPLETE | -| 15 | BUDDY | 18 | PARTIAL | -| 16 | HISTORY_SNIP | 16 | PARTIAL | -| 17 | MONITOR_TOOL | 13 | PARTIAL | -| 18 | CACHED_MICROCOMPACT | 12 | COMPLETE | -| 19 | COMMIT_ATTRIBUTION | 12 | COMPLETE | -| 20 | BG_SESSIONS | 11 | COMPLETE | -| 21 | AGENT_TRIGGERS | 11 | COMPLETE | -| 22 | WORKFLOW_SCRIPTS | 10 | PARTIAL | -| 23 | ULTRAPLAN | 10 | COMPLETE | -| 24 | SHOT_STATS | 10 | COMPLETE | -| 25 | TOKEN_BUDGET | 9 | COMPLETE | -| 26 | PROMPT_CACHE_BREAK_DETECTION | 9 | COMPLETE | -| 27 | MCP_SKILLS | 9 | PARTIAL | -| 28 | CONNECTOR_TEXT | 8 | PARTIAL | -| 29 | EXTRACT_MEMORIES | 7 | COMPLETE | -| 30 | TEMPLATES | 6 | PARTIAL | -| 31 | LODESTONE | 6 | PARTIAL | -| 32 | DOWNLOAD_USER_SETTINGS | 5 | STUB | -| 33 | TREE_SITTER_BASH_SHADOW | 5 | STUB | -| 34 | QUICK_SEARCH | 5 | STUB | -| 35 | MESSAGE_ACTIONS | 5 | PARTIAL | -| 36 | DIRECT_CONNECT | 5 | STUB | -| 37 | TERMINAL_PANEL | 5 | PARTIAL | -| 38 | FORK_SUBAGENT | 5 | PARTIAL | -| 39 | REACTIVE_COMPACT | 5 | COMPLETE | -| 40 | WEB_BROWSER_TOOL | 4 | PARTIAL | -| 41 | VERIFICATION_AGENT | 4 | COMPLETE | -| 42 | SSH_REMOTE | 4 | STUB | -| 43 | REVIEW_ARTIFACT | 4 | PARTIAL | -| 44 | KAIROS_PUSH_NOTIFICATION | 4 | STUB | -| 45 | HISTORY_PICKER | 4 | PARTIAL | -| 46 | CCR_MIRROR | 4 | STUB | -| 47 | KAIROS_GITHUB_WEBHOOKS | 4 | PARTIAL | -| 48 | TREE_SITTER_BASH | 3 | STUB | -| 49 | MEMORY_SHAPE_TELEMETRY | 3 | STUB | -| 50 | MCP_RICH_OUTPUT | 3 | STUB | -| 51 | FILE_PERSISTENCE | 3 | STUB | -| 52 | DAEMON | 3 | STUB | -| 53 | CCR_AUTO_CONNECT | 3 | STUB | -| 54 | AUTO_THEME | 3 | STUB | -| 55 | UPLOAD_USER_SETTINGS | 2 | STUB | -| 56 | POWERSHELL_AUTO_MODE | 2 | STUB | -| 57 | OVERFLOW_TEST_TOOL | 2 | STUB | -| 58 | NEW_INIT | 2 | STUB | -| 59 | NATIVE_CLIPBOARD_IMAGE | 2 | STUB | -| 60 | HARD_FAIL | 2 | STUB | -| 61 | ENHANCED_TELEMETRY_BETA | 2 | STUB | -| 62 | COWORKER_TYPE_TELEMETRY | 2 | STUB | -| 63 | BREAK_CACHE_COMMAND | 2 | STUB | -| 64 | AWAY_SUMMARY | 2 | STUB | -| 65 | ALLOW_TEST_VERSIONS | 2 | STUB | -| 66 | AGENT_TRIGGERS_REMOTE | 2 | STUB | -| 67 | AGENT_MEMORY_SNAPSHOT | 2 | STUB | -| 68 | UNATTENDED_RETRY | 1 | STUB | -| 69 | ULTRATHINK | 1 | STUB | -| 70 | TORCH | 1 | STUB | -| 71 | STREAMLINED_OUTPUT | 1 | STUB | -| 72 | SLOW_OPERATION_LOGGING | 1 | STUB | -| 73 | SKILL_IMPROVEMENT | 1 | STUB | -| 74 | SELF_HOSTED_RUNNER | 1 | STUB | -| 75 | RUN_SKILL_GENERATOR | 1 | STUB | -| 76 | PERFETTO_TRACING | 1 | STUB | -| 77 | NATIVE_CLIENT_ATTESTATION | 1 | STUB | -| 78 | KAIROS_DREAM | 1 | STUB | -| 79 | IS_LIBC_MUSL | 1 | STUB | -| 80 | IS_LIBC_GLIBC | 1 | STUB | -| 81 | HOOK_PROMPTS | 1 | STUB | -| 82 | DUMP_SYSTEM_PROMPT | 1 | STUB | -| 83 | COMPACTION_REMINDERS | 1 | STUB | -| 84 | CCR_REMOTE_SETUP | 1 | COMPLETE | -| 85 | BYOC_ENVIRONMENT_RUNNER | 1 | STUB | -| 86 | BUILTIN_EXPLORE_PLAN_AGENTS | 1 | STUB | -| 87 | BUILDING_CLAUDE_APPS | 1 | STUB | -| 88 | ANTI_DISTILLATION_CC | 1 | STUB | -| 89 | ABLATION_BASELINE | 1 | STUB | -| 90 | SKIP_DETECTION_WHEN_AUTOUPDATES_DISABLED | 1 | STUB | - ---- - -# 六、代码量统计 - -| 分类 | 标志数 | 总引用次数 | 专属代码行数(估算) | -|------|--------|------------|---------------------| -| COMPLETE | 22 | 约 640 | 约 35,000 行 | -| PARTIAL | 19 | 约 330 | 约 5,500 行 | -| STUB | 51 | 约 95 | 约 2,000 行(主要是附带的工具文件) | -| **总计** | **92** | **约 1,065** | **约 42,500 行** | - -**最大功能模块(按代码行数排序)**: -1. BRIDGE_MODE: 12,619 行(src/bridge/ 目录) -2. COORDINATOR_MODE: 7,990 行(src/coordinator/ + src/utils/swarm/) -3. SHOT_STATS: 2,722 行(统计系统) -4. CONTEXT_COLLAPSE: 2,258 行(上下文分析) -5. COMMIT_ATTRIBUTION: 1,354 行(提交归属) -6. BUDDY: 1,298 行(伙伴精灵) -7. VOICE_MODE: 1,410 行(语音模式) -8. TEAMMEM: 1,026 行(团队记忆) -9. UDS_INBOX: 966 行(Unix 套接字消息,但大部分是桩) -10. BG_SESSIONS: 801 行(后台会话) - ---- - -*本文档由自动审计生成,基于对 Claude Code 源代码中所有 `feature('...')` 引用的穷举搜索。每个标志的引用次数包含单引号和双引号两种形式。* diff --git a/docs/openai-task-tools.md b/docs/openai-task-tools.md deleted file mode 100644 index 22d415e4b..000000000 --- a/docs/openai-task-tools.md +++ /dev/null @@ -1,190 +0,0 @@ -# OpenAI兼容模型中task工具使用指南 - -## 问题描述 - -当使用OpenAI兼容模型(如DeepSeek、Ollama、vLLM等)时,调用task工具(TaskGet、TaskCreate、TaskUpdate、TaskList)可能会出现以下错误: - -``` -Error: InputValidationError: TaskGet failed due to the following issues: - The required parameter `taskId` is missing - An unexpected parameter `task_id` was provided - - This tool's schema was not sent to the API — it was not in the discovered-tool set derived from message history. Without the schema in your prompt, typed parameters (arrays, numbers, booleans) get emitted as strings and the client-side parser rejects them. Load the tool first: call ToolSearch with query "select:TaskGet", then retry this call. -``` - -## 问题原因 - -### 1. 延迟加载工具(Deferred Tools) -task工具都是延迟加载的(`shouldDefer: true`),这意味着: -- 工具的模式(schema)不会在初始API调用中发送 -- 需要先通过`ToolSearch`工具发现 -- 只有在被发现后,工具模式才会被发送给API - -### 2. 参数名转换问题 -- task工具使用驼峰命名:`taskId` -- OpenAI兼容模型可能输出蛇形命名:`task_id` -- 当工具模式没有被发送时,模型会猜测参数名,可能导致不匹配 - -## 解决方案 - -### 方案1:先使用ToolSearch(推荐) -在使用task工具之前,先调用`ToolSearch`工具: - -```javascript -// 第一步:发现task工具 -ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList") - -// 第二步:正常使用task工具 -TaskCreate({ subject: "任务标题", description: "任务描述" }) -TaskGet({ taskId: "1" }) -TaskUpdate({ taskId: "1", status: "completed" }) -TaskList() -``` - -### 方案2:批量发现所有task工具 -```javascript -// 一次性发现所有task工具 -ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList") - -// 然后可以任意使用task工具 -const task = await TaskCreate({ subject: "新任务", description: "任务描述" }) -console.log(`创建的任务ID: ${task.id}`) - -const taskList = await TaskList() -console.log(`当前有 ${taskList.tasks.length} 个任务`) -``` - -### 方案3:单独发现特定工具 -```javascript -// 只发现需要的工具 -ToolSearch("select:TaskGet") - -// 然后使用该工具 -TaskGet({ taskId: "1" }) -``` - -## 参数名注意事项 - -在使用OpenAI兼容模型时,请注意参数名格式: - -### ✅ 正确(驼峰命名) -```javascript -TaskGet({ taskId: "1" }) -TaskCreate({ subject: "标题", description: "描述" }) -TaskUpdate({ taskId: "1", status: "completed" }) -``` - -### ❌ 错误(蛇形命名) -```javascript -TaskGet({ task_id: "1" }) // 错误:应该使用taskId -TaskCreate({ subject: "标题", description: "描述" }) // 正确 -TaskUpdate({ task_id: "1", status: "completed" }) // 错误:应该使用taskId -``` - -## 常见问题解答 - -### Q1: 为什么需要先使用ToolSearch? -A: task工具是延迟加载的,它们的模式只有在被`ToolSearch`工具发现后才会发送给API。没有工具模式,模型无法知道正确的参数名和类型。 - -### Q2: 每次会话都需要使用ToolSearch吗? -A: 是的,每次新的会话都需要先使用ToolSearch发现工具。工具发现状态不会在会话之间保留。 - -### Q3: 使用Anthropic官方模型也需要这样吗? -A: 通常不需要。Anthropic官方模型对延迟加载工具的处理更智能,但为了兼容性,建议在使用task工具前都先使用ToolSearch。 - -### Q4: 可以一次性发现所有工具吗? -A: 可以,使用`ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList")`可以一次性发现所有task工具。 - -### Q5: 如果忘记使用ToolSearch会怎样? -A: 会收到参数验证错误,提示需要先使用ToolSearch。按照错误信息的指导操作即可。 - -## 最佳实践 - -1. **会话开始时发现工具**:在开始使用task工具前,先调用ToolSearch -2. **批量发现**:一次性发现所有需要的task工具 -3. **检查参数名**:确保使用正确的驼峰命名参数 -4. **查看错误信息**:如果遇到错误,仔细阅读错误信息中的指导 - -## 示例工作流 - -```javascript -// 1. 开始新会话 -// 2. 发现task工具 -ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList") - -// 3. 创建任务 -const newTask = await TaskCreate({ - subject: "修复OpenAI兼容性问题", - description: "解决task工具在OpenAI兼容模型下的参数名问题" -}) - -// 4. 获取任务详情 -const taskDetails = await TaskGet({ taskId: newTask.id }) - -// 5. 更新任务状态 -await TaskUpdate({ - taskId: newTask.id, - status: "in_progress", - activeForm: "修复OpenAI兼容性问题" -}) - -// 6. 查看所有任务 -const allTasks = await TaskList() -console.log(`当前有 ${allTasks.tasks.length} 个任务`) - -// 7. 完成任务 -await TaskUpdate({ - taskId: newTask.id, - status: "completed" -}) -``` - -## 故障排除 - -### 错误:参数名不匹配 -**症状**:`taskId`参数缺失,发现`task_id`参数 -**解决**:确保使用驼峰命名的`taskId`,而不是蛇形命名的`task_id` - -### 错误:工具模式未发送 -**症状**:`This tool's schema was not sent to the API` -**解决**:先使用`ToolSearch("select:工具名")`发现工具 - -### 错误:工具不可用 -**症状**:工具调用失败,没有具体错误信息 -**解决**:检查工具是否启用(通过`isTodoV2Enabled()`),确保环境变量设置正确 - -## 相关配置 - -### 环境变量 -```bash -# 启用OpenAI兼容模式 -export CLAUDE_CODE_USE_OPENAI=1 -export OPENAI_API_KEY=your-api-key -export OPENAI_BASE_URL=https://api.deepseek.com - -# 配置模型映射 -export OPENAI_DEFAULT_SONNET_MODEL=deepseek-chat -export OPENAI_DEFAULT_OPUS_MODEL=deepseek-chat -export OPENAI_DEFAULT_HAIKU_MODEL=deepseek-chat -``` - -### 设置文件 -通过`/login`命令配置OpenAI兼容模式后,设置会保存在`~/.claude/settings.json`: -```json -{ - "modelType": "openai", - "openai": { - "baseURL": "https://api.deepseek.com", - "apiKey": "your-api-key", - "models": { - "haiku": "deepseek-chat", - "sonnet": "deepseek-chat", - "opus": "deepseek-chat" - } - } -} -``` - -## 总结 - -在使用OpenAI兼容模型时,task工具需要先通过`ToolSearch`发现才能正常使用。遵循"先发现,后使用"的原则,并注意参数名的正确格式(驼峰命名),可以确保task工具在OpenAI兼容模型下正常工作。 \ No newline at end of file diff --git a/docs/plans/openai-compatibility.md b/docs/plans/openai-compatibility.md deleted file mode 100644 index 35cc41657..000000000 --- a/docs/plans/openai-compatibility.md +++ /dev/null @@ -1,425 +0,0 @@ -# OpenAI 协议兼容层 - -## 概述 - -claude-code 支持通过 OpenAI Chat Completions API(`/v1/chat/completions`)兼容任意 OpenAI 协议端点,包括 Ollama、DeepSeek、vLLM、One API、LiteLLM 等。 - -核心策略为**流适配器模式**:在 `queryModel()` 中插入提前返回分支,将 Anthropic 格式请求转为 OpenAI 格式,调用 OpenAI SDK,再将 SSE 流转换回 `BetaRawMessageStreamEvent` 格式。下游代码(流处理循环、query.ts、QueryEngine.ts、REPL)**完全不改**。 - -## 环境变量 - -| 变量 | 必需 | 说明 | -|---|---|---| -| `CLAUDE_CODE_USE_OPENAI` | 是 | 设为 `1` 启用 OpenAI 后端 | -| `OPENAI_API_KEY` | 是 | API key(Ollama 等可设为任意值) | -| `OPENAI_BASE_URL` | 推荐 | 端点 URL(如 `http://localhost:11434/v1`) | -| `OPENAI_MODEL` | 可选 | 覆盖所有请求的模型名(跳过映射) | -| `OPENAI_DEFAULT_OPUS_MODEL` | 可选 | 覆盖 opus 家族对应的模型(如 `o3`, `o3-mini`, `o1-pro`) | -| `OPENAI_DEFAULT_SONNET_MODEL` | 可选 | 覆盖 sonnet 家族对应的模型(如 `gpt-4o`, `gpt-4.1`) | -| `OPENAI_DEFAULT_HAIKU_MODEL` | 可选 | 覆盖 haiku 家族对应的模型(如 `gpt-4o-mini`, `gpt-4.0-mini`) | -| `OPENAI_ORG_ID` | 可选 | Organization ID | -| `OPENAI_PROJECT_ID` | 可选 | Project ID | - -### 使用示例 - -```bash -# Ollama -CLAUDE_CODE_USE_OPENAI=1 \ -OPENAI_API_KEY=ollama \ -OPENAI_BASE_URL=http://localhost:11434/v1 \ -OPENAI_MODEL=qwen2.5-coder-32b \ -bun run dev - -# DeepSeek(自动支持 Thinking) -CLAUDE_CODE_USE_OPENAI=1 \ -OPENAI_API_KEY=sk-xxx \ -OPENAI_BASE_URL=https://api.deepseek.com/v1 \ -OPENAI_MODEL=deepseek-chat \ -bun run dev - -# vLLM -CLAUDE_CODE_USE_OPENAI=1 \ -OPENAI_API_KEY=token-abc123 \ -OPENAI_BASE_URL=http://localhost:8000/v1 \ -OPENAI_MODEL=Qwen/Qwen2.5-Coder-32B-Instruct \ -bun run dev - -# One API / LiteLLM -CLAUDE_CODE_USE_OPENAI=1 \ -OPENAI_API_KEY=sk-your-key \ -OPENAI_BASE_URL=https://your-one-api.example.com/v1 \ -OPENAI_MODEL=gpt-4o \ -bun run dev - -# 自定义模型映射(使用家族变量) -CLAUDE_CODE_USE_OPENAI=1 \ -OPENAI_API_KEY=sk-xxx \ -OPENAI_BASE_URL=https://my-gateway.example.com/v1 \ -OPENAI_DEFAULT_SONNET_MODEL="gpt-4o-2024-11-20" \ -OPENAI_DEFAULT_HAIKU_MODEL="gpt-4o-mini" \ -bun run dev -``` - -## 架构 - -### 请求流程 - -``` -queryModel() [claude.ts] - ├── 共享预处理(消息归一化、工具过滤、媒体裁剪) - └── if (getAPIProvider() === 'openai') - └── queryModelOpenAI() [openai/index.ts] - ├── resolveOpenAIModel() → 解析模型名 - ├── normalizeMessagesForAPI() → 共享消息预处理 - ├── toolToAPISchema() → 构建工具 schema - ├── anthropicMessagesToOpenAI() → 消息格式转换 - ├── anthropicToolsToOpenAI() → 工具格式转换 - ├── openai.chat.completions.create({ stream: true }) - └── adaptOpenAIStreamToAnthropic() → 流格式转换 - ├── delta.reasoning_content → thinking 块 - ├── delta.content → text 块 - ├── delta.tool_calls → tool_use 块 - ├── usage.cached_tokens → cache_read_input_tokens - └── yield BetaRawMessageStreamEvent -``` - -### 模型名解析优先级 - -`resolveOpenAIModel()` 的解析顺序: - -1. `OPENAI_MODEL` 环境变量 → 直接使用,覆盖所有 -2. `OPENAI_DEFAULT_{FAMILY}_MODEL` 变量(如 `OPENAI_DEFAULT_SONNET_MODEL`)→ 按模型家族覆盖 -3. `ANTHROPIC_DEFAULT_{FAMILY}_MODEL` 变量(向后兼容) -4. 内置默认映射(见下表) -5. 以上都不匹配 → 原名透传 - -### 内置模型映射 - -| Anthropic 模型 | OpenAI 映射 | -|---|---| -| `claude-sonnet-4-6` | `gpt-4o` | -| `claude-sonnet-4-5-20250929` | `gpt-4o` | -| `claude-sonnet-4-20250514` | `gpt-4o` | -| `claude-3-7-sonnet-20250219` | `gpt-4o` | -| `claude-3-5-sonnet-20241022` | `gpt-4o` | -| `claude-opus-4-6` | `o3` | -| `claude-opus-4-5-20251101` | `o3` | -| `claude-opus-4-1-20250805` | `o3` | -| `claude-opus-4-20250514` | `o3` | -| `claude-haiku-4-5-20251001` | `gpt-4o-mini` | -| `claude-3-5-haiku-20241022` | `gpt-4o-mini` | - -同时会自动剥离 `[1m]` 后缀(Claude 特有的 modifier)。 - -## 文件结构 - -### 新增文件 - -``` -src/services/api/openai/ -├── client.ts # OpenAI SDK 客户端工厂(~50 行) -├── convertMessages.ts # Anthropic → OpenAI 消息格式转换(~190 行) -├── convertTools.ts # Anthropic → OpenAI 工具格式转换(~70 行) -├── streamAdapter.ts # SSE 流转换核心,含 thinking + caching(~270 行) -├── modelMapping.ts # 模型名解析(~60 行) -├── index.ts # 公共入口 queryModelOpenAI()(~110 行) -└── __tests__/ - ├── convertMessages.test.ts # 10 个测试 - ├── convertTools.test.ts # 7 个测试 - ├── modelMapping.test.ts # 6 个测试 - └── streamAdapter.test.ts # 14 个测试(含 thinking + caching) -``` - -### 修改文件 - -| 文件 | 改动 | -|---|---| -| `src/utils/model/providers.ts` | 添加 `'openai'` provider 类型 + `CLAUDE_CODE_USE_OPENAI` 检查(最高优先级) | -| `src/utils/model/configs.ts` | 每个 ModelConfig 添加 `openai` 键 | -| `src/services/api/claude.ts` | 在 `stripExcessMediaItems()` 后插入 OpenAI 提前返回分支(~8 行) | -| `package.json` | 添加 `"openai": "^4.73.0"` 依赖 | - -## 消息转换规则 - -### Anthropic → OpenAI - -| Anthropic | OpenAI | -|---|---| -| `system` prompt(`string[]`) | `role: "system"` 消息(`\n\n` 拼接) | -| `user` + `text` 块 | `role: "user"` 消息 | -| `assistant` + `text` 块 | `role: "assistant"` + `content` | -| `assistant` + `tool_use` 块 | `role: "assistant"` + `tool_calls[]` | -| `user` + `tool_result` 块 | `role: "tool"` + `tool_call_id` | -| `thinking` 块 | 静默丢弃(请求侧) | - -### 工具转换 - -| Anthropic | OpenAI | -|---|---| -| `{ name, description, input_schema }` | `{ type: "function", function: { name, description, parameters } }` | -| `cache_control`, `defer_loading` 等字段 | 剥离 | -| `tool_choice: { type: "auto" }` | `"auto"` | -| `tool_choice: { type: "any" }` | `"required"` | -| `tool_choice: { type: "tool", name }` | `{ type: "function", function: { name } }` | - -### 消息转换示例 - -``` -Anthropic: OpenAI: -[ - system: ["You are helpful."], [ - { role: "system", - { role: "user", content: "You are helpful." }, - content: [ { role: "user", - { type: "text", text: "Run ls" } content: "Run ls" - ] }, - }, { role: "assistant", - { role: "assistant", content: "I'll check.", - content: [ tool_calls: [{ - { type: "text", text: "I'll check."}, id: "tu_123", - { type: "tool_use", type: "function", - id: "tu_123", name: "bash", function: { - input: { command: "ls" } } name: "bash", - ] arguments: '{"command":"ls"}' - }, }] } - { role: "user", { role: "tool", - content: [ tool_call_id: "tu_123", - { type: "tool_result", content: "file1\nfile2" - tool_use_id: "tu_123", } - content: "file1\nfile2" ] - ] - } -] -``` - -## 流转换规则 - -### SSE Chunk → Anthropic Event 映射 - -| OpenAI Chunk | Anthropic Event | -|---|---| -| 首个 chunk | `message_start`(含 usage) | -| `delta.reasoning_content` | `content_block_start(thinking)` + `thinking_delta` | -| `delta.content` | `content_block_start(text)` + `text_delta` | -| `delta.tool_calls` | `content_block_start(tool_use)` + `input_json_delta` | -| `finish_reason: "stop"` | `message_delta(stop_reason: "end_turn")` | -| `finish_reason: "tool_calls"` | `message_delta(stop_reason: "tool_use")` | -| `finish_reason: "length"` | `message_delta(stop_reason: "max_tokens")` | - -### 块顺序 - -当模型返回 `reasoning_content` 时(如 DeepSeek),块顺序与 Anthropic 一致: - -``` -thinking block (index 0) ← delta.reasoning_content -text block (index 1) ← delta.content -``` - -或: - -``` -thinking block (index 0) ← delta.reasoning_content -tool_use block (index 1) ← delta.tool_calls -``` - -无 `reasoning_content` 时: - -``` -text block (index 0) ← delta.content -tool_use block (index 1) ← delta.tool_calls(如果有) -``` - -### finish_reason 映射 - -| OpenAI | Anthropic | -|---|---| -| `stop` | `end_turn` | -| `tool_calls` | `tool_use` | -| `length` | `max_tokens` | -| `content_filter` | `end_turn` | - -### 事件序列示例 - -**纯文本响应**: -``` -OpenAI chunks: - delta.content = "Hello" - delta.content = " world" - finish_reason = "stop" - -→ Anthropic events: - message_start { message: { id, role: 'assistant', usage: {...} } } - content_block_start { index: 0, content_block: { type: 'text' } } - content_block_delta { index: 0, delta: { type: 'text_delta', text: 'Hello' } } - content_block_delta { index: 0, delta: { type: 'text_delta', text: ' world' } } - content_block_stop { index: 0 } - message_delta { delta: { stop_reason: 'end_turn' } } - message_stop -``` - -**Thinking + 文本(DeepSeek 风格)**: -``` -OpenAI chunks: - delta.reasoning_content = "Let me think..." - delta.reasoning_content = " step by step." - delta.content = "The answer is 42." - finish_reason = "stop" - -→ Anthropic events: - message_start { ... } - content_block_start { index: 0, content_block: { type: 'thinking', signature: '' } } - content_block_delta { index: 0, delta: { type: 'thinking_delta', thinking: 'Let me think...' } } - content_block_delta { index: 0, delta: { type: 'thinking_delta', thinking: ' step by step.' } } - content_block_stop { index: 0 } - content_block_start { index: 1, content_block: { type: 'text' } } - content_block_delta { index: 1, delta: { type: 'text_delta', text: 'The answer is 42.' } } - content_block_stop { index: 1 } - message_delta { delta: { stop_reason: 'end_turn' } } - message_stop -``` - -**工具调用**: -``` -OpenAI chunks: - delta.tool_calls[0] = { id: 'call_xxx', function: { name: 'bash', arguments: '' } } - delta.tool_calls[0].function.arguments = '{"comm' - delta.tool_calls[0].function.arguments = 'and":"ls"}' - finish_reason = "tool_calls" - -→ Anthropic events: - message_start { ... } - content_block_start { index: 0, content_block: { type: 'tool_use', id: 'call_xxx', name: 'bash' } } - content_block_delta { index: 0, delta: { type: 'input_json_delta', partial_json: '{"comm' } } - content_block_delta { index: 0, delta: { type: 'input_json_delta', partial_json: 'and":"ls"}' } } - content_block_stop { index: 0 } - message_delta { delta: { stop_reason: 'tool_use' } } - message_stop -``` - -## 功能支持 - -### Thinking(思维链) - -**请求侧**:不需要显式配置。支持思维链的模型(DeepSeek 等)会自动返回 `delta.reasoning_content`。 - -**响应侧**:`delta.reasoning_content` 被转换为 Anthropic `thinking` content block: - -```ts -// content_block_start -{ type: 'content_block_start', index: 0, - content_block: { type: 'thinking', thinking: '', signature: '' } } - -// content_block_delta -{ type: 'content_block_delta', index: 0, - delta: { type: 'thinking_delta', thinking: 'Let me analyze...' } } -``` - -thinking block 在 text/tool_use block 之前自动关闭,保持 Anthropic 的块顺序。 - -### Prompt Caching - -**请求侧**:OpenAI 端点使用自动缓存,无需显式设置 `cache_control`。 - -**响应侧**:OpenAI 的 `usage.prompt_tokens_details.cached_tokens` 被映射到 Anthropic 的 `cache_read_input_tokens`: - -``` -OpenAI: usage.prompt_tokens_details.cached_tokens = 800 - ↓ -Anthropic: message_start.message.usage.cache_read_input_tokens = 800 -``` - -在 `message_start` 的 usage 中报告缓存命中量。 - -### 工具调用(Tool Use) - -完整支持 OpenAI function calling 格式。所有本地工具(Bash、FileEdit、Grep、Glob、Agent 等)透明工作——它们通过 JSON 输入输出通信,格式无关。 - -工具参数以 `input_json_delta` 形式流式传输,由下游代码拼接解析。 - -### 不支持的功能 - -| 功能 | 策略 | -|---|---| -| Beta Headers | 不发送 | -| Server Tools (advisor) | 不发送 | -| Structured Output | 不发送 | -| Fast Mode / Effort | 不发送 | -| Tool Search / defer_loading | 不启用,所有工具直接发送 | -| Anthropic Signature | thinking block 的 `signature` 字段为空字符串 | -| cache_creation_input_tokens | 始终为 0(OpenAI 不区分创建/读取) | - -## 测试 - -```bash -# 运行所有 OpenAI 适配层测试 -bun test src/services/api/openai/__tests__/ - -# 单独运行 -bun test src/services/api/openai/__tests__/streamAdapter.test.ts # 14 tests(含 thinking + caching) -bun test src/services/api/openai/__tests__/convertMessages.test.ts # 10 tests -bun test src/services/api/openai/__tests__/convertTools.test.ts # 7 tests -bun test src/services/api/openai/__tests__/modelMapping.test.ts # 6 tests -``` - -当前测试覆盖:**39 tests / 73 assertions / 0 fail**。 - -### 测试覆盖矩阵 - -| 功能 | convertMessages | convertTools | streamAdapter | modelMapping | -|---|---|---|---|---| -| 文本消息转换 | ✅ | | | | -| tool_use 转换 | ✅ | | | | -| tool_result 转换 | ✅ | | | | -| thinking 剥离 | ✅ | | | | -| 完整对话流程 | ✅ | | | | -| 工具 schema 转换 | | ✅ | | | -| tool_choice 映射 | | ✅ | | | -| 纯文本流 | | | ✅ | | -| 工具调用流 | | | ✅ | | -| 混合文本+工具 | | | ✅ | | -| finish_reason 映射 | | | ✅ | | -| thinking 流 | | | ✅ | | -| thinking+text 切换 | | | ✅ | | -| thinking+tool_use 切换 | | | ✅ | | -| 块索引正确性 | | | ✅ | | -| cached_tokens 映射 | | | ✅ | | -| OPENAI_MODEL 覆盖 | | | | ✅ | -| 默认模型映射 | | | | ✅ | -| 未知模型透传 | | | | ✅ | -| [1m] 后缀剥离 | | | | ✅ | - -## 端到端验证 - -```bash -# 1. 安装依赖 -bun install - -# 2. 运行单元测试 -bun test src/services/api/openai/__tests__/ - -# 3. 连接实际端点(以 Ollama 为例) -CLAUDE_CODE_USE_OPENAI=1 \ -OPENAI_API_KEY=ollama \ -OPENAI_BASE_URL=http://localhost:11434/v1 \ -OPENAI_MODEL=qwen2.5-coder-32b \ -bun run dev - -# 4. 连接 DeepSeek(测试 thinking 支持) -CLAUDE_CODE_USE_OPENAI=1 \ -OPENAI_API_KEY=sk-xxx \ -OPENAI_BASE_URL=https://api.deepseek.com/v1 \ -OPENAI_MODEL=deepseek-reasoner \ -bun run dev - -# 5. 确认现有测试不受影响 -bun test # 无 CLAUDE_CODE_USE_OPENAI 时走原有路径 -``` - -## 代码统计 - -| 类别 | 行数 | -|---|---| -| 新增源码 | ~620 行 | -| 新增测试 | ~450 行 | -| 改动现有代码 | ~25 行 | -| **总计** | **~1100 行** | diff --git a/docs/projects-collection.md b/docs/projects-collection.md deleted file mode 100644 index 861b84773..000000000 --- a/docs/projects-collection.md +++ /dev/null @@ -1,35 +0,0 @@ -# 社区项目 & Blog 合集 - -> 每日更新,欢迎自荐! - -## 工具 & 应用 - -| 项目 | 描述 | 作者 | -|------|------|------| -| [4qtask.vercel.app](https://4qtask.vercel.app/) | 免费四象限时间管理工具 | @kevinhuky | -| [kaying.studio](https://kaying.studio/) | 个人 AI 工具箱 | @kayingai | -| [supsub.ai](https://supsub.ai/) | 高效阅读工具 | @hidumou | -| [x-video-download.net](https://x-video-download.net/) | 视频下载工具 | @syakadou | -| [1openapi.com](https://1openapi.com/) | API 中转站 | @thinker007 | -| [claw-z.com](https://claw-z.com/) | 一键部署 OpenClaw AI Agent(场景驱动、全面管理) | @uhhc | -| [gemini-watermark-remover.net](https://gemini-watermark-remover.net/) | Gemini 水印移除工具 | @syakadou | - -## GitHub 开源项目 - -| 项目 | 描述 | 作者 | -|------|------|------| -| [VersperClaw](https://github.com/versperai/VersperClaw) | 全自动科研流 | @versperai | -| [claude-reviews-claude](https://github.com/openedclaude/claude-reviews-claude) | 原汤化原食——Claude 如何看待眼中的老己 | @openedclaude | -| [agentica](https://github.com/shibing624/agentica) | 自研 Agent 框架,借鉴 claude-code 多 Agent 处理 | @shibing624 | -| [macman](https://github.com/tonngw/macman) | Mac 从 0 到 1 保姆级配置教程 | @tonngw | -| [SuperSpec](https://github.com/asasugar/SuperSpec) | SDD / Spec-Driven Development | @asasugar | -| [adnify](https://github.com/adnaan-worker/adnify) | 高颜值高定制化 AI 编辑器 | @adnaan-worker | -| [another-rule-engine](https://github.com/eatmoreduck/another-rule-engine) | 基于 Groovy 的开源多功能决策引擎 | @eatmoreduck | -| [creative_master](https://github.com/chatabc/creative_master) | AI 驱动的创意灵感管理工具 | @chatabc | -| [RapidDoc](https://github.com/RapidAI/RapidDoc) | Office 文件解析工具转 Markdown(支持 PDF/Image/Word/PPT/Excel) | @hzkitt | - -## Blog - -| 链接 | 作者 | -|------|------| -| [blog.xiaohuangyu.space](https://blog.xiaohuangyu.space/) | @eatmoreduck | diff --git a/docs/test-plans/01-tool-system.md b/docs/test-plans/01-tool-system.md deleted file mode 100644 index da76750ee..000000000 --- a/docs/test-plans/01-tool-system.md +++ /dev/null @@ -1,147 +0,0 @@ -# Tool 系统测试计划 - -## 概述 - -Tool 系统是 Claude Code 的核心,负责工具的定义、注册、发现和过滤。本计划覆盖 `src/Tool.ts` 中的工具接口与工具函数、`src/tools.ts` 中的注册/过滤逻辑,以及各工具目录下可独立测试的纯函数。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/Tool.ts` | `buildTool`, `toolMatchesName`, `findToolByName`, `getEmptyToolPermissionContext`, `filterToolProgressMessages` | -| `src/tools.ts` | `parseToolPreset`, `filterToolsByDenyRules`, `getAllBaseTools`, `getTools`, `assembleToolPool` | -| `src/tools/shared/gitOperationTracking.ts` | `parseGitCommitId`, `detectGitOperation` | -| `src/tools/shared/spawnMultiAgent.ts` | `resolveTeammateModel`, `generateUniqueTeammateName` | -| `src/tools/GrepTool/GrepTool.ts` | `applyHeadLimit`, `formatLimitInfo`(内部辅助函数) | -| `src/tools/FileEditTool/utils.ts` | 字符串匹配/补丁相关纯函数 | - ---- - -## 测试用例 - -### src/Tool.ts - -#### describe('buildTool') - -- test('fills in default isEnabled as true') — 不传 isEnabled 时,构建的 tool.isEnabled() 应返回 true -- test('fills in default isConcurrencySafe as false') — 默认值应为 false(fail-closed) -- test('fills in default isReadOnly as false') — 默认假设有写操作 -- test('fills in default isDestructive as false') — 默认非破坏性 -- test('fills in default checkPermissions as allow') — 默认 checkPermissions 应返回 `{ behavior: 'allow', updatedInput }` -- test('fills in default userFacingName from tool name') — userFacingName 默认应返回 tool.name -- test('preserves explicitly provided methods') — 传入自定义 isEnabled 等方法时应覆盖默认值 -- test('preserves all non-defaultable properties') — name, inputSchema, call, description 等属性原样保留 - -#### describe('toolMatchesName') - -- test('returns true for exact name match') — `{ name: 'Bash' }` 匹配 'Bash' -- test('returns false for non-matching name') — `{ name: 'Bash' }` 不匹配 'Read' -- test('returns true when name matches an alias') — `{ name: 'Bash', aliases: ['BashTool'] }` 匹配 'BashTool' -- test('returns false when aliases is undefined') — `{ name: 'Bash' }` 不匹配 'BashTool' -- test('returns false when aliases is empty') — `{ name: 'Bash', aliases: [] }` 不匹配 'BashTool' - -#### describe('findToolByName') - -- test('finds tool by primary name') — 从 tools 列表中按 name 找到工具 -- test('finds tool by alias') — 从 tools 列表中按 alias 找到工具 -- test('returns undefined when no match') — 找不到时返回 undefined -- test('returns first match when duplicates exist') — 多个同名工具时返回第一个 - -#### describe('getEmptyToolPermissionContext') - -- test('returns default permission mode') — mode 应为 'default' -- test('returns empty maps and arrays') — additionalWorkingDirectories 为空 Map,rules 为空对象 -- test('returns isBypassPermissionsModeAvailable as false') - -#### describe('filterToolProgressMessages') - -- test('filters out hook_progress messages') — 移除 type 为 hook_progress 的消息 -- test('keeps tool progress messages') — 保留非 hook_progress 的消息 -- test('returns empty array for empty input') -- test('handles messages without type field') — data 不含 type 时应保留 - ---- - -### src/tools.ts - -#### describe('parseToolPreset') - -- test('returns "default" for "default" input') — 精确匹配 -- test('returns "default" for "Default" input') — 大小写不敏感 -- test('returns null for unknown preset') — 未知字符串返回 null -- test('returns null for empty string') - -#### describe('filterToolsByDenyRules') - -- test('returns all tools when no deny rules') — 空 deny 规则不过滤任何工具 -- test('filters out tools matching blanket deny rule') — deny rule `{ toolName: 'Bash' }` 应移除 Bash -- test('does not filter tools with content-specific deny rules') — deny rule `{ toolName: 'Bash', ruleContent: 'rm -rf' }` 不移除 Bash(只在运行时阻止特定命令) -- test('filters MCP tools by server name prefix') — deny rule `mcp__server` 应移除该 server 下所有工具 -- test('preserves tools not matching any deny rule') - -#### describe('getAllBaseTools') - -- test('returns a non-empty array of tools') — 至少包含核心工具 -- test('each tool has required properties') — 每个工具应有 name, inputSchema, call 等属性 -- test('includes BashTool, FileReadTool, FileEditTool') — 核心工具始终存在 -- test('includes TestingPermissionTool when NODE_ENV is test') — 需设置 env - -#### describe('getTools') - -- test('returns filtered tools based on permission context') — 根据 deny rules 过滤 -- test('returns simple tools in CLAUDE_CODE_SIMPLE mode') — 仅返回 Bash/Read/Edit -- test('filters disabled tools via isEnabled') — isEnabled 返回 false 的工具被排除 - ---- - -### src/tools/shared/gitOperationTracking.ts - -#### describe('parseGitCommitId') - -- test('extracts commit hash from git commit output') — 从 `[main abc1234] message` 中提取 `abc1234` -- test('returns null for non-commit output') — 无法解析时返回 null -- test('handles various branch name formats') — `[feature/foo abc1234]` 等 - -#### describe('detectGitOperation') - -- test('detects git commit operation') — 命令含 `git commit` 时识别为 commit -- test('detects git push operation') — 命令含 `git push` 时识别 -- test('returns null for non-git commands') — 非 git 命令返回 null -- test('detects git merge operation') -- test('detects git rebase operation') - ---- - -### src/tools/shared/spawnMultiAgent.ts - -#### describe('resolveTeammateModel') - -- test('returns specified model when provided') -- test('falls back to default model when not specified') - -#### describe('generateUniqueTeammateName') - -- test('generates a name when no existing names') — 无冲突时返回基础名 -- test('appends suffix when name conflicts') — 与已有名称冲突时添加后缀 -- test('handles multiple conflicts') — 多次冲突时递增后缀 - ---- - -## Mock 需求 - -| 依赖 | Mock 方式 | 说明 | -|------|-----------|------| -| `bun:bundle` (feature) | 已 polyfill 为 `() => false` | 不需额外 mock | -| `process.env` | `bun:test` mock | 测试 `USER_TYPE`、`NODE_ENV`、`CLAUDE_CODE_SIMPLE` | -| `getDenyRuleForTool` | mock module | `filterToolsByDenyRules` 测试中需控制返回值 | -| `isToolSearchEnabledOptimistic` | mock module | `getAllBaseTools` 中条件加载 | - -## 集成测试场景 - -放在 `tests/integration/tool-chain.test.ts`: - -### describe('Tool registration and discovery') - -- test('getAllBaseTools returns tools that can be found by findToolByName') — 注册 → 查找完整链路 -- test('filterToolsByDenyRules + getTools produces consistent results') — 过滤管线一致性 -- test('assembleToolPool deduplicates built-in and MCP tools') — 合并去重逻辑 diff --git a/docs/test-plans/02-utils-pure-functions.md b/docs/test-plans/02-utils-pure-functions.md deleted file mode 100644 index 62d5c994a..000000000 --- a/docs/test-plans/02-utils-pure-functions.md +++ /dev/null @@ -1,416 +0,0 @@ -# 工具函数(纯函数)测试计划 - -## 概述 - -覆盖 `src/utils/` 下所有可独立单元测试的纯函数。这些函数无外部依赖,输入输出确定性强,是测试金字塔的底层基石。 - -## 被测文件 - -| 文件 | 状态 | 关键导出 | -|------|------|----------| -| `src/utils/array.ts` | **已有测试** | intersperse, count, uniq | -| `src/utils/set.ts` | **已有测试** | difference, intersects, every, union | -| `src/utils/xml.ts` | 待测 | escapeXml, escapeXmlAttr | -| `src/utils/hash.ts` | 待测 | djb2Hash, hashContent, hashPair | -| `src/utils/stringUtils.ts` | 待测 | escapeRegExp, capitalize, plural, firstLineOf, countCharInString, normalizeFullWidthDigits, normalizeFullWidthSpace, safeJoinLines, truncateToLines, EndTruncatingAccumulator | -| `src/utils/semver.ts` | 待测 | gt, gte, lt, lte, satisfies, order | -| `src/utils/uuid.ts` | 待测 | validateUuid, createAgentId | -| `src/utils/format.ts` | 待测 | formatFileSize, formatSecondsShort, formatDuration, formatNumber, formatTokens, formatRelativeTime, formatRelativeTimeAgo | -| `src/utils/json.ts` | 待测 | safeParseJSON, safeParseJSONC, parseJSONL, addItemToJSONCArray | -| `src/utils/truncate.ts` | 待测 | truncatePathMiddle, truncateToWidth, truncateStartToWidth, truncateToWidthNoEllipsis, truncate, wrapText | -| `src/utils/diff.ts` | 待测 | adjustHunkLineNumbers, getPatchFromContents | -| `src/utils/frontmatterParser.ts` | 待测 | parseFrontmatter, splitPathInFrontmatter, parsePositiveIntFromFrontmatter, parseBooleanFrontmatter, parseShellFrontmatter | -| `src/utils/file.ts` | 待测(纯函数部分) | convertLeadingTabsToSpaces, addLineNumbers, stripLineNumberPrefix, pathsEqual, normalizePathForComparison | -| `src/utils/glob.ts` | 待测(纯函数部分) | extractGlobBaseDirectory | -| `src/utils/tokens.ts` | 待测 | getTokenCountFromUsage | -| `src/utils/path.ts` | 待测(纯函数部分) | containsPathTraversal, normalizePathForConfigKey | - ---- - -## 测试用例 - -### src/utils/xml.ts — 测试文件: `src/utils/__tests__/xml.test.ts` - -#### describe('escapeXml') - -- test('escapes ampersand') — `&` → `&` -- test('escapes less-than') — `<` → `<` -- test('escapes greater-than') — `>` → `>` -- test('does not escape quotes') — `"` 和 `'` 保持原样 -- test('handles empty string') — `""` → `""` -- test('handles string with no special chars') — `"hello"` 原样返回 -- test('escapes multiple special chars in one string') — `` → `<a & b>` - -#### describe('escapeXmlAttr') - -- test('escapes all xml chars plus quotes') — `"` → `"`, `'` → `'` -- test('escapes double quotes') — `he said "hi"` 正确转义 -- test('escapes single quotes') — `it's` 正确转义 - ---- - -### src/utils/hash.ts — 测试文件: `src/utils/__tests__/hash.test.ts` - -#### describe('djb2Hash') - -- test('returns consistent hash for same input') — 相同输入返回相同结果 -- test('returns different hashes for different inputs') — 不同输入大概率不同 -- test('returns a 32-bit integer') — 结果在 int32 范围内 -- test('handles empty string') — 空字符串有确定的哈希值 -- test('handles unicode strings') — 中文/emoji 等正确处理 - -#### describe('hashContent') - -- test('returns consistent hash for same content') — 确定性 -- test('returns string result') — 返回值为字符串 - -#### describe('hashPair') - -- test('returns consistent hash for same pair') — 确定性 -- test('order matters') — hashPair(a, b) ≠ hashPair(b, a) -- test('handles empty strings') - ---- - -### src/utils/stringUtils.ts — 测试文件: `src/utils/__tests__/stringUtils.test.ts` - -#### describe('escapeRegExp') - -- test('escapes dots') — `.` → `\\.` -- test('escapes asterisks') — `*` → `\\*` -- test('escapes brackets') — `[` → `\\[` -- test('escapes all special chars') — `.*+?^${}()|[]\` 全部转义 -- test('leaves normal chars unchanged') — `hello` 原样 -- test('escaped string works in RegExp') — `new RegExp(escapeRegExp('a.b'))` 精确匹配 `a.b` - -#### describe('capitalize') - -- test('uppercases first char') — `"foo"` → `"Foo"` -- test('does NOT lowercase rest') — `"fooBar"` → `"FooBar"`(区别于 lodash capitalize) -- test('handles single char') — `"a"` → `"A"` -- test('handles empty string') — `""` → `""` -- test('handles already capitalized') — `"Foo"` → `"Foo"` - -#### describe('plural') - -- test('returns singular for n=1') — `plural(1, 'file')` → `'file'` -- test('returns plural for n=0') — `plural(0, 'file')` → `'files'` -- test('returns plural for n>1') — `plural(3, 'file')` → `'files'` -- test('uses custom plural form') — `plural(2, 'entry', 'entries')` → `'entries'` - -#### describe('firstLineOf') - -- test('returns first line of multi-line string') — `"a\nb\nc"` → `"a"` -- test('returns full string when no newline') — `"hello"` → `"hello"` -- test('handles empty string') — `""` → `""` -- test('handles string starting with newline') — `"\nhello"` → `""` - -#### describe('countCharInString') - -- test('counts occurrences') — `countCharInString("aabac", "a")` → `3` -- test('returns 0 when char not found') — `countCharInString("hello", "x")` → `0` -- test('handles empty string') — `countCharInString("", "a")` → `0` -- test('respects start position') — `countCharInString("aaba", "a", 2)` → `1` - -#### describe('normalizeFullWidthDigits') - -- test('converts full-width digits to half-width') — `"0123"` → `"0123"` -- test('leaves half-width digits unchanged') — `"0123"` → `"0123"` -- test('mixed content') — `"port 8080"` → `"port 8080"` - -#### describe('normalizeFullWidthSpace') - -- test('converts ideographic space to regular space') — `"\u3000"` → `" "` -- test('converts multiple spaces') — `"a\u3000b\u3000c"` → `"a b c"` - -#### describe('safeJoinLines') - -- test('joins lines with default delimiter') — `["a","b"]` → `"a,b"` -- test('truncates when exceeding maxSize') — 超限时截断并添加 `...[truncated]` -- test('handles empty array') — `[]` → `""` -- test('uses custom delimiter') — delimiter 为 `"\n"` 时按行连接 - -#### describe('truncateToLines') - -- test('returns full text when within limit') — 行数不超限时原样返回 -- test('truncates and adds ellipsis') — 超限时截断并加 `…` -- test('handles exact limit') — 刚好等于 maxLines 时不截断 -- test('handles single line') — 单行文本不截断 - -#### describe('EndTruncatingAccumulator') - -- test('accumulates strings normally within limit') -- test('truncates when exceeding maxSize') -- test('reports truncated status correctly') -- test('reports totalBytes including truncated content') -- test('toString includes truncation marker') -- test('clear resets all state') -- test('append with Buffer works') — 接受 Buffer 类型 - ---- - -### src/utils/semver.ts — 测试文件: `src/utils/__tests__/semver.test.ts` - -#### describe('gt / gte / lt / lte') - -- test('gt: 2.0.0 > 1.0.0') → true -- test('gt: 1.0.0 > 1.0.0') → false -- test('gte: 1.0.0 >= 1.0.0') → true -- test('lt: 1.0.0 < 2.0.0') → true -- test('lte: 1.0.0 <= 1.0.0') → true -- test('handles pre-release versions') — `1.0.0-beta < 1.0.0` - -#### describe('satisfies') - -- test('version satisfies caret range') — `satisfies('1.2.3', '^1.0.0')` → true -- test('version does not satisfy range') — `satisfies('2.0.0', '^1.0.0')` → false -- test('exact match') — `satisfies('1.0.0', '1.0.0')` → true - -#### describe('order') - -- test('returns -1 for lesser') — `order('1.0.0', '2.0.0')` → -1 -- test('returns 0 for equal') — `order('1.0.0', '1.0.0')` → 0 -- test('returns 1 for greater') — `order('2.0.0', '1.0.0')` → 1 - ---- - -### src/utils/uuid.ts — 测试文件: `src/utils/__tests__/uuid.test.ts` - -#### describe('validateUuid') - -- test('accepts valid v4 UUID') — `'550e8400-e29b-41d4-a716-446655440000'` → 返回 UUID -- test('returns null for invalid format') — `'not-a-uuid'` → null -- test('returns null for empty string') — `''` → null -- test('returns null for null/undefined input') -- test('accepts uppercase UUIDs') — 大写字母有效 - -#### describe('createAgentId') - -- test('returns string starting with "a"') — 前缀为 `a` -- test('has correct length') — 前缀 + 16 hex 字符 -- test('generates unique ids') — 连续两次调用结果不同 - ---- - -### src/utils/format.ts — 测试文件: `src/utils/__tests__/format.test.ts` - -#### describe('formatFileSize') - -- test('formats bytes') — `500` → `"500 bytes"` -- test('formats kilobytes') — `1536` → `"1.5KB"` -- test('formats megabytes') — `1572864` → `"1.5MB"` -- test('formats gigabytes') — `1610612736` → `"1.5GB"` -- test('removes trailing .0') — `1024` → `"1KB"` (不是 `"1.0KB"`) - -#### describe('formatSecondsShort') - -- test('formats milliseconds to seconds') — `1234` → `"1.2s"` -- test('formats zero') — `0` → `"0.0s"` - -#### describe('formatDuration') - -- test('formats seconds') — `5000` → `"5s"` -- test('formats minutes and seconds') — `65000` → `"1m 5s"` -- test('formats hours') — `3661000` → `"1h 1m 1s"` -- test('formats days') — `90061000` → `"1d 1h 1m"` -- test('returns "0s" for zero') — `0` → `"0s"` -- test('hideTrailingZeros omits zero components') — `3600000` + `hideTrailingZeros` → `"1h"` -- test('mostSignificantOnly returns largest unit') — `3661000` + `mostSignificantOnly` → `"1h"` - -#### describe('formatNumber') - -- test('formats thousands') — `1321` → `"1.3k"` -- test('formats small numbers as-is') — `900` → `"900"` -- test('lowercase output') — `1500` → `"1.5k"` (不是 `"1.5K"`) - -#### describe('formatTokens') - -- test('strips .0 suffix') — `1000` → `"1k"` (不是 `"1.0k"`) -- test('keeps non-zero decimal') — `1500` → `"1.5k"` - -#### describe('formatRelativeTime') - -- test('formats past time') — now - 3600s → `"1h ago"` (narrow style) -- test('formats future time') — now + 3600s → `"in 1h"` (narrow style) -- test('formats less than 1 second') — now → `"0s ago"` -- test('uses custom now parameter for deterministic output') - ---- - -### src/utils/json.ts — 测试文件: `src/utils/__tests__/json.test.ts` - -#### describe('safeParseJSON') - -- test('parses valid JSON') — `'{"a":1}'` → `{ a: 1 }` -- test('returns null for invalid JSON') — `'not json'` → null -- test('returns null for null input') — `null` → null -- test('returns null for undefined input') — `undefined` → null -- test('returns null for empty string') — `""` → null -- test('handles JSON with BOM') — BOM 前缀不影响解析 -- test('caches results for repeated calls') — 同一输入不重复解析 - -#### describe('safeParseJSONC') - -- test('parses JSON with comments') — 含 `//` 注释的 JSON 正确解析 -- test('parses JSON with trailing commas') — 宽松模式 -- test('returns null for invalid input') -- test('returns null for null input') - -#### describe('parseJSONL') - -- test('parses multiple JSON lines') — `'{"a":1}\n{"b":2}'` → `[{a:1}, {b:2}]` -- test('skips malformed lines') — 含错误行时跳过该行 -- test('handles empty input') — `""` → `[]` -- test('handles trailing newline') — 尾部换行不产生空元素 -- test('accepts Buffer input') — Buffer 类型同样工作 -- test('handles BOM prefix') - -#### describe('addItemToJSONCArray') - -- test('adds item to existing array') — `[1, 2]` + 3 → `[1, 2, 3]` -- test('creates new array for empty content') — `""` + item → `[item]` -- test('creates new array for non-array content') — `'"hello"'` + item → `[item]` -- test('preserves comments in JSONC') — 注释不被丢弃 -- test('handles empty array') — `"[]"` + item → `[item]` - ---- - -### src/utils/diff.ts — 测试文件: `src/utils/__tests__/diff.test.ts` - -#### describe('adjustHunkLineNumbers') - -- test('shifts line numbers by positive offset') — 所有 hunk 的 oldStart/newStart 增加 offset -- test('shifts by negative offset') — 负 offset 减少行号 -- test('handles empty hunk array') — `[]` → `[]` - -#### describe('getPatchFromContents') - -- test('returns empty array for identical content') — 相同内容无差异 -- test('detects added lines') — 新内容多出行 -- test('detects removed lines') — 旧内容缺少行 -- test('detects modified lines') — 行内容变化 -- test('handles empty old content') — 从空文件到有内容 -- test('handles empty new content') — 删除所有内容 - ---- - -### src/utils/frontmatterParser.ts — 测试文件: `src/utils/__tests__/frontmatterParser.test.ts` - -#### describe('parseFrontmatter') - -- test('extracts YAML frontmatter between --- delimiters') — 正确提取 frontmatter 并返回 body -- test('returns empty frontmatter for content without ---') — 无 frontmatter 时 data 为空 -- test('handles empty content') — `""` 正确处理 -- test('handles frontmatter-only content') — 只有 frontmatter 无 body -- test('falls back to quoting on YAML parse error') — 无效 YAML 不崩溃 - -#### describe('splitPathInFrontmatter') - -- test('splits comma-separated paths') — `"a.ts, b.ts"` → `["a.ts", "b.ts"]` -- test('expands brace patterns') — `"*.{ts,tsx}"` → `["*.ts", "*.tsx"]` -- test('handles string array input') — `["a.ts", "b.ts"]` → `["a.ts", "b.ts"]` -- test('respects braces in comma splitting') — 大括号内的逗号不作为分隔符 - -#### describe('parsePositiveIntFromFrontmatter') - -- test('returns number for valid positive int') — `5` → `5` -- test('returns undefined for negative') — `-1` → undefined -- test('returns undefined for non-number') — `"abc"` → undefined -- test('returns undefined for float') — `1.5` → undefined - -#### describe('parseBooleanFrontmatter') - -- test('returns true for true') — `true` → true -- test('returns true for "true"') — `"true"` → true -- test('returns false for false') — `false` → false -- test('returns false for other values') — `"yes"`, `1` → false - -#### describe('parseShellFrontmatter') - -- test('returns bash for "bash"') — 正确识别 -- test('returns powershell for "powershell"') -- test('returns undefined for invalid value') — `"zsh"` → undefined - ---- - -### src/utils/file.ts(纯函数部分)— 测试文件: `src/utils/__tests__/file.test.ts` - -#### describe('convertLeadingTabsToSpaces') - -- test('converts single tab to 2 spaces') — `"\thello"` → `" hello"` -- test('converts multiple leading tabs') — `"\t\thello"` → `" hello"` -- test('does not convert tabs within line') — `"a\tb"` 保持原样 -- test('handles mixed content') - -#### describe('addLineNumbers') - -- test('adds line numbers starting from 1') — 每行添加 `N\t` 前缀 -- test('respects startLine parameter') — 从指定行号开始 -- test('handles empty content') - -#### describe('stripLineNumberPrefix') - -- test('strips tab-prefixed line number') — `"1\thello"` → `"hello"` -- test('strips padded line number') — `" 1\thello"` → `"hello"` -- test('returns line unchanged when no prefix') - -#### describe('pathsEqual') - -- test('returns true for identical paths') -- test('handles trailing slashes') — 带/不带尾部斜杠视为相同 -- test('handles case sensitivity based on platform') - -#### describe('normalizePathForComparison') - -- test('normalizes forward slashes') -- test('resolves path for comparison') - ---- - -### src/utils/glob.ts(纯函数部分)— 测试文件: `src/utils/__tests__/glob.test.ts` - -#### describe('extractGlobBaseDirectory') - -- test('extracts static prefix from glob') — `"src/**/*.ts"` → `{ baseDir: "src", relativePattern: "**/*.ts" }` -- test('handles root-level glob') — `"*.ts"` → `{ baseDir: ".", relativePattern: "*.ts" }` -- test('handles deep static path') — `"src/utils/model/*.ts"` → baseDir 为 `"src/utils/model"` -- test('handles Windows drive root') — `"C:\\Users\\**\\*.ts"` 正确分割 - ---- - -### src/utils/tokens.ts(纯函数部分)— 测试文件: `src/utils/__tests__/tokens.test.ts` - -#### describe('getTokenCountFromUsage') - -- test('sums input and output tokens') — `{ input_tokens: 100, output_tokens: 50 }` → 150 -- test('includes cache tokens') — cache_creation + cache_read 加入总数 -- test('handles zero values') — 全 0 时返回 0 - ---- - -### src/utils/path.ts(纯函数部分)— 测试文件: `src/utils/__tests__/path.test.ts` - -#### describe('containsPathTraversal') - -- test('detects ../ traversal') — `"../etc/passwd"` → true -- test('detects mid-path traversal') — `"foo/../../bar"` → true -- test('returns false for safe paths') — `"src/utils/file.ts"` → false -- test('returns false for paths containing .. in names') — `"foo..bar"` → false - -#### describe('normalizePathForConfigKey') - -- test('converts backslashes to forward slashes') — `"src\\utils"` → `"src/utils"` -- test('leaves forward slashes unchanged') - ---- - -## Mock 需求 - -本计划中的函数大部分为纯函数,**不需要 mock**。少数例外: - -| 函数 | 依赖 | 处理 | -|------|------|------| -| `hashContent` / `hashPair` | `Bun.hash` | Bun 运行时下自动可用 | -| `formatRelativeTime` | `Date` | 使用 `now` 参数注入确定性时间 | -| `safeParseJSON` | `logError` | 可通过 `shouldLogError: false` 跳过 | -| `safeParseJSONC` | `logError` | mock `logError` 避免测试输出噪音 | diff --git a/docs/test-plans/03-context-building.md b/docs/test-plans/03-context-building.md deleted file mode 100644 index 8b21c8d65..000000000 --- a/docs/test-plans/03-context-building.md +++ /dev/null @@ -1,134 +0,0 @@ -# Context 构建测试计划 - -## 概述 - -Context 构建系统负责组装发送给 Claude API 的系统提示和用户上下文。包括 git 状态获取、CLAUDE.md 文件发现与加载、系统提示拼装三部分。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/context.ts` | `getSystemContext`, `getUserContext`, `getGitStatus`, `setSystemPromptInjection` | -| `src/utils/claudemd.ts` | `stripHtmlComments`, `getClaudeMds`, `isMemoryFilePath`, `getLargeMemoryFiles`, `filterInjectedMemoryFiles`, `getExternalClaudeMdIncludes`, `hasExternalClaudeMdIncludes`, `processMemoryFile`, `getMemoryFiles` | -| `src/utils/systemPrompt.ts` | `buildEffectiveSystemPrompt` | - ---- - -## 测试用例 - -### src/utils/claudemd.ts — 纯函数部分 - -#### describe('stripHtmlComments') - -- test('strips block-level HTML comments') — `"text more"` → content 不含注释 -- test('preserves inline content') — 行内文本保留 -- test('preserves code block content') — ` ```html\n\n``` ` 内的注释不移除 -- test('returns stripped: false when no comments') — 无注释时 stripped 为 false -- test('returns stripped: true when comments exist') -- test('handles empty string') — `""` → `{ content: "", stripped: false }` -- test('handles multiple comments') — 多个注释全部移除 - -#### describe('getClaudeMds') - -- test('assembles memory files with type descriptions') — 不同 type 的文件有不同前缀描述 -- test('includes instruction prompt prefix') — 输出包含指令前缀 -- test('handles empty memory files array') — 空数组返回空字符串或最小前缀 -- test('respects filter parameter') — filter 函数可过滤特定类型 -- test('concatenates multiple files with separators') - -#### describe('isMemoryFilePath') - -- test('returns true for CLAUDE.md path') — `"/project/CLAUDE.md"` → true -- test('returns true for .claude/rules/ path') — `"/project/.claude/rules/foo.md"` → true -- test('returns true for memory file path') — `"~/.claude/memory/foo.md"` → true -- test('returns false for regular file') — `"/project/src/main.ts"` → false -- test('returns false for unrelated .md file') — `"/project/README.md"` → false - -#### describe('getLargeMemoryFiles') - -- test('returns files exceeding 40K chars') — 内容 > MAX_MEMORY_CHARACTER_COUNT 的文件被返回 -- test('returns empty array when all files are small') -- test('correctly identifies threshold boundary') - -#### describe('filterInjectedMemoryFiles') - -- test('filters out AutoMem type files') — feature flag 开启时移除自动记忆 -- test('filters out TeamMem type files') -- test('preserves other types') — 非 AutoMem/TeamMem 的文件保留 - -#### describe('getExternalClaudeMdIncludes') - -- test('returns includes from outside CWD') — 外部 @include 路径被识别 -- test('returns empty array when all includes are internal') - -#### describe('hasExternalClaudeMdIncludes') - -- test('returns true when external includes exist') -- test('returns false when no external includes') - ---- - -### src/utils/systemPrompt.ts - -#### describe('buildEffectiveSystemPrompt') - -- test('returns default system prompt when no overrides') — 无任何覆盖时使用默认提示 -- test('overrideSystemPrompt replaces everything') — override 模式替换全部内容 -- test('customSystemPrompt replaces default') — `--system-prompt` 参数替换默认 -- test('appendSystemPrompt is appended after main prompt') — append 在主提示之后 -- test('agent definition replaces default prompt') — agent 模式使用 agent prompt -- test('agent definition with append combines both') — agent prompt + append -- test('override takes precedence over agent and custom') — 优先级最高 -- test('returns array of strings') — 返回值为 SystemPrompt 类型(字符串数组) - ---- - -### src/context.ts — 需 Mock 的部分 - -#### describe('getGitStatus') - -- test('returns formatted git status string') — 包含 branch、status、log、user -- test('truncates status at 2000 chars') — 超长 status 被截断 -- test('returns null in test environment') — `NODE_ENV=test` 时返回 null -- test('returns null in non-git directory') — 非 git 仓库返回 null -- test('runs git commands in parallel') — 多个 git 命令并行执行 - -#### describe('getSystemContext') - -- test('includes gitStatus key') — 返回对象包含 gitStatus -- test('returns memoized result on subsequent calls') — 多次调用返回同一结果 -- test('skips git when instructions disabled') - -#### describe('getUserContext') - -- test('includes currentDate key') — 返回对象包含当前日期 -- test('includes claudeMd key when CLAUDE.md exists') — 加载 CLAUDE.md 内容 -- test('respects CLAUDE_CODE_DISABLE_CLAUDE_MDS env') — 设置后不加载 CLAUDE.md -- test('returns memoized result') - -#### describe('setSystemPromptInjection') - -- test('clears memoized context caches') — 调用后下次 getSystemContext/getUserContext 重新计算 -- test('injection value is accessible via getSystemPromptInjection') - ---- - -## Mock 需求 - -| 依赖 | Mock 方式 | 用途 | -|------|-----------|------| -| `execFileNoThrow` | `mock.module` | `getGitStatus` 中的 git 命令 | -| `getMemoryFiles` | `mock.module` | `getUserContext` 中的 CLAUDE.md 加载 | -| `getCwd` | `mock.module` | 路径解析上下文 | -| `process.env.NODE_ENV` | 直接设置 | 测试环境检测 | -| `process.env.CLAUDE_CODE_DISABLE_CLAUDE_MDS` | 直接设置 | 禁用 CLAUDE.md | - -## 集成测试场景 - -放在 `tests/integration/context-build.test.ts`: - -### describe('Context assembly pipeline') - -- test('getUserContext produces claudeMd containing CLAUDE.md content') — 端到端验证 CLAUDE.md 被正确加载到 context -- test('buildEffectiveSystemPrompt + getUserContext produces complete prompt') — 系统提示 + 用户上下文完整性 -- test('setSystemPromptInjection invalidates and rebuilds context') — 注入后重新构建上下文 diff --git a/docs/test-plans/04-permission-system.md b/docs/test-plans/04-permission-system.md deleted file mode 100644 index 3b482fa91..000000000 --- a/docs/test-plans/04-permission-system.md +++ /dev/null @@ -1,104 +0,0 @@ -# 权限系统测试计划 - -## 概述 - -权限系统控制工具是否可以执行,包含规则解析器、权限检查管线和权限模式判断。测试重点是纯函数解析器和规则匹配逻辑。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/utils/permissions/permissionRuleParser.ts` | `permissionRuleValueFromString`, `permissionRuleValueToString`, `escapeRuleContent`, `unescapeRuleContent`, `normalizeLegacyToolName`, `getLegacyToolNames` | -| `src/utils/permissions/PermissionMode.ts` | 权限模式常量和辅助函数 | -| `src/utils/permissions/permissions.ts` | `hasPermissionsToUseTool`, `getDenyRuleForTool`, `checkRuleBasedPermissions` | -| `src/types/permissions.ts` | `PermissionMode`, `PermissionBehavior`, `PermissionRule` 类型定义 | - ---- - -## 测试用例 - -### src/utils/permissions/permissionRuleParser.ts - -#### describe('escapeRuleContent') - -- test('escapes backslashes first') — `'test\\value'` → `'test\\\\value'` -- test('escapes opening parentheses') — `'print(1)'` → `'print\\(1\\)'` -- test('escapes closing parentheses') — `'func()'` → `'func\\(\\)'` -- test('handles combined escape') — `'echo "test\\nvalue"'` 中的 `\\` 先转义 -- test('handles empty string') — `''` → `''` -- test('no-op for string without special chars') — `'npm install'` 原样返回 - -#### describe('unescapeRuleContent') - -- test('unescapes parentheses') — `'print\\(1\\)'` → `'print(1)'` -- test('unescapes backslashes last') — `'test\\\\nvalue'` → `'test\\nvalue'` -- test('handles empty string') -- test('roundtrip: escape then unescape returns original') — `unescapeRuleContent(escapeRuleContent(x)) === x` - -#### describe('permissionRuleValueFromString') - -- test('parses tool name only') — `'Bash'` → `{ toolName: 'Bash' }` -- test('parses tool name with content') — `'Bash(npm install)'` → `{ toolName: 'Bash', ruleContent: 'npm install' }` -- test('parses content with escaped parentheses') — `'Bash(python -c "print\\(1\\)")'` → ruleContent 为 `'python -c "print(1)"'` -- test('treats empty parens as tool-wide rule') — `'Bash()'` → `{ toolName: 'Bash' }`(无 ruleContent) -- test('treats wildcard content as tool-wide rule') — `'Bash(*)'` → `{ toolName: 'Bash' }` -- test('normalizes legacy tool names') — `'Task'` → `{ toolName: 'Agent' }`(或对应的 AGENT_TOOL_NAME) -- test('handles malformed input: no closing paren') — `'Bash(npm'` → 整个字符串作为 toolName -- test('handles malformed input: content after closing paren') — `'Bash(npm)extra'` → 整个字符串作为 toolName -- test('handles missing tool name') — `'(foo)'` → 整个字符串作为 toolName - -#### describe('permissionRuleValueToString') - -- test('serializes tool name only') — `{ toolName: 'Bash' }` → `'Bash'` -- test('serializes with content') — `{ toolName: 'Bash', ruleContent: 'npm install' }` → `'Bash(npm install)'` -- test('escapes content with parentheses') — ruleContent 含 `()` 时正确转义 -- test('roundtrip: fromString then toString preserves value') — 往返一致 - -#### describe('normalizeLegacyToolName') - -- test('maps Task to Agent tool name') — `'Task'` → AGENT_TOOL_NAME -- test('maps KillShell to TaskStop tool name') — `'KillShell'` → TASK_STOP_TOOL_NAME -- test('maps AgentOutputTool to TaskOutput tool name') -- test('returns unknown names unchanged') — `'UnknownTool'` → `'UnknownTool'` - -#### describe('getLegacyToolNames') - -- test('returns legacy names for canonical name') — 给定 AGENT_TOOL_NAME 返回包含 `'Task'` -- test('returns empty array for name with no legacy aliases') - ---- - -### src/utils/permissions/permissions.ts — 需 Mock - -#### describe('getDenyRuleForTool') - -- test('returns deny rule matching tool name') — 匹配到 blanket deny 规则时返回 -- test('returns null when no deny rules match') — 无匹配时返回 null -- test('matches MCP tools by server prefix') — `mcp__server` 规则匹配该 server 下的 MCP 工具 -- test('does not match content-specific deny rules') — 有 ruleContent 的 deny 规则不作为 blanket deny - -#### describe('checkRuleBasedPermissions')(集成级别) - -- test('deny rule takes precedence over allow') — 同时有 allow 和 deny 时 deny 优先 -- test('ask rule prompts user') — 匹配 ask 规则返回 `{ behavior: 'ask' }` -- test('allow rule permits execution') — 匹配 allow 规则返回 `{ behavior: 'allow' }` -- test('passthrough when no rules match') — 无匹配规则返回 passthrough - ---- - -## Mock 需求 - -| 依赖 | Mock 方式 | 说明 | -|------|-----------|------| -| `bun:bundle` (feature) | 已 polyfill | BRIEF_TOOL_NAME 条件加载 | -| Tool 常量导入 | 实际值 | AGENT_TOOL_NAME 等从常量文件导入 | -| `appState` | mock object | `hasPermissionsToUseTool` 中的状态依赖 | -| Tool 对象 | mock object | 模拟 tool 的 name, checkPermissions 等 | - -## 集成测试场景 - -### describe('Permission pipeline end-to-end') - -- test('deny rule blocks tool before it runs') — deny 规则在 call 前拦截 -- test('bypassPermissions mode allows all') — bypass 模式下 ask → allow -- test('dontAsk mode converts ask to deny') — dontAsk 模式下 ask → deny diff --git a/docs/test-plans/05-model-routing.md b/docs/test-plans/05-model-routing.md deleted file mode 100644 index 5b701ad35..000000000 --- a/docs/test-plans/05-model-routing.md +++ /dev/null @@ -1,113 +0,0 @@ -# 模型路由测试计划 - -## 概述 - -模型路由系统负责 API provider 选择、模型别名解析、模型名规范化和运行时模型决策。测试重点是纯函数和环境变量驱动的逻辑。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/utils/model/aliases.ts` | `MODEL_ALIASES`, `MODEL_FAMILY_ALIASES`, `isModelAlias`, `isModelFamilyAlias` | -| `src/utils/model/providers.ts` | `APIProvider`, `getAPIProvider`, `isFirstPartyAnthropicBaseUrl` | -| `src/utils/model/model.ts` | `firstPartyNameToCanonical`, `getCanonicalName`, `parseUserSpecifiedModel`, `normalizeModelStringForAPI`, `getRuntimeMainLoopModel`, `getDefaultMainLoopModelSetting` | - ---- - -## 测试用例 - -### src/utils/model/aliases.ts - -#### describe('isModelAlias') - -- test('returns true for "sonnet"') — 有效别名 -- test('returns true for "opus"') -- test('returns true for "haiku"') -- test('returns true for "best"') -- test('returns true for "sonnet[1m]"') -- test('returns true for "opus[1m]"') -- test('returns true for "opusplan"') -- test('returns false for full model ID') — `'claude-sonnet-4-6-20250514'` → false -- test('returns false for unknown string') — `'gpt-4'` → false -- test('is case-sensitive') — `'Sonnet'` → false(别名是小写) - -#### describe('isModelFamilyAlias') - -- test('returns true for "sonnet"') -- test('returns true for "opus"') -- test('returns true for "haiku"') -- test('returns false for "best"') — best 不是 family alias -- test('returns false for "opusplan"') -- test('returns false for "sonnet[1m]"') - ---- - -### src/utils/model/providers.ts - -#### describe('getAPIProvider') - -- test('returns "firstParty" by default') — 无相关 env 时返回 firstParty -- test('returns "bedrock" when CLAUDE_CODE_USE_BEDROCK is set') — env 为 truthy 值 -- test('returns "vertex" when CLAUDE_CODE_USE_VERTEX is set') -- test('returns "foundry" when CLAUDE_CODE_USE_FOUNDRY is set') -- test('bedrock takes precedence over vertex') — 多个 env 同时设置时 bedrock 优先 - -#### describe('isFirstPartyAnthropicBaseUrl') - -- test('returns true when ANTHROPIC_BASE_URL is not set') — 默认 API -- test('returns true for api.anthropic.com') — `'https://api.anthropic.com'` → true -- test('returns false for custom URL') — `'https://my-proxy.com'` → false -- test('returns false for invalid URL') — 非法 URL → false -- test('returns true for staging URL when USER_TYPE is ant') — `'https://api-staging.anthropic.com'` + ant → true - ---- - -### src/utils/model/model.ts - -#### describe('firstPartyNameToCanonical') - -- test('maps opus-4-6 full name to canonical') — `'claude-opus-4-6-20250514'` → `'claude-opus-4-6'` -- test('maps sonnet-4-6 full name') — `'claude-sonnet-4-6-20250514'` → `'claude-sonnet-4-6'` -- test('maps haiku-4-5') — `'claude-haiku-4-5-20251001'` → `'claude-haiku-4-5'` -- test('maps 3P provider format') — `'us.anthropic.claude-opus-4-6-v1:0'` → `'claude-opus-4-6'` -- test('maps claude-3-7-sonnet') — `'claude-3-7-sonnet-20250219'` → `'claude-3-7-sonnet'` -- test('maps claude-3-5-sonnet') → `'claude-3-5-sonnet'` -- test('maps claude-3-5-haiku') → `'claude-3-5-haiku'` -- test('maps claude-3-opus') → `'claude-3-opus'` -- test('is case insensitive') — `'Claude-Opus-4-6'` → `'claude-opus-4-6'` -- test('falls back to input for unknown model') — `'unknown-model'` → `'unknown-model'` -- test('differentiates opus-4 vs opus-4-5 vs opus-4-6') — 更具体的版本优先匹配 - -#### describe('parseUserSpecifiedModel') - -- test('resolves "sonnet" to default sonnet model') -- test('resolves "opus" to default opus model') -- test('resolves "haiku" to default haiku model') -- test('resolves "best" to best model') -- test('resolves "opusplan" to default sonnet model') — opusplan 默认用 sonnet -- test('appends [1m] suffix when alias has [1m]') — `'sonnet[1m]'` → 模型名 + `'[1m]'` -- test('preserves original case for custom model names') — `'my-Custom-Model'` 保留大小写 -- test('handles [1m] suffix on non-alias models') — `'custom-model[1m]'` → `'custom-model[1m]'` -- test('trims whitespace') — `' sonnet '` → 正确解析 - -#### describe('getRuntimeMainLoopModel') - -- test('returns mainLoopModel by default') — 无特殊条件时原样返回 -- test('returns opus in plan mode when opusplan is set') — opusplan + plan mode → opus -- test('returns sonnet in plan mode when haiku is set') — haiku + plan mode → sonnet 升级 -- test('returns mainLoopModel in non-plan mode') — 非 plan 模式不做替换 - ---- - -## Mock 需求 - -| 依赖 | Mock 方式 | 说明 | -|------|-----------|------| -| `process.env.CLAUDE_CODE_USE_BEDROCK/VERTEX/FOUNDRY` | 直接设置/恢复 | provider 选择 | -| `process.env.ANTHROPIC_BASE_URL` | 直接设置/恢复 | URL 检测 | -| `process.env.USER_TYPE` | 直接设置/恢复 | staging URL 和 ant 功能 | -| `getModelStrings()` | mock.module | 返回固定模型 ID | -| `getMainLoopModelOverride` | mock.module | 会话中模型覆盖 | -| `getSettings_DEPRECATED` | mock.module | 用户设置中的模型 | -| `getUserSpecifiedModelSetting` | mock.module | `getRuntimeMainLoopModel` 依赖 | -| `isModelAllowed` | mock.module | allowlist 检查 | diff --git a/docs/test-plans/06-message-handling.md b/docs/test-plans/06-message-handling.md deleted file mode 100644 index fb593f463..000000000 --- a/docs/test-plans/06-message-handling.md +++ /dev/null @@ -1,165 +0,0 @@ -# 消息处理测试计划 - -## 概述 - -消息处理系统负责消息的创建、查询、规范化和文本提取。覆盖消息类型定义、消息工厂函数、消息过滤/查询工具和 API 规范化管线。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/types/message.ts` | `MessageType`, `Message`, `AssistantMessage`, `UserMessage`, `SystemMessage` 等类型 | -| `src/utils/messages.ts` | 消息创建、查询、规范化、文本提取等函数(~3100 行) | -| `src/utils/messages/mappers.ts` | 消息映射工具 | - ---- - -## 测试用例 - -### src/utils/messages.ts — 消息创建 - -#### describe('createAssistantMessage') - -- test('creates message with type "assistant"') — type 字段正确 -- test('creates message with role "assistant"') — role 正确 -- test('creates message with empty content array') — 默认 content 为空 -- test('generates unique uuid') — 每次调用 uuid 不同 -- test('includes costUsd as 0') - -#### describe('createUserMessage') - -- test('creates message with type "user"') — type 字段正确 -- test('creates message with provided content') — content 正确传入 -- test('generates unique uuid') - -#### describe('createSystemMessage') - -- test('creates system message with correct type') -- test('includes message content') - -#### describe('createProgressMessage') - -- test('creates progress message with data') -- test('has correct type "progress"') - ---- - -### src/utils/messages.ts — 消息查询 - -#### describe('getLastAssistantMessage') - -- test('returns last assistant message from array') — 多条消息中返回最后一条 assistant -- test('returns undefined for empty array') -- test('returns undefined when no assistant messages exist') - -#### describe('hasToolCallsInLastAssistantTurn') - -- test('returns true when last assistant has tool_use content') — content 含 tool_use block -- test('returns false when last assistant has only text') -- test('returns false for empty messages') - -#### describe('isSyntheticMessage') - -- test('identifies interrupt message as synthetic') — INTERRUPT_MESSAGE 标记 -- test('identifies cancel message as synthetic') -- test('returns false for normal user messages') - -#### describe('isNotEmptyMessage') - -- test('returns true for message with content') -- test('returns false for message with empty content array') -- test('returns false for message with empty text content') - ---- - -### src/utils/messages.ts — 文本提取 - -#### describe('getAssistantMessageText') - -- test('extracts text from text blocks') — content 含 `{ type: 'text', text: 'hello' }` 时提取 -- test('returns empty string for non-text content') — 仅含 tool_use 时返回空 -- test('concatenates multiple text blocks') - -#### describe('getUserMessageText') - -- test('extracts text from string content') — content 为纯字符串 -- test('extracts text from content array') — content 为数组时提取 text 块 -- test('handles empty content') - -#### describe('extractTextContent') - -- test('extracts text items from mixed content') — 过滤出 type: 'text' 的项 -- test('returns empty array for all non-text content') - ---- - -### src/utils/messages.ts — 规范化 - -#### describe('normalizeMessages') - -- test('converts raw messages to normalized format') — 消息数组规范化 -- test('handles empty array') — `[]` → `[]` -- test('preserves message order') -- test('handles mixed message types') - -#### describe('normalizeMessagesForAPI') - -- test('filters out system messages') — 系统消息不发送给 API -- test('filters out progress messages') -- test('filters out attachment messages') -- test('preserves user and assistant messages') -- test('reorders tool results to match API expectations') -- test('handles empty array') - ---- - -### src/utils/messages.ts — 合并 - -#### describe('mergeUserMessages') - -- test('merges consecutive user messages') — 相邻用户消息合并 -- test('does not merge non-consecutive user messages') -- test('preserves assistant messages between user messages') - -#### describe('mergeAssistantMessages') - -- test('merges consecutive assistant messages') -- test('combines content arrays') - ---- - -### src/utils/messages.ts — 辅助函数 - -#### describe('buildMessageLookups') - -- test('builds index by message uuid') — 按 uuid 建立查找表 -- test('returns empty lookups for empty messages') -- test('handles duplicate uuids gracefully') - ---- - -## Mock 需求 - -| 依赖 | Mock 方式 | 说明 | -|------|-----------|------| -| `crypto.randomUUID` | `mock` 或 spy | 消息创建中的 uuid 生成 | -| Message 对象 | 手动构造 | 创建符合类型的 mock 消息对象 | - -### Mock 消息工厂(放在 `tests/mocks/messages.ts`) - -```typescript -// 通用 mock 消息构造器 -export function mockAssistantMessage(overrides?: Partial): AssistantMessage -export function mockUserMessage(content: string, overrides?: Partial): UserMessage -export function mockSystemMessage(overrides?: Partial): SystemMessage -export function mockToolUseBlock(name: string, input: unknown): ToolUseBlock -export function mockToolResultMessage(toolUseId: string, content: string): UserMessage -``` - -## 集成测试场景 - -### describe('Message pipeline') - -- test('create → normalize → API format produces valid request') — 创建消息 → normalizeMessagesForAPI → 验证输出结构 -- test('tool use and tool result pairing is preserved through normalization') -- test('merge + normalize handles conversation with interruptions') diff --git a/docs/test-plans/07-cron.md b/docs/test-plans/07-cron.md deleted file mode 100644 index caf5436cc..000000000 --- a/docs/test-plans/07-cron.md +++ /dev/null @@ -1,112 +0,0 @@ -# Cron 调度测试计划 - -## 概述 - -Cron 模块提供 cron 表达式解析、下次运行时间计算和人类可读描述。全部为纯函数,无外部依赖,是最适合单元测试的模块之一。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/utils/cron.ts` | `CronFields`, `parseCronExpression`, `computeNextCronRun`, `cronToHuman` | - ---- - -## 测试用例 - -### describe('parseCronExpression') - -#### 有效表达式 - -- test('parses wildcard fields') — `'* * * * *'` → 每个字段为完整范围 -- test('parses specific values') — `'30 14 1 6 3'` → minute=[30], hour=[14], dom=[1], month=[6], dow=[3] -- test('parses step syntax') — `'*/5 * * * *'` → minute=[0,5,10,...,55] -- test('parses range syntax') — `'1-5 * * * *'` → minute=[1,2,3,4,5] -- test('parses range with step') — `'1-10/3 * * * *'` → minute=[1,4,7,10] -- test('parses comma-separated list') — `'1,15,30 * * * *'` → minute=[1,15,30] -- test('parses day-of-week 7 as Sunday alias') — `'0 0 * * 7'` → dow=[0] -- test('parses range with day-of-week 7') — `'0 0 * * 5-7'` → dow=[0,5,6] -- test('parses complex combined expression') — `'0,30 9-17 * * 1-5'` → 工作日 9-17 每半小时 - -#### 无效表达式 - -- test('returns null for wrong field count') — `'* * *'` → null -- test('returns null for out-of-range values') — `'60 * * * *'` → null(minute max=59) -- test('returns null for invalid step') — `'*/0 * * * *'` → null(step=0) -- test('returns null for reversed range') — `'10-5 * * * *'` → null(lo>hi) -- test('returns null for empty string') — `''` → null -- test('returns null for non-numeric tokens') — `'abc * * * *'` → null - -#### 字段范围验证 - -- test('minute: 0-59') -- test('hour: 0-23') -- test('dayOfMonth: 1-31') -- test('month: 1-12') -- test('dayOfWeek: 0-6 (plus 7 alias)') - ---- - -### describe('computeNextCronRun') - -#### 基本匹配 - -- test('finds next minute') — from 14:30:45, cron `'31 14 * * *'` → 14:31:00 同天 -- test('finds next hour') — from 14:30, cron `'0 15 * * *'` → 15:00 同天 -- test('rolls to next day') — from 14:30, cron `'0 10 * * *'` → 10:00 次日 -- test('rolls to next month') — from 1月31日, cron `'0 0 1 * *'` → 2月1日 -- test('is strictly after from date') — from 恰好匹配时应返回下一次而非当前时间 - -#### DOM/DOW 语义 - -- test('OR semantics when both dom and dow constrained') — dom=15, dow=3 → 匹配 15 号 OR 周三 -- test('only dom constrained uses dom') — dom=15, dow=* → 只匹配 15 号 -- test('only dow constrained uses dow') — dom=*, dow=3 → 只匹配周三 -- test('both wildcarded matches every day') — dom=*, dow=* → 每天 - -#### 边界情况 - -- test('handles month boundary') — 从 2 月 28 日寻找 2 月 29 日或 3 月 1 日 -- test('returns null after 366-day search') — 不可能匹配的表达式返回 null(理论上不会发生) -- test('handles step across midnight') — `'0 0 * * *'` 从 23:59 → 次日 0:00 - -#### 每 N 分钟 - -- test('every 5 minutes from arbitrary time') — `'*/5 * * * *'` from 14:32 → 14:35 -- test('every minute') — `'* * * * *'` from 14:32:45 → 14:33:00 - ---- - -### describe('cronToHuman') - -#### 常见模式 - -- test('every N minutes') — `'*/5 * * * *'` → `'Every 5 minutes'` -- test('every minute') — `'*/1 * * * *'` → `'Every minute'` -- test('every hour at :00') — `'0 * * * *'` → `'Every hour'` -- test('every hour at :30') — `'30 * * * *'` → `'Every hour at :30'` -- test('every N hours') — `'0 */2 * * *'` → `'Every 2 hours'` -- test('daily at specific time') — `'30 9 * * *'` → `'Every day at 9:30 AM'` -- test('specific day of week') — `'0 9 * * 3'` → `'Every Wednesday at 9:00 AM'` -- test('weekdays') — `'0 9 * * 1-5'` → `'Weekdays at 9:00 AM'` - -#### Fallback - -- test('returns raw cron for complex patterns') — 非常见模式返回原始 cron 字符串 -- test('returns raw cron for wrong field count') — `'* * *'` → 原样返回 - -#### UTC 模式 - -- test('UTC option formats time in local timezone') — `{ utc: true }` 时 UTC 时间转本地显示 -- test('UTC midnight crossing adjusts day name') — UTC 时间跨天时本地星期名正确 - ---- - -## Mock 需求 - -**无需 Mock**。所有函数为纯函数,唯一的外部依赖是 `Date` 构造器和 `toLocaleTimeString`,可通过传入确定性的 `from` 参数控制。 - -## 注意事项 - -- `cronToHuman` 的时间格式化依赖系统 locale,测试中建议使用 `'en-US'` locale 或只验证部分输出 -- `computeNextCronRun` 使用本地时区,DST 相关测试需注意运行环境 diff --git a/docs/test-plans/08-git-utils.md b/docs/test-plans/08-git-utils.md deleted file mode 100644 index cd088fd7c..000000000 --- a/docs/test-plans/08-git-utils.md +++ /dev/null @@ -1,106 +0,0 @@ -# Git 工具测试计划 - -## 概述 - -Git 工具模块提供 git 远程 URL 规范化、仓库根目录查找、裸仓库安全检测等功能。测试重点是纯函数的 URL 规范化和需要文件系统 mock 的仓库发现逻辑。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/utils/git.ts` | `normalizeGitRemoteUrl`, `findGitRoot`, `findCanonicalGitRoot`, `getIsGit`, `isAtGitRoot`, `getRepoRemoteHash`, `isCurrentDirectoryBareGitRepo`, `gitExe`, `getGitState`, `stashToCleanState`, `preserveGitStateForIssue` | - ---- - -## 测试用例 - -### describe('normalizeGitRemoteUrl')(纯函数) - -#### SSH 格式 - -- test('normalizes SSH URL') — `'git@github.com:owner/repo.git'` → `'github.com/owner/repo'` -- test('normalizes SSH URL without .git suffix') — `'git@github.com:owner/repo'` → `'github.com/owner/repo'` -- test('handles GitLab SSH') — `'git@gitlab.com:group/subgroup/repo.git'` → `'gitlab.com/group/subgroup/repo'` - -#### HTTPS 格式 - -- test('normalizes HTTPS URL') — `'https://github.com/owner/repo.git'` → `'github.com/owner/repo'` -- test('normalizes HTTPS URL without .git suffix') — `'https://github.com/owner/repo'` → `'github.com/owner/repo'` -- test('normalizes HTTP URL') — `'http://github.com/owner/repo.git'` → `'github.com/owner/repo'` - -#### SSH:// 协议格式 - -- test('normalizes ssh:// URL') — `'ssh://git@github.com/owner/repo'` → `'github.com/owner/repo'` -- test('handles user prefix in ssh://') — `'ssh://user@host/path'` → `'host/path'` - -#### 代理 URL(CCR git proxy) - -- test('normalizes legacy proxy URL') — `'http://local_proxy@127.0.0.1:16583/git/owner/repo'` → `'github.com/owner/repo'` -- test('normalizes GHE proxy URL') — `'http://user@127.0.0.1:8080/git/ghe.company.com/owner/repo'` → `'ghe.company.com/owner/repo'` - -#### 边界情况 - -- test('returns null for empty string') — `''` → null -- test('returns null for whitespace') — `' '` → null -- test('returns null for unrecognized format') — `'not-a-url'` → null -- test('output is lowercase') — `'git@GitHub.com:Owner/Repo.git'` → `'github.com/owner/repo'` -- test('SSH and HTTPS for same repo produce same result') — 相同仓库不同协议 → 相同输出 - ---- - -### describe('findGitRoot')(需文件系统 Mock) - -- test('finds git root from nested directory') — `/project/src/utils/` → `/project/`(假设 `/project/.git` 存在) -- test('finds git root from root directory') — `/project/` → `/project/` -- test('returns null for non-git directory') — 无 `.git` → null -- test('handles worktree .git file') — `.git` 为文件时也识别 -- test('memoizes results') — 同一路径不重复查找 - -### describe('findCanonicalGitRoot') - -- test('returns same as findGitRoot for regular repo') -- test('resolves worktree to main repo root') — worktree 路径 → 主仓库根目录 -- test('returns null for non-git directory') - -### describe('gitExe') - -- test('returns git path string') — 返回字符串 -- test('memoizes the result') — 多次调用返回同一值 - ---- - -### describe('getRepoRemoteHash')(需 Mock) - -- test('returns 16-char hex hash') — 返回值为 16 位十六进制字符串 -- test('returns null when no remote') — 无 remote URL 时返回 null -- test('same repo SSH/HTTPS produce same hash') — 不同协议同一仓库 hash 相同 - ---- - -### describe('isCurrentDirectoryBareGitRepo')(需文件系统 Mock) - -- test('detects bare git repo attack vector') — 目录含 HEAD + objects/ + refs/ 但无有效 .git/HEAD → true -- test('returns false for normal directory') — 普通目录 → false -- test('returns false for regular git repo') — 有效 .git 目录 → false - ---- - -## Mock 需求 - -| 依赖 | Mock 方式 | 说明 | -|------|-----------|------| -| `statSync` | mock module | `findGitRoot` 中的 .git 检测 | -| `readFileSync` | mock module | worktree .git 文件读取 | -| `realpathSync` | mock module | 路径解析 | -| `execFileNoThrow` | mock module | git 命令执行 | -| `whichSync` | mock module | `gitExe` 中的 git 路径查找 | -| `getCwd` | mock module | 当前工作目录 | -| `getRemoteUrl` | mock module | `getRepoRemoteHash` 依赖 | -| 临时目录 | `mkdtemp` | 集成测试中创建临时 git 仓库 | - -## 集成测试场景 - -### describe('Git repo discovery')(放在 tests/integration/) - -- test('findGitRoot works in actual git repo') — 在临时 git init 的目录中验证 -- test('normalizeGitRemoteUrl + getRepoRemoteHash produces stable hash') — URL → hash 端到端验证 diff --git a/docs/test-plans/09-config-settings.md b/docs/test-plans/09-config-settings.md deleted file mode 100644 index 8df08cfac..000000000 --- a/docs/test-plans/09-config-settings.md +++ /dev/null @@ -1,161 +0,0 @@ -# 配置系统测试计划 - -## 概述 - -配置系统包含全局配置(GlobalConfig)、项目配置(ProjectConfig)和设置(Settings)三层。测试重点是纯函数校验逻辑、Zod schema 验证和配置合并策略。 - -## 被测文件 - -| 文件 | 关键导出 | -|------|----------| -| `src/utils/config.ts` | `getGlobalConfig`, `saveGlobalConfig`, `getCurrentProjectConfig`, `checkHasTrustDialogAccepted`, `isPathTrusted`, `getOrCreateUserID`, `isAutoUpdaterDisabled` | -| `src/utils/settings/settings.ts` | `getSettingsForSource`, `parseSettingsFile`, `getSettingsFilePathForSource`, `getInitialSettings` | -| `src/utils/settings/types.ts` | `SettingsSchema`(Zod schema) | -| `src/utils/settings/validation.ts` | 设置验证函数 | -| `src/utils/settings/constants.ts` | 设置常量 | - ---- - -## 测试用例 - -### src/utils/config.ts — 纯函数/常量 - -#### describe('DEFAULT_GLOBAL_CONFIG') - -- test('has all required fields') — 默认配置对象包含所有必需字段 -- test('has null auth fields by default') — oauthAccount 等为 null - -#### describe('DEFAULT_PROJECT_CONFIG') - -- test('has empty allowedTools') — 默认为空数组 -- test('has empty mcpServers') — 默认为空对象 - -#### describe('isAutoUpdaterDisabled') - -- test('returns true when CLAUDE_CODE_DISABLE_AUTOUPDATER is set') — env 设置时禁用 -- test('returns true when disableAutoUpdater config is true') -- test('returns false by default') - ---- - -### src/utils/config.ts — 需 Mock - -#### describe('getGlobalConfig') - -- test('returns cached config on subsequent calls') — 缓存机制 -- test('returns TEST_GLOBAL_CONFIG_FOR_TESTING in test mode') -- test('reads config from ~/.claude.json') -- test('returns default config when file does not exist') - -#### describe('saveGlobalConfig') - -- test('applies updater function to current config') — updater 修改被保存 -- test('creates backup before writing') — 写入前备份 -- test('prevents auth state loss') — `wouldLoseAuthState` 检查 - -#### describe('getCurrentProjectConfig') - -- test('returns project config for current directory') -- test('returns default config when no project config exists') - -#### describe('checkHasTrustDialogAccepted') - -- test('returns true when trust is accepted in current directory') -- test('returns true when parent directory is trusted') — 父目录信任传递 -- test('returns false when no trust accepted') -- test('caches positive results') - -#### describe('isPathTrusted') - -- test('returns true for trusted path') -- test('returns false for untrusted path') - -#### describe('getOrCreateUserID') - -- test('returns existing user ID from config') -- test('creates and persists new ID when none exists') -- test('returns consistent ID across calls') - ---- - -### src/utils/settings/settings.ts - -#### describe('getSettingsFilePathForSource') - -- test('returns ~/.claude/settings.json for userSettings') — 全局用户设置路径 -- test('returns .claude/settings.json for projectSettings') — 项目设置路径 -- test('returns .claude/settings.local.json for localSettings') — 本地设置路径 - -#### describe('parseSettingsFile')(需 Mock 文件读取) - -- test('parses valid settings JSON') — 有效 JSON → `{ settings, errors: [] }` -- test('returns errors for invalid fields') — 无效字段 → errors 非空 -- test('returns empty settings for non-existent file') -- test('handles JSON with comments') — JSONC 格式支持 - -#### describe('getInitialSettings') - -- test('merges settings from all sources') — user + project + local 合并 -- test('later sources override earlier ones') — 优先级:policy > user > project > local - ---- - -### src/utils/settings/types.ts — Zod Schema 验证 - -#### describe('SettingsSchema validation') - -- test('accepts valid minimal settings') — `{}` → 有效 -- test('accepts permissions block') — `{ permissions: { allow: ['Bash(*)'] } }` → 有效 -- test('accepts model setting') — `{ model: 'sonnet' }` → 有效 -- test('accepts hooks configuration') — 有效的 hooks 对象被接受 -- test('accepts env variables') — `{ env: { FOO: 'bar' } }` → 有效 -- test('rejects unknown top-level keys') — 未知字段被拒绝或忽略(取决于 schema 配置) -- test('rejects invalid permission mode') — `{ permissions: { defaultMode: 'invalid' } }` → 错误 -- test('rejects non-string model') — `{ model: 123 }` → 错误 -- test('accepts mcpServers configuration') — MCP server 配置有效 -- test('accepts sandbox configuration') - ---- - -### src/utils/settings/validation.ts - -#### describe('settings validation') - -- test('validates permission rules format') — `'Bash(npm install)'` 格式正确 -- test('rejects malformed permission rules') -- test('validates hook configuration structure') -- test('provides helpful error messages') — 错误信息包含字段路径 - ---- - -## Mock 需求 - -| 依赖 | Mock 方式 | 说明 | -|------|-----------|------| -| 文件系统 | 临时目录 + mock | config 文件读写 | -| `lockfile` | mock module | 文件锁 | -| `getCwd` | mock module | 项目路径判断 | -| `findGitRoot` | mock module | 项目根目录 | -| `process.env` | 直接设置/恢复 | `CLAUDE_CODE_DISABLE_AUTOUPDATER` 等 | - -### 测试用临时文件结构 - -``` -/tmp/claude-test-xxx/ -├── .claude/ -│ ├── settings.json # projectSettings -│ └── settings.local.json # localSettings -├── home/ -│ └── .claude/ -│ └── settings.json # userSettings(mock HOME) -└── project/ - └── .git/ -``` - -## 集成测试场景 - -### describe('Config + Settings merge pipeline') - -- test('user settings + project settings merge correctly') — 验证合并优先级 -- test('deny rules from settings are reflected in tool permission context') -- test('trust dialog state persists across config reads') diff --git a/docs/test-plans/10-fix-weak-tests.md b/docs/test-plans/10-fix-weak-tests.md deleted file mode 100644 index 7fe03532b..000000000 --- a/docs/test-plans/10-fix-weak-tests.md +++ /dev/null @@ -1,361 +0,0 @@ -# Plan 10 — 修复 WEAK 评分测试文件 - -> 优先级:高 | 8 个文件 | 预估新增/修改 ~60 个测试用例 - -本计划修复 testing-spec.md 中评定为 WEAK 的 8 个测试文件的断言缺陷和覆盖缺口。 - ---- - -## 10.1 `src/utils/__tests__/format.test.ts` - -**问题**:`formatNumber`、`formatTokens`、`formatRelativeTime` 使用 `toContain` 代替精确匹配,无法检测格式回归。 - -### 修改清单 - -#### formatNumber — toContain → toBe - -```typescript -// 当前(弱) -expect(formatNumber(1321)).toContain("k"); -expect(formatNumber(1500000)).toContain("m"); - -// 修复为 -expect(formatNumber(1321)).toBe("1.3k"); -expect(formatNumber(1500000)).toBe("1.5m"); -``` - -> 注意:`Intl.NumberFormat` 输出可能因 locale 不同。若 CI locale 不一致,改用 `toMatch(/^\d+(\.\d)?[km]$/)` 正则匹配。 - -#### formatTokens — 补精确断言 - -```typescript -expect(formatTokens(1000)).toBe("1k"); -expect(formatTokens(1500)).toBe("1.5k"); -``` - -#### formatRelativeTime — toContain → toBe - -```typescript -// 当前(弱) -expect(formatRelativeTime(diff, now)).toContain("30"); -expect(formatRelativeTime(diff, now)).toContain("ago"); - -// 修复为 -expect(formatRelativeTime(diff, now)).toBe("30s ago"); -``` - -#### 新增:formatDuration 进位边界 - -| 用例 | 输入 | 期望 | -|------|------|------| -| 59.5s 进位 | 59500ms | 至少含 `1m` | -| 59m59s 进位 | 3599000ms | 至少含 `1h` | -| sub-millisecond | 0.5ms | `"<1ms"` 或 `"0ms"` | - -#### 新增:未测试函数 - -| 函数 | 最少用例 | -|------|---------| -| `formatRelativeTimeAgo` | 2(过去 / 未来) | -| `formatLogMetadata` | 1(基本调用不抛错) | -| `formatResetTime` | 2(有值 / null) | -| `formatResetText` | 1(基本调用) | - ---- - -## 10.2 `src/tools/shared/__tests__/gitOperationTracking.test.ts` - -**问题**:`detectGitOperation` 内部调用 `getCommitCounter()`、`getPrCounter()`、`logEvent()`,测试产生分析副作用。 - -### 修改清单 - -#### 添加 analytics mock - -在文件顶部添加 `mock.module`: - -```typescript -import { mock, afterAll, afterEach, beforeEach } from "bun:test"; - -mock.module("src/services/analytics/index.ts", () => ({ - logEvent: mock(() => {}), -})); - -mock.module("src/bootstrap/state.ts", () => ({ - getCommitCounter: mock(() => ({ increment: mock(() => {}) })), - getPrCounter: mock(() => ({ increment: mock(() => {}) })), -})); -``` - -> 需验证 `detectGitOperation` 的实际导入路径,按需调整 mock 目标。 - -#### 新增:缺失的 GH PR actions - -| 用例 | 输入 | 期望 | -|------|------|------| -| gh pr edit | `'gh pr edit 123 --title "fix"'` | `result.pr.number === 123` | -| gh pr close | `'gh pr close 456'` | `result.pr.number === 456` | -| gh pr ready | `'gh pr ready 789'` | `result.pr.number === 789` | -| gh pr comment | `'gh pr comment 123 --body "done"'` | `result.pr.number === 123` | - -#### 新增:parseGitCommitId 边界 - -| 用例 | 输入 | 期望 | -|------|------|------| -| 完整 40 字符 SHA | `'[abcdef0123456789abcdef0123456789abcdef01] ...'` | 返回完整 40 字符 | -| 畸形括号输出 | `'create mode 100644 file.txt'` | 返回 `null` | - ---- - -## 10.3 `src/utils/permissions/__tests__/PermissionMode.test.ts` - -**问题**:`isExternalPermissionMode` 在非 ant 环境永远返回 true,false 路径从未执行;mode 覆盖不完整。 - -### 修改清单 - -#### 补全 mode 覆盖 - -| 函数 | 缺失的 mode | -|------|-------------| -| `permissionModeTitle` | `bypassPermissions`, `dontAsk` | -| `permissionModeShortTitle` | `dontAsk`, `acceptEdits` | -| `getModeColor` | `dontAsk`, `acceptEdits`, `plan` | -| `permissionModeFromString` | `acceptEdits`, `bypassPermissions` | -| `toExternalPermissionMode` | `acceptEdits`, `bypassPermissions` | - -#### 修复 isExternalPermissionMode - -```typescript -// 当前:只测了非 ant 环境(永远 true) -// 需要新增 ant 环境测试 -describe("when USER_TYPE is 'ant'", () => { - beforeEach(() => { - process.env.USER_TYPE = "ant"; - }); - afterEach(() => { - delete process.env.USER_TYPE; - }); - - test("returns false for 'auto' in ant context", () => { - expect(isExternalPermissionMode("auto")).toBe(false); - }); - - test("returns false for 'bubble' in ant context", () => { - expect(isExternalPermissionMode("bubble")).toBe(false); - }); - - test("returns true for non-ant modes in ant context", () => { - expect(isExternalPermissionMode("plan")).toBe(true); - }); -}); -``` - -#### 新增:permissionModeSchema - -| 用例 | 输入 | 期望 | -|------|------|------| -| 有效 mode | `'plan'` | `success: true` | -| 无效 mode | `'invalid'` | `success: false` | - ---- - -## 10.4 `src/utils/permissions/__tests__/dangerousPatterns.test.ts` - -**问题**:纯数据 smoke test,无行为验证。 - -### 修改清单 - -#### 新增:重复值检查 - -```typescript -test("CROSS_PLATFORM_CODE_EXEC has no duplicates", () => { - const set = new Set(CROSS_PLATFORM_CODE_EXEC); - expect(set.size).toBe(CROSS_PLATFORM_CODE_EXEC.length); -}); - -test("DANGEROUS_BASH_PATTERNS has no duplicates", () => { - const set = new Set(DANGEROUS_BASH_PATTERNS); - expect(set.size).toBe(DANGEROUS_BASH_PATTERNS.length); -}); -``` - -#### 新增:全量成员断言(用 Set 确保精确) - -```typescript -test("CROSS_PLATFORM_CODE_EXEC contains expected interpreters", () => { - const expected = ["node", "python", "python3", "ruby", "perl", "php", - "bun", "deno", "npx", "tsx"]; - const set = new Set(CROSS_PLATFORM_CODE_EXEC); - for (const entry of expected) { - expect(set.has(entry)).toBe(true); - } -}); -``` - -#### 新增:空字符串不匹配 - -```typescript -test("empty string does not match any pattern", () => { - for (const pattern of DANGEROUS_BASH_PATTERNS) { - expect("".startsWith(pattern)).toBe(false); - } -}); -``` - ---- - -## 10.5 `src/utils/__tests__/zodToJsonSchema.test.ts` - -**问题**:object 属性仅 `toBeDefined` 未验证类型结构;optional 字段未验证 absence。 - -### 修改清单 - -#### 修复 object schema 测试 - -```typescript -// 当前(弱) -expect(schema.properties!.name).toBeDefined(); -expect(schema.properties!.age).toBeDefined(); - -// 修复为 -expect(schema.properties!.name).toEqual({ type: "string" }); -expect(schema.properties!.age).toEqual({ type: "number" }); -``` - -#### 修复 optional 字段测试 - -```typescript -test("optional field is not in required array", () => { - const schema = zodToJsonSchema(z.object({ - required: z.string(), - optional: z.string().optional(), - })); - expect(schema.required).toEqual(["required"]); - expect(schema.required).not.toContain("optional"); -}); -``` - -#### 新增:缺失的 schema 类型 - -| 用例 | 输入 | 期望 | -|------|------|------| -| `z.literal("foo")` | `z.literal("foo")` | `{ const: "foo" }` | -| `z.null()` | `z.null()` | `{ type: "null" }` | -| `z.union()` | `z.union([z.string(), z.number()])` | `{ anyOf: [...] }` | -| `z.record()` | `z.record(z.string(), z.number())` | `{ type: "object", additionalProperties: { type: "number" } }` | -| `z.tuple()` | `z.tuple([z.string(), z.number()])` | `{ type: "array", items: [...], additionalItems: false }` | -| 嵌套 object | `z.object({ a: z.object({ b: z.string() }) })` | 验证嵌套属性结构 | - ---- - -## 10.6 `src/utils/__tests__/envValidation.test.ts` - -**问题**:`validateBoundedIntEnvVar` lower bound=100 时 value=1 返回 `status: "valid"`,疑似源码 bug。 - -### 修改清单 - -#### 验证 lower bound 行为 - -```typescript -// 当前测试 -test("value of 1 with lower bound 100", () => { - const result = validateBoundedIntEnvVar("1", { defaultValue: 100, upperLimit: 1000, lowerLimit: 100 }); - // 如果源码有 bug,这里应该暴露 - expect(result.effective).toBeGreaterThanOrEqual(100); - expect(result.status).toBe(result.effective !== 100 ? "capped" : "valid"); -}); -``` - -#### 新增边界用例 - -| 用例 | value | lowerLimit | 期望 | -|------|-------|------------|------| -| 低于 lower bound | `"50"` | 100 | `effective: 100, status: "capped"` | -| 等于 lower bound | `"100"` | 100 | `effective: 100, status: "valid"` | -| 浮点截断 | `"50.7"` | 100 | `effective: 100`(parseInt 截断后 cap) | -| 空白字符 | `" 500 "` | 1 | `effective: 500, status: "valid"` | -| defaultValue 为 0 | `"0"` | 0 | 需确认 `parsed <= 0` 逻辑 | - -> **行动**:先确认 `validateBoundedIntEnvVar` 源码中 lower bound 的实际执行路径。如果确实不生效,需先修源码再补测试。 - ---- - -## 10.7 `src/utils/__tests__/file.test.ts` - -**问题**:`addLineNumbers` 仅 `toContain`,未验证完整格式。 - -### 修改清单 - -#### 修复 addLineNumbers 断言 - -```typescript -// 当前(弱) -expect(result).toContain("1"); -expect(result).toContain("hello"); - -// 修复为(需确定 isCompactLinePrefixEnabled 行为) -// 假设 compact=false,格式为 " 1→hello" -test("formats single line with tab prefix", () => { - // 先确认环境,如果 compact 模式不确定,用正则 - expect(result).toMatch(/^\s*\d+[→\t]hello$/m); -}); -``` - -#### 新增:stripLineNumberPrefix 边界 - -| 用例 | 输入 | 期望 | -|------|------|------| -| 纯数字行 | `"123"` | `""` | -| 无内容前缀 | `"→"` | `""` | -| compact 格式 `"1\thello"` | `"1\thello"` | `"hello"` | - -#### 新增:pathsEqual 边界 - -| 用例 | a | b | 期望 | -|------|---|---|------| -| 尾部斜杠差异 | `"/a/b"` | `"/a/b/"` | `false` | -| `..` 段 | `"/a/../b"` | `"/b"` | 视实现而定 | - ---- - -## 10.8 `src/utils/__tests__/notebook.test.ts` - -**问题**:`mapNotebookCellsToToolResult` 内容检查用 `toContain`,未验证 XML 格式。 - -### 修改清单 - -#### 修复 content 断言 - -```typescript -// 当前(弱) -expect(result).toContain("cell-0"); -expect(result).toContain("print('hello')"); - -// 修复为 -expect(result).toContain(''); -expect(result).toContain(""); -``` - -#### 新增:parseCellId 边界 - -| 用例 | 输入 | 期望 | -|------|------|------| -| 负数 | `"cell--1"` | `null` | -| 前导零 | `"cell-007"` | `7` | -| 极大数 | `"cell-999999999"` | `999999999` | - -#### 新增:mapNotebookCellsToToolResult 边界 - -| 用例 | 输入 | 期望 | -|------|------|------| -| 空 data 数组 | `{ cells: [] }` | 空字符串或空结果 | -| 无 cell_id | `{ cell_type: "code", source: "x" }` | fallback 到 `cell-${index}` | -| error output | `{ output_type: "error", ename: "Error", evalue: "msg" }` | 包含 error 信息 | - ---- - -## 验收标准 - -- [ ] `bun test` 全部通过 -- [ ] 8 个文件评分从 WEAK 提升至 ACCEPTABLE 或 GOOD -- [ ] `toContain` 仅用于警告文本等确实不确定精确值的场景 -- [ ] envValidation bug 确认并修复(或确认非 bug 并更新测试) diff --git a/docs/test-plans/11-strengthen-acceptable-tests.md b/docs/test-plans/11-strengthen-acceptable-tests.md deleted file mode 100644 index c1f563ca9..000000000 --- a/docs/test-plans/11-strengthen-acceptable-tests.md +++ /dev/null @@ -1,177 +0,0 @@ -# Plan 11 — 加强 ACCEPTABLE 评分测试 - -> 优先级:中 | ~15 个文件 | 预估新增 ~80 个测试用例 - -本计划对 ACCEPTABLE 评分文件中的具体缺陷进行定向加强。每个条目只列出需要改动的部分,不做全量重写。 - ---- - -## 11.1 `src/utils/__tests__/diff.test.ts` - -| 改动 | 当前 | 改为 | -|------|------|------| -| `getPatchFromContents` 断言 | `hunks.length > 0` | 验证具体 `+`/`-` 行内容 | -| `$` 字符转义 | 未测试 | 新增含 `$` 的内容测试 | -| `ignoreWhitespace` 选项 | 未测试 | 新增 `ignoreWhitespace: true` 用例 | -| 删除全部内容 | 未测试 | `newContent: ""` | -| 多 hunk 偏移 | `adjustHunkLineNumbers` 仅单 hunk | 新增多 hunk 同数组测试 | - ---- - -## 11.2 `src/utils/__tests__/path.test.ts` - -当前仅覆盖 2/5+ 导出函数。新增: - -| 函数 | 最少用例 | 关键边界 | -|------|---------|---------| -| `expandPath` | 6 | `~/` 展开、绝对路径直通、相对路径、空串、含 null 字节、`~user` 格式 | -| `toRelativePath` | 3 | 同级文件、子目录、父目录 | -| `sanitizePath` | 3 | 正常路径、含 `..` 段、空串 | - -`containsPathTraversal` 补充: -- URL 编码 `%2e%2e%2f`(确认不匹配,记录为非需求) -- 混合分隔符 `foo/..\bar` - -`normalizePathForConfigKey` 补充: -- 混合分隔符 `foo/bar\baz` -- 冗余分隔符 `foo//bar` -- Windows 盘符 `C:\foo\bar` - ---- - -## 11.3 `src/utils/__tests__/uuid.test.ts` - -| 改动 | 说明 | -|------|------| -| 大写测试断言强化 | `not.toBeNull()` → 验证标准化输出(小写+连字符格式) | -| 新增 `createAgentId` | 3 用例:无 label / 有 label / 输出格式正则 `/^a[a-z]*-[a-f0-9]{16}$/` | -| 前后空白 | `" 550e8400-... "` 期望 `null` | - ---- - -## 11.4 `src/utils/__tests__/semver.test.ts` - -| 用例 | 输入 | 期望 | -|------|------|------| -| pre-release 比较 | `gt("1.0.0", "1.0.0-alpha")` | `true` | -| pre-release 间比较 | `order("1.0.0-alpha", "1.0.0-beta")` | `-1` | -| tilde range | `satisfies("1.2.5", "~1.2.3")` | `true` | -| `*` 通配符 | `satisfies("2.0.0", "*")` | `true` | -| 畸形版本 | `order("abc", "1.0.0")` | 确认不抛错 | -| `0.0.0` | `gt("0.0.0", "0.0.0")` | `false` | - ---- - -## 11.5 `src/utils/__tests__/hash.test.ts` - -| 改动 | 当前 | 改为 | -|------|------|------| -| djb2 32 位检查 | `hash \| 0`(恒 true) | `Number.isSafeInteger(hash) && Math.abs(hash) <= 0x7FFFFFFF` | -| hashContent 空串 | 未测试 | 新增 | -| hashContent 格式 | 未验证输出为数字串 | `toMatch(/^\d+$/)` | -| hashPair 空串 | 未测试 | `hashPair("", "b")`, `hashPair("", "")` | -| 已知答案 | 无 | 断言 `djb2Hash("hello")` 为特定值(需先在控制台运行一次确定) | - ---- - -## 11.6 `src/utils/__tests__/claudemd.test.ts` - -当前仅覆盖 3 个辅助函数。新增: - -| 用例 | 函数 | 说明 | -|------|------|------| -| 未闭合注释 | `stripHtmlComments` | `"text"` → `"text"` | -| 同行注释+内容 | `stripHtmlComments` | `"some text"` → `"some text"` | -| 内联代码中的注释 | `stripHtmlComments` | `` `` `` → 保留 | -| 大小写不敏感 | `isMemoryFilePath` | `"claude.md"`, `"CLAUDE.MD"` | -| 非 .md 规则文件 | `isMemoryFilePath` | `.claude/rules/foo.txt` → `false` | -| 空数组 | `getLargeMemoryFiles` | `[]` → `[]` | - ---- - -## 11.7 `src/tools/FileEditTool/__tests__/utils.test.ts` - -| 函数 | 新增用例 | -|------|---------| -| `normalizeQuotes` | 混合引号 `"`she said 'hello'"` | -| `stripTrailingWhitespace` | CR-only `\r`、无尾部换行、全空白串 | -| `findActualString` | 空 content、Unicode content | -| `preserveQuoteStyle` | 单引号、缩写中的撇号(如 `it's`)、空串 | -| `applyEditToFile` | `replaceAll=true` 零匹配、`oldString` 无尾部 `\n`、多行内容 | - ---- - -## 11.8 `src/utils/model/__tests__/providers.test.ts` - -| 改动 | 说明 | -|------|------| -| 删除 `originalEnv` | 未使用,消除死代码 | -| env 恢复改为快照 | `beforeEach` 保存 `process.env`,`afterEach` 恢复 | -| 新增三变量同时设置 | bedrock + vertex + foundry 全部为 `"1"`,验证优先级 | -| 新增非 `"1"` 值 | `"true"`, `"0"`, `""` | -| `isFirstPartyAnthropicBaseUrl` | URL 含路径 `/v1`、含尾斜杠、非 HTTPS | - ---- - -## 11.9 `src/utils/__tests__/hyperlink.test.ts` - -| 用例 | 说明 | -|------|------| -| 空 URL | `createHyperlink("http://x.com", "", { supported: true })` 不抛错 | -| undefined supportsHyperlinks | 选项未传时走默认检测 | -| 非 ant staging URL | `USER_TYPE !== "ant"` 时 staging 返回 `false` | - ---- - -## 11.10 `src/utils/__tests__/objectGroupBy.test.ts` - -| 用例 | 说明 | -|------|------| -| key 返回 undefined | `(_, i) => undefined` → 全部归入 `undefined` 组 | -| key 为特殊字符 | `({ name }) => name` 含空格/中文 | - ---- - -## 11.11 `src/utils/__tests__/CircularBuffer.test.ts` - -| 用例 | 说明 | -|------|------| -| capacity=1 | 添加 2 个元素,仅保留最后一个 | -| 空 buffer 调用 getRecent | 返回空数组 | -| getRecent(0) | 返回空数组 | - ---- - -## 11.12 `src/utils/__tests__/contentArray.test.ts` - -| 用例 | 说明 | -|------|------| -| 混合交替 | `[tool_result, text, tool_result]` — 验证插入到正确位置 | - ---- - -## 11.13 `src/utils/__tests__/argumentSubstitution.test.ts` - -| 用例 | 说明 | -|------|------| -| 转义引号 | `"he said \"hello\""` | -| 越界索引 | `$ARGUMENTS[99]`(参数不够时) | -| 多占位符 | `"cmd $0 $1 $0"` | - ---- - -## 11.14 `src/utils/__tests__/messages.test.ts` - -| 改动 | 说明 | -|------|------| -| `normalizeMessages` 断言加强 | 验证拆分后的消息内容,不只是长度 | -| `isNotEmptyMessage` 空白 | `[{ type: "text", text: " " }]` | - ---- - -## 验收标准 - -- [ ] `bun test` 全部通过 -- [ ] 目标文件评分从 ACCEPTABLE 提升至 GOOD -- [ ] 无 `toContain` 用于精确值检查的场景 diff --git a/docs/test-plans/12-mock-reliability.md b/docs/test-plans/12-mock-reliability.md deleted file mode 100644 index 0deb02d95..000000000 --- a/docs/test-plans/12-mock-reliability.md +++ /dev/null @@ -1,145 +0,0 @@ -# Plan 12 — Mock 可靠性修复 - -> 优先级:高 | 影响 4 个测试文件 | 预估修改 ~15 处 - -本计划修复测试中 mock 相关的副作用、状态泄漏和虚假测试。 - ---- - -## 12.1 `gitOperationTracking.test.ts` — 消除分析副作用 - -**当前问题**:`detectGitOperation` 内部调用 `logEvent()`、`getCommitCounter().increment()`、`getPrCounter().increment()`,每次测试运行都触发真实分析代码。 - -**修复步骤**: - -1. 读取 `src/tools/shared/gitOperationTracking.ts`,确认 analytics 导入路径 -2. 在测试文件顶部添加 `mock.module`: - -```typescript -import { mock } from "bun:test"; - -mock.module("src/services/analytics/index.ts", () => ({ - logEvent: mock(() => {}), - // 按需补充其他导出 -})); -``` - -3. 如果 `getCommitCounter` / `getPrCounter` 来自 `src/bootstrap/state.ts`: - -```typescript -mock.module("src/bootstrap/state.ts", () => ({ - getCommitCounter: mock(() => ({ increment: mock(() => {}) })), - getPrCounter: mock(() => ({ increment: mock(() => {}) })), - // 保留其他被测函数实际需要的导出 -})); -``` - -4. 使用 `await import()` 模式加载被测模块 -5. 运行测试验证无副作用 - -**风险**:`mock.module` 会替换整个模块。如果 `detectGitOperation` 还需要其他来自这些模块的导出,需在 mock 工厂中提供。 - ---- - -## 12.2 `PermissionMode.test.ts` — 修复 `isExternalPermissionMode` 虚假测试 - -**当前问题**:`isExternalPermissionMode` 依赖 `process.env.USER_TYPE`。非 ant 环境下所有 mode 都返回 true,测试从未覆盖 false 分支。 - -**修复步骤**: - -1. 新增 ant 环境测试组(见 Plan 10.3 详细用例) -2. 使用 `beforeEach`/`afterEach` 管理 `process.env.USER_TYPE` - -```typescript -describe("when USER_TYPE is 'ant'", () => { - const originalUserType = process.env.USER_TYPE; - beforeEach(() => { process.env.USER_TYPE = "ant"; }); - afterEach(() => { - if (originalUserType !== undefined) { - process.env.USER_TYPE = originalUserType; - } else { - delete process.env.USER_TYPE; - } - }); - - test("returns false for 'auto'", () => { - expect(isExternalPermissionMode("auto")).toBe(false); - }); - test("returns false for 'bubble'", () => { - expect(isExternalPermissionMode("bubble")).toBe(false); - }); - test("returns true for 'plan'", () => { - expect(isExternalPermissionMode("plan")).toBe(true); - }); -}); -``` - -3. 验证新增测试确实执行 false 路径 - ---- - -## 12.3 `providers.test.ts` — 环境变量快照恢复 - -**当前问题**: -- `originalEnv` 声明后未使用 -- `afterEach` 仅删除已知 3 个 key,如果源码新增 env var,测试间状态泄漏 - -**修复步骤**: - -```typescript -let savedEnv: Record; - -beforeEach(() => { - savedEnv = {}; - for (const key of Object.keys(process.env)) { - savedEnv[key] = process.env[key]; - } -}); - -afterEach(() => { - // 删除所有当前 env,恢复快照 - for (const key of Object.keys(process.env)) { - delete process.env[key]; - } - for (const [key, value] of Object.entries(savedEnv)) { - if (value !== undefined) { - process.env[key] = value; - } - } -}); -``` - -> 简化方案:只保存/恢复相关 key 列表 `["CLAUDE_CODE_USE_BEDROCK", "CLAUDE_CODE_USE_VERTEX", "CLAUDE_CODE_USE_FOUNDRY", "ANTHROPIC_BASE_URL", "USER_TYPE"]`,但需注释说明新增 env var 时需同步更新。 - ---- - -## 12.4 `envUtils.test.ts` — 验证环境变量恢复完整性 - -**当前状态**:已有 `afterEach` 恢复。需审查: - -1. 确认所有 `describe` 块中的 `afterEach` 都完整恢复了修改的 env var -2. 确认 `process.argv` 修改也被恢复(`getClaudeConfigHomeDir` 测试修改了 argv) -3. 新增:`afterEach` 中断言无意外 env 泄漏(可选,CI-only) - ---- - -## 12.5 `sleep.test.ts` / `memoize.test.ts` — 时间敏感测试加固 - -**当前状态**:已有合理 margin。可选加固: - -| 文件 | 用例 | 当前 | 加固 | -|------|------|------|------| -| `sleep.test.ts` | `resolves after timeout` | `sleep(50)`, check `>= 40ms` | 增大 margin:`sleep(50)`, check `>= 30ms` | -| `memoize.test.ts` | stale serve & refresh | TTL=1ms, wait 10ms | 增大 margin:TTL=5ms, wait 50ms | - -> 仅在 CI 出现 flaky 时执行此加固。 - ---- - -## 验收标准 - -- [ ] `gitOperationTracking.test.ts` 无分析副作用(可通过在 mock 中增加 `expect(logEvent).toHaveBeenCalledTimes(N)` 验证) -- [ ] `PermissionMode.test.ts` 的 `isExternalPermissionMode` 覆盖 true + false 分支 -- [ ] `providers.test.ts` 的 `originalEnv` 死代码已删除 -- [ ] 所有修改 env 的测试文件恢复完整 -- [ ] `bun test` 全部通过 diff --git a/docs/test-plans/13-cjk-truncate-tests.md b/docs/test-plans/13-cjk-truncate-tests.md deleted file mode 100644 index 1bd1cd7f6..000000000 --- a/docs/test-plans/13-cjk-truncate-tests.md +++ /dev/null @@ -1,71 +0,0 @@ -# Plan 13 — truncate CJK/Emoji 补充测试 - -> 优先级:中 | 1 个文件 | 预估新增 ~15 个测试用例 - -`truncate.ts` 使用 `stringWidth` 和 grapheme segmentation 实现宽度感知截断,但现有测试仅覆盖 ASCII。这是核心场景缺失。 - ---- - -## 被测函数 - -- `truncateToWidth(text, maxWidth)` — 尾部截断加 `…` -- `truncateStartToWidth(text, maxWidth)` — 头部截断加 `…` -- `truncateToWidthNoEllipsis(text, maxWidth)` — 尾部截断无省略号 -- `truncatePathMiddle(path, maxLength)` — 路径中间截断 -- `wrapText(text, maxWidth)` — 按宽度换行 - ---- - -## 新增用例 - -### CJK 全角字符 - -| 用例 | 函数 | 输入 | maxWidth | 期望行为 | -|------|------|------|----------|----------| -| 纯中文截断 | `truncateToWidth` | `"你好世界"` | 4 | `"你好…"` (每个中文字占 2 宽度) | -| 中英混合 | `truncateToWidth` | `"hello你好"` | 8 | `"hello你…"` | -| 全角不截断 | `truncateToWidth` | `"你好"` | 4 | `"你好"` (恰好 4) | -| emoji 单字符 | `truncateToWidth` | `"👋"` | 2 | `"👋"` (emoji 通常 2 宽度) | -| emoji 截断 | `truncateToWidth` | `"hello 👋 world"` | 8 | 确认宽度计算正确 | -| 头部中文 | `truncateStartToWidth` | `"你好世界"` | 4 | `"…界"` | -| 无省略中文 | `truncateToWidthNoEllipsis` | `"你好世界"` | 4 | `"你好"` | - -> **注意**:`stringWidth` 对 CJK/emoji 的宽度计算取决于具体实现。先在 REPL 中运行确认实际宽度再写断言: -> ```typescript -> import { stringWidth } from "src/utils/truncate.ts"; -> console.log(stringWidth("你好")); // 确认是 4 还是 2 -> console.log(stringWidth("👋")); // 确认 emoji 宽度 -> ``` - -### 路径中间截断补充 - -| 用例 | 输入 | maxLength | 期望 | -|------|------|-----------|------| -| 文件名超长 | `"/very/long/path/to/MyComponent.tsx"` | 10 | 含 `…` 且以 `.tsx` 结尾 | -| 无斜杠短串 | `"abc"` | 1 | 确认行为不抛错 | -| maxLength 极小 | `"/a/b"` | 1 | 确认不抛错 | -| maxLength=4 | `"/a/b/c.ts"` | 4 | 确认行为 | - -### wrapText 补充 - -| 用例 | 输入 | maxWidth | 期望 | -|------|------|----------|------| -| 含换行符 | `"hello\nworld"` | 10 | 保留原有换行 | -| 宽度=0 | `"hello"` | 0 | 空串或原串(确认不抛错) | - ---- - -## 实施步骤 - -1. 在 REPL 中确认 `stringWidth` 对 CJK/emoji 的实际返回值 -2. 按实际值编写精确断言 -3. 如果 `stringWidth` 依赖 ICU 或平台特性,添加平台检查(`process.platform !== "win32"` 跳过条件) -4. 运行测试 - ---- - -## 验收标准 - -- [ ] 至少 5 个 CJK/emoji 相关测试通过 -- [ ] 断言基于实际 `stringWidth` 返回值,非猜测 -- [ ] `bun test` 全部通过 diff --git a/docs/test-plans/14-integration-tests.md b/docs/test-plans/14-integration-tests.md deleted file mode 100644 index 9777a8763..000000000 --- a/docs/test-plans/14-integration-tests.md +++ /dev/null @@ -1,191 +0,0 @@ -# Plan 14 — 集成测试搭建 - -> 优先级:中 | 新建 ~3 个测试文件 | 预估 ~30 个测试用例 - -当前 `tests/integration/` 目录为空,spec 设计的三个集成测试均未创建。本计划搭建 mock 基础设施并实现核心集成测试。 - ---- - -## 14.1 搭建 `tests/mocks/` 基础设施 - -### 文件结构 - -``` -tests/ -├── mocks/ -│ ├── api-responses.ts # Claude API mock 响应 -│ ├── file-system.ts # 临时文件系统工具 -│ └── fixtures/ -│ ├── sample-claudemd.md # CLAUDE.md 样本 -│ └── sample-messages.json # 消息样本 -├── integration/ -│ ├── tool-chain.test.ts -│ ├── context-build.test.ts -│ └── message-pipeline.test.ts -└── helpers/ - └── setup.ts # 共享 beforeAll/afterAll -``` - -### `tests/mocks/file-system.ts` - -```typescript -import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; - -export async function createTempDir(prefix = "claude-test-"): Promise { - const dir = await mkdtemp(join(tmpdir(), prefix)); - return dir; -} - -export async function cleanupTempDir(dir: string): Promise { - await rm(dir, { recursive: true, force: true }); -} - -export async function writeTempFile(dir: string, name: string, content: string): Promise { - const path = join(dir, name); - await writeFile(path, content, "utf-8"); - return path; -} -``` - -### `tests/mocks/fixtures/sample-claudemd.md` - -```markdown -# Project Instructions - -This is a sample CLAUDE.md file for testing. -``` - -### `tests/mocks/api-responses.ts` - -```typescript -export const mockStreamResponse = { - type: "message_start" as const, - message: { - id: "msg_mock_001", - type: "message" as const, - role: "assistant", - content: [], - model: "claude-sonnet-4-20250514", - stop_reason: null, - stop_sequence: null, - usage: { input_tokens: 100, output_tokens: 0 }, - }, -}; - -export const mockTextBlock = { - type: "content_block_start" as const, - index: 0, - content_block: { type: "text" as const, text: "Mock response" }, -}; - -export const mockToolUseBlock = { - type: "content_block_start" as const, - index: 1, - content_block: { - type: "tool_use" as const, - id: "toolu_mock_001", - name: "Read", - input: { file_path: "/tmp/test.txt" }, - }, -}; - -export const mockEndEvent = { - type: "message_stop" as const, -}; -``` - ---- - -## 14.2 `tests/integration/tool-chain.test.ts` - -**目标**:验证 Tool 注册 → 发现 → 权限检查链路。 - -### 前置条件 - -`src/tools.ts` 的 `getAllBaseTools` / `getTools` 导入链过重。策略: -- 尝试直接 import 并 mock 最重依赖 -- 若不可行,改为测试 `src/Tool.ts` 的 `findToolByName` + 手动构造 tool 列表 - -### 用例 - -| # | 用例 | 验证点 | -|---|------|--------| -| 1 | `findToolByName("Bash")` 在已注册列表中查找 | 返回正确的 tool 定义 | -| 2 | `findToolByName("NonExistent")` | 返回 `undefined` | -| 3 | `findToolByName` 大小写不敏感 | `"bash"` 也能找到 | -| 4 | `filterToolsByDenyRules` 拒绝特定工具 | 被拒绝工具不在结果中 | -| 5 | `parseToolPreset("default")` 返回已知列表 | 包含核心 tools | -| 6 | `buildTool` 构建的 tool 可被 `findToolByName` 发现 | 端到端验证 | - -> 如果 `getAllBaseTools` 确实不可导入,改用 mock tool list 替代。 - ---- - -## 14.3 `tests/integration/context-build.test.ts` - -**目标**:验证系统提示组装流程(CLAUDE.md 加载 + git status + 日期注入)。 - -### 前置条件 - -`src/context.ts` 依赖链极重。策略: -- Mock `src/bootstrap/state.ts`(提供 cwd、projectRoot) -- Mock `src/utils/git.ts`(提供 git status) -- 使用真实 `src/utils/claudemd.ts` + 临时文件 - -### 用例 - -| # | 用例 | 验证点 | -|---|------|--------| -| 1 | 基本 context 构建 | 返回值包含系统提示字符串 | -| 2 | CLAUDE.md 内容出现在 context 中 | `stripHtmlComments` 后的内容被包含 | -| 3 | 多层目录 CLAUDE.md 合并 | 父目录 + 子目录 CLAUDE.md 都被加载 | -| 4 | 无 CLAUDE.md 时不报错 | context 正常返回,无 crash | -| 5 | git status 为 null | context 正常构建(测试环境中 git 不可用时) | - -> **风险评估**:如果 mock `context.ts` 的依赖链成本过高,退化为测试 `buildEffectiveSystemPrompt`(已在 systemPrompt.test.ts 中完成),记录为已知限制。 - ---- - -## 14.4 `tests/integration/message-pipeline.test.ts` - -**目标**:验证用户输入 → 消息格式化 → API 请求构建。 - -### 前置条件 - -`src/services/api/claude.ts` 构建最终 API 请求。策略: -- Mock Anthropic SDK 的 streaming endpoint -- 验证请求参数结构 - -### 用例 - -| # | 用例 | 验证点 | -|---|------|--------| -| 1 | 文本消息格式化 | `createUserMessage` 生成正确 role+content | -| 2 | tool_result 消息格式化 | 包含 tool_use_id 和 content | -| 3 | 多轮消息序列化 | messages 数组保持顺序 | -| 4 | 系统提示注入到请求 | API 请求的 system 字段非空 | -| 5 | 消息 normalize 后格式一致 | `normalizeMessages` 输出结构正确 | - -> **现实评估**:消息格式化的大部分已在 `messages.test.ts` 覆盖。API 请求构建需要 mock SDK,复杂度高。如果投入产出比低,仅实现用例 1-3 和 5,用例 4 标记为 stretch goal。 - ---- - -## 实施步骤 - -1. 创建 `tests/mocks/` 目录和基础文件 -2. 实现 `tool-chain.test.ts`(最低风险,最高价值) -3. 评估 `context-build.test.ts` 可行性,决定是否实施 -4. 实现 `message-pipeline.test.ts`(可降级为单元测试) -5. 更新 `testing-spec.md` 状态 - ---- - -## 验收标准 - -- [ ] `tests/mocks/` 基础设施可用 -- [ ] 至少 `tool-chain.test.ts` 实现并通过 -- [ ] 集成测试独立于单元测试运行:`bun test tests/integration/` -- [ ] 所有集成测试使用 `createTempDir` + `cleanupTempDir`,不留文件系统残留 -- [ ] `bun test` 全部通过 diff --git a/docs/test-plans/15-cli-coverage-baseline.md b/docs/test-plans/15-cli-coverage-baseline.md deleted file mode 100644 index 09fa9eacd..000000000 --- a/docs/test-plans/15-cli-coverage-baseline.md +++ /dev/null @@ -1,67 +0,0 @@ -# Plan 15 — CLI 参数测试 + 覆盖率基线 - -> 优先级:低 | 预估 ~15 个测试用例 - ---- - -## 15.1 `src/main.tsx` CLI 参数测试 - -**目标**:覆盖 Commander.js 配置的参数解析和模式切换。 - -### 前置条件 - -`src/main.tsx` 的 Commander 实例通常在模块顶层创建。测试策略: -- 直接构造 Commander 实例或 mock `main.tsx` 的 program 导出 -- 使用 `parseArgs` 而非 `parse`(不触发 `process.exit`) - -### 用例 - -| # | 用例 | 输入 | 期望 | -|---|------|------|------| -| 1 | 默认模式 | `[]` | 模式为 REPL | -| 2 | pipe 模式 | `["-p"]` | 模式为 pipe | -| 3 | pipe 带输入 | `["-p", "say hello"]` | 输入为 `"say hello"` | -| 4 | print 模式 | `["--print", "hello"]` | 等效于 pipe | -| 5 | verbose | `["-v"]` | verbose 标志为 true | -| 6 | model 选择 | `["--model", "claude-opus-4-6"]` | model 值正确传递 | -| 7 | system prompt | `["--system-prompt", "custom"]` | system prompt 被设置 | -| 8 | help | `["--help"]` | 显示帮助信息,不报错 | -| 9 | version | `["--version"]` | 显示版本号 | -| 10 | unknown flag | `["--nonexistent"]` | 不报错(Commander 允许未知参数时) | - -> **风险**:`main.tsx` 可能执行初始化逻辑(auth、analytics),需要在 mock 环境中运行。如果复杂度过高,降级为只测试参数解析部分。 - ---- - -## 15.2 覆盖率基线 - -### 运行命令 - -```bash -bun test --coverage 2>&1 | tail -50 -``` - -### 记录内容 - -| 模块 | 当前覆盖率 | 目标 | -|------|-----------|------| -| `src/utils/` | 待测量 | >= 80% | -| `src/utils/permissions/` | 待测量 | >= 60% | -| `src/utils/model/` | 待测量 | >= 60% | -| `src/Tool.ts` + `src/tools.ts` | 待测量 | >= 80% | -| `src/utils/claudemd.ts` | 待测量 | >= 40%(核心逻辑难测) | -| 整体 | 待测量 | 不设强制指标 | - -### 后续行动 - -- 将基线数据填入 `testing-spec.md` §4 -- 识别覆盖率最低的 10 个文件,排入后续测试计划 -- 如 `bun test --coverage` 输出不可用(Bun 版本限制),改用手动计算已测/总导出函数比 - ---- - -## 验收标准 - -- [ ] CLI 参数至少覆盖 5 个核心 flag -- [ ] 覆盖率基线数据记录到 testing-spec.md -- [ ] `bun test` 全部通过 diff --git a/docs/test-plans/phase-16-zero-dep-pure-functions.md b/docs/test-plans/phase-16-zero-dep-pure-functions.md deleted file mode 100644 index 23ebcb097..000000000 --- a/docs/test-plans/phase-16-zero-dep-pure-functions.md +++ /dev/null @@ -1,188 +0,0 @@ -# Phase 16 — 零依赖纯函数测试 - -> 创建日期:2026-04-02 -> 预计:+120 tests / 8 files -> 目标:覆盖所有零外部依赖的纯函数/类模块 - -所有模块均为纯函数或零外部依赖类,mock 成本为零,ROI 最高。 - ---- - -## 16.1 `src/utils/__tests__/stream.test.ts`(~15 tests) - -**目标模块**: `src/utils/stream.ts`(76 行) -**导出**: `Stream` class — 手动异步队列,实现 `AsyncIterator` - -| 测试用例 | 验证点 | -|---------|--------| -| enqueue then read | 单条消息正确传递 | -| enqueue multiple then drain | 多条消息顺序消费 | -| done resolves pending readers | `done()` 后迭代结束 | -| done with no pending readers | 无等待时安全关闭 | -| error rejects pending readers | `error(e)` 传播异常 | -| error after done | 后续操作安全处理 | -| single-iteration guard | `return()` 后不可再迭代 | -| empty stream done immediately | 无数据时 done 返回 `{ done: true }` | -| concurrent enqueue | 多次 enqueue 不丢失 | -| backpressure | reader 慢于 writer 时不丢数据 | - ---- - -## 16.2 `src/utils/__tests__/abortController.test.ts`(~12 tests) - -**目标模块**: `src/utils/abortController.ts`(99 行) -**导出**: `createAbortController()`, `createChildAbortController()` - -| 测试用例 | 验证点 | -|---------|--------| -| parent abort propagates to child | `parent.abort()` → child aborted | -| child abort does NOT propagate to parent | `child.abort()` → parent still active | -| already-aborted parent → child immediately aborted | 创建时即继承 abort 状态 | -| child listener cleanup after parent abort | WeakRef 回收后无泄漏 | -| multiple children of same parent | 独立 abort 传播 | -| child abort then parent abort | 顺序无关 | -| signal.maxListeners raised | MaxListenersExceededWarning 不触发 | - ---- - -## 16.3 `src/utils/__tests__/bufferedWriter.test.ts`(~14 tests) - -**目标模块**: `src/utils/bufferedWriter.ts`(100 行) -**导出**: `createBufferedWriter()` - -| 测试用例 | 验证点 | -|---------|--------| -| single write buffered | write → buffer 累积 | -| flush on size threshold | 超过 maxSize 时自动 flush | -| flush on timer | 定时器触发 flush | -| immediate mode | `{ immediate: true }` 跳过缓冲 | -| overflow coalescing | overflow 内容合并到下次 flush | -| empty buffer flush | 无数据时 flush 无副作用 | -| close flushes remaining | close 触发最终 flush | -| multiple writes before flush | 批量写入合并 | -| flush callback receives concatenated data | writeFn 参数正确 | - -**Mock**: 注入 `writeFn` 回调,可选 fake timers - ---- - -## 16.4 `src/utils/__tests__/gitDiff.test.ts`(~20 tests) - -**目标模块**: `src/utils/gitDiff.ts`(532 行) -**可测函数**: `parseGitNumstat()`, `parseGitDiff()`, `parseShortstat()` - -| 测试用例 | 验证点 | -|---------|--------| -| parseGitNumstat — single file | `1\t2\tpath` → { added: 1, deleted: 2, file: "path" } | -| parseGitNumstat — binary file | `-\t-\timage.png` → binary flag | -| parseGitNumstat — rename | `{ old => new }` 格式解析 | -| parseGitNumstat — empty diff | 空字符串 → [] | -| parseGitNumstat — multiple files | 多行正确分割 | -| parseGitDiff — added lines | `+` 开头行计数 | -| parseGitDiff — deleted lines | `-` 开头行计数 | -| parseGitDiff — hunk header | `@@ -a,b +c,d @@` 解析 | -| parseGitDiff — new file mode | `new file mode 100644` 检测 | -| parseGitDiff — deleted file mode | `deleted file mode` 检测 | -| parseGitDiff — binary diff | Binary files differ 处理 | -| parseShortstat — all components | `1 file changed, 5 insertions(+), 3 deletions(-)` | -| parseShortstat — insertions only | 无 deletions | -| parseShortstat — deletions only | 无 insertions | -| parseShortstat — files only | 仅 file changed | -| parseShortstat — empty | 空字符串 → 默认值 | -| parseShortstat — rename | `1 file changed, ...` 重命名 | - -**Mock**: 无需 mock — 全部是纯字符串解析 - ---- - -## 16.5 `src/__tests__/history.test.ts`(~18 tests) - -**目标模块**: `src/history.ts`(464 行) -**可测函数**: `parseReferences()`, `expandPastedTextRefs()`, `formatPastedTextRef()`, `formatImageRef()`, `getPastedTextRefNumLines()` - -| 测试用例 | 验证点 | -|---------|--------| -| parseReferences — text ref | `#1` → [{ type: "text", ref: 1 }] | -| parseReferences — image ref | `@1` → [{ type: "image", ref: 1 }] | -| parseReferences — multiple refs | `#1 #2 @3` → 3 refs | -| parseReferences — no refs | `"hello"` → [] | -| parseReferences — duplicate refs | `#1 #1` → 去重或保留 | -| parseReferences — zero ref | `#0` → 边界 | -| parseReferences — large ref | `#999` → 正常 | -| formatPastedTextRef — basic | 输出格式验证 | -| formatPastedTextRef — multiline | 多行内容格式 | -| getPastedTextRefNumLines — 1 line | 返回 1 | -| getPastedTextRefNumLines — multiple lines | 换行计数 | -| expandPastedTextRefs — single ref | 替换单个引用 | -| expandPastedTextRefs — multiple refs | 替换多个引用 | -| expandPastedTextRefs — no refs | 原样返回 | -| expandPastedTextRefs — mixed content | 文本 + 引用混合 | -| formatImageRef — basic | 输出格式 | - -**Mock**: `mock.module("src/bootstrap/state.ts", ...)` 解锁模块 - ---- - -## 16.6 `src/utils/__tests__/sliceAnsi.test.ts`(~16 tests) - -**目标模块**: `src/utils/sliceAnsi.ts`(91 行) -**导出**: `sliceAnsi()` — ANSI 感知的字符串切片 - -| 测试用例 | 验证点 | -|---------|--------| -| plain text slice | `"hello".slice(1,3)` 等价 | -| preserve ANSI codes | `\x1b[31mhello\x1b[0m` 切片后保留颜色 | -| close opened styles | 切片点在 ANSI 样式中间时正确关闭 | -| hyperlink handling | OSC 8 超链接不被切断 | -| combining marks (diacritics) | `é` = `e\u0301` 不被切开 | -| Devanagari matras | 零宽字符不被切断 | -| full-width characters | CJK 字符宽度 = 2 | -| empty slice | 返回空字符串 | -| full slice | 返回完整字符串 | -| boundary at ANSI code | 边界恰好在 escape 序列上 | -| nested ANSI styles | 多层嵌套时正确处理 | -| slice start > end | 空结果 | - -**Mock**: `mock.module("@alcalzone/ansi-tokenize", ...)`, `mock.module("ink/stringWidth", ...)` - ---- - -## 16.7 `src/utils/__tests__/treeify.test.ts`(~15 tests) - -**目标模块**: `src/utils/treeify.ts`(170 行) -**导出**: `treeify()` — 递归树渲染 - -| 测试用例 | 验证点 | -|---------|--------| -| simple flat tree | `{ a: {}, b: {} }` → 2 行 | -| nested tree | `{ a: { b: { c: {} } } }` → 3 行缩进 | -| array values | `[1, 2, 3]` 渲染为列表 | -| circular reference | 不无限递归 | -| empty object | `{}` 处理 | -| single key | 布局适配 | -| branch vs last-branch character | ├─ vs └─ | -| custom prefix | options 前缀传递 | -| deep nesting | 5+ 层缩进正确 | -| mixed object/array | 混合结构 | - -**Mock**: `mock.module("figures", ...)`, color 模块 mock - ---- - -## 16.8 `src/utils/__tests__/words.test.ts`(~10 tests) - -**目标模块**: `src/utils/words.ts`(800 行,大部分是词表数据) -**导出**: `generateWordSlug()`, `generateShortWordSlug()` - -| 测试用例 | 验证点 | -|---------|--------| -| generateWordSlug format | `adjective-verb-noun` 三段式 | -| generateShortWordSlug format | `adjective-noun` 两段式 | -| all parts non-empty | 无空段 | -| hyphen separator | `-` 分隔 | -| all parts from word lists | 成分来自预定义词表 | -| multiple calls uniqueness | 连续调用不总是相同 | -| no consecutive hyphens | 无 `--` | -| lowercase only | 全小写 | - -**Mock**: `mock.module("crypto", ...)` 控制 `randomBytes` 实现确定性测试 diff --git a/docs/test-plans/phase-17-tool-submodules.md b/docs/test-plans/phase-17-tool-submodules.md deleted file mode 100644 index e01b080f3..000000000 --- a/docs/test-plans/phase-17-tool-submodules.md +++ /dev/null @@ -1,203 +0,0 @@ -# Phase 17 — Tool 子模块纯逻辑测试 - -> 创建日期:2026-04-02 -> 预计:+150 tests / 11 files -> 目标:覆盖 Tool 目录下有丰富纯逻辑但零测试的子模块 - ---- - -## 17.1 `src/tools/PowerShellTool/__tests__/powershellSecurity.test.ts`(~25 tests) - -**目标模块**: `src/tools/PowerShellTool/powershellSecurity.ts`(1091 行) - -**安全关键** — 检测 ~20 种攻击向量。 - -| 测试分组 | 测试数 | 验证点 | -|---------|-------|--------| -| Invoke-Expression 检测 | 3 | `IEX`, `Invoke-Expression`, 变形 | -| Download cradle 检测 | 3 | `Net.WebClient`, `Invoke-WebRequest`, pipe | -| Privilege escalation | 3 | `Start-Process -Verb RunAs`, `runas.exe` | -| COM object | 2 | `New-Object -ComObject`, WScript.Shell | -| Scheduled tasks | 2 | `schtasks`, `Register-ScheduledTask` | -| WMI | 2 | `Invoke-WmiMethod`, `Get-WmiObject` | -| Module loading | 2 | `Import-Module` 从网络路径 | -| 安全命令通过 | 3 | `Get-Process`, `Get-ChildItem`, `Write-Host` | -| 混淆绕过尝试 | 3 | base64, 字符串拼接, 空格变形 | -| 组合命令 | 2 | `;` 分隔的多命令 | - -**Mock**: 构造 `ParsedPowerShellCommand` 对象(不需要真实 AST) - ---- - -## 17.2 `src/tools/PowerShellTool/__tests__/commandSemantics.test.ts`(~10 tests) - -**目标模块**: `src/tools/PowerShellTool/commandSemantics.ts`(143 行) - -| 测试用例 | 验证点 | -|---------|--------| -| grep exit 0/1/2 | 语义映射 | -| robocopy exit codes | Windows 特殊退出码 | -| findstr exit codes | Windows find 工具 | -| unknown command | 默认语义 | -| extractBaseCommand — basic | `grep "pattern" file` → `grep` | -| extractBaseCommand — path | `C:\tools\rg.exe` → `rg` | -| heuristicallyExtractBaseCommand | 模糊匹配 | - ---- - -## 17.3 `src/tools/PowerShellTool/__tests__/destructiveCommandWarning.test.ts`(~15 tests) - -**目标模块**: `src/tools/PowerShellTool/destructiveCommandWarning.ts`(110 行) - -| 测试用例 | 验证点 | -|---------|--------| -| Remove-Item -Recurse -Force | 危险 | -| Format-Volume | 危险 | -| git reset --hard | 危险 | -| DROP TABLE | 危险 | -| Remove-Item (no -Force) | 安全 | -| Get-ChildItem | 安全 | -| 管道组合 | `rm -rf` + pipe | -| 大小写混合 | `ReMoVe-ItEm` | - ---- - -## 17.4 `src/tools/PowerShellTool/__tests__/gitSafety.test.ts`(~12 tests) - -**目标模块**: `src/tools/PowerShellTool/gitSafety.ts`(177 行) - -| 测试用例 | 验证点 | -|---------|--------| -| normalizeGitPathArg — forward slash | 规范化 | -| normalizeGitPathArg — backslash | Windows 路径规范化 | -| normalizeGitPathArg — NTFS short name | `GITFI~1` → `.git` | -| isGitInternalPathPS — .git/config | true | -| isGitInternalPathPS — normal file | false | -| isDotGitPathPS — hidden git dir | true | -| isDotGitPathPS — .gitignore | false | -| bare repo attack | `.git` 路径遍历 | - ---- - -## 17.5 `src/tools/LSPTool/__tests__/formatters.test.ts`(~20 tests) - -**目标模块**: `src/tools/LSPTool/formatters.ts`(593 行) - -| 测试用例 | 验证点 | -|---------|--------| -| formatGoToDefinitionResult — single | 单个定义 | -| formatGoToDefinitionResult — multiple | 多个定义(分组) | -| formatFindReferencesResult | 引用列表 | -| formatHoverResult — markdown | markdown 内容 | -| formatHoverResult — plaintext | 纯文本 | -| formatDocumentSymbolResult — classes | 类符号 | -| formatDocumentSymbolResult — functions | 函数符号 | -| formatDocumentSymbolResult — nested | 嵌套符号 | -| formatWorkspaceSymbolResult | 工作区符号 | -| formatPrepareCallHierarchyResult | 调用层次 | -| formatIncomingCallsResult | 入调用 | -| formatOutgoingCallsResult | 出调用 | -| empty results | 各函数空结果 | -| groupByFile helper | 文件分组逻辑 | - ---- - -## 17.6 `src/tools/GrepTool/__tests__/utils.test.ts`(~10 tests) - -**目标模块**: `src/tools/GrepTool/GrepTool.ts`(577 行) - -| 测试用例 | 验证点 | -|---------|--------| -| applyHeadLimit — within limit | 不截断 | -| applyHeadLimit — exceeds limit | 正确截断 | -| applyHeadLimit — offset + limit | 分页逻辑 | -| applyHeadLimit — zero limit | 边界 | -| formatLimitInfo — basic | 格式化输出 | - -**Mock**: `mock.module("src/utils/log.ts", ...)` 解锁导入 - ---- - -## 17.7 `src/tools/WebFetchTool/__tests__/utils.test.ts`(~15 tests) - -**目标模块**: `src/tools/WebFetchTool/utils.ts`(531 行) - -| 测试用例 | 验证点 | -|---------|--------| -| validateURL — valid http | 通过 | -| validateURL — valid https | 通过 | -| validateURL — ftp | 拒绝 | -| validateURL — no protocol | 拒绝 | -| validateURL — localhost | 处理 | -| isPermittedRedirect — same host | 允许 | -| isPermittedRedirect — different host | 拒绝 | -| isPermittedRedirect — subdomain | 处理 | -| isRedirectInfo — valid object | true | -| isRedirectInfo — invalid | false | - ---- - -## 17.8 `src/tools/WebFetchTool/__tests__/preapproved.test.ts`(~10 tests) - -**目标模块**: `src/tools/WebFetchTool/preapproved.ts`(167 行) - -| 测试用例 | 验证点 | -|---------|--------| -| exact hostname match | 通过 | -| subdomain match | 处理 | -| path prefix match | `/docs/api` 匹配 | -| path non-match | `/internal` 不匹配 | -| unknown hostname | false | -| empty pathname | 边界 | - ---- - -## 17.9 `src/tools/FileReadTool/__tests__/utils.test.ts`(~15 tests) - -**目标模块**: `src/tools/FileReadTool/FileReadTool.ts`(1184 行) - -| 测试用例 | 验证点 | -|---------|--------| -| isBlockedDevicePath — /dev/sda | true | -| isBlockedDevicePath — /dev/null | 处理 | -| isBlockedDevicePath — normal file | false | -| detectSessionFileType — .jsonl | 会话文件类型 | -| detectSessionFileType — unknown | 未知类型 | -| formatFileLines — basic | 行号格式 | -| formatFileLines — empty | 空文件 | - ---- - -## 17.10 `src/tools/AgentTool/__tests__/agentToolUtils.test.ts`(~18 tests) - -**目标模块**: `src/tools/AgentTool/agentToolUtils.ts`(688 行) - -| 测试用例 | 验证点 | -|---------|--------| -| filterToolsForAgent — builtin only | 只返回内置工具 | -| filterToolsForAgent — exclude async | 排除异步工具 | -| filterToolsForAgent — permission mode | 权限过滤 | -| resolveAgentTools — wildcard | 通配符展开 | -| resolveAgentTools — explicit list | 显式列表 | -| countToolUses — multiple | 消息中工具调用计数 | -| countToolUses — zero | 无工具调用 | -| extractPartialResult — text only | 提取文本 | -| extractPartialResult — mixed | 混合内容 | -| getLastToolUseName — basic | 最后工具名 | -| getLastToolUseName — no tool use | 无工具调用 | - -**Mock**: `mock.module("src/bootstrap/state.ts", ...)`, `mock.module("src/utils/log.ts", ...)` - ---- - -## 17.11 `src/tools/LSPTool/__tests__/schemas.test.ts`(~5 tests) - -**目标模块**: `src/tools/LSPTool/schemas.ts`(216 行) - -| 测试用例 | 验证点 | -|---------|--------| -| isValidLSPOperation — goToDefinition | true | -| isValidLSPOperation — findReferences | true | -| isValidLSPOperation — hover | true | -| isValidLSPOperation — invalid | false | -| isValidLSPOperation — empty string | false | diff --git a/docs/test-plans/phase-18-weak-fixes.md b/docs/test-plans/phase-18-weak-fixes.md deleted file mode 100644 index 0e78f9a01..000000000 --- a/docs/test-plans/phase-18-weak-fixes.md +++ /dev/null @@ -1,110 +0,0 @@ -# Phase 18 — WEAK 修复 + ACCEPTABLE 加固 - -> 创建日期:2026-04-02 -> 预计:+30 tests / 4 files (修改现有) -> 目标:修复所有 WEAK 评分测试文件,消除系统性问题 - ---- - -## 18.1 `src/utils/__tests__/format.test.ts` — 断言精确化(+5 tests) - -**问题**: `formatNumber`/`formatTokens`/`formatRelativeTime` 使用 `toContain` -**修复**: 改为 `toBe` 精确匹配 - -```diff -- expect(formatNumber(1500000)).toContain("1.5") -+ expect(formatNumber(1500000)).toBe("1.5m") -``` - -新增测试: - -| 测试用例 | 验证点 | -|---------|--------| -| formatNumber — 0 | `"0"` | -| formatNumber — billions | `"1.5b"` | -| formatTokens — thousands | 精确匹配 | -| formatRelativeTime — hours ago | 精确匹配 | -| formatRelativeTime — days ago | 精确匹配 | - ---- - -## 18.2 `src/utils/__tests__/envValidation.test.ts` — Bug 确认(+3 tests) - -**问题**: `value=1, lowerBound=100` 返回 `status: "valid"` — 函数名暗示有下界检查 -**计划**: 先读取源码确认 `defaultValue` 和 `lowerBound` 的语义关系,然后: -- 如果是源码 bug → 在测试中注释标记,不修改源码 -- 如果是设计意图 → 更新测试描述明确语义 - -新增测试: - -| 测试用例 | 验证点 | -|---------|--------| -| parseFloat truncation | `"50.9"` → 50 | -| whitespace handling | `" 500 "` → 500 | -| very large number | overflow 处理 | - ---- - -## 18.3 `src/utils/permissions/__tests__/PermissionMode.test.ts` — false 路径(+8 tests) - -**问题**: `isExternalPermissionMode` false 路径从未执行 -**修复**: 覆盖所有 5 种 mode 的 true/false 期望 - -| 测试用例 | 验证点 | -|---------|--------| -| isExternalPermissionMode — plan | false | -| isExternalPermissionMode — auto | false | -| isExternalPermissionMode — default | false | -| permissionModeFromString — all modes | 5 种 mode 全覆盖 | -| permissionModeFromString — invalid | 默认值 | -| permissionModeFromString — case insensitive | 大小写 | -| isPermissionMode — valid strings | true | -| isPermissionMode — invalid strings | false | - ---- - -## 18.4 `src/tools/shared/__tests__/gitOperationTracking.test.ts` — mock analytics(+4 tests) - -**问题**: 未 mock analytics 依赖,测试产生副作用 -**修复**: 添加 `mock.module("src/services/analytics/...", ...)` - -新增测试: - -| 测试用例 | 验证点 | -|---------|--------| -| parseGitCommitId — all GH PR actions | 补齐 6 个 action | -| detectGitOperation — no analytics call | mock 验证 | -| detectGitCommitId — various formats | SHA/短 SHA/HEAD | -| git operation tracking — edge cases | 空输入、畸形输入 | - ---- - -## 排除清单 - -以下模块 **不纳入测试**,原因合理: - -| 模块 | 行数 | 排除原因 | -|------|------|---------| -| `query.ts` | 1732 | 核心循环,40+ 依赖,需完整集成环境 | -| `QueryEngine.ts` | 1320 | 编排器,30+ 依赖 | -| `utils/hooks.ts` | 5121 | 51 exports,spawn 子进程 | -| `utils/config.ts` | 1817 | 文件系统 + lockfile + 全局状态 | -| `utils/auth.ts` | 2002 | 多 provider 认证,平台特定 | -| `utils/fileHistory.ts` | 1115 | 重 I/O 文件备份 | -| `utils/sessionRestore.ts` | 551 | 恢复状态涉及多个子系统 | -| `utils/ripgrep.ts` | 679 | spawn 子进程 | -| `utils/yaml.ts` | 15 | 两行 wrapper | -| `utils/lockfile.ts` | 43 | trivial wrapper | -| `screens/` / `components/` | — | Ink 渲染测试环境 | -| `bridge/` / `remote/` / `ssh/` | — | 网络层 | -| `daemon/` / `server/` | — | 进程管理 | - ---- - -## 预期成果 - -| 指标 | Phase 16 后 | Phase 17 后 | Phase 18 后 | -|------|-----------|-----------|-----------| -| 测试数 | ~1417 | ~1567 | ~1597 | -| 文件数 | 76 | 87 | 91 | -| WEAK 文件 | 6 | 4 | **0** | diff --git a/docs/test-plans/phase19-batch1-micro-utils.md b/docs/test-plans/phase19-batch1-micro-utils.md deleted file mode 100644 index 4b8c4c6a6..000000000 --- a/docs/test-plans/phase19-batch1-micro-utils.md +++ /dev/null @@ -1,435 +0,0 @@ -# Phase 19 - Batch 1: 零依赖微型 utils - -> 预计 ~154 tests / 13 文件 | 全部纯函数,无需 mock - ---- - -## 1. `src/utils/__tests__/semanticBoolean.test.ts` (~8 tests) - -**源文件**: `src/utils/semanticBoolean.ts` (30 行) -**依赖**: `zod/v4` - -### 测试用例 - -```typescript -describe("semanticBoolean", () => { - // 基本 Zod 行为 - test("parses boolean true to true") - test("parses boolean false to false") - test("parses string 'true' to true") - test("parses string 'false' to false") - // 边界 - test("rejects string 'TRUE' (case-sensitive)") - test("rejects string 'FALSE' (case-sensitive)") - test("rejects number 1") - test("rejects null") - test("rejects undefined") - // 自定义 inner schema - test("works with custom inner schema (z.boolean().optional())") -}) -``` - -### Mock 需求 -无 - ---- - -## 2. `src/utils/__tests__/semanticNumber.test.ts` (~10 tests) - -**源文件**: `src/utils/semanticNumber.ts` (37 行) -**依赖**: `zod/v4` - -### 测试用例 - -```typescript -describe("semanticNumber", () => { - test("parses number 42") - test("parses number 0") - test("parses negative number -5") - test("parses float 3.14") - test("parses string '42' to 42") - test("parses string '-7.5' to -7.5") - test("rejects string 'abc'") - test("rejects empty string ''") - test("rejects null") - test("rejects boolean true") - test("works with custom inner schema (z.number().int().min(0))") -}) -``` - -### Mock 需求 -无 - ---- - -## 3. `src/utils/__tests__/lazySchema.test.ts` (~6 tests) - -**源文件**: `src/utils/lazySchema.ts` (9 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("lazySchema", () => { - test("returns a function") - test("calls factory on first invocation") - test("returns cached result on subsequent invocations") - test("factory is called only once (call count verification)") - test("works with different return types") - test("each call to lazySchema returns independent cache") -}) -``` - -### Mock 需求 -无 - ---- - -## 4. `src/utils/__tests__/withResolvers.test.ts` (~8 tests) - -**源文件**: `src/utils/withResolvers.ts` (14 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("withResolvers", () => { - test("returns object with promise, resolve, reject") - test("promise resolves when resolve is called") - test("promise rejects when reject is called") - test("resolve passes value through") - test("reject passes error through") - test("promise is instanceof Promise") - test("works with generic type parameter") - test("resolve/reject can be called asynchronously") -}) -``` - -### Mock 需求 -无 - ---- - -## 5. `src/utils/__tests__/userPromptKeywords.test.ts` (~12 tests) - -**源文件**: `src/utils/userPromptKeywords.ts` (28 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("matchesNegativeKeyword", () => { - test("matches 'wtf'") - test("matches 'shit'") - test("matches 'fucking broken'") - test("does not match normal input like 'fix the bug'") - test("is case-insensitive") - test("matches partial word in sentence") -}) - -describe("matchesKeepGoingKeyword", () => { - test("matches exact 'continue'") - test("matches 'keep going'") - test("matches 'go on'") - test("does not match 'cont'") - test("does not match empty string") - test("matches within larger sentence 'please continue'") -}) -``` - -### Mock 需求 -无 - ---- - -## 6. `src/utils/__tests__/xdg.test.ts` (~15 tests) - -**源文件**: `src/utils/xdg.ts` (66 行) -**依赖**: 无(通过 options 参数注入) - -### 测试用例 - -```typescript -describe("getXDGStateHome", () => { - test("returns ~/.local/state by default") - test("respects XDG_STATE_HOME env var") - test("uses custom homedir from options") -}) - -describe("getXDGCacheHome", () => { - test("returns ~/.cache by default") - test("respects XDG_CACHE_HOME env var") -}) - -describe("getXDGDataHome", () => { - test("returns ~/.local/share by default") - test("respects XDG_DATA_HOME env var") -}) - -describe("getUserBinDir", () => { - test("returns ~/.local/bin") - test("uses custom homedir from options") -}) - -describe("resolveOptions", () => { - test("defaults env to process.env") - test("defaults homedir to os.homedir()") - test("merges partial options") -}) - -describe("path construction", () => { - test("all paths end with correct subdirectory") - test("respects HOME env via homedir override") -}) -``` - -### Mock 需求 -无(通过 options.env 和 options.homedir 注入) - ---- - -## 7. `src/utils/__tests__/horizontalScroll.test.ts` (~20 tests) - -**源文件**: `src/utils/horizontalScroll.ts` (138 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("calculateHorizontalScrollWindow", () => { - // 基本场景 - test("all items fit within available width") - test("single item selected within view") - test("selected item at beginning") - test("selected item at end") - test("selected item beyond visible range scrolls right") - test("selected item before visible range scrolls left") - - // 箭头指示器 - test("showLeftArrow when items hidden on left") - test("showRightArrow when items hidden on right") - test("no arrows when all items visible") - test("both arrows when items hidden on both sides") - - // 边界条件 - test("empty itemWidths array") - test("single item") - test("available width is 0") - test("item wider than available width") - test("all items same width") - test("varying item widths") - test("firstItemHasSeparator adds separator width to first item") - test("selectedIdx in middle of overflow") - test("scroll snaps to show selected at left edge") - test("scroll snaps to show selected at right edge") -}) -``` - -### Mock 需求 -无 - ---- - -## 8. `src/utils/__tests__/generators.test.ts` (~18 tests) - -**源文件**: `src/utils/generators.ts` (89 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("lastX", () => { - test("returns last yielded value") - test("returns only value from single-yield generator") - test("throws on empty generator") -}) - -describe("returnValue", () => { - test("returns generator return value") - test("returns undefined for void return") -}) - -describe("toArray", () => { - test("collects all yielded values") - test("returns empty array for empty generator") - test("preserves order") -}) - -describe("fromArray", () => { - test("yields all array elements") - test("yields nothing for empty array") -}) - -describe("all", () => { - test("merges multiple generators preserving yield order") - test("respects concurrency cap") - test("handles empty generator array") - test("handles single generator") - test("handles generators of different lengths") - test("yields all values from all generators") -}) -``` - -### Mock 需求 -无(用 fromArray 构造测试数据) - ---- - -## 9. `src/utils/__tests__/sequential.test.ts` (~12 tests) - -**源文件**: `src/utils/sequential.ts` (57 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("sequential", () => { - test("wraps async function, returns same result") - test("single call resolves normally") - test("concurrent calls execute sequentially (FIFO order)") - test("preserves arguments correctly") - test("error in first call does not block subsequent calls") - test("preserves rejection reason") - test("multiple args passed correctly") - test("returns different wrapper for each call to sequential") - test("handles rapid concurrent calls") - test("execution order matches call order") - test("works with functions returning different types") - test("wrapper has same arity expectations") -}) -``` - -### Mock 需求 -无 - ---- - -## 10. `src/utils/__tests__/fingerprint.test.ts` (~15 tests) - -**源文件**: `src/utils/fingerprint.ts` (77 行) -**依赖**: `crypto` (内置) - -### 测试用例 - -```typescript -describe("FINGERPRINT_SALT", () => { - test("has expected value '59cf53e54c78'") -}) - -describe("extractFirstMessageText", () => { - test("extracts text from first user message") - test("extracts text from single user message with array content") - test("returns empty string when no user messages") - test("skips assistant messages") - test("handles mixed content blocks (text + image)") -}) - -describe("computeFingerprint", () => { - test("returns deterministic 3-char hex string") - test("same input produces same fingerprint") - test("different message text produces different fingerprint") - test("different version produces different fingerprint") - test("handles short strings (length < 21)") - test("handles empty string") - test("fingerprint is valid hex") -}) - -describe("computeFingerprintFromMessages", () => { - test("end-to-end: messages -> fingerprint") -}) -``` - -### Mock 需求 -需要 `mock.module` 处理 `UserMessage`/`AssistantMessage` 类型依赖(查看实际 import 情况) - ---- - -## 11. `src/utils/__tests__/configConstants.test.ts` (~8 tests) - -**源文件**: `src/utils/configConstants.ts` (22 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("NOTIFICATION_CHANNELS", () => { - test("contains expected channels") - test("is readonly array") - test("includes 'auto', 'iterm2', 'terminal_bell'") -}) - -describe("EDITOR_MODES", () => { - test("contains 'normal' and 'vim'") - test("has exactly 2 entries") -}) - -describe("TEAMMATE_MODES", () => { - test("contains 'auto', 'tmux', 'in-process'") - test("has exactly 3 entries") -}) -``` - -### Mock 需求 -无 - ---- - -## 12. `src/utils/__tests__/directMemberMessage.test.ts` (~12 tests) - -**源文件**: `src/utils/directMemberMessage.ts` (70 行) -**依赖**: 仅类型(可 mock) - -### 测试用例 - -```typescript -describe("parseDirectMemberMessage", () => { - test("parses '@agent-name hello world'") - test("parses '@agent-name single-word'") - test("returns null for non-matching input") - test("returns null for empty string") - test("returns null for '@name' without message") - test("handles hyphenated agent names like '@my-agent msg'") - test("handles multiline message content") - test("extracts correct recipientName and message") -}) - -// sendDirectMemberMessage 需要 mock teamContext/writeToMailbox -describe("sendDirectMemberMessage", () => { - test("returns error when no team context") - test("returns error for unknown recipient") - test("calls writeToMailbox with correct args for valid recipient") - test("returns success for valid message") -}) -``` - -### Mock 需求 -`sendDirectMemberMessage` 需要 mock `AppState['teamContext']` 和 `WriteToMailboxFn` - ---- - -## 13. `src/utils/__tests__/collapseHookSummaries.test.ts` (~12 tests) - -**源文件**: `src/utils/collapseHookSummaries.ts` (60 行) -**依赖**: 仅类型 - -### 测试用例 - -```typescript -describe("collapseHookSummaries", () => { - test("returns same messages when no hook summaries") - test("collapses consecutive messages with same hookLabel") - test("does not collapse messages with different hookLabels") - test("aggregates hookCount across collapsed messages") - test("merges hookInfos arrays") - test("merges hookErrors arrays") - test("takes max totalDurationMs") - test("takes any truthy preventContinuation") - test("leaves single hook summary unchanged") - test("handles three consecutive same-label summaries") - test("preserves non-hook messages in between") - test("returns empty array for empty input") -}) -``` - -### Mock 需求 -需要构造 `RenderableMessage` mock 对象 diff --git a/docs/test-plans/phase19-batch2-utils-state-commands.md b/docs/test-plans/phase19-batch2-utils-state-commands.md deleted file mode 100644 index bbf69c39b..000000000 --- a/docs/test-plans/phase19-batch2-utils-state-commands.md +++ /dev/null @@ -1,287 +0,0 @@ -# Phase 19 - Batch 2: 更多 utils + state + commands - -> 预计 ~120 tests / 8 文件 | 部分需轻量 mock - ---- - -## 1. `src/utils/__tests__/collapseTeammateShutdowns.test.ts` (~10 tests) - -**源文件**: `src/utils/collapseTeammateShutdowns.ts` (56 行) -**依赖**: 仅类型 - -### 测试用例 - -```typescript -describe("collapseTeammateShutdowns", () => { - test("returns same messages when no teammate shutdowns") - test("leaves single shutdown message unchanged") - test("collapses consecutive shutdown messages into batch") - test("batch attachment has correct count") - test("does not collapse non-consecutive shutdowns") - test("preserves non-shutdown messages between shutdowns") - test("handles empty array") - test("handles mixed message types") - test("collapses more than 2 consecutive shutdowns") - test("non-teammate task_status messages are not collapsed") -}) -``` - -### Mock 需求 -构造 `RenderableMessage` mock 对象(带 `task_status` attachment,`status=completed`,`taskType=in_process_teammate`) - ---- - -## 2. `src/utils/__tests__/privacyLevel.test.ts` (~12 tests) - -**源文件**: `src/utils/privacyLevel.ts` (56 行) -**依赖**: `process.env` - -### 测试用例 - -```typescript -describe("getPrivacyLevel", () => { - test("returns 'default' when no env vars set") - test("returns 'essential-traffic' when CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC is set") - test("returns 'no-telemetry' when DISABLE_TELEMETRY is set") - test("'essential-traffic' takes priority over 'no-telemetry'") -}) - -describe("isEssentialTrafficOnly", () => { - test("returns true for 'essential-traffic' level") - test("returns false for 'default' level") - test("returns false for 'no-telemetry' level") -}) - -describe("isTelemetryDisabled", () => { - test("returns true for 'no-telemetry' level") - test("returns true for 'essential-traffic' level") - test("returns false for 'default' level") -}) - -describe("getEssentialTrafficOnlyReason", () => { - test("returns env var name when restricted") - test("returns null when unrestricted") -}) -``` - -### Mock 需求 -`process.env` 保存/恢复模式(参考现有 `envUtils.test.ts`) - ---- - -## 3. `src/utils/__tests__/textHighlighting.test.ts` (~18 tests) - -**源文件**: `src/utils/textHighlighting.ts` (167 行) -**依赖**: `@alcalzone/ansi-tokenize` - -### 测试用例 - -```typescript -describe("segmentTextByHighlights", () => { - // 基本 - test("returns single segment with no highlights") - test("returns highlighted segment for single highlight") - test("returns two segments for highlight covering middle portion") - test("returns three segments for highlight in the middle") - - // 多高亮 - test("handles non-overlapping highlights") - test("handles overlapping highlights (priority-based)") - test("handles adjacent highlights") - - // 边界 - test("highlight starting at 0") - test("highlight ending at text length") - test("highlight covering entire text") - test("empty text with highlights") - test("empty highlights array returns single segment") - - // ANSI 处理 - test("correctly segments text with ANSI escape codes") - test("handles text with mixed ANSI and highlights") - - // 属性 - test("preserves highlight color property") - test("preserves highlight priority property") - test("preserves dimColor and inverse flags") - test("highlights with start > end are handled gracefully") -}) -``` - -### Mock 需求 -可能需要 mock `@alcalzone/ansi-tokenize`,或直接使用(如果有安装) - ---- - -## 4. `src/utils/__tests__/detectRepository.test.ts` (~15 tests) - -**源文件**: `src/utils/detectRepository.ts` (179 行) -**依赖**: git 命令(`getRemoteUrl`) - -### 重点测试函数 - -**`parseGitRemote(input: string): ParsedRepository | null`** — 纯正则解析 -**`parseGitHubRepository(input: string): string | null`** — 纯函数 - -### 测试用例 - -```typescript -describe("parseGitRemote", () => { - // HTTPS - test("parses HTTPS URL: https://github.com/owner/repo.git") - test("parses HTTPS URL without .git suffix") - test("parses HTTPS URL with subdirectory path (only takes first 2 segments)") - - // SSH - test("parses SSH URL: git@github.com:owner/repo.git") - test("parses SSH URL without .git suffix") - - // ssh:// - test("parses ssh:// URL: ssh://git@github.com/owner/repo.git") - - // git:// - test("parses git:// URL") - - // 边界 - test("returns null for invalid URL") - test("returns null for empty string") - test("handles GHE hostname") - test("handles port number in URL") -}) - -describe("parseGitHubRepository", () => { - test("extracts 'owner/repo' from valid remote URL") - test("handles plain 'owner/repo' string input") - test("returns null for non-GitHub host (if restricted)") - test("returns null for invalid input") - test("is case-sensitive for owner/repo") -}) -``` - -### Mock 需求 -仅测试 `parseGitRemote` 和 `parseGitHubRepository`(纯函数),不需要 mock git - ---- - -## 5. `src/utils/__tests__/markdown.test.ts` (~20 tests) - -**源文件**: `src/utils/markdown.ts` (382 行) -**依赖**: `marked`, `cli-highlight`, theme types - -### 重点测试函数 - -**`padAligned(content, displayWidth, targetWidth, align)`** — 纯函数 - -### 测试用例 - -```typescript -describe("padAligned", () => { - test("left-aligns: pads with spaces on right") - test("right-aligns: pads with spaces on left") - test("center-aligns: pads with spaces on both sides") - test("no padding when displayWidth equals targetWidth") - test("handles content wider than targetWidth") - test("null/undefined align defaults to left") - test("handles empty string content") - test("handles zero displayWidth") - test("handles zero targetWidth") - test("center alignment with odd padding distribution") -}) -``` - -注意:`numberToLetter`/`numberToRoman`/`getListNumber` 是私有函数,除非从模块导出否则无法直接测试。如果确实私有,则通过 `applyMarkdown` 间接测试列表渲染: - -```typescript -describe("list numbering (via applyMarkdown)", () => { - test("numbered list renders with digits") - test("nested ordered list uses letters (a, b, c)") - test("deep nested list uses roman numerals") - test("unordered list uses bullet markers") -}) -``` - -### Mock 需求 -`padAligned` 无需 mock。`applyMarkdown` 可能需要 mock theme 依赖。 - ---- - -## 6. `src/state/__tests__/store.test.ts` (~15 tests) - -**源文件**: `src/state/store.ts` (35 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("createStore", () => { - test("returns object with getState, setState, subscribe") - test("getState returns initial state") - test("setState updates state via updater function") - test("setState does not notify when state unchanged (Object.is)") - test("setState notifies subscribers on change") - test("subscribe returns unsubscribe function") - test("unsubscribe stops notifications") - test("multiple subscribers all get notified") - test("onChange callback is called on state change") - test("onChange is not called when state unchanged") - test("works with complex state objects") - test("works with primitive state") - test("updater receives previous state") - test("sequential setState calls produce final state") - test("subscriber called after all state changes in synchronous batch") -}) -``` - -### Mock 需求 -无 - ---- - -## 7. `src/commands/plugin/__tests__/parseArgs.test.ts` (~18 tests) - -**源文件**: `src/commands/plugin/parseArgs.ts` (104 行) -**依赖**: 无 - -### 测试用例 - -```typescript -describe("parsePluginArgs", () => { - // 无参数 - test("returns { type: 'menu' } for undefined") - test("returns { type: 'menu' } for empty string") - test("returns { type: 'menu' } for whitespace only") - - // help - test("returns { type: 'help' } for 'help'") - - // install - test("parses 'install my-plugin' -> { type: 'install', name: 'my-plugin' }") - test("parses 'install my-plugin@github' with marketplace") - test("parses 'install https://github.com/...' as URL marketplace") - - // uninstall - test("returns { type: 'uninstall', name: '...' }") - - // enable/disable - test("returns { type: 'enable', name: '...' }") - test("returns { type: 'disable', name: '...' }") - - // validate - test("returns { type: 'validate', name: '...' }") - - // manage - test("returns { type: 'manage' }") - - // marketplace 子命令 - test("parses 'marketplace add ...'") - test("parses 'marketplace remove ...'") - test("parses 'marketplace list'") - - // 边界 - test("handles extra whitespace") - test("handles unknown subcommand gracefully") -}) -``` - -### Mock 需求 -无 diff --git a/docs/test-plans/phase19-batch3-tool-submodules.md b/docs/test-plans/phase19-batch3-tool-submodules.md deleted file mode 100644 index f19e70ec6..000000000 --- a/docs/test-plans/phase19-batch3-tool-submodules.md +++ /dev/null @@ -1,258 +0,0 @@ -# Phase 19 - Batch 3: Tool 子模块纯逻辑 - -> 预计 ~113 tests / 6 文件 | 采用 `mock.module()` + `await import()` 模式 - ---- - -## 1. `src/tools/GrepTool/__tests__/headLimit.test.ts` (~20 tests) - -**源文件**: `src/tools/GrepTool/GrepTool.ts` (578 行) -**目标函数**: `applyHeadLimit`, `formatLimitInfo` (非导出,需确认可测性) - -### 测试策略 -如果函数是文件内导出的,直接 `await import()` 获取。如果私有,则通过 GrepTool 的输出间接测试,或提取到独立文件。 - -### 测试用例 - -```typescript -describe("applyHeadLimit", () => { - test("returns full array when limit is undefined (default 250)") - test("applies limit correctly: limits to N items") - test("limit=0 means no limit (returns all)") - test("applies offset correctly") - test("offset + limit combined") - test("offset beyond array length returns empty") - test("returns appliedLimit when truncation occurred") - test("returns appliedLimit=undefined when no truncation") - test("limit larger than array returns all items with appliedLimit=undefined") - test("empty array returns empty with appliedLimit=undefined") - test("offset=0 is default") - test("negative limit behavior") -}) - -describe("formatLimitInfo", () => { - test("formats 'limit: N, offset: M' when both present") - test("formats 'limit: N' when only limit") - test("formats 'offset: M' when only offset") - test("returns empty string when both undefined") - test("handles limit=0 (no limit, should not appear)") -}) -``` - -### Mock 需求 -需 mock 重依赖链(`log`, `slowOperations` 等),通过 `mock.module()` + `await import()` 只取目标函数 - ---- - -## 2. `src/tools/MCPTool/__tests__/classifyForCollapse.test.ts` (~25 tests) - -**源文件**: `src/tools/MCPTool/classifyForCollapse.ts` (605 行) -**目标函数**: `classifyMcpToolForCollapse`, `normalize` - -### 测试用例 - -```typescript -describe("normalize", () => { - test("leaves snake_case unchanged: 'search_issues'") - test("converts camelCase to snake_case: 'searchIssues' -> 'search_issues'") - test("converts kebab-case to snake_case: 'search-issues' -> 'search_issues'") - test("handles mixed: 'searchIssuesByStatus' -> 'search_issues_by_status'") - test("handles already lowercase single word") - test("handles empty string") - test("handles PascalCase: 'SearchIssues' -> 'search_issues'") -}) - -describe("classifyMcpToolForCollapse", () => { - // 搜索工具 - test("classifies Slack search_messages as search") - test("classifies GitHub search_code as search") - test("classifies Linear search_issues as search") - test("classifies Datadog search_logs as search") - test("classifies Notion search as search") - - // 读取工具 - test("classifies Slack get_message as read") - test("classifies GitHub get_file_contents as read") - test("classifies Linear get_issue as read") - test("classifies Filesystem read_file as read") - - // 双重分类 - test("some tools are both search and read") - test("some tools are neither search nor read") - - // 未知工具 - test("unknown tool returns { isSearch: false, isRead: false }") - test("tool name with camelCase variant still matches") - test("tool name with kebab-case variant still matches") - - // server name 不影响分类 - test("server name parameter is accepted but unused in current logic") - - // 边界 - test("empty tool name returns false/false") - test("case sensitivity check (should match after normalize)") - test("handles tool names with numbers") -}) -``` - -### Mock 需求 -文件自包含(仅内部 Set + normalize 函数),需确认 `normalize` 是否导出 - ---- - -## 3. `src/tools/FileReadTool/__tests__/blockedPaths.test.ts` (~18 tests) - -**源文件**: `src/tools/FileReadTool/FileReadTool.ts` (1184 行) -**目标函数**: `isBlockedDevicePath`, `getAlternateScreenshotPath` - -### 测试用例 - -```typescript -describe("isBlockedDevicePath", () => { - // 阻止的设备 - test("blocks /dev/zero") - test("blocks /dev/random") - test("blocks /dev/urandom") - test("blocks /dev/full") - test("blocks /dev/stdin") - test("blocks /dev/tty") - test("blocks /dev/console") - test("blocks /dev/stdout") - test("blocks /dev/stderr") - test("blocks /dev/fd/0") - test("blocks /dev/fd/1") - test("blocks /dev/fd/2") - - // 阻止 /proc - test("blocks /proc/self/fd/0") - test("blocks /proc/123/fd/2") - - // 允许的路径 - test("allows /dev/null") - test("allows regular file paths") - test("allows /home/user/file.txt") -}) - -describe("getAlternateScreenshotPath", () => { - test("returns undefined for path without AM/PM") - test("returns alternate path for macOS screenshot with regular space before AM") - test("returns alternate path for macOS screenshot with U+202F before PM") - test("handles path without time component") - test("handles multiple AM/PM occurrences") - test("returns undefined when no space variant difference") -}) -``` - -### Mock 需求 -需 mock 重依赖链,通过 `await import()` 获取函数 - ---- - -## 4. `src/tools/AgentTool/__tests__/agentDisplay.test.ts` (~15 tests) - -**源文件**: `src/tools/AgentTool/agentDisplay.ts` (105 行) -**目标函数**: `resolveAgentOverrides`, `compareAgentsByName` - -### 测试用例 - -```typescript -describe("resolveAgentOverrides", () => { - test("marks no overrides when all agents active") - test("marks inactive agent as overridden") - test("overriddenBy shows the overriding agent source") - test("deduplicates agents by (agentType, source)") - test("preserves agent definition properties") - test("handles empty arrays") - test("handles agent from git worktree (duplicate detection)") -}) - -describe("compareAgentsByName", () => { - test("sorts alphabetically ascending") - test("returns negative when a.name < b.name") - test("returns positive when a.name > b.name") - test("returns 0 for same name") - test("is case-sensitive") -}) - -describe("AGENT_SOURCE_GROUPS", () => { - test("contains expected source groups in order") - test("has unique labels") -}) -``` - -### Mock 需求 -需 mock `AgentDefinition`, `AgentSource` 类型依赖 - ---- - -## 5. `src/tools/AgentTool/__tests__/agentToolUtils.test.ts` (~20 tests) - -**源文件**: `src/tools/AgentTool/agentToolUtils.ts` (688 行) -**目标函数**: `countToolUses`, `getLastToolUseName`, `extractPartialResult` - -### 测试用例 - -```typescript -describe("countToolUses", () => { - test("counts tool_use blocks in messages") - test("returns 0 for messages without tool_use") - test("returns 0 for empty array") - test("counts multiple tool_use blocks across messages") - test("counts tool_use in single message with multiple blocks") -}) - -describe("getLastToolUseName", () => { - test("returns last tool name from assistant message") - test("returns undefined for message without tool_use") - test("returns the last tool when multiple tool_uses present") - test("handles message with non-array content") -}) - -describe("extractPartialResult", () => { - test("extracts text from last assistant message") - test("returns undefined for messages without assistant content") - test("handles interrupted agent with partial text") - test("returns undefined for empty messages") - test("concatenates multiple text blocks") - test("skips non-text content blocks") -}) -``` - -### Mock 需求 -需 mock 消息类型依赖 - ---- - -## 6. `src/tools/SkillTool/__tests__/skillSafety.test.ts` (~15 tests) - -**源文件**: `src/tools/SkillTool/SkillTool.ts` (1110 行) -**目标函数**: `skillHasOnlySafeProperties`, `extractUrlScheme` - -### 测试用例 - -```typescript -describe("skillHasOnlySafeProperties", () => { - test("returns true for command with only safe properties") - test("returns true for command with undefined extra properties") - test("returns false for command with unsafe meaningful property") - test("returns true for command with null extra properties") - test("returns true for command with empty array extra property") - test("returns true for command with empty object extra property") - test("returns false for command with non-empty unsafe array") - test("returns false for command with non-empty unsafe object") - test("returns true for empty command object") -}) - -describe("extractUrlScheme", () => { - test("extracts 'gs' from 'gs://bucket/path'") - test("extracts 'https' from 'https://example.com'") - test("extracts 'http' from 'http://example.com'") - test("extracts 's3' from 's3://bucket/path'") - test("defaults to 'gs' for unknown scheme") - test("defaults to 'gs' for path without scheme") - test("defaults to 'gs' for empty string") -}) -``` - -### Mock 需求 -需 mock 重依赖链,`await import()` 获取函数 diff --git a/docs/test-plans/phase19-batch4-services.md b/docs/test-plans/phase19-batch4-services.md deleted file mode 100644 index 132e22093..000000000 --- a/docs/test-plans/phase19-batch4-services.md +++ /dev/null @@ -1,215 +0,0 @@ -# Phase 19 - Batch 4: Services 纯逻辑 - -> 预计 ~84 tests / 5 文件 | 部分需轻量 mock - ---- - -## 1. `src/services/compact/__tests__/grouping.test.ts` (~15 tests) - -**源文件**: `src/services/compact/grouping.ts` (64 行) -**目标函数**: `groupMessagesByApiRound` - -### 测试用例 - -```typescript -describe("groupMessagesByApiRound", () => { - test("returns single group for single API round") - test("splits at new assistant message ID") - test("keeps tool_result messages with their parent assistant message") - test("handles streaming chunks (same assistant ID stays grouped)") - test("returns empty array for empty input") - test("handles all user messages (no assistant)") - test("handles alternating assistant IDs") - test("three API rounds produce three groups") - test("user messages before first assistant go in first group") - test("consecutive user messages stay in same group") - test("does not produce empty groups") - test("handles single message") - test("preserves message order within groups") - test("handles system messages") - test("tool_result after assistant stays in same round") -}) -``` - -### Mock 需求 -需构造 `Message` mock 对象(type: 'user'/'assistant', message: { id, content }) - ---- - -## 2. `src/services/compact/__tests__/stripMessages.test.ts` (~20 tests) - -**源文件**: `src/services/compact/compact.ts` (1709 行) -**目标函数**: `stripImagesFromMessages`, `collectReadToolFilePaths` (私有) - -### 测试用例 - -```typescript -describe("stripImagesFromMessages", () => { - // user 消息处理 - test("replaces image block with [image] text") - test("replaces document block with [document] text") - test("preserves text blocks unchanged") - test("handles multiple image/document blocks in single message") - test("returns original message when no media blocks") - - // tool_result 内嵌套 - test("replaces image inside tool_result content") - test("replaces document inside tool_result content") - test("preserves non-media tool_result content") - - // 非用户消息 - test("passes through assistant messages unchanged") - test("passes through system messages unchanged") - - // 边界 - test("handles empty message array") - test("handles string content (non-array) in user message") - test("does not mutate original messages") -}) - -describe("collectReadToolFilePaths", () => { - // 注意:这是私有函数,可能需要通过 stripImagesFromMessages 或其他导出间接测试 - // 如果不可直接测试,则跳过或通过集成测试覆盖 - test("collects file_path from Read tool_use blocks") - test("skips tool_use with FILE_UNCHANGED_STUB result") - test("returns empty set for messages without Read tool_use") - test("handles multiple Read calls across messages") - test("normalizes paths via expandPath") -}) -``` - -### Mock 需求 -需 mock `expandPath`(如果 collectReadToolFilePaths 要测) -需 mock `log`, `slowOperations` 等重依赖 -构造 `Message` mock 对象 - ---- - -## 3. `src/services/compact/__tests__/prompt.test.ts` (~12 tests) - -**源文件**: `src/services/compact/prompt.ts` (375 行) -**目标函数**: `formatCompactSummary` - -### 测试用例 - -```typescript -describe("formatCompactSummary", () => { - test("strips ... block") - test("replaces ... with 'Summary:\\n' prefix") - test("handles analysis + summary together") - test("handles summary without analysis") - test("handles analysis without summary") - test("collapses multiple newlines to double") - test("trims leading/trailing whitespace") - test("handles empty string") - test("handles plain text without tags") - test("handles multiline analysis content") - test("preserves content between analysis and summary") - test("handles nested-like tags gracefully") -}) -``` - -### Mock 需求 -需 mock 重依赖链(`log`, feature flags 等) -`formatCompactSummary` 是纯字符串处理,如果 import 链不太重则无需复杂 mock - ---- - -## 4. `src/services/mcp/__tests__/channelPermissions.test.ts` (~25 tests) - -**源文件**: `src/services/mcp/channelPermissions.ts` (241 行) -**目标函数**: `hashToId`, `shortRequestId`, `truncateForPreview`, `filterPermissionRelayClients` - -### 测试用例 - -```typescript -describe("hashToId", () => { - test("returns 5-char string") - test("uses only letters a-z excluding 'l'") - test("is deterministic (same input = same output)") - test("different inputs produce different outputs (with high probability)") - test("handles empty string") -}) - -describe("shortRequestId", () => { - test("returns 5-char string from tool use ID") - test("is deterministic") - test("avoids profanity substrings (retries with salt)") - test("returns a valid ID even if all retries hit bad words (unlikely)") -}) - -describe("truncateForPreview", () => { - test("returns JSON string for object input") - test("truncates to <=200 chars when input is long") - test("adds ellipsis or truncation indicator") - test("returns short input unchanged") - test("handles string input") - test("handles null/undefined input") -}) - -describe("filterPermissionRelayClients", () => { - test("keeps connected clients in allowlist with correct capabilities") - test("filters out disconnected clients") - test("filters out clients not in allowlist") - test("filters out clients missing required capabilities") - test("returns empty array for empty input") - test("type predicate narrows correctly") -}) - -describe("PERMISSION_REPLY_RE", () => { - test("matches 'y abcde'") - test("matches 'yes abcde'") - test("matches 'n abcde'") - test("matches 'no abcde'") - test("is case-insensitive") - test("does not match without ID") -}) -``` - -### Mock 需求 -`hashToId` 可能需要确认导出状态 -`filterPermissionRelayClients` 需要 mock 客户端类型 -`truncateForPreview` 可能依赖 `jsonStringify`(需 mock `slowOperations`) - ---- - -## 5. `src/services/mcp/__tests__/officialRegistry.test.ts` (~12 tests) - -**源文件**: `src/services/mcp/officialRegistry.ts` (73 行) -**目标函数**: `normalizeUrl` (私有), `isOfficialMcpUrl`, `resetOfficialMcpUrlsForTesting` - -### 测试用例 - -```typescript -describe("normalizeUrl", () => { - // 注意:如果是私有的,通过 isOfficialMcpUrl 间接测试 - test("removes trailing slash") - test("removes query parameters") - test("preserves path") - test("handles URL with port") - test("handles URL with hash fragment") -}) - -describe("isOfficialMcpUrl", () => { - test("returns false when registry not loaded (initial state)") - test("returns true for URL added to registry") - test("returns false for non-registered URL") - test("uses normalized URL for comparison") -}) - -describe("resetOfficialMcpUrlsForTesting", () => { - test("clears the cached URLs") - test("allows fresh start after reset") -}) - -describe("URL normalization + lookup integration", () => { - test("URL with trailing slash matches normalized version") - test("URL with query params matches normalized version") - test("different URLs do not match") - test("case sensitivity check") -}) -``` - -### Mock 需求 -需 mock `axios`(避免网络请求) -使用 `resetOfficialMcpUrlsForTesting` 做测试隔离 diff --git a/docs/test-plans/phase19-batch5-mcp-config.md b/docs/test-plans/phase19-batch5-mcp-config.md deleted file mode 100644 index 1763cb470..000000000 --- a/docs/test-plans/phase19-batch5-mcp-config.md +++ /dev/null @@ -1,200 +0,0 @@ -# Phase 19 - Batch 5: MCP 配置 + modelCost - -> 预计 ~80 tests / 4 文件 | 需中等 mock - ---- - -## 1. `src/services/mcp/__tests__/configUtils.test.ts` (~30 tests) - -**源文件**: `src/services/mcp/config.ts` (1580 行) -**目标函数**: `unwrapCcrProxyUrl`, `urlPatternToRegex` (私有), `commandArraysMatch` (私有), `toggleMembership` (私有), `addScopeToServers` (私有), `dedupPluginMcpServers`, `getMcpServerSignature` (如导出) - -### 测试策略 -私有函数如不可直接测试,通过公开的 `dedupPluginMcpServers` 间接覆盖。导出函数直接测。 - -### 测试用例 - -```typescript -describe("unwrapCcrProxyUrl", () => { - test("returns original URL when no CCR proxy markers") - test("extracts mcp_url from CCR proxy URL with /v2/session_ingress/shttp/mcp/") - test("extracts mcp_url from CCR proxy URL with /v2/ccr-sessions/") - test("returns original URL when mcp_url param is missing") - test("handles malformed URL gracefully") - test("handles URL with both proxy marker and mcp_url") - test("preserves non-CCR URLs unchanged") -}) - -describe("dedupPluginMcpServers", () => { - test("keeps unique plugin servers") - test("suppresses plugin server duplicated by manual config") - test("suppresses plugin server duplicated by earlier plugin") - test("keeps servers with null signature") - test("returns empty for empty inputs") - test("reports suppressed with correct duplicateOf name") - test("handles multiple plugins with same config") -}) - -describe("toggleMembership (via integration)", () => { - test("adds item when shouldContain=true and not present") - test("removes item when shouldContain=false and present") - test("returns same array when already in desired state") -}) - -describe("addScopeToServers (via integration)", () => { - test("adds scope to each server config") - test("returns empty object for undefined input") - test("returns empty object for empty input") - test("preserves all original config properties") -}) - -describe("urlPatternToRegex (via integration)", () => { - test("matches exact URL") - test("matches wildcard pattern *.example.com") - test("matches multiple wildcards") - test("does not match non-matching URL") - test("escapes regex special characters in pattern") -}) - -describe("commandArraysMatch (via integration)", () => { - test("returns true for identical arrays") - test("returns false for different lengths") - test("returns false for same length different elements") - test("returns true for empty arrays") -}) -``` - -### Mock 需求 -需 mock `feature()` (bun:bundle), `jsonStringify`, `safeParseJSON`, `log` 等 -通过 `mock.module()` + `await import()` 解锁 - ---- - -## 2. `src/services/mcp/__tests__/filterUtils.test.ts` (~20 tests) - -**源文件**: `src/services/mcp/utils.ts` (576 行) -**目标函数**: `filterToolsByServer`, `hashMcpConfig`, `isToolFromMcpServer`, `isMcpTool`, `parseHeaders` - -### 测试用例 - -```typescript -describe("filterToolsByServer", () => { - test("filters tools matching server name prefix") - test("returns empty for no matching tools") - test("handles empty tools array") - test("normalizes server name for matching") -}) - -describe("hashMcpConfig", () => { - test("returns 16-char hex string") - test("is deterministic") - test("excludes scope from hash") - test("different configs produce different hashes") - test("key order does not affect hash (sorted)") -}) - -describe("isToolFromMcpServer", () => { - test("returns true when tool belongs to specified server") - test("returns false for different server") - test("returns false for non-MCP tool name") - test("handles empty tool name") -}) - -describe("isMcpTool", () => { - test("returns true for tool name starting with 'mcp__'") - test("returns true when tool.isMcp is true") - test("returns false for regular tool") - test("returns false when neither condition met") -}) - -describe("parseHeaders", () => { - test("parses 'Key: Value' format") - test("parses multiple headers") - test("trims whitespace around key and value") - test("throws on missing colon") - test("throws on empty key") - test("handles value with colons (like URLs)") - test("returns empty object for empty array") - test("handles duplicate keys (last wins)") -}) -``` - -### Mock 需求 -需 mock `normalizeNameForMCP`, `mcpInfoFromString`, `jsonStringify`, `createHash` 等 -`parseHeaders` 是最独立的,可能不需要太多 mock - ---- - -## 3. `src/services/mcp/__tests__/channelNotification.test.ts` (~15 tests) - -**源文件**: `src/services/mcp/channelNotification.ts` (317 行) -**目标函数**: `wrapChannelMessage`, `findChannelEntry` - -### 测试用例 - -```typescript -describe("wrapChannelMessage", () => { - test("wraps content in tag with source attribute") - test("escapes server name in attribute") - test("includes meta attributes when provided") - test("escapes meta values via escapeXmlAttr") - test("filters out meta keys not matching SAFE_META_KEY pattern") - test("handles empty meta") - test("handles content with special characters") - test("formats with newlines between tags and content") -}) - -describe("findChannelEntry", () => { - test("finds server entry by exact name match") - test("finds plugin entry by matching second segment") - test("returns undefined for no match") - test("handles empty channels array") - test("handles server name without colon") - test("handles 'plugin:name' format correctly") - test("prefers exact match over partial match") -}) -``` - -### Mock 需求 -需 mock `escapeXmlAttr`(来自 xml.ts,已有测试)或直接使用 -`CHANNEL_TAG` 常量需确认导出 - ---- - -## 4. `src/utils/__tests__/modelCost.test.ts` (~15 tests) - -**源文件**: `src/utils/modelCost.ts` (232 行) -**目标函数**: `formatModelPricing`, `COST_TIER_*` 常量 - -### 测试用例 - -```typescript -describe("COST_TIER constants", () => { - test("COST_TIER_3_15 has inputTokens=3, outputTokens=15") - test("COST_TIER_15_75 has inputTokens=15, outputTokens=75") - test("COST_TIER_5_25 has inputTokens=5, outputTokens=25") - test("COST_TIER_30_150 has inputTokens=30, outputTokens=150") - test("COST_HAIKU_35 has inputTokens=0.8, outputTokens=4") - test("COST_HAIKU_45 has inputTokens=1, outputTokens=5") -}) - -describe("formatModelPricing", () => { - test("formats integer prices without decimals: '$3/$15 per Mtok'") - test("formats float prices with 2 decimals: '$0.80/$4.00 per Mtok'") - test("formats mixed: '$5/$25 per Mtok'") - test("formats large prices: '$30/$150 per Mtok'") - test("formats $1/$5 correctly (integer but small)") - test("handles zero prices: '$0/$0 per Mtok'") -}) - -describe("MODEL_COSTS", () => { - test("maps known model names to cost tiers") - test("contains entries for claude-sonnet-4-6") - test("contains entries for claude-opus-4-6") - test("contains entries for claude-haiku-4-5") -}) -``` - -### Mock 需求 -需 mock `log`, `slowOperations` 等重依赖(modelCost.ts 通常 import 链较重) -`formatModelPricing` 和 `COST_TIER_*` 是纯数据/纯函数,mock 成功后直接测 diff --git a/docs/testing-spec.md b/docs/testing-spec.md deleted file mode 100644 index c2bc2fd8b..000000000 --- a/docs/testing-spec.md +++ /dev/null @@ -1,296 +0,0 @@ -# Testing Specification - -本文档定义 claude-code 项目的测试规范、当前覆盖状态和改进计划。 - -## 1. 技术栈 - -| 项 | 选型 | -|----|------| -| 测试框架 | `bun:test` | -| 断言/Mock | `bun:test` 内置 | -| 覆盖率 | `bun test --coverage` | -| CI | GitHub Actions,push/PR 到 main 自动运行 | - -## 2. 测试层次 - -本项目采用 **单元测试 + 集成测试** 两层结构,不做 E2E 或快照测试。 - -- **单元测试** — 纯函数、工具类、解析器。文件就近放置于 `src/**/__tests__/`。 -- **集成测试** — 多模块协作流程。集中于 `tests/integration/`。 - -## 3. 文件结构与命名 - -``` -src/ -├── utils/__tests__/ # 纯函数单元测试 -├── tools//__tests__/ # Tool 单元测试 -├── services/mcp/__tests__/ # MCP 单元测试 -├── utils/permissions/__tests__/ -├── utils/model/__tests__/ -├── utils/settings/__tests__/ -├── utils/shell/__tests__/ -├── utils/git/__tests__/ -└── __tests__/ # 顶层模块测试 (Tool.ts, tools.ts) -tests/ -├── integration/ # 集成测试(尚未创建) -├── mocks/ # 共享 mock/fixture(尚未创建) -└── helpers/ # 测试辅助函数 -``` - -- 测试文件:`.test.ts` -- 命名风格:`describe("functionName")` + `test("行为描述")`,英文 -- 编写原则:Arrange-Act-Assert、单一职责、独立性、边界覆盖 - -## 4. 当前覆盖状态 - -> 更新日期:2026-04-02 | **1623 tests, 84 files, 0 fail, 851ms** - -### 4.1 可靠度评分 - -每个测试文件按断言深度、边界覆盖、mock 质量、测试独立性综合评定: - -| 等级 | 含义 | -|------|------| -| **GOOD** | 断言精确(exact match),边界充分,结构清晰 | -| **ACCEPTABLE** | 正常路径覆盖完整,部分边界或断言可加强 | -| **WEAK** | 存在明显缺陷:断言过弱、重要边界缺失、或有脆弱性风险 | - -### 4.2 按模块分布 - -#### P0 — 核心模块 - -| 文件 | Tests | 评分 | 覆盖范围 | 主要不足 | -|------|-------|------|----------|----------| -| `src/__tests__/Tool.test.ts` | 20 | GOOD | buildTool, toolMatchesName, findToolByName, filterToolProgressMessages | — | -| `src/__tests__/tools.test.ts` | 9 | ACCEPTABLE | parseToolPreset, filterToolsByDenyRules | 预设覆盖仅测 "default";有冗余用例 | -| `src/tools/FileEditTool/__tests__/utils.test.ts` | 22 | ACCEPTABLE | normalizeQuotes, applyEditToFile, preserveQuoteStyle | `findActualString` 断言过弱(`not.toBeNull`);`preserveQuoteStyle` 仅 2 用例 | -| `src/tools/shared/__tests__/gitOperationTracking.test.ts` | 20 | ACCEPTABLE | parseGitCommitId, detectGitOperation | 6 个 GH PR action 全覆盖;缺 `trackGitOperations` 测试(需 mock analytics) | -| `src/tools/BashTool/__tests__/destructiveCommandWarning.test.ts` | 21 | ACCEPTABLE | git/rm/SQL/k8s/terraform 危险模式 | safe commands 4 断言合一;缺少 `rm -rf /`、`DROP DATABASE`、管道命令 | -| `src/tools/BashTool/__tests__/commandSemantics.test.ts` | 10 | ACCEPTABLE | grep/diff/test/rg/find 退出码语义 | mock `splitCommand_DEPRECATED` 与实现可能分歧;覆盖可更全面 | - -**Utils 纯函数(19 文件):** - -| 文件 | Tests | 评分 | 覆盖范围 | 主要不足 | -|------|-------|------|----------|----------| -| `utils/__tests__/array.test.ts` | 12 | GOOD | intersperse, count, uniq | — | -| `utils/__tests__/set.test.ts` | 11 | GOOD | difference, intersects, every, union | — | -| `utils/__tests__/xml.test.ts` | 9 | GOOD | escapeXml, escapeXmlAttr | 缺 null/undefined 输入测试 | -| `utils/__tests__/hash.test.ts` | 12 | ACCEPTABLE | djb2Hash, hashContent, hashPair | `hashContent`/`hashPair` 无已知答案断言(仅测确定性) | -| `utils/__tests__/stringUtils.test.ts` | 30 | GOOD | 10 个函数全覆盖,含 Unicode 边界 | — | -| `utils/__tests__/semver.test.ts` | 16 | ACCEPTABLE | gt/gte/lt/lte/satisfies/order | 缺 pre-release、tilde range、畸形版本串 | -| `utils/__tests__/uuid.test.ts` | 6 | ACCEPTABLE | validateUuid | 大写测试仅 `not.toBeNull`,未验证标准化输出 | -| `utils/__tests__/format.test.ts` | 27 | GOOD | formatFileSize, formatDuration, formatNumber, formatTokens, formatRelativeTime | 全部 `toBe` 精确匹配,含 billions/weeks/days 边界 | -| `utils/__tests__/frontmatterParser.test.ts` | 22 | GOOD | parseFrontmatter, splitPathInFrontmatter, parsePositiveIntFromFrontmatter | — | -| `utils/__tests__/file.test.ts` | 13 | ACCEPTABLE | convertLeadingTabsToSpaces, addLineNumbers, stripLineNumberPrefix | `addLineNumbers` 仅 `toContain`;缺 Windows 路径分隔符测试 | -| `utils/__tests__/glob.test.ts` | 6 | ACCEPTABLE | extractGlobBaseDirectory | 缺绝对路径、根 `/`、Windows 路径 | -| `utils/__tests__/diff.test.ts` | 8 | ACCEPTABLE | adjustHunkLineNumbers, getPatchFromContents | `getPatchFromContents` 仅检查结构,未验证 diff 内容正确性 | -| `utils/__tests__/json.test.ts` | 15 | GOOD | safeParseJSON, parseJSONL, addItemToJSONCArray | — | -| `utils/__tests__/truncate.test.ts` | 18 | ACCEPTABLE | truncateToWidth, wrapText, truncatePathMiddle | **缺 CJK/emoji/wide-char 测试**(这是宽度感知实现的核心场景) | -| `utils/__tests__/path.test.ts` | 15 | ACCEPTABLE | containsPathTraversal, normalizePathForConfigKey | 仅覆盖 2/5+ 导出函数 | -| `utils/__tests__/tokens.test.ts` | 18 | GOOD | getTokenCountFromUsage, doesMostRecentAssistantMessageExceed200k 等 | — | -| `utils/__tests__/stream.test.ts` | 15 | GOOD | Stream\ enqueue/read/drain/next/done/error/for-await | — | -| `utils/__tests__/abortController.test.ts` | 13 | GOOD | createAbortController/createChildAbortController 父子传播 | — | -| `utils/__tests__/bufferedWriter.test.ts` | 10 | GOOD | createBufferedWriter 立即/缓冲/flush/overflow | — | -| `utils/__tests__/gitDiff.test.ts` | 25 | GOOD | parseGitNumstat/parseGitDiff/parseShortstat 纯解析 | — | -| `utils/__tests__/sliceAnsi.test.ts` | 13 | GOOD | sliceAnsi ANSI 感知切片 + undoAnsiCodes | — | -| `utils/__tests__/treeify.test.ts` | 13 | ACCEPTABLE | treeify 扁平/嵌套/循环引用 | 缺深度嵌套性能测试 | -| `utils/__tests__/words.test.ts` | 11 | GOOD | slug 格式 (adjective-verb-noun)、唯一性 | — | - -**Context 构建(3 文件):** - -| 文件 | Tests | 评分 | 覆盖范围 | 主要不足 | -|------|-------|------|----------|----------| -| `utils/__tests__/claudemd.test.ts` | 14 | ACCEPTABLE | stripHtmlComments, isMemoryFilePath, getLargeMemoryFiles | **仅测 3 个辅助函数**,核心发现/加载/`@include` 指令/memoization 未覆盖 | -| `utils/__tests__/systemPrompt.test.ts` | 8 | GOOD | buildEffectiveSystemPrompt | — | -| `__tests__/history.test.ts` | 26 | GOOD | parseReferences/expandPastedTextRefs/formatPastedTextRef 等 5 个函数 | — | - -#### P1 — 重要模块 - -| 文件 | Tests | 评分 | 覆盖范围 | 主要不足 | -|------|-------|------|----------|----------| -| `permissions/__tests__/permissionRuleParser.test.ts` | 16 | GOOD | escape/unescape 规则,roundtrip 完整性 | — | -| `permissions/__tests__/permissions.test.ts` | 12 | ACCEPTABLE | getDenyRuleForTool, getAskRuleForTool, filterDeniedAgents | `as any` cast;缺 MCP tool deny 测试 | -| `permissions/__tests__/shellRuleMatching.test.ts` | 19 | GOOD | 通配符、转义、正则特殊字符 | — | -| `permissions/__tests__/PermissionMode.test.ts` | 22 | ACCEPTABLE | permissionModeFromString, isExternalPermissionMode 等 | isExternalPermissionMode ant false 路径已覆盖;缺 `bubble` 模式独立测试 | -| `permissions/__tests__/dangerousPatterns.test.ts` | 7 | WEAK | CROSS_PLATFORM_CODE_EXEC, DANGEROUS_BASH_PATTERNS | 纯数据 smoke test,无行为测试;不验证数组无重复 | -| `model/__tests__/aliases.test.ts` | 15 | ACCEPTABLE | isModelAlias, isModelFamilyAlias | 缺 null/undefined/空串输入 | -| `model/__tests__/model.test.ts` | 13 | ACCEPTABLE | firstPartyNameToCanonical | 缺空串、非标准日期后缀 | -| `model/__tests__/providers.test.ts` | 9 | ACCEPTABLE | getAPIProvider, isFirstPartyAnthropicBaseUrl | `originalEnv` 声明未使用;env 恢复不完整 | -| `utils/__tests__/messages.test.ts` | 36 | GOOD | createAssistantMessage, createUserMessage, extractTag 等 16 个 describe | `normalizeMessages` 仅检查长度未验证内容 | - -**Tool 子模块(8 文件):** - -| 文件 | Tests | 评分 | 覆盖范围 | 主要不足 | -|------|-------|------|----------|----------| -| `tools/PowerShellTool/__tests__/powershellSecurity.test.ts` | 24 | GOOD | AST 安全检测:Invoke-Expression/iex/encoded/dynamic/download/COM | — | -| `tools/PowerShellTool/__tests__/commandSemantics.test.ts` | 21 | GOOD | grep/rg/findstr/robocopy 退出码、pipeline last-segment | — | -| `tools/PowerShellTool/__tests__/destructiveCommandWarning.test.ts` | 38 | GOOD | Remove-Item/Format-Volume/Clear-Disk/git/SQL/COMPUTER/alias 全覆盖 | — | -| `tools/PowerShellTool/__tests__/gitSafety.test.ts` | 29 | GOOD | .git 路径检测/NTFS 短名/反斜杠/引号/反引号转义 | — | -| `tools/LSPTool/__tests__/formatters.test.ts` | 18 | GOOD | 全部 8 个 format 函数 null/empty/valid 输入 | — | -| `tools/LSPTool/__tests__/schemas.test.ts` | 13 | GOOD | isValidLSPOperation 类型守卫 9 种操作 + 无效/空/大小写 | — | -| `tools/WebFetchTool/__tests__/preapproved.test.ts` | 18 | GOOD | isPreapprovedHost 精确/路径作用域/子路径/大小写/子域名 | — | -| `tools/WebFetchTool/__tests__/urlValidation.test.ts` | 18 | GOOD | validateURL/isPermittedRedirect 本地重实现(避免重依赖链) | — | - -#### P2 — 补充模块 - -| 文件 | Tests | 评分 | 覆盖范围 | 主要不足 | -|------|-------|------|----------|----------| -| `utils/__tests__/cron.test.ts` | 31 | GOOD | parseCronExpression, computeNextCronRun, cronToHuman | 缺月边界、闰年 | -| `utils/__tests__/git.test.ts` | 15 | ACCEPTABLE | normalizeGitRemoteUrl (SSH/HTTPS/ssh://) | 缺 git://、file://、端口号 | -| `settings/__tests__/config.test.ts` | 38 | GOOD | SettingsSchema, type guards, validateSettingsFileContent, formatZodError | 缺 DeniedMcpServerEntrySchema | - -#### P3-P6 — 扩展覆盖(27 文件) - -| 文件 | Tests | 评分 | 备注 | -|------|-------|------|------| -| `utils/__tests__/errors.test.ts` | 33 | GOOD | — | -| `utils/__tests__/envUtils.test.ts` | 33 | GOOD | env 保存/恢复规范 | -| `utils/__tests__/effort.test.ts` | 30 | GOOD | 5 个 mock 模块,边界完整 | -| `utils/__tests__/argumentSubstitution.test.ts` | 22 | ACCEPTABLE | 缺转义引号、越界索引 | -| `utils/__tests__/sanitization.test.ts` | 14 | ACCEPTABLE | — | -| `utils/__tests__/sleep.test.ts` | 14 | GOOD | 时间相关测试,margin 充足 | -| `utils/__tests__/CircularBuffer.test.ts` | 11 | ACCEPTABLE | 缺 capacity=1、空 buffer getRecent | -| `utils/__tests__/memoize.test.ts` | 18 | GOOD | 缓存 hit/stale/LRU 全覆盖 | -| `utils/__tests__/tokenBudget.test.ts` | 21 | GOOD | — | -| `utils/__tests__/displayTags.test.ts` | 17 | GOOD | — | -| `utils/__tests__/taggedId.test.ts` | 10 | GOOD | — | -| `utils/__tests__/controlMessageCompat.test.ts` | 15 | GOOD | — | -| `utils/__tests__/gitConfigParser.test.ts` | 21 | GOOD | — | -| `utils/__tests__/windowsPaths.test.ts` | 19 | GOOD | 双向 round-trip 测试 | -| `utils/__tests__/envExpansion.test.ts` | 15 | GOOD | — | -| `utils/__tests__/formatBriefTimestamp.test.ts` | 10 | GOOD | 固定 now 时间戳,确定性 | -| `utils/__tests__/notebook.test.ts` | 9 | ACCEPTABLE | 合并断言偏弱 | -| `utils/__tests__/hyperlink.test.ts` | 10 | ACCEPTABLE | 空串测试行为注释混乱 | -| `utils/__tests__/zodToJsonSchema.test.ts` | 9 | WEAK | **object 属性仅 `toBeDefined` 未验证类型**;optional 字段未验证 absence | -| `utils/__tests__/objectGroupBy.test.ts` | 5 | ACCEPTABLE | 极简,缺 undefined key 测试 | -| `utils/__tests__/contentArray.test.ts` | 6 | ACCEPTABLE | 缺混合 tool_result+text 交替 | -| `utils/__tests__/slashCommandParsing.test.ts` | 8 | GOOD | — | -| `utils/__tests__/groupToolUses.test.ts` | 10 | GOOD | — | -| `utils/__tests__/shell/__tests__/outputLimits.test.ts` | 7 | ACCEPTABLE | — | -| `utils/__tests__/envValidation.test.ts` | 12 | GOOD | validateBoundedIntEnvVar | value=1 无下界确认为设计意图(函数仅校验 >0 和 <=upperLimit) | -| `utils/git/__tests__/gitConfigParser.test.ts` | 20 | GOOD | — | -| `services/mcp/__tests__/mcpStringUtils.test.ts` | 16 | GOOD | — | -| `services/mcp/__tests__/normalization.test.ts` | 10 | GOOD | — | - -### 4.3 评分汇总 - -| 等级 | 文件数 | 占比 | -|------|--------|------| -| **GOOD** | 46 | 55% | -| **ACCEPTABLE** | 32 | 38% | -| **WEAK** | 6 | 7% | - -## 5. 系统性问题 - -### 5.1 断言过弱(Smell: `toContain` 代替精确匹配) - -以下文件的部分测试使用 `toContain` 或 `not.toBeNull` 检查结果,当实现返回包含目标子串的任何字符串时测试仍通过,无法检测格式错误: - -| 文件 | 受影响函数 | 建议 | -|------|-----------|------| -| `file.test.ts` | addLineNumbers | 断言完整输出格式 | -| `diff.test.ts` | getPatchFromContents | 验证 hunk 内容正确性 | -| `notebook.test.ts` | mapNotebookCellsToToolResult | 验证合并后内容 | -| `uuid.test.ts` | validateUuid (uppercase) | 断言标准化后的精确值 | - -### 5.2 集成测试空白 - -Spec 定义的三个集成测试均未创建: - -| 计划 | 状态 | 依赖 | -|------|------|------| -| `tests/integration/tool-chain.test.ts` | 未创建 | 需 mock tools.ts 完整注册链 | -| `tests/integration/context-build.test.ts` | 未创建 | 需 mock context.ts 重依赖链 | -| `tests/integration/message-pipeline.test.ts` | 未创建 | 需 mock API 层 | - -`tests/mocks/` 目录也不存在,无共享 mock/fixture 基础设施。 - -### 5.3 Mock 相关 - -| 问题 | 影响文件 | 说明 | -|------|----------|------| -| 未 mock 重依赖 | `gitOperationTracking.test.ts` | `trackGitOperations` 调用 analytics/bootstrap,测试仅覆盖 `detectGitOperation`(无副作用) | -| env 恢复不完整 | `providers.test.ts` | 仅删除已知 key,新增 env var 会导致测试泄漏 | - -### 5.4 潜在 Bug - -| 文件 | 函数 | 问题 | -|------|------|------| -| ~~`envValidation.test.ts`~~ | ~~validateBoundedIntEnvVar~~ | ~~value=1 无下界检查~~ — **已确认**:函数仅校验 `parsed > 0` 和 `parsed <= upperLimit`,不强制 `parsed >= defaultValue`,为设计意图 | - -### 5.5 已知限制 - -| 模块 | 问题 | -|------|------| -| `Bun.JSONL.parseChunk` | 畸形行时无限挂起(Bun 1.3.10 bug) | -| `context.ts` 核心逻辑 | 依赖 bootstrap/state + git + 50+ 模块,mock 不可行 | -| `tools.ts` (getAllBaseTools) | 导入链过重 | -| `spawnMultiAgent.ts` | 50+ 依赖 | -| `messages.ts` 部分函数 | 依赖 `getFeatureValue_CACHED_MAY_BE_STALE` | -| UI 组件 (`screens/`, `components/`) | 需 Ink 渲染测试环境 | - -### 5.6 Mock 模式 - -通过 `mock.module()` + `await import()` 解锁重依赖模块: - -| 被 Mock 模块 | 解锁的测试 | -|-------------|-----------| -| `src/utils/log.ts` | json, tokens, FileEditTool/utils, permissions, memoize, PermissionMode | -| `src/services/tokenEstimation.ts` | tokens | -| `src/utils/slowOperations.ts` | tokens, permissions, memoize, PermissionMode | -| `src/utils/debug.ts` | envValidation, outputLimits | -| `src/utils/bash/commands.ts` | commandSemantics | -| `src/utils/thinking.js` | effort | -| `src/utils/settings/settings.js` | effort | -| `src/utils/auth.js` | effort | -| `src/services/analytics/growthbook.js` | effort, tokenBudget | -| `src/utils/powershell/dangerousCmdlets.js` | powershellSecurity | -| `src/utils/cwd.js` | gitSafety | -| `src/utils/powershell/parser.js` | gitSafety | -| `src/utils/stringUtils.js` | LSP formatters | -| `figures` | treeify | - -**约束**:`mock.module()` 必须在每个测试文件内联调用,不能从共享 helper 导入。 - -## 6. 完成状态 - -> 更新日期:2026-04-02 | **1623 tests, 84 files, 0 fail, 851ms** - -### 已完成 - -| 计划 | 状态 | 新增测试 | 说明 | -|------|------|---------|------| -| Plan 12 — Mock 可靠性 | **已完成** | +9 | PermissionMode ant false 路径、providers env 快照恢复 | -| Plan 10 — WEAK 修复 | **已完成** | +15 | format 断言精确化、envValidation 修正、zodToJsonSchema/destructors/notebook 加固 | -| Plan 13 — CJK/Emoji | **已完成** | +17 | truncate CJK/emoji 宽度感知测试 | -| Plan 11 — ACCEPTABLE 加强 | **已完成** | +62 | diff/uuid/hash/semver/path/claudemd/fileEdit/providers/messages 等 15 文件 | -| Plan 14 — 集成测试 | **已完成** | +43 | 搭建 tests/mocks/ + tool-chain/context-build/message-pipeline/cli-arguments | -| Plan 15 — CLI + 覆盖率 | **已完成** | +11 | Commander.js 参数解析、覆盖率基线 | -| Phase 16 — 零依赖纯函数 | **已完成** | +126 | stream/abortController/bufferedWriter/gitDiff/history/sliceAnsi/treeify/words 8 文件 | -| Phase 17 — 工具子模块 | **已完成** | +179 | PowerShell 安全/语义/破坏性/gitSafety + LSP 格式化/schema + WebFetch 预批准/URL 8 文件 | -| Phase 18 — WEAK 修复 | **已完成** | +20 | format 精确匹配、envValidation 边界、PermissionMode 补强、gitOperationTracking PR actions | - -### 覆盖率基线 - -| 指标 | 数值 | -|------|------| -| 总测试数 | 1623 | -| 测试文件数 | 84 | -| 失败数 | 0 | -| 断言数 | 2516 | -| 运行耗时 | ~851ms | -| Tool.ts 行覆盖率 | 100% | -| 整体行覆盖率 | ~33%(Bun coverage 限制:`mock.module` 模式下的模块不报告) | - -> **注意**:Bun `--coverage` 仅报告测试 import 链中直接加载的文件。使用 `mock.module()` + `await import()` 模式的源文件(大多数 `src/utils/` 纯函数)不显示在覆盖率报告中。实际测试覆盖率高于报告值。 - -### 不纳入计划 - -| 模块 | 原因 | -|------|------| -| `query.ts` / `QueryEngine.ts` | 核心循环,需完整集成环境 | -| `services/api/claude.ts` | 需 mock SDK 流式响应 | -| `spawnMultiAgent.ts` | 50+ 依赖 | -| `modelCost.ts` | 依赖 bootstrap/state + analytics | -| `mcp/dateTimeParser.ts` | 调用 Haiku API | -| `screens/` / `components/` | 需 Ink 渲染测试 | diff --git a/docs/ultraplan-implementation.md b/docs/ultraplan-implementation.md deleted file mode 100644 index 5cf80d57c..000000000 --- a/docs/ultraplan-implementation.md +++ /dev/null @@ -1,444 +0,0 @@ -# ULTRAPLAN(增强规划)实现分析 - -> 生成日期:2026-04-02 -> Feature Flag:`FEATURE_ULTRAPLAN=1` -> 引用数:10(跨 8 个文件) - ---- - -## 一、功能概述 - -ULTRAPLAN 是一个**远程增强规划**功能,将用户的规划请求发送到 Claude Code on the Web(CCR,云端容器)执行。使用 Opus 模型在云端生成高级计划,用户可以在浏览器中编辑和审批,然后选择在云端继续执行或将计划"传送"回本地终端执行。 - -**核心卖点**: -- 终端不被阻塞 — 远程在云端规划,本地可继续工作 -- 使用最强大的模型(Opus) -- 用户可在浏览器中实时查看和编辑计划 -- 支持多轮迭代(云端可追问,用户在浏览器回复) - ---- - -## 二、架构总览 - -``` -用户输入 "ultraplan xxx" - │ - ▼ -┌─────────────────────────────────┐ -│ 关键字检测层 (keyword.ts) │ 识别 "ultraplan" 关键字 -│ + 输入处理层 (processUserInput) │ 重写为 /ultraplan 命令 -└───────────┬─────────────────────┘ - │ - ▼ -┌─────────────────────────────────┐ -│ 命令处理层 (ultraplan.tsx) │ launchUltraplan() -│ - 前置校验(资格、防重入) │ → launchDetached() -│ - 构建提示词 │ buildUltraplanPrompt() -└───────────┬─────────────────────┘ - │ - ▼ -┌─────────────────────────────────┐ -│ 远程会话层 │ teleportToRemote() -│ - 创建 CCR 云端会话 │ permissionMode: 'plan' -│ - 设置 plan 权限模式 │ model: Opus -└───────────┬─────────────────────┘ - │ - ▼ -┌─────────────────────────────────┐ -│ 轮询层 (ccrSession.ts) │ pollForApprovedExitPlanMode() -│ - ExitPlanModeScanner │ 每 3 秒轮询事件流 -│ - 状态机: running → needs_input │ 超时: 30 分钟 -│ → plan_ready │ -└───────────┬─────────────────────┘ - │ - ┌─────┴─────┐ - ▼ ▼ - approved teleport - (云端执行) (传送回本地) - │ │ - │ ▼ - │ UltraplanChoiceDialog - │ 用户选择执行方式 - ▼ ▼ - 完成通知 本地执行计划 -``` - ---- - -## 三、模块详解 - -### 3.1 关键字检测 — `src/utils/ultraplan/keyword.ts` - -负责检测用户输入中的 "ultraplan" 关键字。检测逻辑相当精细,避免误触发: - -**触发条件**:输入中包含独立的 `ultraplan` 单词(大小写不敏感)。 - -**不触发的场景**: -- 在引号/括号内:`` `ultraplan` ``、`"ultraplan"`、`[ultraplan]`、`{ultraplan}` -- 路径/标识符上下文:`src/ultraplan/foo.ts`、`ultraplan.tsx`、`--ultraplan-mode` -- 问句:`ultraplan?` -- 斜杠命令内:`/rename ultraplan foo` -- 已有 ultraplan 会话运行中或正在启动时 - -**关键字替换**:触发后将 `ultraplan` 替换为 `plan`,保持语法通顺(如 "please ultraplan this" → "please plan this")。 - -```typescript -// 核心导出函数 -findUltraplanTriggerPositions(text) // 返回触发位置数组 -hasUltraplanKeyword(text) // 布尔判断 -replaceUltraplanKeyword(text) // 替换第一个触发词为 "plan" -``` - -### 3.2 命令注册 — `src/commands.ts` - -```typescript -const ultraplan = feature('ULTRAPLAN') - ? require('./commands/ultraplan.js').default - : null -``` - -命令仅在 `FEATURE_ULTRAPLAN=1` 时加载。命令定义: - -```typescript -{ - type: 'local-jsx', - name: 'ultraplan', - description: '~10–30 min · Claude Code on the web drafts an advanced plan...', - argumentHint: '', - isEnabled: () => process.env.USER_TYPE === 'ant', // 仅 ant 用户可用 -} -``` - -> 注意:`isEnabled` 检查 `USER_TYPE === 'ant'`(Anthropic 内部用户),这是命令级限制。关键字触发路径没有此限制,只要 feature flag 开启即可。 - -### 3.3 核心命令实现 — `src/commands/ultraplan.tsx` - -#### 3.3.1 入口函数 `call()` - -处理 `/ultraplan ` 斜杠命令: - -1. **无参数调用**:显示使用帮助文本 -2. **已有活跃会话**:返回 "already polling" 提示 -3. **正常调用**:设置 `ultraplanLaunchPending` 状态,触发 `UltraplanLaunchDialog` 对话框 - -#### 3.3.2 `launchUltraplan()` - -公共启动入口,被三个路径共享: -- 斜杠命令 (`/ultraplan`) -- 关键字触发 (`processUserInput.ts`) -- Plan 审批对话框的 "Ultraplan" 按钮 (`ExitPlanModePermissionRequest`) - -关键逻辑: -1. 防重入检查(`ultraplanSessionUrl` / `ultraplanLaunching`) -2. 同步设置 `ultraplanLaunching = true` 防止竞态 -3. 异步调用 `launchDetached()` -4. 立即返回启动消息(不等远程会话创建) - -#### 3.3.3 `launchDetached()` - -异步后台流程: - -1. **获取模型**:从 GrowthBook 读取 `tengu_ultraplan_model`,默认 `opus46` 的 firstParty ID -2. **资格检查**:`checkRemoteAgentEligibility()` — 验证用户是否有权限使用远程 agent -3. **构建提示词**:`buildUltraplanPrompt(blurb, seedPlan)` - - 如有 `seedPlan`(来自 plan 审批对话框),作为草稿前缀 - - 加载 `prompt.txt` 中的指令模板 - - 附加用户 blurb -4. **创建远程会话**:`teleportToRemote()` - - `permissionMode: 'plan'` — 远程以 plan 模式运行 - - `ultraplan: true` — 标记为 ultraplan 会话 - - `useDefaultEnvironment: true` — 使用默认云端环境 -5. **注册任务**:`registerRemoteAgentTask()` 创建 `RemoteAgentTask` 追踪条目 -6. **启动轮询**:`startDetachedPoll()` 后台轮询审批状态 - -#### 3.3.4 提示词构建 - -``` -buildUltraplanPrompt(blurb, seedPlan?) -``` - -- `prompt.txt`:当前为空文件(反编译丢失),原始内容应包含指导远程 agent 生成计划的系统指令 -- 开发者可通过 `ULTRAPLAN_PROMPT_FILE` 环境变量覆盖提示词文件(仅 `USER_TYPE=ant` 时生效) - -#### 3.3.5 `startDetachedPoll()` - -后台轮询管理: - -1. 调用 `pollForApprovedExitPlanMode()` 等待计划审批 -2. 阶段变化时更新 `RemoteAgentTask.ultraplanPhase`(UI 展示) -3. 审批完成后的两种路径: - - **`executionTarget: 'remote'`**:用户选择在云端执行 - - 标记任务完成 - - 清除 `ultraplanSessionUrl` - - 发送通知:结果将以 PR 形式提交 - - **`executionTarget: 'local'`**:用户选择传送回本地(teleport) - - 设置 `ultraplanPendingChoice` - - 触发 `UltraplanChoiceDialog` 对话框 -4. 失败时:归档远程会话、清除状态、发送错误通知 - -#### 3.3.6 `stopUltraplan()` - -用户主动停止: - -1. `RemoteAgentTask.kill()` 归档远程会话 -2. 清除所有 ultraplan 状态(`ultraplanSessionUrl`、`ultraplanPendingChoice`、`ultraplanLaunching`) -3. 发送停止通知 - -### 3.4 CCR 会话轮询 — `src/utils/ultraplan/ccrSession.ts` - -#### 3.4.1 `ExitPlanModeScanner` - -纯状态机,无 I/O。摄入 `SDKMessage[]` 事件批次,分类 `ExitPlanMode` 工具调用的结果。 - -**状态类型**: - -```typescript -type ScanResult = - | { kind: 'approved' } // 用户批准了计划 - | { kind: 'teleport' } // 用户点击"传送回本地" - | { kind: 'rejected' } // 用户拒绝(可继续迭代) - | { kind: 'pending' } // 等待用户审批中 - | { kind: 'terminated' } // 远程会话意外终止 - | { kind: 'unchanged' } // 无新事件,状态不变 -``` - -**优先级**:approved > terminated > rejected > pending > unchanged - -**关键设计**: -- 同一批事件可能包含审批和后续崩溃 — 不丢弃已审批的计划 -- 拒绝后重新扫描(`rescanAfterRejection`),因为新事件可能包含修改后的计划 -- 使用 `is_error: true` 判断拒绝,`content` 中查找标记提取计划文本 - -#### 3.4.2 `pollForApprovedExitPlanMode()` - -轮询主循环: - -- **轮询间隔**:3 秒 -- **超时**:30 分钟 -- **容错**:连续 5 次网络错误才放弃 -- **阶段推断**: - - `hasPendingPlan`(有 ExitPlanMode 无结果)→ `plan_ready` - - `quietIdle`(空闲且无新事件)→ `needs_input`(远程在等用户输入) - - 其他 → `running` - -#### 3.4.3 计划文本提取 - -两种提取路径: - -1. **Approved**:从 `tool_result` 中查找 `## Approved Plan:\n` 或 `## Approved Plan (edited by user):\n` 标记 -2. **Teleport**:从 `tool_result` 中查找 `__ULTRAPLAN_TELEPORT_SENTINEL__` 标记(浏览器端嵌入) - -### 3.5 输入处理集成 — `src/utils/processUserInput/processUserInput.ts` - -关键字触发路径(在斜杠命令处理之前): - -```typescript -if (feature('ULTRAPLAN') && - mode === 'prompt' && // 非非交互模式 - !isNonInteractiveSession && // 非后台会话 - inputString !== null && - !inputString.startsWith('/') && // 非斜杠命令 - !ultraplanSessionUrl && // 无活跃会话 - !ultraplanLaunching && // 非正在启动 - hasUltraplanKeyword(inputString)) { - // 重写为 /ultraplan 命令 - const rewritten = replaceUltraplanKeyword(inputString).trim() - await processSlashCommand(`/ultraplan ${rewritten}`, ...) -} -``` - -### 3.6 UI 层 - -#### 3.6.1 彩虹高亮 — `src/components/PromptInput/PromptInput.tsx` - -当输入中检测到 `ultraplan` 关键字时: -- 对每个字符施加**彩虹渐变色**高亮(`getRainbowColor()`) -- 显示通知:"This prompt will launch an ultraplan session in Claude Code on the web" - -#### 3.6.2 预启动对话框 — `UltraplanLaunchDialog` - -在 REPL 的 `focusedInputDialog === 'ultraplan-launch'` 时渲染。 - -用户选择: -- **确认**:调用 `launchUltraplan()`,先添加命令回显,异步启动远程会话 -- **取消**:清除 `ultraplanLaunchPending` 状态 - -#### 3.6.3 计划选择对话框 — `UltraplanChoiceDialog` - -在 `focusedInputDialog === 'ultraplan-choice'` 时渲染。 - -当 teleport 路径返回已审批计划时,用户可选择执行方式。 - -#### 3.6.4 Plan 审批按钮 — `ExitPlanModePermissionRequest` - -本地 Plan Mode 的审批对话框中,如果 `feature('ULTRAPLAN')` 开启,会显示额外的 "Ultraplan" 按钮: -- 将当前本地计划作为 `seedPlan` 发送给远程 -- 按钮仅在无活跃 ultraplan 会话时显示 - -### 3.7 应用状态 — `src/state/AppStateStore.ts` - -```typescript -interface AppState { - ultraplanLaunching?: boolean // 防重入锁(5 秒窗口) - ultraplanSessionUrl?: string // 活跃远程会话 URL - ultraplanPendingChoice?: { // 已审批计划等待选择 - plan: string - sessionId: string - taskId: string - } - ultraplanLaunchPending?: { // 预启动对话框 - blurb: string - } - isUltraplanMode?: boolean // 远程端:CCR 侧的 ultraplan 标记 -} -``` - -### 3.8 远程任务追踪 — `src/tasks/RemoteAgentTask/RemoteAgentTask.tsx` - -Ultraplan 使用 `RemoteAgentTask` 基础设施追踪远程会话: - -```typescript -registerRemoteAgentTask({ - remoteTaskType: 'ultraplan', - session: { id, title }, - command: blurb, - isUltraplan: true // 特殊标记,跳过通用轮询逻辑 -}) -``` - -`extractPlanFromLog()` 从 `...` XML 标签中提取计划内容。 - ---- - -## 四、数据流时序 - -``` -时间线 → - -用户 本地 CLI CCR 云端 - │ │ │ - │ "ultraplan xxx" │ │ - │──────────────────────>│ │ - │ │ keyword 检测 + 重写 │ - │ │ /ultraplan "plan xxx" │ - │ │ │ - │ [UltraplanLaunch │ │ - │ Dialog] │ │ - │──── confirm ─────────>│ │ - │ │ launchDetached() │ - │ │─────────────────────────────>│ - │ │ teleportToRemote() │ - │ │ (permissionMode: 'plan') │ - │ │ │ - │ "Starting..." │ │ - │<──────────────────────│ │ - │ │ │ - │ (终端空闲,可继续) │ startDetachedPoll() │ - │ │ ═══ 3s 轮询循环 ═══ │ - │ │ │ - │ │ [浏览器打开]│ - │ │ [云端生成计划] - │ │ │ - │ │ ← needs_input ─────────────│ - │ │ (云端追问用户) │ - │ │ │ - │ │ [用户在浏览器回复] - │ │ │ - │ │ ← plan_ready ──────────────│ - │ │ (ExitPlanMode 等待审批) │ - │ │ │ - │ │ [用户审批/编辑] - │ │ │ - │ ┌───────┤ ← approved ────────────────│ - │ │ │ │ - │ [远程执行] │ │ │ - │ 通知完成 │ │ │ - │ │ │ │ - │ └── OR ─┤ ← teleport ───────────────│ - │ │ │ - │ [UltraplanChoice │ │ - │ Dialog] │ │ - │── 选择执行方式 ───────>│ │ - │ │ 本地执行计划 │ -``` - ---- - -## 五、关键文件清单 - -| 文件 | 职责 | -|------|------| -| `src/utils/ultraplan/keyword.ts` | 关键字检测、高亮位置计算、关键字替换 | -| `src/utils/ultraplan/ccrSession.ts` | CCR 会话轮询、ExitPlanMode 状态机、计划文本提取 | -| `src/utils/ultraplan/prompt.txt` | 远程指令模板(当前为空,需重建) | -| `src/commands/ultraplan.tsx` | `/ultraplan` 命令、启动/停止逻辑、提示词构建 | -| `src/utils/processUserInput/processUserInput.ts` | 关键字触发 → `/ultraplan` 命令路由 | -| `src/components/PromptInput/PromptInput.tsx` | 彩虹高亮 + 通知提示 | -| `src/screens/REPL.tsx` | 对话框渲染(UltraplanLaunchDialog / UltraplanChoiceDialog) | -| `src/components/permissions/ExitPlanModePermissionRequest/` | Plan 审批中的 "Ultraplan" 按钮 | -| `src/state/AppStateStore.ts` | ultraplan 相关状态字段定义 | -| `src/tasks/RemoteAgentTask/RemoteAgentTask.tsx` | 远程任务追踪 + `` 标签提取 | -| `src/constants/xml.ts` | `ULTRAPLAN_TAG = 'ultraplan'` | - ---- - -## 六、依赖关系 - -### 外部依赖 - -| 依赖 | 用途 | 必要性 | -|------|------|--------| -| `teleportToRemote()` | 创建 CCR 云端会话 | 必须 — 核心功能 | -| `checkRemoteAgentEligibility()` | 验证用户远程 agent 使用资格 | 必须 — 前置检查 | -| `archiveRemoteSession()` | 归档/终止远程会话 | 必须 — 清理 | -| GrowthBook `tengu_ultraplan_model` | 获取使用的模型 ID | 可选 — 默认 opus46 | -| `@anthropic-ai/sdk` | SDKMessage 类型 | 必须 — 类型定义 | -| `pollRemoteSessionEvents()` | 事件流分页轮询 | 必须 — 轮询基础设施 | - -### 内部依赖 - -- **ExitPlanModeV2Tool**:远程端调用的工具,触发 plan 审批流程 -- **RemoteAgentTask**:任务追踪和状态管理基础设施 -- **AppState Store**:ultraplan 状态管理 - ---- - -## 七、当前状态与补全要点 - -| 组件 | 状态 | 说明 | -|------|------|------| -| 关键字检测 | ✅ 完整 | `keyword.ts` 逻辑完善 | -| 命令框架 | ✅ 完整 | 注册、路由、防重入完整 | -| 启动流程 | ✅ 完整 | `launchUltraplan` / `launchDetached` 完整 | -| CCR 轮询 | ✅ 完整 | `ccrSession.ts` 状态机完整 | -| UI 高亮/通知 | ✅ 完整 | 彩虹高亮 + 提示通知完整 | -| 状态管理 | ✅ 完整 | AppState 字段完整 | -| `prompt.txt` | ❌ 空文件 | 需要重建远程指令模板 | -| `UltraplanLaunchDialog` | ⚠️ 全局声明 | 组件实现未找到(可能在内置包中) | -| `UltraplanChoiceDialog` | ⚠️ 全局声明 | 组件实现未找到(可能在内置包中) | -| `isEnabled` 限制 | ⚠️ `USER_TYPE === 'ant'` | 命令级限制,仅 Anthropic 内部用户 | - -### 补全建议 - -1. **重建 `prompt.txt`**:这是远程 agent 的核心指令,定义如何进行多 agent 探索式规划。需要设计: - - 规划方法论(多角度分析、风险评估、分阶段执行) - - ExitPlanMode 工具的使用引导 - - 输出格式要求 - -2. **对话框组件**:`UltraplanLaunchDialog` 和 `UltraplanChoiceDialog` 在 `global.d.ts` 中声明但实现缺失,需要新建: - - Launch Dialog:确认对话框(含 CCR 使用条款链接) - - Choice Dialog:展示已审批计划 + 执行方式选择 - -3. **放宽 `isEnabled`**:如果要让非 ant 用户使用斜杠命令,需移除 `USER_TYPE === 'ant'` 检查 - ---- - -## 八、与相关 Feature 的关系 - -| Feature | 关系 | -|---------|------| -| `ULTRATHINK` | 类似的高能力模式,但 `ULTRATHINK` 只调高 effort,不启动远程会话 | -| `FORK_SUBAGENT` | Ultraplan 不使用 fork subagent,使用的是 CCR 远程 agent | -| `COORDINATOR_MODE` | 不同范式的多 agent,Coordinator 在本地编排,Ultraplan 在云端 | -| `BRIDGE_MODE` | 底层依赖相同的 `teleportToRemote()` 基础设施 | -| `ExitPlanModeTool` | 远程端的审批机制,Ultraplan 的核心交互模型 |