Skip to main content
X

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-rtk

README

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。

它做什么

五件事,可以分别关闭:

  1. 透明改写(默认开启)——在 DSH 的 tools/pre-execute 阶段拦截目标 shell 工具,把 exec.arguments 里的命令换成一个新的 exec.arguments 引用。改写前有一道语义校验:提案必须仍在跑原命令(允许 rtk 已知的无损映射,如 next build → rtk next),否则退回原命令。模型无感,工具定义不变,会话日志保留原命令。
  2. awareness 提示——往系统提示里加一段说明:输出已是完整结果、不要为了"看全量"重跑、逃生门有哪些。若本机存在 rtk init 写下的 RTK.md,直接注入官方原文(仅把 Claude Code 专有措辞换成 DSH 说法);找不到就用内置短文。
  3. 两个显式工具——rtk 执行任意命令并走 RTK;rtk_stats 只读地报告省了多少 token(gain / discover / session)。
  4. 改写可见性(默认开启)——结果够大时在开头加一行 [rtk] ran: rtk git status;语义校验拦下提案时写 [rtk] fell back: <原命令>。模型能分辨"这是压缩输出"与"这是原始输出"。
  5. 输出二次压缩(默认开启)——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无改写原样执行
2deny 命中有消息 → 用消息阻止;无消息 → 交宿主权限体系
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 名不限:

  1. 在 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"
  }
}
  1. 安装并启动:
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 预留给纯改写场景
shellToolsstring[][]追加要改写的工具名(自定义 shell 工具用)
excludeToolsstring[][]从改写目标中排除的工具名
timeoutMsnumber4000rtk rewrite 探针超时,超时按"不改写"放行
maxCommandCharsnumber16384超过该长度的命令直接放行,不探针
askPolicy'allow' | 'block''allow'exit 3 时用改写(allow)还是放弃改写(block)
awareness'auto' | 'builtin' | 'off''auto'auto 优先注入官方 RTK.md,缺失时用内置文本;builtin 不读磁盘;off 不注册提示段
guidanceFilestring''指定使用某个 RTK.md,优先于自动探测
awarenessMaxCharsnumber4000注入提示段的最大字符数,超长截断
semanticGuardbooleantrue校验 rtk rewrite 提案是否仍在跑原命令;发现丢参数/新增控制操作符就退回原命令。仅在复现上游行为时关闭
dbRedirectbooleanfalse改写后的命令是否加 RTK_DB_PATH 前缀(POSIX shell 风格;rtk/rtk_stats 工具始终用 PowerShell 风格)
annotateRewritesbooleantrue在足够大的结果前加 [rtk] ran: / [rtk] fell back: 一行
annotateMinBytesnumber500注解阈值,按压缩前长度判断
compressbooleantrue是否用 rtk pipe 对结果做二次压缩;关闭后只保留注解
compressToolsstring[]['*']允许压缩的工具名,'*' 表示全部
compressExcludeToolsstring[][]不压缩的工具名
compressFileToolsbooleanfalse是否也压缩文件读取类工具的结果(按 Read 形状构造)
compressThresholdBytesnumber2048压缩阈值;git/cargo/docker/pytest/npm 等密集输出族降到 512
compressMaxBytesnumber2097152超过该长度的结果不送压缩,避免白拷 2 MB 以上文本
compressTimeoutMsnumber4000rtk 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 沙箱回退。

许可证

MIT

Related plugins