关于 duck-tape
为 Agent 管理会话记忆,将持久化仓库上下文与短期工作区状态分开。支持保存检查点、恢复会话,并有选择地合并长期有效决策;写入时执行脱敏与审批规则。
主要功能
- 01持久记忆与工作记忆分层
- 02会话检查点保存与恢复
- 03有选择地合并到 CONTEXT.md
- 04秘密和个人信息脱敏
- 05状态文件最多保留 10 个
- 06工作区变更审批机制
适用场景
- 01在会话结束前保存 Agent 上下文
- 02上下文压缩后恢复工作
- 03将长期有效决策写入 CONTEXT.md
- 04避免敏感信息进入记忆文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sprngr/ rubber-duck --skill duck-tape安装后在 Claude Code 中输入/duck-tape确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdexamples/bootstrap-CONTEXT.mdCHANGELOG.mdCONTEXT.mdSTATE.mdhooks/claude-code.hooks.jsonclaude-code.hooks.windows.jsoncopilot.hooks.jsonextract-raw.ps1extract-raw.shextract-state.ps1extract-state.shopencode.plugin.jspre-compact.ps1pre-compact.shreferences/GUARDRAILS.mdHOOKS_GUIDE.mdOUTPUT_SCHEMA.mdSCHEMA.mdSTATE_SCHEMA.md详细介绍
会话记忆管理 🦆📼。上下文卫生、持久记忆、会话状态交接。
用途
用于活动会话的双层记忆管理。
- 第 1 层(持久): cwd 中的
CONTEXT.md。长期保存、纳入仓库跟踪。包含 7 个固定章节,按确定性规则合并。 - 第 2 层(工作):
.duck-tape/<session_id>.state.md。短期保存,不提交。采用 Agent State 架构。作为合并到 CONTEXT.md 的暂存输入。
保持内容准确,尽量减少中断。
理念护栏(技能专用)
继承 references/GUARDRAILS.md 中的共享护栏。
- 上下文不完整时,提出 1-3 个有针对性的澄清问题
- 缺少证据时,明确说明假设
技能专属补充:
- 持久产物安全:CONTEXT.md 的保存期限长,且很可能会提交。写入前必须进行脱敏,不可妥协。
- 会话状态安全:
.duck-tape/<id>.state.md属于工作区本地文件,但下一个 Agent 可能会读取。必须脱敏。 - 转换边界:状态文件保留会话细节以供恢复。写入 CONTEXT.md 前,只保留持久有效的决策、约束、惯例、事实、技术债务和待解决问题。
- 不要转换开发过程叙述、被否决的方案、近期变更上下文、仅限会话的审批标记,或仅描述工作产出过程的评审/测试/会话引用。
- 只有当测试或评审引用说明了持久有效的验证要求时,才将其保留在 CONTEXT.md 中。历史原因应保存在 ADR 或变更日志中,而非普通的 CONTEXT.md 条目。
- 轮换上限:
.duck-tape/中最多保留 10 个状态文件。清理优先级:先删除自动生成的文件,然后是恢复生成的文件,最后是手动生成的文件。 - 压缩前标记(
.duck-tape/.last-compact)由运行框架写入,不包含语义信息。写入标记无需审批。
激活方式
仅状态(默认):写入会话状态文件。触发信号:/duck-tape、“save session state”、“checkpoint session”。
合并(CONTEXT.md):写入状态文件,并合并到 CONTEXT.md。触发信号:“compact session”、“update CONTEXT.md”、“persist memory”、/duck-tape merge。
恢复:检测压缩并重新加载检查点。触发信号:/duck-tape resume、“resume session”。存在手动检查点时为只读模式。如果只有自动检查点或没有状态文件,则调用 LLM 辅助恢复,写入 -recovered.state.md(需要审批)。
方法
1. 对输入内容脱敏
扫描会话内容,查找机密/个人身份信息:API 密钥、密码、令牌、连接字符串、环境变量值、个人身份信息。发现后:拒绝包含标记内容的部分,报告发现项,并请用户脱敏源内容或确认就地屏蔽(<REDACTED>)。如果写入内容会包含未脱敏的机密,则拒绝写入;仅在用户确认后写入屏蔽后的内容。两层均适用。
2. 写入会话状态文件
使用 Agent State 架构写入 .duck-tape/<session_id>.state.md。会话 ID 格式:<YYYY-MM-DD-HHMM>。完整架构见 references/STATE_SCHEMA.md。输出格式见 references/OUTPUT_SCHEMA.md。示例见 examples/STATE.md。如果 .duck-tape/.gitignore 不存在,则自动创建并写入 *。
会话 ID 处理:
- 默认:为
/duck-tape和/duck-tape merge静默自动生成<YYYY-MM-DD-HHMM>。 - 仅当用户明确要求自定义 ID 时,才询问会话 ID。
- 如果提供的自定义 ID 无效,则提出一个纠正问题,说明所需格式,然后继续。
应用轮换上限:状态文件最多保留 10 个。清理优先级:先删除自动生成的文件(最旧的自动生成文件优先),然后是恢复生成的文件,最后是手动生成的文件。
报告状态文件路径:.duck-tape/<session_id>.state.md — 用户之后可以用此路径重新加载会话状态。
3. 合并到 CONTEXT.md(有条件)
仅在检测到合并信号时运行。状态文件专用模式下跳过。
初始化(缺少 CONTEXT.md):使用 references/STATE_SCHEMA.md 中的严格映射,根据状态文件中的已翻译内容创建 CONTEXT.md。空白章节使用 examples/bootstrap-CONTEXT.md 中的框架。在标题下方根据 8 个章节标题生成目录。输出格式见 references/OUTPUT_SCHEMA.md。示例见 examples/CONTEXT.md。如果 Goals 或 Conventions 条目缺失,则留空;不要推断。
合并(已存在 CONTEXT.md):使用严格映射,根据会话状态文件进行翻译。在摘要为持久上下文的粒度(决策级,而非提交级)之前,先应用持久上下文筛选,然后再应用各章节合并规则。仅当 ## 章节标题集合发生变化时才刷新目录。各章节合并规则见 references/SCHEMA.md。摘要:
- Goals/Decisions/Conventions/Glossary:按键去重,冲突时以新内容取代旧内容,追加新条目
- Deferred-Debt:仅追加并带状态标记
- Open-Questions:追加新条目,按文本去重
- Notes:仅追加带时间戳的条目,不重写。
- Re-derivation + Suggested Skills:仅属于状态文件,不翻译到 CONTEXT.md
- Position.Current + Position.Done:仅属于状态文件,不翻译到 CONTEXT.md。下一个 Agent 恢复时读取状态文件。
- 开发过程叙述、被否决的方案、近期变更背景、仅会话有效的标记以及生产历史引用均不写入 CONTEXT.md。如果存在底层持久决策或约束,则保留该内容。
按照 references/OUTPUT_SCHEMA.md 生成变更日志。示例见 examples/CHANGELOG.md:
Added: <section> <key>Superseded: <section> <key> (<old> -> <new>)Dropped: <section> <key> (<reason>)
删除条目必须注明原因。不得静默删除。
4. 执行审批
会更改工作区的操作(须根据变更类型获得审批):
语义变更(须获得完整执行审批):
- 代码/逻辑变更
- 文档/规划变更(README、Markdown 文档、ADR、CONTEXT.md、运行手册、设计说明),仅非代码文本文件中的拼写错误修复除外
- 配置/架构变更(设置、环境变量、构建配置)
- 依赖项变更(package.json、requirements.txt 等)
- 文件操作(创建、删除、移动)
- 会产生修改的命令(git commit、install、build、deploy)
- 委派实施/修补任务
外观变更(须获得轻量确认):
- 仅格式/空白字符变更
- 非代码文本文件中的拼写错误修复
- 确认语句:"Confirm to proceed with [formatting change/typo fix]?"
边界情况:
- 代码文件中的 JSDoc/docstring 变更属于语义变更(会影响生成的文档、代码契约)
- 解释代码逻辑的注释属于语义变更(会影响对可维护性的理解)
- 配置注释属于语义变更(会影响对配置的解释)
- 文档更新(ADR、CONTEXT.md)属于语义变更
- README 中作为代码片段的示例属于语义变更(用户会复制粘贴)
审批流程: 进行任何语义变更前,必须获得执行审批:
- 预检(每次请求批准时都必须提供;如果缺少任何字段,请提出一个澄清问题):
- 目标阶段:
- Phase 1:存根/骨架/接口
- Phase 2:连接/集成
- Phase 3:具体实现
- 阶段适配说明(解释此差异为何符合阶段限制)
- 目标文件(限定在所选阶段的范围内)
- 预期行为变更
- 最小验证检查
- 目标阶段:
- 按文件列出变更,并以格式化差异呈现
- 文件已存在:统一差异格式(
---/+++/@@区块,使用-/+前缀) - 文件不存在:在围栏代码块中提供完整内容,并以文件路径作为标题
- 每个差异块只包含一个文件
- 在差异区块上方添加内联注释,说明每项变更
- 如果任何文件违反阶段限制,请拆分提案,并在请求批准前重新提出
- 文件已存在:统一差异格式(
- 请求批准:
批准此范围吗?(例如:approve/ok/confirm) - 等待批准:在用户明确表示批准之前,不得继续编辑、运行命令或委派任务
规则:
-
未经用户批准/确认,不得执行任何会更改工作区的操作 批准意向词:
-
接受以下内容作为批准意向:"approve"、"approved"、"ok"、"go ahead"、"confirm"、"yes"
-
也接受明确引用选项的批准句子:"Proceed with option B in files X and Y."、"Approved. Run verification plan as proposed."
-
示例并非穷举。任何清楚表达批准意向的内容都可接受。
-
不要将非批准式的继续信号视为批准:单独的 "continue"、选项字母("B")、"next"。没有批准动词,也未提及范围,不算批准。
范围规则:
-
阶段上限(默认值):
- Phase 1(存根/骨架/接口):最多 6 个文件
- Phase 2(连接/集成):最多 4 个文件
- Phase 3(具体实现):最多 2 个文件
-
阶段内容限制(硬性门槛):
- Phase 1(存根/骨架/接口)只能包含:
- 文件/模块骨架结构(文件夹、导出、章节布局)
- 类型/接口声明
- 函数/类签名
- 占位返回值/错误/TODO 标记
- 不包含业务逻辑的最简无操作连接
- Phase 1 不得包含:
- 完整功能/业务逻辑
- 有副作用的流程(数据库/网络/身份验证/文件写入)
- 超出占位内容的完整 UI 行为
- Phase 2(连接/集成)可以包含:
- 路由注册、DI/容器连接、模块组合、事件挂接
- 现有组件之间的适配胶水代码
- Phase 2 不得包含:
- 大量新增业务逻辑代码块
- Phase 3(具体实现)包含:
- 业务逻辑、算法、副作用、完整行为实现
- Phase 1(存根/骨架/接口)只能包含:
-
新建文件引导规则:
- 如果范围包含新功能文件,首次审批必须仅包含 Phase 1 的存根/骨架/接口。
- 在后续的 Phase 2/3 审批中实现具体内容。
- 如果新文件超出存根/骨架的意图,将该文件拆分为先创建存根、再跟进实现。
-
如果某个阶段超出上限,应在执行前拆分为更小、范围明确的审批。
-
审查疲劳触发条件(客观标准):
- Phase 1(存根/骨架/接口):
- 如果单次审批的拟议差异超过 180 行变更(新增行数 + 删除行数),则将当前阶段的文件上限至少降低 1 个文件(最低上限为 1 个文件)。
- 如果任何单个文件超过 90 行变更(新增行数 + 删除行数),则将该文件拆分为单独审批或更小的连续编辑。
- Phase 2(接线/集成):
- 如果单次审批的拟议差异超过 120 行变更(新增行数 + 删除行数),则将当前阶段的文件上限至少降低 1 个文件(最低上限为 1 个文件)。
- 如果任何单个文件超过 60 行变更(新增行数 + 删除行数),则将该文件拆分为单独审批或更小的连续编辑。
- Phase 3(具体实现):
- 如果单次审批的拟议差异超过 80 行变更(新增行数 + 删除行数),则将当前阶段的文件上限至少降低 1 个文件(最低上限为 1 个文件)。
- 如果任何单个文件超过 40 行变更(新增行数 + 删除行数),则将该文件拆分为单独审批或更小的连续编辑。
- 如果审查者要求对同一批次中的超过 2 个文件进行澄清,则将下一批次的文件数至少减少 1 个。
- Phase 1(存根/骨架/接口):
-
如果复杂度或审查疲劳加剧,应进一步降低上限,并继续拆分为更小的批次。
-
即使目标不变,也要在各阶段之间重新开启执行审批。
-
如果审批后范围发生变化,应在继续前重新确认范围。
-
阶段示例(应用方式):
- Phase 1 示例:5 个文件,共 170 行变更(新增行数 + 删除行数),单个文件最多 80 行变更(新增行数 + 删除行数)。符合上限和阈值,因此可通过一次审批继续。
- Phase 2 示例:4 个文件,共 130 行变更(新增行数 + 删除行数)。超过阶段总量阈值,因此应在执行前拆分为 2 次审批。
- Phase 3 示例:2 个文件,其中一个文件有 45 行变更(新增行数 + 删除行数)。超过单文件阈值,因此应拆分为更小的连续编辑。
每项操作的预检:
仅状态文件:
- 目标文件:
.duck-tape/<id>.state.md、.duck-tape/.gitignore(如果缺失) - 预期:使用 Agent State 架构编写状态文件
- 验证:重新读取状态文件,确认其中包含 Agent State 各部分
合并:
- 目标文件:
.duck-tape/<id>.state.md、.duck-tape/.gitignore(如果缺失)、CONTEXT.md - 预期:编写状态文件,并根据架构规则将翻译后的状态合并到 CONTEXT.md
- 验证:重新读取这两个文件,确认变更日志与 CONTEXT.md 差异一致
Harness 集成
压缩前触发器会在上下文压缩前写入 .duck-tape/.last-compact 标记和 .duck-tape/<id>-auto.state.md 状态文件。状态文件会从会话转录(Claude Code、Copilot)中自动提取,或通过 SDK 获取(opencode)。触发器以 Shell 脚本或插件的形式在 LLM 上下文之外运行。
标记格式:<timestamp> | cwd: <path> | latest-state: <file> | transcript: <path>。transcript 字段记录源转录路径(Claude Code、Copilot)或 opencode 快照路径(<id>-transcript.json)。Angle B 之前写入的旧标记中没有此字段。
状态文件(<id>-auto.state.md)包含自动提取的 Approved Workflow、Position(工具调用 + 最新助手文本)和 Decision Log(通过模式匹配、去重,保留最近 10 条)。这是低保真度的恢复备用方案。模式匹配可以捕获明确的决策(APPROVED/DECIDED/CHOSE),但会遗漏隐含决策。手动执行 /duck-tape 检查点可生成保真度更高的状态。使用 LLM 辅助恢复(Angle B)执行 /duck-tape resume 会生成 -recovered.state.md,其语义决策提取的保真度介于自动提取和手动检查点之间。
触发器失败时会回退为仅写入标记:缺少 jq(bash)、转录缺失、格式未知、未提取到内容或 SDK 出错(opencode)。pre-compact.sh/.ps1 作为仅写入标记的备用脚本保留。
从压缩中恢复: 参见下方的 ## Resume。由 /duck-tape resume 触发,不会自动触发。
引导式安装: 参见下方的 ## Init 部分。手动安装和故障排除请参见 references/HOOKS_GUIDE.md。
可移植性: 三种 harness 的触发器行为完全相同。不检测阈值。不重新注入上下文。使用状态文件 + 标记。
恢复
/duck-tape resume — 检测压缩并重新加载检查点。
- 检查
.duck-tape/.last-compact。如果文件不存在,则未发生压缩。报告“未找到压缩标记”并停止。 - 将标记时间戳与会话开始时间进行比较。如果标记早于会话开始时间,注明“本会话未发生压缩——标记来自之前的会话”;检查点是跨会话交接点,因此继续执行步骤 3 并重新加载它。
- 读取标记字段:
cwd、latest-state、transcript(转录文件路径或 opencode 快照路径;较旧的标记中可能不存在)。 - 按优先级选择状态文件:手动 > 恢复 > 自动。
a. 如果存在手动检查点(无后缀),且其时间晚于最新的自动检查点,则使用手动检查点。报告其中的位置。跳至步骤 6。
b. 如果只存在自动检查点(
-auto后缀),或者完全不存在状态文件,则调用 LLM 辅助恢复(步骤 5)。 - LLM 辅助恢复(角度 B)。通过对转录内容进行语义综合,生成比自动检查点保真度更高的状态文件。
- 转录路径:如果存在,则使用标记中的
transcript字段。如果不存在,则无法恢复;报告“发生了压缩,但标记中没有转录路径”,并建议运行/duck-tape创建新检查点。 - 运行
hooks/extract-raw.sh <transcript_path>(bash),或在 Windows 上运行hooks/extract-raw.ps1 <transcript_path>(PowerShell),以获取原始材料。脚本会输出结构化 Markdown:用户提示词、工具调用、最后 10 条助手消息、潜在决策(按模式筛选并去重)、失败的工具结果以及会话元数据。 - 阅读原始材料输出。
- 根据原始材料综合生成 Agent 状态文件。以语义方式提取决策(而非像角度 A 那样按模式匹配):“我们选择方案 2,因为 X”即使不含关键词,也属于决策。从助手消息和工具调用中提取位置(当前/已完成/剩余)。从工具结果和用户确认中提取已确立的事实。
- 将状态文件写入
.duck-tape/<YYYY-MM-DD-HHMM>-recovered.state.md。 - 轮换:
-recovered文件与手动和自动检查点共用 10 个文件的上限。淘汰优先级:先删除自动检查点,然后是恢复检查点,最后是手动检查点。 - 报告新文件中的位置。
- 转录路径:如果存在,则使用标记中的
- 如果
CONTEXT.md存在,则读取它。 - 报告:压缩时间戳、会话状态位置(当前/已完成/剩余),以及与当前工作相关的任何
CONTEXT.md决策。 - 写入状态文件(仅步骤 5)需要批准。只读恢复(步骤 1–4、6–7)无需写入。
如果不存在状态文件,且标记中没有转录路径,则报告“发生了压缩,但没有可恢复的状态”,并建议运行 /duck-tape 创建新检查点。
初始化
/duck-tape init — 引导式安装钩子。需主动选择。
- 询问用户使用哪种工具环境:opencode、Claude Code 或 Copilot。
- 根据选择,确定正确的钩子配置片段:
- opencode:
hooks/opencode.plugin.js - Claude Code(unix):
hooks/claude-code.hooks.json - Claude Code(Windows):
hooks/claude-code.hooks.windows.json - Copilot:
hooks/copilot.hooks.json
- opencode:
- 向用户显示其工具环境对应的目标安装路径:
- opencode:
.opencode/plugins/duck-tape.js - Claude Code:合并到仓库本地的
.claude/settings.local.json(推荐),或.claude/settings.json中的hooks键下 - Copilot:
.github/hooks/duck-tape.json
- opencode:
- 询问用户希望由技能写入文件,还是显示配置片段供手动放置。
- 如果选择写入:与用户确认目标路径(需要批准,创建文件)。写入文件并报告成功。
- 如果选择显示:打印配置片段和放置说明。遇到问题时,请参考
references/HOOKS_GUIDE.md。 - 确认项目中存在
hooks/extract-state.sh(unix)或hooks/extract-state.ps1(Windows)。opencode 无需 shell 脚本(插件通过 SDK 获取)。同时确认存在hooks/extract-raw.sh/.ps1,供/duck-tape resume执行 LLM 辅助恢复。
预检:
- 目标文件:工具环境专用的配置路径(最多 1 个文件)
- 预期:将钩子配置片段写入工具环境的安装路径
- 验证:重新读取写入的文件,确认 JSON 或 JS 语法有效
清理
/duck-tape prune — 手动操作。仅针对 Notes 部分。
- 列出带时间戳的 Notes 条目。
- 由用户选择要删除的条目。
- 写入清理后的文件,并报告删除内容。
预检:
- 目标文件:
CONTEXT.md(仅限 Notes 部分) - 预期:删除用户选定的 Notes 条目,固定架构部分保持不变
- 验证:重新读取 CONTEXT.md,确认只有 Notes 部分发生变化,并确认已删除选定条目
清理时,不要修改固定架构部分。
迁移
/duck-tape migrate — 将现有的 CONTEXT.md 重新整理为架构格式。将自由格式内容分类到各部分,追加缺失的标题,并将无法匹配的内容保留在架构部分上方。
- 读取现有的 CONTEXT.md。
- 确定 7 个架构部分中哪些已存在、哪些缺失。
- 解析自由格式内容(不在架构
##标题下的内容块)。 - 将每个自由格式内容块归入最接近的架构部分:
- Goals:优化目标、优先级、北极星陈述
- Decisions:已批准或否决的选择、理由、带日期的决策
- Conventions:命名规则、文件布局、风格、工具选择
- Glossary:术语定义、领域词汇、首字母缩略词释义
- Deferred-Debt:TODO/FIXME/HACK 条目、延期工作
- Open-Questions:未解决的问题、待澄清事项
- Open-Questions:未解决的问题、待澄清事项
- Notes:其他所有内容、自由格式的观察记录
- 向用户提议重新整理方案。显示映射关系:现有内容块 -> 目标部分。标记置信度较低的归类。将无法匹配的内容块列在架构部分上方。
- 获取批准后执行重新整理。根据 8 个部分标题,在标题下方生成目录。报告哪些内容已移动,哪些仍保留在架构部分上方。
预检:
- 目标文件:
CONTEXT.md - 预期:将自由格式内容分类到架构部分,追加缺失的标题,并将无法匹配的内容保留在架构部分上方
- 验证:重新读取 CONTEXT.md,确认 7 个标题均已存在,并确认所有原始内容都已妥善处理(已移动或保留在架构部分上方)
边界
- 默认模式仅写入状态。仅在收到合并信号时才写入 CONTEXT.md。
- 仅写入
CONTEXT.md、.duck-tape/<id>.state.md、.duck-tape/<id>-recovered.state.md、.duck-tape/<id>-transcript.json、.duck-tape/.gitignore、.duck-tape/.last-compact标记。 - 遵循现有的 CONTEXT.md 结构。如果文件缺少架构章节,请在首次合并前提示用户迁移。
- 如果引导内容缺少 Goals 或 Conventions 条目,仅根据用户明确作出的决定添加;不要推断。
- 如果 Note 在写入时需要更改,请追加,而不要模糊改写。
- 如果删除某个条目,请在变更日志中记录原因;不要悄然删除。