给 DeepSeek Harness 装一个 Ctrl+Z:dsh-file-undo 操作级回退插件
给 DSH Agent 装一个 Ctrl+Z:dsh-file-undo 操作级回退插件
让 AI agent 改文件不再"覆水难收"——自动记录每次写入前的状态,一条
/undo回到操作前。
痛点:agent 改文件,快、狠、难反悔
用 DeepSeek Harness(DSH)这类 agent 跑活的人都有过这种时刻:你让它改一个配置文件,它一口气动了十几个文件;你盯着看改到一半,发现某个关键文件已经被覆盖了,而它没有留底。
Git 能救,但前提是这个文件恰好被 commit 过、且你记得上一次提交点。现实里更多的情况是:文件在 git 之外,或者 agent 改得太快太碎,等你回过神来已经错过了干净的回滚点。
dsh-file-undo 解决的就是这件事:给 DSH 里的 agent 加一个文件级的"撤销键"。
它做什么
一句话:自动记录 agent 每次 write / edit 操作前的文件内容,用 /undo 还原。
四个命令,一张表讲清:
| 命令 | 作用 |
|---|---|
/undo |
撤销最近一次文件写入/编辑 |
/undo list |
查看所有已记录的操作(索引 + 时间 + 工具 + 文件路径) |
/undo <n> |
撤销指定索引的那一次操作 |
/undo prune [days] |
清理 N 天前的快照,默认 7 天 |
一个典型场景:
/undo list
[0] 14:20 edit config.yaml
[1] 14:21 write src/utils.ts
[2] 14:23 edit src/index.ts
/undo 1 # 只回退 src/utils.ts 那一次,其余不动
它怎么工作
原理不复杂,关键在设计上绕开了一个坑:
- 拦截时机:插件挂在 DSH 的
tools/pre-execute瀑布上——在write/edit工具真正执行之前,先把目标文件的当前内容读出来存成快照。 - 为什么是"执行前"而不是"执行后":因为 DSH 的 write/edit 工具执行完只返回一句成功文案,把文件操作前的内容丢掉了。所以要留底,必须在工具动手之前自己读一遍。
- 存储:快照 append-only 追加到本地 JSONL(
~/.dsh/file-undo/snapshots.jsonl),一条一个操作,避免并发写时的读改写竞争。 - 还原:
/undo时把快照里的"操作前内容"通过官方 fs 服务写回文件,并携带当前会话的沙箱策略,确保写回权限和原操作一致。
边界:诚实说清楚它管不了什么
这个插件做得很窄,也正因为窄才可靠。以下场景不在覆盖范围内:
- 只覆盖
write/edit两个工具——agent 通过 shell 直接改文件(Set-Content、重定向等)管不了。 - 删除回退不支持——官方 fs 至今没有
delete/unlink接口,文件删除无法被追踪或还原;等官方补上这个能力才能做。 - 快照全局共享——不按会话隔离。
这些不是"没做完",而是被明确的设计边界挡住了(比如删除回退,硬做就意味着绕过官方 API,得不偿失)。
安装
本地构建(link)三步:
# 1. 构建插件
pnpm install && pnpm build
# 2. profile 的 package.json 加依赖
# "dependencies": { "dsh-file-undo": "link:<本目录绝对路径>" }
# 3. profile 的 cordis.patch.yml 挂载
# - insert:
# - id: file-undo
# name: 'dsh-file-undo'
# 4. 重装并重启
cd ~/.dsh/profiles/web && pnpm install && dsh web
(若发布到 npm,则一条 dsh plugin --profile web add dsh-file-undo 即可。)
开发中踩的几个坑(给后来者)
写这个插件的过程,Cordis 的几处"运行时才报"的机制比想象中更狠,列出来供避雷:
- inject 漏声明服务:代码里用了
ctx.commands但没写进inject,tsc类型检查照样过(类型合并掩盖了),要到运行时 Guard 才抛cannot get property "commands" without inject。类型过 ≠ 能跑。 - 想当然的工具名:一开始拦截
str_replace_editor,结果这个环境里 agent 实际用的文件工具是write/edit,拦截永远不命中。先查工具清单再写拦截。 - 带参数命令要声明
input:/undo list、/undo <n>这种带参数的形式,命令不声明input提示的话,会被前端降级成普通消息,根本不触发。 - 写回要带沙箱策略:插件在全局 context 里写文件,不传 session 的
sandboxPolicy会直接file access denied——还原和原操作必须在同一个 workspace 边界内。
这四条里,任何一条单看都是"哦,注意一下",但它们叠加起来,足够让一个插件"能加载、能注册、就是不干活"。
结语
dsh-file-undo 不是一个宏大的东西——它就是一个文件级的撤销器,范围只有 write/edit 两个工具,但在这个范围内是实测可用的:拦截 → 快照 → 列出 → 还原,全链路跑通。
如果你的 agent 经常改文件,又不想每次都靠 Git 兜底,值得一试。
- 仓库:github.com/QinLuza/dsh-file-undo(MIT,开源)
更多推荐



所有评论(0)