| name | daily-recap |
| description | 将所有机器上的 Pi 和 OMP 会话整理成主题聚合的结构化日报,写入 Obsidian 工作日志或个人日记。 |
| disable-model-invocation | true |
Daily Recap - 每日工作整理
将所有机器上的 Agent 会话整理为一份主题聚合的日报,写入 Obsidian 工作日志或个人日记。
nmem 是 source of truth:多台机器上的会话通过 nmem 同步,覆盖全部机器的全部会话。会话的唯一来源是 nmem,不区分本地/远程。
流程
Step 0:确定目标工作日 + 列举候选会话
确定目标工作日:工作日窗口以 CST 04:00 为界 [工作日 04:00, 次日 04:00),凌晨 00:00-04:00 的会话归前一工作日。"总结哪个工作日"以 12:00 为界(<12:00 昨天 / ≥12:00 今天),由 extract_today.py 按 recap 时刻自动选择。
cd <skill目录> && uv run --script scripts/extract_today.py
不带 --date,脚本自动选择目标工作日并在 JSON 顶层 date 字段返回。拿到 date 后立即回显:「整理目标工作日:{date}(脚本自动选择,改期用 --date)」,让日期错误第一轮就能被发现。整理历史日期才显式传 --date YYYY-MM-DD。
列举候选会话(nmem 是唯一来源):
nmem threads list --limit 200 --json | uv run --script scripts/extract_today.py --filter
--filter 模式从 stdin 读 nmem 线程列表,按 UUID v7 时间戳(线程 id 前 48 位 = 会话开始时间毫秒戳,见 CONTEXT.md「线程 ID 时间戳」)精确判定窗口归属,输出 candidates(窗口内)和 excluded(窗口外)。
分页:nmem threads list 排序不是严格日期序(按导入批次混杂),--offset 递增翻页,每页管道 --filter,直到连续多页无窗口内候选才停,并把翻页范围告知用户。
滤 stub 与无关线程(从 candidates 中):
- stub 过滤:消息数 <10 的线程多为测试探针(e2e 子会话群、reply-ok 测试等),按来源/标题特征聚合为一行备注,不逐条进候选表。
- advisor 线程:标题以
### Session update [in progress 开头的是 OMP advisor 子会话,默认忽略。
- 当前日报:标题含"日报/daily-recap"的线程是本次整理本身,跳过(<12:00 运行时当前会话在窗口外,已被
--filter 排除)。
空集处理:candidates 为空时直接告知用户"目标工作日没有会话记录"并终止。不得用「当前会话应在窗口内」反证--12:00 前跑 recap 时目标工作日是昨天,当前会话必然在窗口外,反证前提恒不成立。
降级:nmem CLI 连接失败/超时(退出码非 0,stderr 报 connecting/timeout)-> Hard stop,见 references/recovery-guide.md。
完成条件:目标工作日已回显,候选会话列表已确认非空,或已告知用户"目标工作日没有会话记录"并终止。
Step 1:候选表确认(HITL)
将候选会话整理为候选条目表,直接按主题域分组:
| # | 会话主题 | 主题域 | 去向 | 理由 |
|---|
| 1 | HPC 巡检 | genome-assembly | 工作日志 | 之江实验室日常工作 |
| 2 | 双语 HTML 转换 | learn-mattpocock | 不记录 | 个人练习,无产出变化 |
去向:工作日志 · 个人日记 · 不记录
呈现给用户确认。用户修正去向或标记跳过后,仅批准的条目进入下一步。
完成条件:用户已确认或修正每个候选条目的去向。
Step 2:主题聚合并输出日报
2a. 扇出 recap-collector 按主题域并行收集事件
候选表确认后,按主题域扇出 recap-collector 并行收集(一个主题域一个 collector,包揽该主题下所有会话;主题域超过 6 个也先全部收集--合并发生在 2b 写作阶段,不预先砍)。collector 读 nmem 会话线程,蒸馏成事件(选骨架、填字段、带来源指针),返回结构化文本证据包。写入由主 agent 独占。
recap-collector 的角色、统一接口(nmem CLI 读法 / UUID v7 窗口复核 / 大线程首尾 / 产出物确认)、返回格式、降级策略全在 agents/recap-collector.md 的 systemPrompt 里,不在此重复。每次调用的 task 按 references/collector-task-template.md 模板填充(主题域 / 目标工作日 / 会话清单 / 重点核实项)。
耗时预期:单个收集 collector 约 5-12 分钟,核验 subagent 约 10 分钟。扇出前向用户说明预期耗时,避免误以为卡死。
扇出参数:
agent: recap-collector(只读 subagent,无 write/edit 工具,写入风险由选型消除)
task:按 references/collector-task-template.md 填充;不传去向(去向由主 agent Step 3 掌握)
批准条目的处置(Step 1 已批准但无法落地的情况):collector 核验发现窗口外线程、或读取失败无原文支撑--都不静默丢弃也不硬写,回头告知用户,由用户拍板删除、留待补充或降级为不记录。
会话未完成但内容有效(如 CI 修复会话在结论落地前截断):标注「未完成」,只写已确认的部分,不把未验证的意图写成既成事实。
降级:collector 环境 nmem CLI 不可用时,远程线程改由主 agent 串行 nmem threads show,混合主题的综合也落回主 agent。单会话读取失败标记「内容待补充」,不阻塞该主题域(但触发上方「批准条目的处置」)。
完成条件:每个批准的主题域都有一份事件证据包,每个事件选了骨架、填了字段、带了来源(thread_id#消息index)。
2b. 聚合写作
collector 返回的事件(非会话摘要)是 2b 的输入。每个事件已选骨架、填原生字段、带来源。对确认的条目进行主题聚合:
- 语义相近的事件合并到同一个主题域下
- 同一主题下的多个事件 -> 1 个标题 + 子 bullet 展开
- 每个子 bullet 1-2 行,只写结论和决策
- 按重要性排序:部署/功能交付在前,基础设施/配置在后,探索/学习最后
- 不超过 6 个主题域(理想 3-5 个)--收集阶段若超过 6 个,语义相近的主题域在此步合并;无零散单事件段落
事件骨架(collector 已选型填字段,2b 据此成文):
- 功能(实现了功能):功能名称 + 核心特性 + 关键技术决策
- 修复(修复了问题):问题 + 根因 + 修复方式
- 决策(做了决策):决策内容 + 理由 + 放弃的替代方案
- 调研(调研了方案):调研对象 + 结论 + 选型判断(为什么选 A 不选 B)
- 部署(部署/配置):环境 + 关键参数 + 验证结果
- 验证(验证假设/回答问题):原始问题 + 验证方法 + 结论 + 下一步含义
学到什么:事件带可复用结论(根因/选型判断/下一步含义/决策理由)才点出;纯交付(功能/部署)无可复用结论不硬编。受来源指针约束(blocker ①),终稿审查兜底。
数据密集判定:bullet 含可核对的具体数字/参数/验证结果 = 数据密集,触发 2c 条件性核验;纯定性(决策理由、调研结论)跳过。按 2b 输出 bullet 判定(与 2c 核验单元一致)。
输出模板(标题层级遵循 references/diary-rules.md 变体规则,文件名已含日期,文件内一级标题是主题/子系统,不重复日期标题):
# 主题域(work: 子系统名 / personal: 主题标题)
- 关键事件 bullet...
- 关键事件 bullet...
完成条件:每个批准主题域已写成 1-2 行 bullet,每条对应一种事件骨架。
写入门禁
聚合草稿完成后、核验前,逐项检查以下 3 个 blocker。任一不通过 -> 必须先修正:
| Blocker | 检查项 | 不通过则 |
|---|
| ① 事实虚构 | 每条 bullet 能追溯到事件证据包中的来源(thread_id#消息index)?不写"根据习惯""随手""一般来说"等无来源内容 | 删除虚构内容 |
| ② 结构散乱 | 按主题域聚合而非按时间平铺?证据不足的主题域在此暴露(给不出 bullet 说明证据缺失,剔除或补充收集,不带病进写入) | 重新聚合主题 |
| ③ 流程噪音 | 混入了 agent 操作日志或内部项目标签?包括:会话全景表、会话编号、消息量、Git 提交、验证流程、阶段标记、配置路径,以及内部项目标签(map/ticket/issue 编号、里程碑、生命周期阶段如 原型/验收/标定/闭环)。另区分"完成了什么任务"与"产出了什么结论"--前者是任务状态(噪音),后者才是应写的结论,目标形状见上方事件骨架 | 删除噪音,只保留人的结论和决策 |
完成条件:正文通过 3 blocker 门禁。
2c. 条件性核验
门禁通过后、写入前,对数据密集 bullet(2b 判定含可核对数字/参数/验证结果的)扇出核验 subagent:让它对着事件证据包里的原文,逐条核对数字、百分比、公式、口径是否与来源一致。纯定性 bullet 跳过。
核验 subagent 参数:
agent: reviewer(只读 + bash 只读命令已内建)
task:事件证据包原文 + 待核验 bullet 清单(每条含声称的数字和来源出处)
核验 subagent 报告不一致项(数字错配、口径混淆、最高/最低写反等);发现不一致 -> HITL:报告给你,你拍板怎么改,改完再写入。无不一致则直接进入 Step 3。
完成条件:所有数据密集 bullet 已与来源核对一致,或已按你拍板修正。
Step 3:写入 Obsidian
3a. 选择变体
按候选表去向(工作日志/个人日记)确定变体。变体判断规则见 references/diary-rules.md:work(公司项目/部署运维/团队协作)vs personal(个人技术探索/学习/投资/生活/个人开源项目)。混合内容拆分到两个变体分别写入。
3b. 获取日记上下文
cd <skill目录> && uv run --script scripts/obsidian-helper.py --vault <work|personal> [--date YYYY-MM-DD]
输出三段:DIARY_PATH/DATE、RECENT(近期 2-3 篇日记摘要,了解写作风格)、TODAY(今日日记全文)。日记不存在时脚本自动从模板创建。写入时始终传 --date(用 Step 0 的目标工作日),指向目标工作日的日记。
首次配置:脚本输出 CONFIG_MISSING=true 时,按打印的示例创建 ~/.config/cnife-skills/obsidian-diary.json(含各 vault 的 base/diary_dir/template/exclude_meta),询问用户 vault 根目录后填入,重新运行脚本。
3c. 补全式整合写入
先读后写:基于 TODAY 全文补全整合,不覆盖。新内容能塞进已有章节的不新开标题(blocker ② 的「已有章节对齐」在此检查):
- 已有章节 -> 在该章节末尾追加新 bullet,不重写已有内容
- 新章节 -> 插入到已有章节之后,按重要性排序
- 用 Edit(patch)工具写入
完成条件:日记已补全式整合写入,新内容已与已有章节对齐。
终稿审查(HITL · 每次都拦)
写入 Obsidian 后,用户读终稿手动改。这是写作阶段的唯一强制门--最轻且最可靠的人工准确性兜底;写入后用户本就要读日记,成本近零。
主 agent 写入后提示用户:「已写入 {变体} 日记,请读一遍终稿,有问题直接在 Obsidian 改或告诉我」。
完成条件:用户已确认终稿可接受。
参考
- 日报收集任务模板 ->
references/collector-task-template.md
- 日记格式规则 ->
references/diary-rules.md
- 故障恢复 ->
references/recovery-guide.md