mcpField
S
session-recover

session-recover

github.com/ngmeyerAgent 工作流4326 次安装今天更新
代码仓库

关于 session-recover

session-recover 是面向 Claude Code 用户的技能,用于处理同一项目因工作目录路径不同而分散的会话和记忆。它会识别重复命名空间、合并或评估记忆,并归档而非删除转录。

主要功能

  1. 01查找重复的项目命名空间
  2. 02确定规范记忆路径
  3. 03合并或评估记忆文件
  4. 04归档孤立的会话转录
  5. 05核对文件数量

适用场景

  1. 01恢复当前会话缺失的记忆
  2. 02找回 /resume 中预期的会话
  3. 03整合符号链接或迁移路径的命名空间

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ngmeyer/skills --skill session-recover

安装后在 Claude Code 中输入/session-recover确认已加载。

附带文件references/merge-example.mdscripts/inventory.sh

详细介绍

/session-recover — 手动合并重复的 Claude Code 项目目录

Claude Code 会将每个项目的会话记录和持久记忆存储在 ~/.claude/projects/{encoded-cwd}/ 下。编码后的 cwd 是绝对路径,其中每个非字母数字字符都会替换为 -。两个指向同一逻辑项目的不同 cwd 路径(例如真实目录及其符号链接镜像、外部挂载路径及其 ~/Projects/ 快捷路径、Linux 用户从 /home/me 迁移到 /Users/me 等)会生成两个完全独立的命名空间——/resume 中的会话列表彼此独立,记忆目录也彼此独立,互相不可见。

此技能用于清理这些重复项:检测重复目录,将记忆统一到规范位置,归档孤立的会话记录,并记录经验教训,避免后续会话再次产生分裂。

适用场景

  • 你打开了一个会话,发现记忆目录为空,但记得以前的会话中添加过条目
  • claude --resume 显示的会话比你预期的少
  • 你发现两个 ~/.claude/projects/-*-foo/ 目录的末尾标记相同
  • 对话中引用的跨会话记忆无法解析([[some-memory]] 链接无效)
  • 在 ~/.claude/projects/ 中搜索项目名称时返回多个匹配项

如果以上情况都不符合,此技能无需执行——提前退出并告知用户。

核心原则

规范路径是 Claude Code 当前系统提示词告知你的路径。 当运行环境启动会话时,它会提示:

你有一个基于文件的持久记忆系统,位于 /Users/me/.claude/projects/{ENCODED}/memory/

这是胜出目录。其他所有末尾标记相似的目录都是候选落败目录。用户可以指定其他路径作为规范路径,但默认采用运行环境选定的路径。

操作步骤

阶段 1 — 识别:查找重复的项目目录

将可选参数用作项目名称标记。若未提供,则根据 cwd 的末尾部分(basename "$PWD")或对话内容推断。

bash ~/.claude/skills/session-recover/scripts/inventory.sh <project-token>

脚本会列出每个 ~/.claude/projects/*<token>*/ 目录,并显示:

  • jsonl 会话文件数量 + 总大小 + 最新文件的修改时间
  • 记忆目录中的条目数量
  • 编码后的路径是否属于 Lexar(Volumes-Lexar)、主目录下的 Projects(Users-.*-Projects)或其他类型

遇到以下情况时停止并询问用户:

  • 只有一个匹配项 → 没有重复项 → 退出
  • 存在三个或更多匹配项 → 继续之前确认哪个目录是规范目录
  • 候选目录的 jsonl 修改时间差异很大(例如,一个上周活跃,另一个今天才活跃)→ 确认用户是否希望将两者合并
阶段 2 — 识别:选出胜出目录

默认使用当前会话的系统提示词中 “基于文件的持久记忆系统,位于……” 所提及的路径。

如果该路径不在重复目录候选项中(例如,用户在项目外调用此技能),则询问用户想将哪个目录设为规范目录。通常,这会是用户开始工作时实际执行 cd 进入的路径,或项目级 CLAUDE.md 中标注为“规范”的路径。

阶段 3 — 合并记忆

对于每个落败目录,将其中的记忆内容移至胜出目录。始终使用 mv,绝不使用 cp,这样落败目录的记忆目录会被清空,不再形成互相竞争的信息来源。

WINNER=~/.claude/projects/<canonical-encoded-path>
LOSER=~/.claude/projects/<loser-encoded-path>

mkdir -p "$WINNER/memory"
mv "$LOSER/memory/"* "$WINNER/memory/" 2>/dev/null
rmdir "$LOSER/memory" 2>/dev/null

**冲突处理:**如果两边存在同名文件(最常见的是 MEMORY.md),不要覆盖。读取两份文件,手动编写统一版本并保存到胜出目录,然后删除落败目录中的副本。这通常只影响 MEMORY.md(索引文件)。

阶段 4 — 归档 jsonl 会话记录

将落败目录中的 jsonl 会话文件及任何会话子目录移至 ~/.claude/archive/。不要使用 rm——归档可撤销,删除则不可撤销。

ARCHIVE_DIR=~/.claude/archive/$(basename "$LOSER")
mkdir -p "$ARCHIVE_DIR"
mv "$LOSER"/*.jsonl "$ARCHIVE_DIR/" 2>/dev/null
# Also archive any UUID subdirs (per-session checkpoint dirs)
for d in "$LOSER"/[0-9a-f]*-[0-9a-f]*/; do
    [ -d "$d" ] && mv "$d" "$ARCHIVE_DIR/"
done
rmdir "$LOSER" 2>/dev/null

如果 rmdir "$LOSER" 因目录非空而失败,请列出其中剩余的内容并询问用户——通常是某个遗留文件(.DS_Store、非标准子目录)需要明确处理。

阶段 5 — 将经验记录为记忆

在胜出方的记忆目录中写入一条 feedback 类型的记忆条目,让未来的会话知道此项目存在双 cwd 隐患:

---
name: dual-cwd-memory-split-{project}
description: Memory dir is path-derived from cwd. Two cwd paths for the same project produced separate memory namespaces; merged YYYY-MM-DD.
metadata:
  type: feedback
---

The {project} project is reachable via:
- `{canonical-cwd-path}` — canonical
- `{loser-cwd-path}` — symlink/mirror/alias

These produce separate `~/.claude/projects/-*/` namespaces. Memory was merged into the canonical path on YYYY-MM-DD; jsonl transcripts archived to `~/.claude/archive/{loser-encoded}/`.

**How to apply:** always `cd` into `{canonical-cwd-path}` before `claude`. If a future session lands at the other path and finds an empty memory dir, re-run /session-recover before doing real work.

然后在胜出方的记忆目录中的 MEMORY.md 添加一行指针:

- [Dual-cwd memory split](feedback_dual_cwd_memory_split_{project}.md) — memory namespaces merged YYYY-MM-DD; cd into the canonical path
阶段 6 — 验证
ls "$WINNER/memory/" | wc -l   # entry count, should be sum of both sides minus dedup
ls "$LOSER" 2>/dev/null         # should report "No such file or directory"
ls ~/.claude/archive/$(basename "$LOSER")/  # archived jsonls + dirs

(V2)协调核对门槛 — 证明没有遗漏。 在宣布成功之前,核对数量:在阶段 3 中、移动之前记录 winner_before 和 loser_count,然后断言 winner_after == winner_before + loser_count − dedup_count,其中 dedup_count 是你手动合并的同名文件数。如果算术结果对不上,停止并说明差异——缺少文件意味着静默丢失,而这是此技能绝不能导致的结果。只有在数量核对一致(或用户接受了有记录的去重)后,才能报告成功。

将最终状态以 3 行表格报告给用户,其中包括核对行(winner_before + loser − dedup = winner_after)。

阶段 7 — 建议运行 /compact

Claude Code 的 /compact 斜杠命令会将当前会话整理成简洁的摘要。合并后,当前对话中包含许多“发现了这个、移动了那个”的细节,之后不会再有用。告诉用户运行 /compact(你无法自行调用——这是用户级命令)。

变体:旧版孤立记忆(评估,不要盲目合并)

上面的阶段 3–4 假设 loser 是同一当前工作的实时镜像,因此将其记忆移入 winner 是安全的。但有时孤立项是项目文件夹迁移前的旧命名空间(例如,项目数月来一直位于 /Users/me/Projects/foo,后来移到了 /Volumes/Drive/Projects/foo)。此时,孤立项的记忆是旧的——包含迁移前的状态、已完成的积压事项和已被取代的事实。**盲目将其合并到规范目录,会让陈旧内容再次污染当前记忆。**如果孤立项记忆文件的修改时间比规范目录早数周,或者孤立项的 MEMORY.md 描述的显然是项目较早的状态,就应识别出这种情况。

此时,将阶段 3 中“全部 mv”的做法替换为逐文件评估:

  1. 阅读每个孤立的记忆文件。 对每个文件进行分类(根据当前仓库/代码核实其中的主张——文件会移动,功能会发布,事实会变化):
    • 保留项 → 仓库:远程 Agent/协作者需要了解、但尚未记录在仓库(docs/、CLAUDE.md)或规范记忆中的决策、注意事项或产品想法。→ 将其写入仓库(存入 docs/decisions/ 下的 ADR、docs/ 知识文档或创意积压清单)。
    • 保留项 → 本地:仍然有效的行为指南(“不要执行 X,因为它已经处理好了”)。→ 复制到规范记忆目录并编入索引。
    • 秘密位置:任何说明令牌/凭据存放位置的内容,或环境到环境的映射。→ 将其纳入本地秘密清单记忆。绝不要提交,即使是提交到私有仓库也不行。
    • 过时项:已被取代的状态、已完成的积压事项、旧审计。→ 归档。
  2. 恢复保留项,将其放回对应位置(仓库或规范记忆);协调核对秘密位置,将其纳入本地清单。
  3. 将过时的孤立文件归档到孤立记忆目录内的 _archived-pre-migration/ 子文件夹中——不要删除,也不要合并到规范目录。
  4. 为孤立项的 MEMORY.md 添加墓碑说明:将其替换为一条说明,注明这是旧路径 <old-path> 的孤立镜像,规范命名空间和仓库是事实来源,且文件已归档。这样可以避免未来从旧路径启动的会话信任过时内容。
  5. 报告一张表格:孤立文件 → 判定 → 操作。显著指出任何确实有价值的恢复项——这是此流程的收获(例如,一个从未加入积压清单的搁置产品想法)。

跳过阶段 4(对话记录归档——除非有人要求,否则不要处理旧对话记录)和阶段 5(仍值得在规范目录中写入双 cwd 反馈记忆,以免再次出现分叉)。

此技能不会做什么

  • 不会将两个 jsonl 转录合并为一个会话。 这是不可能的——Claude Code 没有合并操作。此技能会保留活动会话的转录,并归档其他转录。
  • 除非用户明确要求作为后续操作,否则不会编辑代码、CLAUDE.md 或计划文档。 仅处理记忆和 jsonl。如果项目中有过时的计划/状态文档需要反映合并结果,用户可以在技能运行后要求你更新。
  • 不会删除任何内容。 归档可以撤销;rm 不行。
  • 不会在每次调用时都运行。 如果第 1 阶段未发现重复项,就提前退出并说明情况。不要凭空制造工作。

注意事项

  1. mv 与 cp: 始终使用 mv。如果使用 cp 后忘了删除源文件,随着后续会话写入新条目,败方的记忆目录会继续变化。在源目录删除文件才能阻止这种情况。

  2. MEMORY.md 合并冲突: 索引文件通常在两边都存在。不要盲目使用 mv——mv -n 会将败方副本保留为同级文件,结果就会有两个索引。读取两边内容,手动合并,然后删除败方副本。

  3. 每个会话的 UUID 子目录: Claude Code 会在 <uuid>.jsonl 文件旁创建 <uuid>/ 检查点目录。这些目录大多用于可恢复会话状态。将它们与 jsonl 一并归档;不要留下孤立目录。

  4. rmdir "$LOSER" 失败: 通常是因为 macOS 上有 .DS_Store,或存在多余的 todos/ 目录。重试前先列出内容;不要不加判断就使用 rm -rf。

  5. 当前会话可能位于败方目录中。 如果用户从非规范工作目录打开会话,当前 .jsonl 就会写入败方目录。不要在对话过程中将其归档——正在运行的会话会无法写入。可以先让用户运行 /exit,或者跳过当前会话的 jsonl,仅归档其余内容。

  6. 跨项目名称冲突: 如果两个项目名称包含相同的词元(例如 api 和 api-gateway),清单脚本会同时返回两者。移动任何内容前,务必向用户展示完整的候选列表并确认。

  7. 不要试图聪明地判断“哪个转录更新”。 这是合并内容的问题,而不是合并状态的问题。此技能的任务是统一记忆命名空间;活动会话的转录仍保留在原处。

更新日志

V2(2026-05-27)

通过 skillforge optimize 优化。坦诚说明: 外部结果研究不足——这是针对 Claude Code 某种特定机制的流程型技能,没有值得借鉴的前沿成果。需要保护的真正结果是合并时不丢失任何记忆,因此 V2 的改动是加强安全性,而非引入研究成果:

  • 核对关卡(第 6 阶段)——断言 winner_after == winner_before + loser − dedup;任何不匹配都要停止。将“看起来完成了”变成“可证明没有丢失任何内容”。这与现有的“归档而非删除”规则相辅相成(合并本身已可撤销)。
  • 没有强行加入由结果研究驱动的内容(Agent 记忆的三层模型已体现在旧版孤立项变体的 repo/local/secret 路由中)。

另请参阅

  • references/merge-example.md — 双工作目录合并的脱敏端到端演练。
  • scripts/inventory.sh — 第 1 阶段的辅助脚本。
  • 旧版孤立项变体会将保留项恢复到仓库中(ADR、文档、创意待办),用于仓库侧迁移;此技能负责清理命名空间并创建墓碑。

评论

支持 Markdown