mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 22:05:50 +00:00
feat: 整合功能恢复与技能学习闭环(含 ECC v2.1 parity + Opus 4.7 接入 + prompt 工程优化)
主要变更: - Skill Learning 闭环系统 (9/9 AC) - Opus 4.7 模型层接入 + adaptive thinking - Prompt 工程优化 (64 审计测试) - Agent Teams 简化门控 (默认启用) - Windows Terminal 后端修复 (EncodedCommand/WT_SESSION) - TF-IDF 技能搜索精准化 (字段加权/CJK 优化) - Autonomy 系统 (/autonomy 命令) - ACP 协议完整实现 - mock.module 泄漏修复 (CI 全绿) - 152+ lint/type 修复
This commit is contained in:
592
docs/features/summary-command-design.md
Normal file
592
docs/features/summary-command-design.md
Normal file
@@ -0,0 +1,592 @@
|
||||
# `/summary` 完整实现设计(基于现有代码反推)
|
||||
|
||||
> 更新日期: 2026-04-15
|
||||
> 设计目标: 基于当前仓库已有能力,设计一个**完整可交付**的 `/summary` 命令,而不是只补最小可用版本。
|
||||
> 结论口径: 以当前源码为准,优先复用现有 `SessionMemory`、session transcript、resume/session listing 相关能力,不另起一套平行系统。
|
||||
|
||||
## 一、设计结论
|
||||
|
||||
`/summary` 的完整实现,应该分成两条能力线:
|
||||
|
||||
1. **当前会话摘要**
|
||||
- 显式触发一次最新摘要生成
|
||||
- 读取并展示当前 session memory 的 `summary.md`
|
||||
|
||||
2. **历史会话摘要查看**
|
||||
- 查看最近会话的摘要
|
||||
- 按 session id 查看指定会话的摘要
|
||||
- 按标题关键词查找会话摘要
|
||||
|
||||
这两条能力线应复用两套已有系统:
|
||||
|
||||
- **当前会话**:`SessionMemory`
|
||||
- **历史会话**:`sessionStorage.ts` / `listSessionsImpl.ts`
|
||||
|
||||
不应该做的是:
|
||||
|
||||
- 新造一个“即时摘要模型调用”系统
|
||||
- 用另一套 prompt 平行生成 summary
|
||||
- 把 `/summary` 做成和现有 session memory 脱钩的独立功能
|
||||
|
||||
## 二、现有代码里已经具备的基础
|
||||
|
||||
### 2.1 命令入口已注册,但当前仍是 stub
|
||||
|
||||
文件:
|
||||
|
||||
- `src/commands/summary/index.js`
|
||||
- `src/commands.ts`
|
||||
|
||||
现状:
|
||||
|
||||
- `src/commands.ts` 已静态导入 `summary`
|
||||
- `src/commands/summary/index.js` 仍为隐藏 stub
|
||||
|
||||
这说明:
|
||||
|
||||
- `/summary` 已经是一个明确存在的产品面
|
||||
- 不是“新功能提案”,而是“已注册但未实现的命令”
|
||||
|
||||
### 2.2 当前会话摘要:已有专门的手动触发入口
|
||||
|
||||
文件:
|
||||
|
||||
- `src/services/SessionMemory/sessionMemory.ts`
|
||||
|
||||
现状:
|
||||
|
||||
源码注释已经明确说明:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Manually trigger session memory extraction, bypassing threshold checks.
|
||||
* Used by the /summary command.
|
||||
*/
|
||||
export async function manuallyExtractSessionMemory(...)
|
||||
```
|
||||
|
||||
这意味着 `/summary` 当前会话模式的核心调用入口已经被设计好了。
|
||||
|
||||
### 2.3 当前会话摘要内容:已有统一读取口
|
||||
|
||||
文件:
|
||||
|
||||
- `src/services/SessionMemory/sessionMemoryUtils.ts`
|
||||
- `src/utils/permissions/filesystem.ts`
|
||||
|
||||
现状:
|
||||
|
||||
- `getSessionMemoryPath()` 返回当前 session memory 文件路径
|
||||
- `getSessionMemoryContent()` 返回当前 `summary.md` 内容
|
||||
|
||||
因此 `/summary` 不需要再自己拼装“当前会话摘要文本”,而应直接展示该文件内容。
|
||||
|
||||
### 2.4 历史会话摘要:已有 transcript 元数据能力
|
||||
|
||||
文件:
|
||||
|
||||
- `src/utils/sessionStorage.ts`
|
||||
- `src/utils/listSessionsImpl.ts`
|
||||
|
||||
已有能力:
|
||||
|
||||
- `getLastSessionLog(sessionId)`:读取单个 session 的 transcript 汇总视图
|
||||
- `searchSessionsByCustomTitle(query)`:按自定义标题搜索 session
|
||||
- `listSessionsImpl(options)`:列出 session 摘要元数据
|
||||
- `getSessionFilesLite(projectDir, limit)`:快速拿 lite logs
|
||||
|
||||
这意味着:
|
||||
|
||||
- `/summary session <id>` 不需要重新扫完整 transcript 逻辑
|
||||
- `/summary find <query>` 不需要重新造搜索层
|
||||
- `/summary recent` 可以直接复用 session listing
|
||||
|
||||
### 2.5 现有命令体系支持“一级命令 + 二级动作”
|
||||
|
||||
文件:
|
||||
|
||||
- `src/types/command.ts`
|
||||
- `src/utils/processUserInput/processSlashCommand.tsx`
|
||||
- `src/commands/mcp/mcp.tsx`
|
||||
- `src/commands/job/job.tsx`
|
||||
- `src/commands/daemon/daemon.tsx`
|
||||
|
||||
当前 slash command 体系本来就是:
|
||||
|
||||
1. `processSlashCommand()` 解析 `/command [args]`
|
||||
2. 再把 `args` 原样传给命令实现
|
||||
3. 命令自己解析二级动作
|
||||
|
||||
因此 `/summary` 最合理的实现方式也是:
|
||||
|
||||
- 一级命令:`/summary`
|
||||
- 二级动作:由 `args` 解析
|
||||
|
||||
而不是额外拆成:
|
||||
|
||||
- `/summary-last`
|
||||
- `/summary-find`
|
||||
- `/summary-session`
|
||||
|
||||
这种平铺命名。
|
||||
|
||||
## 三、命令形态:一级命令 + 二级动作
|
||||
|
||||
建议统一语法:
|
||||
|
||||
```bash
|
||||
/summary <subcommand> [args]
|
||||
```
|
||||
|
||||
无参数时:
|
||||
|
||||
```bash
|
||||
/summary
|
||||
```
|
||||
|
||||
等价于:
|
||||
|
||||
```bash
|
||||
/summary refresh
|
||||
```
|
||||
|
||||
也就是:
|
||||
|
||||
- 对当前会话显式触发一次 session memory 提取
|
||||
- 然后展示摘要结果
|
||||
|
||||
### 3.1 当前会话动作
|
||||
|
||||
```bash
|
||||
/summary
|
||||
/summary refresh
|
||||
/summary raw
|
||||
/summary path
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
- `/summary`
|
||||
刷新当前会话摘要并以友好格式展示
|
||||
- `/summary refresh`
|
||||
与 `/summary` 等价,但语义更显式
|
||||
- `/summary raw`
|
||||
刷新后输出完整 `summary.md`
|
||||
- `/summary path`
|
||||
输出当前摘要文件路径
|
||||
|
||||
### 3.2 历史会话动作
|
||||
|
||||
```bash
|
||||
/summary last
|
||||
/summary recent
|
||||
/summary recent <n>
|
||||
/summary session <session-id>
|
||||
/summary find <query>
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
- `/summary last`
|
||||
查看最近一个会话的摘要
|
||||
- `/summary recent`
|
||||
列出最近若干会话摘要
|
||||
- `/summary recent <n>`
|
||||
列出最近 `n` 个会话摘要
|
||||
- `/summary session <session-id>`
|
||||
查看指定 session 的摘要
|
||||
- `/summary find <query>`
|
||||
按标题关键词搜索并展示匹配会话摘要
|
||||
|
||||
### 3.3 为什么 `find <query>` 第一版只查 title
|
||||
|
||||
因为当前已有现成能力就是:
|
||||
|
||||
- `searchSessionsByCustomTitle(query)`
|
||||
|
||||
如果第一版就强行做:
|
||||
|
||||
- title + firstPrompt + summary 全字段模糊搜索
|
||||
|
||||
那就会把简单实现拖进一个新的 session search 设计里。
|
||||
|
||||
完整实现不等于“一口气做最大范围”;完整实现应该先建立稳定语义,再逐步扩展搜索范围。
|
||||
|
||||
## 四、每种模式对应的数据源
|
||||
|
||||
| 模式 | 数据源 | 说明 |
|
||||
|------|------|------|
|
||||
| `summary` / `refresh` / `raw` / `path` | `SessionMemory` | 当前会话,显式触发提取后读取 `summary.md` |
|
||||
| `last` | `listSessionsImpl` + `getLastSessionLog` | 先找最近 session,再读详细摘要 |
|
||||
| `session <id>` | `getLastSessionLog` | 直接读取指定 session |
|
||||
| `recent [n]` | `listSessionsImpl` | 展示摘要列表,不需要全量 transcript |
|
||||
| `find <query>` | `searchSessionsByCustomTitle` | 第一版先按 customTitle 查找 |
|
||||
|
||||
## 五、命令模块设计
|
||||
|
||||
建议实现文件:
|
||||
|
||||
- `src/commands/summary/index.ts`
|
||||
|
||||
导出形态:
|
||||
|
||||
```ts
|
||||
const summary = {
|
||||
type: 'local',
|
||||
name: 'summary',
|
||||
description: 'Generate or view session summaries',
|
||||
supportsNonInteractive: true,
|
||||
load: () => Promise.resolve({ call }),
|
||||
} satisfies Command
|
||||
```
|
||||
|
||||
### 5.1 为什么是 `local`
|
||||
|
||||
因为当前实现需要:
|
||||
|
||||
- 参数路由
|
||||
- 条件分支
|
||||
- 调用已有函数
|
||||
- 错误处理
|
||||
- 文件读取
|
||||
|
||||
这不是“给模型一段说明让它去决定”的场景,而是“命令协调器”的场景。
|
||||
|
||||
### 5.2 为什么不拆成多条平铺命令
|
||||
|
||||
因为当前仓库已有约定是:
|
||||
|
||||
- 一个命令负责一个命名空间
|
||||
- 子动作由 `args` 解析
|
||||
|
||||
所以 `/summary` 的实现应更接近:
|
||||
|
||||
- `/mcp ...`
|
||||
- `/job ...`
|
||||
- `/daemon ...`
|
||||
|
||||
而不是单独拆出多条并列命令。
|
||||
|
||||
## 六、内部实现结构建议
|
||||
|
||||
建议拆成 4 组 helper,而不是把所有逻辑塞进 `call()`:
|
||||
|
||||
### 6.1 参数解析
|
||||
|
||||
建议函数:
|
||||
|
||||
```ts
|
||||
function parseSummaryArgs(args: string): SummaryCommandInput
|
||||
```
|
||||
|
||||
返回一个判别联合:
|
||||
|
||||
```ts
|
||||
type SummaryCommandInput =
|
||||
| { mode: 'current'; raw: boolean }
|
||||
| { mode: 'path' }
|
||||
| { mode: 'last' }
|
||||
| { mode: 'session'; sessionId: UUID }
|
||||
| { mode: 'recent'; limit: number }
|
||||
| { mode: 'find'; query: string }
|
||||
```
|
||||
|
||||
建议实际解析规则:
|
||||
|
||||
```ts
|
||||
'' -> { mode: 'current', raw: false }
|
||||
'refresh' -> { mode: 'current', raw: false }
|
||||
'raw' -> { mode: 'current', raw: true }
|
||||
'path' -> { mode: 'path' }
|
||||
'last' -> { mode: 'last' }
|
||||
'recent' -> { mode: 'recent', limit: DEFAULT_RECENT_LIMIT }
|
||||
'recent 5' -> { mode: 'recent', limit: 5 }
|
||||
'session <id>' -> { mode: 'session', sessionId }
|
||||
'find foo bar' -> { mode: 'find', query: 'foo bar' }
|
||||
```
|
||||
|
||||
### 6.2 当前会话摘要执行
|
||||
|
||||
建议函数:
|
||||
|
||||
```ts
|
||||
async function runCurrentSessionSummary(
|
||||
messages: Message[],
|
||||
toolUseContext: ToolUseContext,
|
||||
opts: { raw?: boolean }
|
||||
): Promise<LocalCommandResult>
|
||||
```
|
||||
|
||||
职责:
|
||||
|
||||
1. 校验是否有消息
|
||||
2. 调用 `manuallyExtractSessionMemory()`
|
||||
3. 调用 `getSessionMemoryContent()`
|
||||
4. 组装文本结果
|
||||
|
||||
### 6.3 历史会话摘要读取
|
||||
|
||||
建议函数:
|
||||
|
||||
```ts
|
||||
async function runHistoricalSummary(
|
||||
input: HistoricalSummaryInput
|
||||
): Promise<LocalCommandResult>
|
||||
```
|
||||
|
||||
支持:
|
||||
|
||||
- `last`
|
||||
- `session`
|
||||
- `recent`
|
||||
- `find`
|
||||
|
||||
### 6.4 格式化输出
|
||||
|
||||
建议统一 formatter:
|
||||
|
||||
```ts
|
||||
function formatCurrentSummary(...)
|
||||
function formatSessionSummary(...)
|
||||
function formatRecentSessionList(...)
|
||||
```
|
||||
|
||||
避免命令逻辑和显示逻辑缠在一起。
|
||||
|
||||
## 七、当前会话模式的完整调用链
|
||||
|
||||
```text
|
||||
/summary
|
||||
-> processSlashCommand()
|
||||
-> commands.ts 中 summary
|
||||
-> summary/index.ts local call()
|
||||
-> parseSummaryArgs()
|
||||
-> runCurrentSessionSummary()
|
||||
-> manuallyExtractSessionMemory(messages, toolUseContext)
|
||||
-> SessionMemory 子代理更新 summary.md
|
||||
-> getSessionMemoryContent()
|
||||
-> formatCurrentSummary()
|
||||
-> 返回 LocalCommandResult { type: 'text' }
|
||||
```
|
||||
|
||||
## 八、历史会话模式的完整调用链
|
||||
|
||||
### 8.1 `/summary last`
|
||||
|
||||
```text
|
||||
/summary last
|
||||
-> listSessionsImpl({ dir: getOriginalCwd(), includeWorktrees: true, limit: 2+ })
|
||||
-> 取最近一条非当前 session
|
||||
-> getLastSessionLog(sessionId)
|
||||
-> formatSessionSummary()
|
||||
```
|
||||
|
||||
### 8.2 `/summary session <id>`
|
||||
|
||||
```text
|
||||
/summary session <id>
|
||||
-> getLastSessionLog(sessionId)
|
||||
-> formatSessionSummary()
|
||||
```
|
||||
|
||||
### 8.3 `/summary recent [n]`
|
||||
|
||||
```text
|
||||
/summary recent 5
|
||||
-> listSessionsImpl({ dir: getOriginalCwd(), includeWorktrees: true, limit: 5 })
|
||||
-> formatRecentSessionList()
|
||||
```
|
||||
|
||||
### 8.4 `/summary find <query>`
|
||||
|
||||
```text
|
||||
/summary find auth
|
||||
-> searchSessionsByCustomTitle('auth')
|
||||
-> formatSessionSummary() or formatRecentSessionList()
|
||||
```
|
||||
|
||||
## 九、输出格式设计
|
||||
|
||||
### 9.1 当前会话默认输出
|
||||
|
||||
建议:
|
||||
|
||||
```text
|
||||
Session summary updated.
|
||||
|
||||
<summary.md 内容>
|
||||
```
|
||||
|
||||
### 9.2 当前会话 path 模式
|
||||
|
||||
```text
|
||||
Session summary path:
|
||||
<absolute-path>
|
||||
```
|
||||
|
||||
### 9.3 历史会话摘要输出
|
||||
|
||||
建议包含:
|
||||
|
||||
- session id
|
||||
- custom title / summary / firstPrompt 的优先展示
|
||||
- modified 时间
|
||||
- tag / gitBranch / projectPath(若存在)
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
Session: <id>
|
||||
Title: Fix auth redirect loop
|
||||
Updated: 2026-04-15 14:20
|
||||
Branch: fix/auth-redirect
|
||||
Tag: auth
|
||||
|
||||
Summary:
|
||||
<summary text>
|
||||
```
|
||||
|
||||
### 9.4 recent 模式输出
|
||||
|
||||
建议压缩成列表:
|
||||
|
||||
```text
|
||||
Recent sessions:
|
||||
|
||||
1. <id> Fix auth redirect loop
|
||||
Updated: 2026-04-15 14:20
|
||||
|
||||
2. <id> Add session memory tests
|
||||
Updated: 2026-04-15 10:03
|
||||
```
|
||||
|
||||
## 十、错误模型
|
||||
|
||||
至少覆盖以下情况:
|
||||
|
||||
### 10.1 当前会话
|
||||
|
||||
- 没有消息可总结
|
||||
- 手动提取失败
|
||||
- 提取成功但读取失败
|
||||
- 文件为空
|
||||
|
||||
### 10.2 历史会话
|
||||
|
||||
- session id 不合法
|
||||
- session 不存在
|
||||
- session 存在但没有可提取摘要
|
||||
- `find` 无匹配结果
|
||||
|
||||
建议文案:
|
||||
|
||||
- `No messages to summarize.`
|
||||
- `Failed to generate session summary: <error>`
|
||||
- `Session summary was updated, but could not be read back.`
|
||||
- `Session summary is empty.`
|
||||
- `Session not found: <id>`
|
||||
- `No matching sessions found for "<query>".`
|
||||
|
||||
## 十一、和现有能力的边界
|
||||
|
||||
### 11.1 不替代 `task summary`
|
||||
|
||||
`task summary` 仍然只负责:
|
||||
|
||||
- 后台会话中途状态
|
||||
- `claude ps` 风格展示
|
||||
|
||||
`/summary` 不要去读或改 `saveTaskSummary()` 这条链。
|
||||
|
||||
### 11.2 不替代 `away summary`
|
||||
|
||||
`away summary` 仍然是:
|
||||
|
||||
- 极短 recap
|
||||
- 离开/回来场景
|
||||
|
||||
`/summary` 应该输出更完整内容。
|
||||
|
||||
### 11.3 不新造第二套 session summary 存储
|
||||
|
||||
当前会话继续使用:
|
||||
|
||||
- `summary.md`
|
||||
|
||||
历史会话继续使用:
|
||||
|
||||
- transcript 中已有 `summary/customTitle/firstPrompt`
|
||||
|
||||
## 十二、测试设计
|
||||
|
||||
建议新建:
|
||||
|
||||
- `src/commands/__tests__/summary.test.ts`
|
||||
|
||||
至少覆盖:
|
||||
|
||||
### 12.1 当前会话
|
||||
|
||||
1. `/summary` 成功路径
|
||||
2. `/summary raw`
|
||||
3. `/summary path`
|
||||
4. `manuallyExtractSessionMemory()` 失败
|
||||
5. `getSessionMemoryContent()` 返回空
|
||||
|
||||
### 12.2 历史会话
|
||||
|
||||
6. `/summary session <id>` 成功
|
||||
7. `/summary session <id>` 找不到 session
|
||||
8. `/summary last`
|
||||
9. `/summary recent`
|
||||
10. `/summary find <query>` 有结果
|
||||
11. `/summary find <query>` 无结果
|
||||
|
||||
### 12.3 参数解析
|
||||
|
||||
12. 无参数
|
||||
13. 非法参数
|
||||
14. 缺少 `session <id>` 的 id
|
||||
15. `recent` 的 limit 非法
|
||||
|
||||
## 十三、分阶段落地
|
||||
|
||||
### Phase 1:当前会话
|
||||
|
||||
- `/summary`
|
||||
- `/summary refresh`
|
||||
- `/summary raw`
|
||||
- `/summary path`
|
||||
|
||||
### Phase 2:历史会话
|
||||
|
||||
- `/summary last`
|
||||
- `/summary session <id>`
|
||||
- `/summary recent [n]`
|
||||
|
||||
### Phase 3:搜索
|
||||
|
||||
- `/summary find <query>`
|
||||
- 搜索范围增强(如标题之外的字段)
|
||||
|
||||
## 十四、验收标准
|
||||
|
||||
完整实现完成时,应满足:
|
||||
|
||||
1. `/summary` 不再是隐藏 stub
|
||||
2. 当前会话摘要链路完整可用
|
||||
3. 历史会话摘要查看链路完整可用
|
||||
4. 参数语义稳定
|
||||
5. 错误分支有清晰输出
|
||||
6. 测试覆盖当前会话 + 历史会话主路径
|
||||
|
||||
## 十五、后续扩展
|
||||
|
||||
在完整实现落地后,再考虑:
|
||||
|
||||
1. section 过滤
|
||||
2. richer search
|
||||
3. 指定输出格式(markdown/plain/json)
|
||||
4. 与 `/resume` 和 session picker 的更强联动
|
||||
|
||||
但这些不应阻塞本次实现。
|
||||
Reference in New Issue
Block a user