Skip to content

dsh-behavior-guard

Verified

dsh-behavior-guard · v0.1.0 · MIT

把行为层里最容易忘的三条变成硬拦截:改文件前必须在本次会话读过它、会丢工作的破坏性命令先拿可逆写法、任务清单别变成摆设。

Install

dsh plugin add dsh-behavior-guard

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-behavior-guard

把行为层里最容易忘的三条,从「提示词里的软约束」变成「工具层的硬拦截」。

DSH 的行为层(~/.dsh/AGENTS.md 那类常驻指令)是注入:模型赶进度时能跳过它,而跳过通常不会立刻报错—— 直到改错文件、删掉不该删的东西、或者任务清单变成摆设。这个插件用 tools.guard 钩子把三条最容易忘的规矩挂成硬闸: 跳过就当场被拒,拒绝理由写在错误消息里。

拦哪三件

拦截面 触发条件 怎么才放行
未读直改 edit / write 的目标文件本次会话没读过 先 read 它;write 新建磁盘上不存在的文件直接放行(并记为已知)
破坏性命令 shell 命令命中会丢工作的写法:rm -rf、git reset --hard、git clean -f、force push、Remove-Item -Recurse -Force、杀进程、格式化 换成可逆写法(先备份 / 先 stash / 只删点名的那一个路径),或先问过用户再动
清单停滞 建过任务清单且还有未完成条目,连续 15 次工具调用没更新它 用 todo_write 把做完的标掉、把不做的删掉

拒绝长这样(原文,不是示意):

dsh-behavior-guard: 这个文件本次会话还没读过(D:\work\a.md)。先 read 它、看清现状再改
——不知道现状就动手,等于赌旧内容还在。(新建文件用 write 不受这条限制。)
dsh-behavior-guard: 这条命令是「Remove-Item 递归强删」,可能把未提交的改动或别人的工作一起带走。
换成可逆写法(先备份 / 先 stash / 只删点名的那一个路径),或者先问过用户再动。真要执行就把目标缩到明确的一个,别用通配。
dsh-behavior-guard: 任务清单里还有 3 条没完成,已经 15 次工具调用没更新过它。
先用 todo_write 把做完的标掉、或把不做的删掉,再继续——清单不更新,它就从进度表变成摆设。

安装

这个包没有发布到 npm,只从 GitHub 分发。 下面两种方式挑一种。

A. 用 CLI 从 GitHub 装

dsh plugin add github:StarflowDream/dsh-behavior-guard --profile <profile>

CLI 认 github:、git+、<url>.git、...#<ref> 这几种写法;它内部走 git clone,所以机器要连得上 github.com。

B. 手工落位(clone 走不通时用这个)

在仓库页点 Code → Download ZIP 解压,或 git clone 到临时目录,然后把整个目录放到:

<DSH_HOME>\profiles\<profile>\node_modules\dsh-behavior-guard\

再改 profiles\<profile>\package.json 两处:

{
  "dependencies": {
    "dsh-behavior-guard": "file:<你放源码的绝对路径>"   // 路径用正斜杠
  },
  "dsh": {
    "profile": {
      "bundles": [ "……", "dsh-behavior-guard" ]        // 追加到数组末尾
    }
  }
}

想直接改源码调试,就把仓库 clone 到 <DSH_HOME>\plugins-src\dsh-behavior-guard\,dependencies 写 "file:<DSH_HOME>/plugins-src/dsh-behavior-guard"。

挂上钩子

在 profile 的 cordis.patch.yml 里加一条(插件自带的 cordis.patch.yml 就是这段的出厂样例):

- insert:
    - id: dsh-behavior-guard
      name: 'dsh-behavior-guard'
      config:
        enabled: true

完全退出并重启桌面端(dsh.profile.bundles 只在启动时读一次)。

想确认挂上了,随手 edit 一个本次会话没读过的文件——被拒就是挂上了。

卸载

删掉 node_modules\dsh-behavior-guard\,把 package.json 里那两处改回去(或从 package.json.bak-* 恢复),重启。

配置

键 默认 说明
enabled true 设 false 时插件不挂钩子,什么也不拦

为什么只拦这三件

tools.guard 只有两态:返回字符串 = 拒绝这次调用,返回 undefined = 放行。它没有"问一下用户"的通道。

所以选择标准是:行为层已经写明正确做法、且误伤概率最低的三类。再宽一点(比如拦所有写操作、拦所有网络请求)就会开始挡正常活, 而一个会挡正常活的守卫最后一定被关掉——那样连这三件也保不住。

已知边界

  • 只拦不教:拒绝消息给替代做法,改不改还是模型自己决定。它是护栏,不是代驾。
  • 按 agent 隔离:子代理读过某文件不算你读过;不同 agent 各记各的。
  • 读工具白名单:只认 read / ops_inspect_local_file / read_image 三种。别的读法(比如用 shell cat)不会被记成"已知现状"。
  • 计数会归零:清单停滞计数只在"还有未完成条目"时累加,任何一次 todo_write 都把它清零——这正是不想让你偷懒的地方。
  • 破坏性命令靠正则:变量拼接、别名、$(...) 拼出来的命令拦不到。它防的是手滑,不是恶意或安全边界。
  • 不做持久化:所有状态都在内存(WeakMap),进程重启即清空——"本次会话"就是字面意思。

开发

npm test

测试不装插件:把 ctx 和 exec 都换成假的,直接喂给 guard 函数,18 条断言覆盖三个拦截面 + 放行边界(反斜杠路径、新建文件、单文件删除、按 agent 隔离)。

它从哪来

从本机那套行为层里长出来的:96 条常驻条文里有三条被反复跳过(改前先读、破坏性动作先确认、清单做完就标), 而跳过往往几轮之后才以"改错了文件""清单和实际进度对不上"的形式暴露。既然条文拦不住,就换成钩子拦。 实现只用了 DSH 的 ctx.inject(['tools'], …) + tools.guard,没有别的依赖。

License

MIT