| name | daily-recap |
| description | 将所有机器上的 Pi 和 OMP 会话整理成主题聚合的结构化日报,可选写入 Obsidian 工作日志。 |
| disable-model-invocation | true |
Daily Recap - 每日工作整理
将所有机器上的 Agent 会话整理为一份主题聚合的日报,输出兼容 obsidian-diary 格式,可直接写入工作日志或个人日记。
nmem 是 source of truth:多台机器上的会话通过 nmem 同步,覆盖全部机器的全部会话。本地 jsonl 文件仅存在于当前机器,是本机会话的内容增强来源。
脚本路径前缀:scripts/ 和 references/ 相对于本 skill 目录。
流程
Step 0:确定目标工作日并检查数据来源
目标工作日:以 CST 04:00 为分界,窗口 [工作日 04:00, 次日 04:00)。recap 时刻 ≥04:00 整理今天,<04:00(凌晨)整理昨天;可用 --date YYYY-MM-DD 整理指定工作日(跨天补整理)。
两路并行检查:
本机:
cd <skill目录> && uv run --script scripts/extract_today.py --min-msgs 3
脚本按 04:00 窗口精确切分(粗筛 UTC 文件名前缀 + 读 session 行 timestamp 转 CST 判断),输出本机会话证据集。
远程(nmem 是 source of truth):
用 nmem_list_threads 列举近期线程(limit 调大如 200,或分页跟随 has_more/offset),按返回的 date(导入日期,日级,英文格式如 "Jul 18, 2026")粗筛目标工作日 ±2 天纳入候选--date 非会话时间,不要用数字日期精确匹配,按月份+日判断是否在范围内;精确切分在 Step 1b 用 nmem_read_thread 的 messages[0].timestamp。
降级:nmem_list_threads 抛 backend_unreachable/timeout(后端不可达,插件已内部重试)-> Hard stop,见 references/recovery-guide.md:询问用户是否仅看本机会话。
空集反证:脚本 total: 0 且远程也无候选时,先反证--当前 daily-recap 会话本身必然在目标工作日窗口内(除非显式整理历史日期)。若本应有当前会话却 total: 0,结果可疑,排查窗口/路径/日期而非直接终止。仅当反证通过(确实无会话)才告知用户"目标工作日没有会话记录"并终止。
分流:
- 仅本机有数据 -> 跳过远程,进入 Step 1a
- 仅远程有候选 -> 纯远程:跳过 Step 1a,直接 1b
- 两路都有 -> 进入 Step 1
当前日报会话(本次整理本身)也在脚本输出中,无需此时排除--Step 2 分类时归入"当前日报"类,不纳入候选。
完成条件:目标工作日证据集已确认非空(本机或远程至少一路有候选),或已告知用户"目标工作日没有会话记录"并终止。
Step 1:收集并提取
两路并行:
1a. 本机会话 - 引用 Step 0 脚本输出
Step 0 已运行的 extract_today.py 输出即为本机会话的完整证据集(标题、时间 CST、项目、消息量、首条用户消息、产出摘要),无需重跑。
参数备忘(Step 0 未带时可补跑):--min-msgs N 跳过小于 N 条消息的 stub 会话;--exclude <uuid> 排除指定 session;--date YYYY-MM-DD(或位置参数)指定目标工作日。
1b. 远程会话 - nmem_read_thread
对 nmem 中 UUID 不在脚本输出 session_id 列表中的线程,远程提取:
调用 nmem_read_thread(thread_id,从 offset: 0 起),自动按字符预算分页,跟随返回的 offset=N hint 续读,直到 hint 显示无更多。
精确切分:首次读取后用 messages[0].timestamp(会话开始时间,ISO 8601 带时区)转 CST,判断是否落在目标工作日窗口 [04:00, 次日 04:00);窗口外会话跳过。
每读一段,问:这段产生了什么可记录的产出? 提取标题和核心事件摘要。远程会话通常 2-3 段即可覆盖核心产出。单条 nmem_read_thread 失败(not_found/bad_request/timeout)标记该会话"内容待补充",不阻塞整体。
完成条件:本机会话(脚本输出)与远程会话(nmem_read_thread)两路证据已收集完毕。
Step 2:分类
分类只处理远程会话(本机会话已被脚本结构化):
| 分类 | 判定 | 内容源 |
|---|
| 本机会话 | UUID 在脚本输出中 | 脚本输出的结构化数据 + 原始 jsonl(需要时) |
| 远程会话 | UUID 仅在 nmem 中 | nmem_read_thread 分段提取 |
| 当前日报 | 标题含"日报/daily-recap"关键词 | 标记"当前日报"并跳过 |
完成条件:每个会话已归入本机/远程/当前日报三类之一,无遗漏。
Step 3:构建候选表(用户确认)
将所有证据整理为候选条目表,直接按主题域分组:
| # | 会话主题 | 主题域 | 来源 | 去向 | 理由 |
|---|
| 1 | HPC 巡检 | genome-assembly | 🖥️ | 工作日志 | 之江实验室日常工作 |
| 2 | 双语 HTML 转换 | learn-mattpocock | 📡 | 不记录 | 个人练习,无产出变化 |
去向:工作日志 · 个人日记 · 不记录
来源:🖥️ 本机 / 📡 其他机器
呈现给用户确认。用户修正去向或标记跳过后,仅批准的条目进入下一步。
完成条件:用户已确认或修正每个候选条目的去向。
Step 4:主题聚合并输出日报
对确认的条目进行主题聚合--按主题域合并,输出兼容 obsidian-diary 格式:
- 语义相近的事件合并到同一个主题域下
- 同一主题下的多个事件 -> 1 个
## 标题 + 子 bullet 展开
- 每个子 bullet 1-2 行,只写结论和决策
- 按重要性排序:部署/功能交付在前,基础设施/配置在后,探索/学习最后
- 不超过 6 个主题域(理想 3-5 个),无零散单事件段落
事件记录原则(镜像 obsidian-diary Step 3a,以 obsidian-diary 为权威):
- 实现了功能:功能名称 + 核心特性 + 关键技术决策
- 修复了问题:问题 + 根因 + 修复方式
- 做了决策:决策内容 + 理由 + 放弃的替代方案
- 调研了方案:调研对象 + 结论 + 选型判断(为什么选 A 不选 B)
- 部署/配置:环境 + 关键参数 + 验证结果
输出模板
# 工作日志 - YYYY-MM-DD
> 项目:`<主项目>`(N 个会话贡献)
## 主题域 1
- 关键事件 bullet...
- 关键事件 bullet...
## 主题域 2
- 关键事件 bullet...
写入前门禁
日记正文只含主题域标题 + 结论决策 bullet。以下中间产物不得写入(对应 obsidian-diary Blocker ③ + daily-recap 专属):会话全景表、会话编号、来源标记、消息量、agent 操作日志、验证流程、阶段标记。
完成条件:每个确认条目都归入一个主题域,无零散单事件;正文通过写入前门禁。
Step 5:可选 - 写入 Obsidian
询问用户是否需要写入工作日志/个人日记。如果确认:
- 先读取目标日记文件现有内容--当天日志可能已被其他会话部分写入过
- 补全式整合:在现有文件的对应主题段落中追加新内容,已有段落不重写;新增主题域插入合适位置
- 优先调用
obsidian-diary skill(如已安装且可用),按该 skill 的门禁规则执行
obsidian-diary 不可用时,直接用 Edit 工具补全整合
完成条件:日记已补全式整合写入,或用户选择不写入。
参考
- Pi 会话 JSONL 格式 ->
references/pi-session-format.md
- OMP 会话 JSONL 格式 ->
references/omp-session-format.md
- 故障恢复 ->
references/recovery-guide.md