session-handoff
跨 session 续接任务,Claude Code 与 Codex 通用。save:把目标、状态、用户裁决原话、下一步、关键路径、验证情况写成交接文件,按仓库身份(规范化的 git origin)存放,目录改名后仍找得到,同一任务覆盖更新;resume:列出当前仓库与全局最近的交接文件,读选中那份、复述后接着干;find:在 Claude Code 与 Codex 的会话记录里按关键词、旧目录名
Files3
View on GitHubSKILL.mdsession-handoff/SKILL.md
GitHubsession-handoff
会话会断(上下文用完、换工具、换目录),任务不能断。交接文件存在 ~/.claude/handoffs/<仓库键>/<任务名>.md,Claude Code 和 Codex 读写同一处。
- 仓库键:有 git origin 就用规范化后的 origin(如
gitlab.example.com/group/repo,协议、用户名、端口、.git都去掉),所以目录改名、换 worktree 都还是同一个键;没有 origin 用local/<主检出目录名>。 - 脚本只给路径、只读会话记录;交接文件的内容由你来写。
脚本:scripts/handoff.py(本 SKILL.md 所在目录下,只用 Python 3 标准库)。下文的 HANDOFF 指 python3 <本 skill 目录>/scripts/handoff.py。
| 用户说 | 做什么 |
|---|---|
| save、存一下、交接 | save |
| 接着上次、续接、上次做到哪 | resume |
| 找回 session、上个 session 在干啥、resume 不回来、我之前说过(但没有交接文件) | find |
save
- 在任务目录里运行
HANDOFF path [--task <任务名>]。在任务分支上可省略--task,脚本取分支名(去掉task/等前缀);在 main/master 上必须给任务名,用短而稳定的名字,下次续写用同一个。 - 输出「已存在:是」时,先读旧文件再在它上面更新:保留全部旧裁决,状态、下一步、路径改成最新。同一任务只有一份文件,不另建。
- 用下面的模板写入。只写这六节;不贴对话流水和大段日志;已在 PR、文档、提交里的内容给路径或链接。
# <任务名>
## 目标
<一句话>
## 状态
完成度 <X%>(口径:<按什么算>)。<停在哪一步,卡在什么>
## 用户裁决
- <YYYY-MM-DD>「<用户原话>」——<约束了什么>
## 下一步
1. <可直接执行的步骤:命令、文件、要改的点>
## 关键路径
- 仓库:<origin> · worktree:<路径> · 分支:<名>
- PR/MR:<链接>
- 产物:<路径>
## 验证
- 已验证:<命令 → 结果>
- 未验证:<项 → 怎么验>
- 回报文件路径,并告诉用户下次怎么续:在新 session(Claude Code 或 Codex 都行)里说「接着上次」。
用户裁决当场落盘
本 session 里用过本 skill(save、resume 过)之后,用户每给出一次裁决(选定方案、否掉某个做法、定下口径、「以后都这样」),立即更新当前任务交接文件的「用户裁决」节:原话引用,标日期,不等 session 结束。新裁决推翻旧裁决时,旧条目保留并注明「已被 <日期> 的裁决推翻」,不静默删除。新 session 动手前先读裁决,不做与之相反的事。
resume
- 运行
HANDOFF list。输出两组:当前仓库的交接文件、全局最近更新的几份(--limit N调整),各组按修改时间倒序,附每份的「目标」一句。 - 只有一份对得上用户说的任务就直接读;有多份就列出任务名和目标让用户选。都对不上,转 find。
- 读选中的文件,先用三行复述:目标、状态、下一步,再列出与下一步相关的用户裁决。然后按「下一步」接着干,除非这一步本身需要用户拍板。
- 文件里的路径已不存在(目录改名、worktree 回收)时,按仓库键找当前检出位置继续,下次 save 时更新「关键路径」。
find
用户找不到某个 session,或者要找的东西不在任何交接文件里时用。
- 运行
HANDOFF find [关键词 ...] [--days N] [--limit N]。- 关键词全部命中才算匹配,匹配范围:会话的工作目录、仓库 origin、用户消息原文;不区分大小写。
- 不给关键词就是列出最近的会话(回答「我最近一个 session 在干啥」)。
- 默认只看最近 30 天有活动的记录,搜不到再加
--days 0搜全部历史。 - 用户说旧目录名时,用路径片段当关键词(如
code/data);命中太多就再加一个话题词缩小。
- 每个匹配输出:工具、session id、工作目录(标注已不存在)、仓库、起止时间、首条和末条用户消息摘要、恢复命令。Codex 的子 agent 线程不单独列出,恢复父会话即可。
- 把候选交给用户,按首末条消息判断是哪一个。需要更多上下文时,只抽该记录文件里的用户消息,不要把整个文件读进来。
- 恢复命令由用户在新终端里运行,当前 session 不能切换成另一个 session:
- Claude Code:
cd <原目录> && claude --resume <id>。原目录不存在时,脚本会给出该会话后来到过、同一仓库的现存目录;没有就请用户 cd 到改名后的新目录再运行。--resume <id>按 id 跨所有项目目录查找,不需要复制记录文件(Claude Code 2.1.312 实测);不带 id 的claude --resume选择器和--continue只列当前目录的会话,这就是改名后「resume 不回来」的原因。 - Codex:
codex resume <id>。当前目录与会话记录的目录不同时,Codex 会问用哪个;原目录不存在就选 Use current directory。
- Claude Code:
- 不想恢复旧会话时,在当前 session 里读出要点,save 一份交接文件接着干。找回之后都建议 save 一份,下次不用再找。
隐私与边界
- 脚本只读会话记录,不改、不删、不复制。消息摘要截断到 80 字,疑似密钥的长串替换为
***;不要把会话记录原文大段贴给用户。 - 不另建记忆系统:用户裁决只记在对应任务的交接文件里。
错误处理
path报「当前不在任务分支上」:加--task <任务名>。- 不在 git 仓库或没有 origin:仓库键退化为
local/<目录名>,目录改名后键会变;list的全局区仍能看到旧文件。 find没结果:换关键词,或加--days 0。~/.claude/projects或~/.codex/sessions不存在说明本机没用过该工具,脚本会跳过。