dsh-rtk
xiao-hans/dsh-rtk
Rewrites DSH shell tool commands through `rtk rewrite` on tools/pre-execute, exposes `rtk` and `rtk_stats` tools, and compresses their results through `rtk pipe`.
Install
dsh plugin --profile web add github:xiao-hans/dsh-rtkREADME
dsh-rtk
RTK × DSH 适配插件:让 agent 执行的 shell 命令自动经过 RTK 压缩——同样的信息,更少的 token。
定位:插件不 fork、不改写 RTK。命令怎么变形、哪些输出该压缩、哪些命令要拒绝,全部由 RTK 本体决定(rtk rewrite 的 registry 是唯一事实源);本插件只做 DSH 这一侧的接入。
- 版本:v0.4.2 · MIT · 零运行时依赖(只用 Node 内置模块)
- 适配:DSH 0.2.1-rc.2(按特性探测,不锁版本)
- 依赖:RTK 1.2.0+ 在
PATH中 - 平台:Windows / macOS / Linux;PowerShell、bash、zsh、sh 均可
问题
Agent 跑一条 git status、npm test、cargo build,原始输出动辄几千 token,其中大量是重复的、装饰性的、可以推导的信息,而且每条命令都要再付一次。
RTK 是一个单文件 CLI,把这些命令的输出压成紧凑形式(git status 只留分支与改动,cargo build 只留错误与警告),自带 60+ 命令族的过滤器。
DSH 是一个 agent 框架,模型通过工具(ops_bash、pwsh、自定义 shell 工具)执行命令。
两者之间缺一层:在命令真正执行前,把它改写成 rtk <cmd>。这就是 dsh-rtk。
它做什么
五件事,可以分别关闭:
- 透明改写(默认开启)——在 DSH 的
tools/pre-execute阶段拦截目标 shell 工具,把exec.arguments里的命令换成一个新的exec.arguments引用。改写前有一道语义校验:提案必须仍在跑原命令(允许 rtk 已知的无损映射,如next build→rtk next),否则退回原命令。模型无感,工具定义不变,会话日志保留原命令。 - awareness 提示——往系统提示里加一段说明:输出已是完整结果、不要为了"看全量"重跑、逃生门有哪些。若本机存在
rtk init写下的RTK.md,直接注入官方原文(仅把 Claude Code 专有措辞换成 DSH 说法);找不到就用内置短文。 - 两个显式工具——
rtk执行任意命令并走 RTK;rtk_stats只读地报告省了多少 token(gain/discover/session)。 - 改写可见性(默认开启)——结果够大时在开头加一行
[rtk] ran: rtk git status;语义校验拦下提案时写[rtk] fell back: <原命令>。模型能分辨"这是压缩输出"与"这是原始输出"。 - 输出二次压缩(默认开启)——
tools/post-execute阶段把结果文本喂给rtk pipe(rtk 1.2.0 的 stdin 过滤器),只在确实变短时替换。给改写层没覆盖到的命令留第二次机会。
工作原理
模型调用 ops_bash("git status")
│
▼
tools/pre-execute(透明改写层)
├─ 目标工具?名单内、未被排除、不是插件自有工具
├─ 命令合规?非空、未超长、非 RTK_DISABLED=1、非已是 rtk 形式
▼
rtk rewrite "git status" ← 改写判断 100% 委托上游 registry
│
├── exit 0/3 + 有改写 → 执行 "rtk git status"
├── exit 1 → 原命令执行
├── exit 2 + 消息 → 用该消息阻止执行(pipeline 规则 deny)
└── 超时/崩溃/异常 → 原命令执行(绝不阻塞)
退出码契约(与 RTK 上游 hooks/README.md 一致):
rtk rewrite 退出码 | 含义 | 插件行为 |
|---|---|---|
| 0 | 有改写,allow 规则全覆盖 | 使用改写结果 |
| 1 | 无改写 | 原样执行 |
| 2 | deny 命中 | 有消息 → 用消息阻止;无消息 → 交宿主权限体系 |
| 3 | 有改写,但命中 ask 或 allow 未全覆盖 | askPolicy: allow(默认)用改写,宿主审批照常;block 放弃改写 |
两条硬保证:
- 绝不阻塞——
rtk缺席、探针超时(默认 4s)、输出为空、任何异常,一律回退原命令。 - 判断交给上游——复合命令(
&&、管道)、exclude_commands、RTK_DISABLED=1、already-rtk 透传,全部由 rtk registry 处理,插件本地零等价逻辑。
安装
前置:Node 18+、DSH 可用、rtk 在 PATH(rtk --version 能打出 1.2.0 或更高)。
本插件是标准 DSH bundle,装进任意 profile,profile 名不限:
- 在 profile 的
package.json里加依赖和 bundle:
{
"name": "dsh-profile-my",
"private": true,
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@xiao-hans/dsh-rtk"]
}
},
"dependencies": {
"@xiao-hans/dsh-rtk": "^0.4.4"
}
}
- 安装并启动:
dsh plugin --profile my install # 参数原样转发给 profile 目录里的 pnpm
dsh --profile my
插件零运行时依赖,pnpm 直接克隆对应 tag,任何机器都能装。 装完或升级后要重启 host:DSH 只在启动时 import 插件模块,运行中不会热重载 profile 的
node_modules。
配置
在 bundle 配置里传入(cordis.patch.yml 的 config,或 profile 配置的同名字段),全部可选:
- insert:
- id: dsh-rtk
name: '@xiao-hans/dsh-rtk'
config:
mode: auto
awareness: auto
| 键 | 类型 | 默认 | 说明 |
|---|---|---|---|
mode | 'auto' | 'rewrite' | 'off' | 'auto' | off 关闭透明改写(rtk/rtk_stats 工具与提示词保留);rewrite 预留给纯改写场景 |
shellTools | string[] | [] | 追加要改写的工具名(自定义 shell 工具用) |
excludeTools | string[] | [] | 从改写目标中排除的工具名 |
timeoutMs | number | 4000 | rtk rewrite 探针超时,超时按"不改写"放行 |
maxCommandChars | number | 16384 | 超过该长度的命令直接放行,不探针 |
askPolicy | 'allow' | 'block' | 'allow' | exit 3 时用改写(allow)还是放弃改写(block) |
awareness | 'auto' | 'builtin' | 'off' | 'auto' | auto 优先注入官方 RTK.md,缺失时用内置文本;builtin 不读磁盘;off 不注册提示段 |
guidanceFile | string | '' | 指定使用某个 RTK.md,优先于自动探测 |
awarenessMaxChars | number | 4000 | 注入提示段的最大字符数,超长截断 |
semanticGuard | boolean | true | 校验 rtk rewrite 提案是否仍在跑原命令;发现丢参数/新增控制操作符就退回原命令。仅在复现上游行为时关闭 |
dbRedirect | boolean | false | 改写后的命令是否加 RTK_DB_PATH 前缀(POSIX shell 风格;rtk/rtk_stats 工具始终用 PowerShell 风格) |
annotateRewrites | boolean | true | 在足够大的结果前加 [rtk] ran: / [rtk] fell back: 一行 |
annotateMinBytes | number | 500 | 注解阈值,按压缩前长度判断 |
compress | boolean | true | 是否用 rtk pipe 对结果做二次压缩;关闭后只保留注解 |
compressTools | string[] | ['*'] | 允许压缩的工具名,'*' 表示全部 |
compressExcludeTools | string[] | [] | 不压缩的工具名 |
compressFileTools | boolean | false | 是否也压缩文件读取类工具的结果(按 Read 形状构造) |
compressThresholdBytes | number | 2048 | 压缩阈值;git/cargo/docker/pytest/npm 等密集输出族降到 512 |
compressMaxBytes | number | 2097152 | 超过该长度的结果不送压缩,避免白拷 2 MB 以上文本 |
compressTimeoutMs | number | 4000 | rtk pipe 超时,超时保留原文 |
工具与用法
| 模型调用 | 实际执行 | 说明 |
|---|---|---|
ops_bash("git status") | rtk git status | 透明改写,token 大头 |
ops_bash("grep -rn TODO src/") | rtk grep -rn TODO src/ | 透明改写 |
ops_bash("cat package.json | head -50") | 原样 | registry 判定管道不改写 |
rtk("git log --oneline -3") | rtk git log --oneline -3 | 显式走 RTK |
rtk("bypass git push") | rtk bypass git push | 跳过 pipeline 规则 |
rtk_stats("gain") | rtk gain --format text | 省 token 总览 |
rtk_stats("gain", format: "json") | rtk gain --format json | 机器可读的统计 |
rtk_stats("discover", project: false) | rtk discover --format text --all | 找出本可压缩的历史命令 |
ops_bash("RTK_DISABLED=1 git status") | 原样 | 单命令逃生门 |
与其它工具插件共存
- 不重注册、不遮蔽任何
ops_*工具——dsh-ops 的工具定义、命名、作用域原样保留。 - 只挂公共扩展点:
tools/pre-execute、agent/created和 system prompt section。 - 自有工具名
rtk、rtk_stats独占,与现有工具无交集。 - rtk 二进制缺席时整体退化为 no-op:命令原样执行,行为与未安装插件完全一致。
平台与退出码
- 不假设平台:命令与输出按宿主提供的字符串原样传递,插件不做编码转换、不解析 shell 语法。
- 探针优先走 subprocess seam(argv 直接 spawn,不经 shell):退出码保真,也没有引号与转义问题。
- 某些 shell 服务会把非零退出码归一化(例如 PowerShell 的
-Command把所有非零值变成 1)。只走 shell 的话,exit 3(ask)会被误读成"不改写",exit 2(deny)会 fail-open。因此 shell seam 只作兜底。 - 语义校验(
lib/verify.js):rtk rewrite是唯一决策源,但它对少数命令会改写前导词(next build→rtk next、python -m pytest→rtk pytest、gradle build→rtk gradlew build)。校验器把这些映射列为白名单,其余一律要求"去掉插入的rtk后与原命令逐 token 相同",并拒绝新增/删除控制操作符与超长提案。 - 目标工具集 = 内置名单(
ops_bash、pwsh、bash、sh、zsh、shell、terminal、run_command、execute_command)∪shellTools−excludeTools− 插件自有工具。
故障排查
| 现象 | 处理 |
|---|---|
| 命令没有被改写 | rtk rewrite "<命令>" 看是否返回改写、退出码是几;exit 1 表示上游本就不改写 |
rtk 找不到 | 确认在 PATH:rtk --version;缺席时插件按设计静默放行 |
| 统计为空 | rtk gain 直接跑一次;需要隔离数据库时设 RTK_DB_PATH 或开 dbRedirect |
| 临时要看全量输出 | RTK_DISABLED=1 <命令> |
| 永久排除某类命令 | ~/.config/rtk/config.toml 的 exclude_commands(支持前缀与 ^regex) |
开发与测试
npm test # 46 个单元/集成/schema 测试,零依赖
npm run verify:live # 真实 rtk 的端到端验证(需 rtk 在 PATH)
测试覆盖:退出码契约(0/1/2/3)、超时降级、env 擦除(RTK_REWRITE_HOST)、结果缓存、双模目标集、agent 作用域 pwsh 按需注册、工具 schema 符合 DSH raw JSON-Schema 子集、awareness 加载与措辞适配、rtk_stats 参数映射。
verify:live 另证明两件事:rtk rewrite 经 subprocess seam 保真 exit 3,而 pwsh shell seam 会把它坍缩成 1,所以改写层必须走 subprocess;git status 改写后输出确为 RTK 压缩格式。
版本
- v0.4.2:
tools/post-execute增加改写可见性([rtk] ran:/[rtk] fell back:)与rtk pipe二次压缩;rtk hook claude在 1.2.0 是 PreToolUse 专用(只返回updatedInput,喂 PostToolUse 载荷得到空输出),故压缩走rtk pipe。 - v0.4.1:新增
semanticGuard(lib/verify.js)——改写提案必须仍执行原命令,拦截 rtk 丢参数的映射;可配置关闭。 - v0.4.0:注入官方
RTK.md(awareness: auto/builtin/off、guidanceFile、awarenessMaxChars);新增只读rtk_stats(gain/discover/session,支持 JSON);工具输出 schema 抽成共享模块。 - v0.3.0:双模透明改写层(
tools/pre-execute)、上游退出码契约、动态 shell 工具目标集、删除本地 Windows 兼容映射、pwsh仅按需注册、rtk rewrite经 subprocess seam 直起。 - v0.2.0:agent 作用域遮蔽修复、服务注入重构、管道/PowerShell 开关保护、
RTK_DB_PATH沙箱回退。
许可证
Related plugins
archify (deepseek-harness)
tt-a1i/archify
WeKnora (dsh-weknora)
tencent/weknora
next-ai-draw-io (dsh-plugin)
dayuanjiang/next-ai-draw-io
weknora
tencent/weknora