@wingsbutterfly/dsh-rtk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/README.zh.md +211 -0
  5. package/THIRD_PARTY_NOTICES.md +51 -0
  6. package/docs/assets/how-it-works.svg +47 -0
  7. package/docs/verification.md +279 -0
  8. package/lib/command.d.ts +41 -0
  9. package/lib/command.d.ts.map +1 -0
  10. package/lib/command.js +137 -0
  11. package/lib/command.js.map +1 -0
  12. package/lib/compact/build.d.ts +15 -0
  13. package/lib/compact/build.d.ts.map +1 -0
  14. package/lib/compact/build.js +140 -0
  15. package/lib/compact/build.js.map +1 -0
  16. package/lib/compact/detect.d.ts +22 -0
  17. package/lib/compact/detect.d.ts.map +1 -0
  18. package/lib/compact/detect.js +54 -0
  19. package/lib/compact/detect.js.map +1 -0
  20. package/lib/compact/dsh-result.d.ts +49 -0
  21. package/lib/compact/dsh-result.d.ts.map +1 -0
  22. package/lib/compact/dsh-result.js +84 -0
  23. package/lib/compact/dsh-result.js.map +1 -0
  24. package/lib/compact/git.d.ts +21 -0
  25. package/lib/compact/git.d.ts.map +1 -0
  26. package/lib/compact/git.js +197 -0
  27. package/lib/compact/git.js.map +1 -0
  28. package/lib/compact/index.d.ts +39 -0
  29. package/lib/compact/index.d.ts.map +1 -0
  30. package/lib/compact/index.js +236 -0
  31. package/lib/compact/index.js.map +1 -0
  32. package/lib/compact/linter.d.ts +12 -0
  33. package/lib/compact/linter.d.ts.map +1 -0
  34. package/lib/compact/linter.js +118 -0
  35. package/lib/compact/linter.js.map +1 -0
  36. package/lib/compact/search.d.ts +16 -0
  37. package/lib/compact/search.d.ts.map +1 -0
  38. package/lib/compact/search.js +67 -0
  39. package/lib/compact/search.js.map +1 -0
  40. package/lib/compact/source.d.ts +22 -0
  41. package/lib/compact/source.d.ts.map +1 -0
  42. package/lib/compact/source.js +224 -0
  43. package/lib/compact/source.js.map +1 -0
  44. package/lib/compact/test-output.d.ts +12 -0
  45. package/lib/compact/test-output.d.ts.map +1 -0
  46. package/lib/compact/test-output.js +168 -0
  47. package/lib/compact/test-output.js.map +1 -0
  48. package/lib/compact/text.d.ts +22 -0
  49. package/lib/compact/text.d.ts.map +1 -0
  50. package/lib/compact/text.js +87 -0
  51. package/lib/compact/text.js.map +1 -0
  52. package/lib/config.d.ts +243 -0
  53. package/lib/config.d.ts.map +1 -0
  54. package/lib/config.js +183 -0
  55. package/lib/config.js.map +1 -0
  56. package/lib/index.d.ts +46 -0
  57. package/lib/index.d.ts.map +1 -0
  58. package/lib/index.js +405 -0
  59. package/lib/index.js.map +1 -0
  60. package/lib/metrics.d.ts +35 -0
  61. package/lib/metrics.d.ts.map +1 -0
  62. package/lib/metrics.js +51 -0
  63. package/lib/metrics.js.map +1 -0
  64. package/lib/rtk-executable.d.ts +51 -0
  65. package/lib/rtk-executable.d.ts.map +1 -0
  66. package/lib/rtk-executable.js +75 -0
  67. package/lib/rtk-executable.js.map +1 -0
  68. package/lib/rtk-rewrite.d.ts +88 -0
  69. package/lib/rtk-rewrite.d.ts.map +1 -0
  70. package/lib/rtk-rewrite.js +150 -0
  71. package/lib/rtk-rewrite.js.map +1 -0
  72. package/lib/runtime-guard.d.ts +31 -0
  73. package/lib/runtime-guard.d.ts.map +1 -0
  74. package/lib/runtime-guard.js +32 -0
  75. package/lib/runtime-guard.js.map +1 -0
  76. package/package.json +82 -0
  77. package/scripts/link-dsh.mjs +135 -0
  78. package/src/command.ts +174 -0
  79. package/src/compact/build.ts +154 -0
  80. package/src/compact/detect.ts +54 -0
  81. package/src/compact/dsh-result.ts +99 -0
  82. package/src/compact/git.ts +209 -0
  83. package/src/compact/index.ts +284 -0
  84. package/src/compact/linter.ts +126 -0
  85. package/src/compact/search.ts +73 -0
  86. package/src/compact/source.ts +244 -0
  87. package/src/compact/test-output.ts +184 -0
  88. package/src/compact/text.ts +86 -0
  89. package/src/config.ts +263 -0
  90. package/src/index.ts +431 -0
  91. package/src/metrics.ts +84 -0
  92. package/src/rtk-executable.ts +117 -0
  93. package/src/rtk-rewrite.ts +179 -0
  94. package/src/runtime-guard.ts +44 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ Initial release — a port of `pi-rtk-optimizer` to the DeepSeek Harness plugin
6
+ model.
7
+
8
+ ### Command rewriting
9
+
10
+ - Rewrites `bash`/`pwsh` commands to their rtk equivalents by delegating to
11
+ `rtk rewrite`; rtk remains the only source of rewrite rules.
12
+ - Honors rtk's exit-code contract: `0`/`3` carry a rewrite on stdout, `1` means
13
+ no equivalent, `2` means rtk refused and stderr explains why.
14
+ - `rewrite` and `suggest` modes, plus a runtime guard that runs the original
15
+ command whenever rtk cannot be proven available.
16
+ - Scopes an isolated `RTK_DB_PATH` onto rewritten commands so rtk's usage
17
+ history does not reach the working tree.
18
+ - Restores the original arguments as soon as dispatch returns, so the session
19
+ log and later pipeline stages keep seeing the call the model made.
20
+
21
+ ### Output compaction
22
+
23
+ - Multi-stage pipeline over `bash`, `read`, and `grep` results: ANSI stripping,
24
+ build filtering, test aggregation, git compaction, linter aggregation, search
25
+ grouping, source filtering, smart truncation, and hard truncation.
26
+ - Preserves the harness result contract: `[exit code: N]`, `[stderr]`,
27
+ `[timed out after …]`, `[sandbox: …]`, and truncation markers are lifted out
28
+ before the body is rewritten and restored verbatim afterwards.
29
+ - Never inflates a result — a technique that would not shrink the text is
30
+ discarded and reported as no change.
31
+ - Lossy `read` compaction and source filtering are off by default, and every
32
+ numeric bound is clamped on load.
33
+
34
+ ### Configuration and commands
35
+
36
+ - Full configuration surface mirroring `pi-rtk-optimizer`'s documented
37
+ defaults, registered as the `dsh-rtk` settings namespace with the
38
+ composition's `config:` block as its base layer.
39
+ - `/rtk`, `/rtk show`, `/rtk path`, `/rtk verify`, `/rtk stats`,
40
+ `/rtk clear-stats`, `/rtk reset`, and `/rtk help`.
41
+ - Session savings metrics by tool and by technique.
42
+
43
+ ### Notes on the port
44
+
45
+ - Streamed bash output is not sanitized: the harness has no equivalent of Pi's
46
+ `tool_execution_*` hooks.
47
+ - The Windows-specific shell fixups and the hashline/anchor-safe read handling
48
+ are not ported; the target deployment is POSIX with the harness's own `read`
49
+ format.
50
+ - A host-plane row and a preset row both want the same process-global settings
51
+ namespace. The second instance to mount now follows the first through the
52
+ settings event instead of failing the mount.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wings1848
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # dsh-rtk
2
+
3
+ [![CI](https://github.com/wings1848/dsh-rtk/actions/workflows/ci.yml/badge.svg)](https://github.com/wings1848/dsh-rtk/actions/workflows/ci.yml)
4
+ [![CodeQL](https://github.com/wings1848/dsh-rtk/actions/workflows/codeql.yml/badge.svg)](https://github.com/wings1848/dsh-rtk/actions/workflows/codeql.yml)
5
+ [![Secrets](https://github.com/wings1848/dsh-rtk/actions/workflows/secrets.yml/badge.svg)](https://github.com/wings1848/dsh-rtk/actions/workflows/secrets.yml)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/wings1848/dsh-rtk/blob/main/LICENSE)
7
+ [![Node](https://img.shields.io/badge/node-%3E%3D22.18.0-brightgreen.svg)](https://nodejs.org)
8
+ [![中文文档](https://img.shields.io/badge/README-%E4%B8%AD%E6%96%87-blue)](https://github.com/wings1848/dsh-rtk/blob/main/README.zh.md)
9
+
10
+ RTK command rewriting and tool-output compaction for the [DeepSeek Harness](https://github.com/deepseek-ai/dsh).
11
+
12
+ `dsh-rtk` rewrites `bash` commands to their [rtk](https://github.com/rtk-ai/rtk) equivalents before they run, and compacts noisy tool output before it reaches the model. It is a port of [`pi-rtk-optimizer`](https://github.com/MasuRii/pi-rtk-optimizer) to the harness's plugin model.
13
+
14
+ ```
15
+ model asks: git status
16
+ runs: rtk git status
17
+ model sees: Branch: main
18
+ Modified: 2 files
19
+ src/a.ts
20
+ ```
21
+
22
+ ![How dsh-rtk works](https://raw.githubusercontent.com/wings1848/dsh-rtk/main/docs/assets/how-it-works.svg)
23
+
24
+ ## Features
25
+
26
+ ### Command rewriting
27
+
28
+ - Every command with an rtk equivalent is rewritten before dispatch; commands rtk does not support run untouched.
29
+ - **rtk stays the source of truth.** The plugin carries no rewrite table of its own — it asks the installed binary via `rtk rewrite`, so support tracks whatever rtk version is installed.
30
+ - **`rewrite` and `suggest` modes.** `rewrite` replaces the command; `suggest` runs it unchanged and reports the equivalent it would have used.
31
+ - **Runtime guard.** With `guardWhenRtkMissing` on (the default), a call that cannot prove rtk is available runs the original command. A missing optimizer never blocks work.
32
+ - Rewritten commands get an isolated `RTK_DB_PATH`, so rtk's usage history does not land in the working tree.
33
+
34
+ ### Output compaction
35
+
36
+ A multi-stage pipeline over `bash`, `read`, and `grep` results:
37
+
38
+ | Stage | What it does |
39
+ |---|---|
40
+ | ANSI stripping | Removes terminal color and formatting sequences |
41
+ | Build filtering | Extracts compiler errors and warnings, drops progress chatter |
42
+ | Test aggregation | Collapses runner output to pass/fail/skip plus failure excerpts |
43
+ | Git compaction | Summarizes `git status`, `git diff`, and `git log` |
44
+ | Linter aggregation | Counts issues and ranks them by rule and file |
45
+ | Search grouping | Groups `grep`-style matches by file |
46
+ | Source filtering | `none`, `minimal`, or `aggressive` comment/whitespace removal (off by default) |
47
+ | Smart truncation | Keeps signatures and imports when dropping lines (off by default) |
48
+ | Hard truncation | Final character budget |
49
+
50
+ Two properties are load-bearing:
51
+
52
+ - **The harness contract survives.** A `bash` result ends with status markers — `[exit code: N]`, `[stderr]`, `[timed out after …]`, `[sandbox: …]`. Compaction lifts those out before rewriting the body and puts them back afterwards; the model is told to check `[exit code: N]` on every call, and the Web UI parses that same line for its exit-status pill.
53
+ - **Compaction never inflates a result.** If a technique would not actually shrink the text, the original is kept and nothing is reported.
54
+
55
+ ### Oversized output belongs to the harness
56
+
57
+ When `dsh-spill-policy` is mounted, it writes a full result to disk and keeps a head/tail preview inline — that truncation is **recoverable**. This plugin's own hard truncation is not, and it runs first: at its 12 000-character default it fires far below spill's 50 000-byte threshold, so spill never sees a large result at all and the file that would have let a reader see the rest is never written.
58
+
59
+ With `deferToHarnessSpill` on (the default) the plugin turns its own truncation off whenever the spill service is present, so a runaway command's output stays readable. Set it to `false` to keep the plugin's bound regardless.
60
+
61
+
62
+ ### Session metrics
63
+
64
+ `/rtk stats` reports how many characters compaction saved, per tool and per technique.
65
+
66
+ ## Install
67
+
68
+ ### 1. Install the package
69
+
70
+ ```bash
71
+ dsh plugin --profile web add @wingsbutterfly/dsh-rtk
72
+ ```
73
+
74
+ Working from a checkout instead? Link it where the composition resolves bare
75
+ specifiers:
76
+
77
+ ```bash
78
+ mkdir -p ~/.dsh/node_modules/@wingsbutterfly
79
+ ln -s /path/to/dsh-rtk ~/.dsh/node_modules/@wingsbutterfly/dsh-rtk
80
+ ```
81
+
82
+ ### 2. Add one row to an agent preset
83
+
84
+ `dsh-rtk` is an **agent-plane** row: it registers listeners into the tool pipeline and contributes no service, so it needs no `isolate` realm. Add it to the preset whose sessions should be optimized:
85
+
86
+ ```yaml
87
+ - id: rtk
88
+ name: '@wingsbutterfly/dsh-rtk'
89
+ config:
90
+ enabled: true
91
+ mode: rewrite
92
+ ```
93
+
94
+ To try it without touching an existing preset, copy one and add the row:
95
+
96
+ ```
97
+ # ask a cordis-preset agent to run this
98
+ agentPresets.copy('standard', 'rtk', 'RTK 优化')
99
+ ```
100
+
101
+ The row works at the host plane too (add it to `~/.dsh/cordis.patch.yml` instead), which covers every session in the deployment — but that needs a host restart, and a host row plus a preset row would both want the same process-global settings namespace. Pick one plane.
102
+
103
+ ## Configuration
104
+
105
+ Every field is optional; defaults are shown.
106
+
107
+ ```yaml
108
+ - id: rtk
109
+ name: '@wingsbutterfly/dsh-rtk'
110
+ config:
111
+ enabled: true # master switch
112
+ mode: rewrite # rewrite | suggest
113
+ guardWhenRtkMissing: true # run the original when rtk is unavailable
114
+ showRewriteNotifications: false # append a one-line rewrite note to the result
115
+ rtkExecutable: rtk # name or absolute path
116
+ rewriteTimeoutMs: 3000 # deadline for one `rtk rewrite` call
117
+ compactedTools: [bash, read, grep]
118
+ outputCompaction:
119
+ enabled: true
120
+ stripAnsi: true
121
+ readCompaction:
122
+ enabled: false # lossy read compaction; off so code reads stay exact
123
+ sourceCodeFilteringEnabled: false
124
+ preserveExactSkillReads: false
125
+ sourceCodeFiltering: none # none | minimal | aggressive
126
+ aggregateTestOutput: true
127
+ filterBuildOutput: true
128
+ compactGitOutput: true
129
+ aggregateLinterOutput: true
130
+ groupSearchOutput: true
131
+ trackSavings: true
132
+ deferToHarnessSpill: true # let the harness spill policy own oversized output
133
+ smartTruncate:
134
+ enabled: false
135
+ maxLines: 220 # 40–4000
136
+ truncate:
137
+ enabled: true
138
+ maxChars: 12000 # 1000–200000
139
+ ```
140
+
141
+ The configuration is also registered as the `dsh-rtk` namespace in the harness settings document, so it can be edited there and takes effect without a restart. A composition `config:` block supplies the `base` layer; the settings document supplies the user layer on top.
142
+
143
+ > **Why `readCompaction` is off by default.** Filtering or truncating a `read` result can leave the model editing against text that no longer matches the file. Everything on by default is lossless for the body text it summarizes, or only fires on outputs whose whole shape is being replaced.
144
+
145
+ ## Commands
146
+
147
+ | Command | Description |
148
+ |---|---|
149
+ | `/rtk` | Show configuration and runtime status |
150
+ | `/rtk show` | Same as `/rtk` |
151
+ | `/rtk path` | Where the configuration is stored |
152
+ | `/rtk verify` | Check whether the rtk executable is usable |
153
+ | `/rtk stats` | Compaction savings for this session |
154
+ | `/rtk clear-stats` | Reset the savings counters |
155
+ | `/rtk reset` | Restore configuration defaults |
156
+ | `/rtk help` | Usage text |
157
+
158
+ ## How it works
159
+
160
+ Both halves ride the tool pipeline rather than wrapping a tool, because the harness offers exactly two seams that fit:
161
+
162
+ - **Rewriting rides `tools/execute`.** `tools/pre-execute` deliberately may not mutate arguments — they are logged and presented before dispatch, so a rewrite there would desync the recorded call from the one that ran. The around-dispatch stage is the only place the executing command may differ, and `dsh-rtk` scopes the change to a single call: the original arguments are restored as soon as dispatch returns, so later pipeline stages and the session log still see what the model asked for.
163
+
164
+ - **Compaction rides `tools/post-execute`**, the stage that may replace result content.
165
+
166
+ Both listeners register in the scope the row was mounted into. A preset row therefore covers exactly its own agent (the preset's standing scope is an ancestor of every session that joins it), and a host row covers every agent in the process.
167
+
168
+ Only the `tools` service is a hard dependency. `settings`, `commands`, and `systemPrompt` are resolved with `ctx.get`, so a composition that omits any of them still gets the optimization.
169
+
170
+ ### Caveat: the rewriting seam is not a sanctioned extension point
171
+
172
+ The harness documents `tools/execute` wrappers as wrappers that "may change only `exec.signal`". Replacing `arguments` is **not a supported extension point** — it works because the execution object is not frozen until its result is notified, and the failed alternative is worse: `tools/pre-execute` deliberately may not rewrite input (arguments are logged and presented before dispatch), and a tool cannot be re-registered over one in the same layer.
173
+
174
+ `dsh-rtk` takes that seam knowingly, and pays for it where it can: the replacement is scoped to a single call and restored in `finally`, and every failure path falls back to the command the model sent. If a future harness version freezes the execution object earlier, rewriting goes quiet rather than breaking — commands run unrewritten and compaction is unaffected, because `tools/post-execute` *is* a documented content-replacement stage.
175
+
176
+ ## Differences from pi-rtk-optimizer
177
+
178
+ | pi-rtk-optimizer | dsh-rtk |
179
+ |---|---|
180
+ | Rewrites `event.input.command` in the `tool_call` hook | Replaces `exec.arguments` for the duration of one dispatch, then restores it |
181
+ | Compacts in the `tool_result` hook | Compacts in the `tools/post-execute` waterfall |
182
+ | `/rtk` opens a TUI settings modal | `/rtk` prints status text; configuration lives in the harness settings document |
183
+ | Config is a JSON file it owns | Config is a harness settings namespace layered over the composition's `config:` block |
184
+ | Streams are sanitized through `tool_execution_*` hooks | No equivalent hook exists, so streamed output is not sanitized |
185
+ | Windows-specific shell fixups | Not ported; the target deployment is POSIX |
186
+
187
+ Everything else — the rewrite delegation to `rtk rewrite`, the exit-code contract, the guard when rtk is missing, the documented default for every compaction switch, and each technique's algorithm — is ported as-is.
188
+
189
+ ## Development
190
+
191
+ ```bash
192
+ pnpm install # or link the peer packages manually
193
+ pnpm run typecheck # tsc --noEmit against the real harness types
194
+ pnpm test # builds first, then runs node --test test/
195
+ pnpm run build # emit lib/
196
+ pnpm run check # typecheck + test
197
+ ```
198
+
199
+ The tests import `lib/`, so `pnpm test` builds before it runs; a `src/` edit that is never built would otherwise pass the suite unnoticed.
200
+
201
+ The port's acceptance evidence, including the eight defects later reviews and live testing surfaced, is in [https://github.com/wings1848/dsh-rtk/blob/main/docs/verification.md](https://github.com/wings1848/dsh-rtk/blob/main/docs/verification.md).
202
+
203
+ `test/integration.test.ts` drives the real `apply()` through a stand-in context and calls the real `rtk` binary, so it fails on a machine without rtk installed — that is intentional: the rewrite path is the feature.
204
+
205
+ ## Star history
206
+
207
+ <picture>
208
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wings1848/dsh-rtk/output/star-history-dark.svg">
209
+ <img alt="Star history chart" src="https://raw.githubusercontent.com/wings1848/dsh-rtk/output/star-history-light.svg">
210
+ </picture>
211
+
212
+ ## License
213
+
214
+ MIT
package/README.zh.md ADDED
@@ -0,0 +1,211 @@
1
+ # dsh-rtk
2
+
3
+ [![CI](https://github.com/wings1848/dsh-rtk/actions/workflows/ci.yml/badge.svg)](https://github.com/wings1848/dsh-rtk/actions/workflows/ci.yml)
4
+ [![CodeQL](https://github.com/wings1848/dsh-rtk/actions/workflows/codeql.yml/badge.svg)](https://github.com/wings1848/dsh-rtk/actions/workflows/codeql.yml)
5
+ [![Secrets](https://github.com/wings1848/dsh-rtk/actions/workflows/secrets.yml/badge.svg)](https://github.com/wings1848/dsh-rtk/actions/workflows/secrets.yml)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/wings1848/dsh-rtk/blob/main/LICENSE)
7
+ [![Node](https://img.shields.io/badge/node-%3E%3D22.18.0-brightgreen.svg)](https://nodejs.org)
8
+ [![English](https://img.shields.io/badge/README-English-blue)](https://github.com/wings1848/dsh-rtk/blob/main/README.md)
9
+
10
+ 给 [DeepSeek Harness](https://github.com/deepseek-ai/dsh) 用的 RTK 命令重写 + 工具输出压缩插件。
11
+
12
+ `dsh-rtk` 会在 `bash` 命令执行前把它改写成等价的 [rtk](https://github.com/rtk-ai/rtk) 命令,并在工具输出进入模型上下文前压缩它。它是 [`pi-rtk-optimizer`](https://github.com/MasuRii/pi-rtk-optimizer) 在 harness 插件模型上的移植。
13
+
14
+ ```
15
+ 模型发出: git status
16
+ 实际执行: rtk git status
17
+ 模型看到: Branch: main
18
+ Modified: 2 files
19
+ src/a.ts
20
+ ```
21
+
22
+ ![How dsh-rtk works](https://raw.githubusercontent.com/wings1848/dsh-rtk/main/docs/assets/how-it-works.svg)
23
+
24
+ ## 功能
25
+
26
+ ### 命令重写
27
+
28
+ - 有 rtk 等价命令的调用会在派发前被改写;rtk 不支持的命令原样执行。
29
+ - **rtk 始终是唯一事实来源。** 插件不带自己的重写表,而是通过 `rtk rewrite` 询问已安装的二进制,所以支持范围跟随你装的 rtk 版本。
30
+ - **`rewrite` 与 `suggest` 两种模式。** `rewrite` 替换命令;`suggest` 原样执行并报告本可使用的等价命令。
31
+ - **运行时守卫。** `guardWhenRtkMissing`(默认开)下,无法确认 rtk 可用的调用会执行原命令——缺少优化器永远不会挡住工作。
32
+ - 被改写的命令会带上隔离的 `RTK_DB_PATH`,rtk 的使用历史不会落进工作目录。
33
+
34
+ ### 输出压缩
35
+
36
+ 针对 `bash`、`read`、`grep` 结果的多阶段管线:
37
+
38
+ | 阶段 | 作用 |
39
+ |---|---|
40
+ | ANSI 剥离 | 去掉终端颜色与格式转义序列 |
41
+ | 构建过滤 | 提取编译错误与警告,丢弃进度噪音 |
42
+ | 测试聚合 | 折叠为 通过/失败/跳过 计数加失败摘录 |
43
+ | Git 压缩 | 归纳 `git status`、`git diff`、`git log` |
44
+ | Linter 聚合 | 统计问题数并按规则与文件排名 |
45
+ | 搜索分组 | 按文件归组 `grep` 风格匹配 |
46
+ | 源码过滤 | `none` / `minimal` / `aggressive` 注释与空白处理(默认关) |
47
+ | 智能截断 | 丢行时保留签名与 import(默认关) |
48
+ | 硬截断 | 最终字符预算 |
49
+
50
+ 有两条性质是**硬性不变量**:
51
+
52
+ - **harness 的结果契约必须存活。** `bash` 结果以状态标记结尾——`[exit code: N]`、`[stderr]`、`[timed out after …]`、`[sandbox: …]`。压缩会先把这些标记摘出来,重写正文后再原样接回;模型被要求在每次调用后检查 `[exit code: N]`,Web UI 也解析同一行来画退出状态。
53
+
54
+ - **压缩绝不把结果变大。** 如果某个技术不能真正缩短文本,就保留原文并且不上报。
55
+
56
+ ### 超大输出归 harness 管
57
+
58
+ `dsh-spill-policy` 挂载时,它会把完整结果落盘、上下文里只留头尾预览——这种截断是**可恢复的**。而本插件的硬截断不可恢复,且**跑在前面**:默认 12000 字符的阈值远低于 spill 的 50000 字节阈值,于是 spill 根本看不到大结果,那个本可以让读者看到剩余内容的落盘文件也就永远不会产生。
59
+
60
+ `deferToHarnessSpill` 开启时(默认),只要 spill 服务在场,插件就关掉自己的截断,让失控命令的输出保持可读。设为 `false` 则无论 spill 是否挂载都保留插件自己的上限。
61
+
62
+
63
+ ### 会话统计
64
+
65
+ `/rtk stats` 报告本次会话省下多少字符,按工具与按技术两个维度。
66
+
67
+ ## 安装
68
+
69
+ ### 1. 安装包
70
+
71
+ ```bash
72
+ dsh plugin --profile web add @wingsbutterfly/dsh-rtk
73
+ ```
74
+
75
+ 想从本地检出直接用?软链到组合能解析裸模块名的位置:
76
+
77
+ ```bash
78
+ mkdir -p ~/.dsh/node_modules/@wingsbutterfly
79
+ ln -s /path/to/dsh-rtk ~/.dsh/node_modules/@wingsbutterfly/dsh-rtk
80
+ ```
81
+
82
+ ### 2. 给一个 agent preset 加一行
83
+
84
+ `dsh-rtk` 是 **agent 平面**的行:它只往工具管线里注册监听器、不发布任何服务,所以不需要 `isolate` realm。把它加到你希望被优化的 preset:
85
+
86
+ ```yaml
87
+ - id: rtk
88
+ name: '@wingsbutterfly/dsh-rtk'
89
+ config:
90
+ enabled: true
91
+ mode: rewrite
92
+ ```
93
+
94
+ 不想动现有 preset,就复制一个再加行:
95
+
96
+ ```
97
+ # 让 cordis preset 的 agent 执行
98
+ agentPresets.copy('standard', 'rtk', 'RTK 优化')
99
+ ```
100
+
101
+ 这一行挂在 **host 平面**同样有效(写进 `~/.dsh/cordis.patch.yml`),会覆盖该部署下的所有会话——但需要重启宿主,而且 host 行与 preset 行会同时想要同一个进程级 settings 命名空间。**两个平面只选一个。**
102
+
103
+ ## 配置
104
+
105
+ 所有字段可选,下列为默认值。
106
+
107
+ ```yaml
108
+ - id: rtk
109
+ name: '@wingsbutterfly/dsh-rtk'
110
+ config:
111
+ enabled: true # 总开关
112
+ mode: rewrite # rewrite | suggest
113
+ guardWhenRtkMissing: true # rtk 不可用时执行原命令
114
+ showRewriteNotifications: false # 在结果末尾附加一行改写说明
115
+ rtkExecutable: rtk # 名字或绝对路径
116
+ rewriteTimeoutMs: 3000 # 单次 `rtk rewrite` 的截止时间
117
+ compactedTools: [bash, read, grep]
118
+ outputCompaction:
119
+ enabled: true
120
+ stripAnsi: true
121
+ readCompaction:
122
+ enabled: false # 有损 read 压缩;默认关,保证读到的代码是精确的
123
+ sourceCodeFilteringEnabled: false
124
+ preserveExactSkillReads: false
125
+ sourceCodeFiltering: none # none | minimal | aggressive
126
+ aggregateTestOutput: true
127
+ filterBuildOutput: true
128
+ compactGitOutput: true
129
+ aggregateLinterOutput: true
130
+ groupSearchOutput: true
131
+ trackSavings: true
132
+ deferToHarnessSpill: true # 把超大输出让给 harness 的 spill 策略
133
+ smartTruncate:
134
+ enabled: false
135
+ maxLines: 220 # 40–4000
136
+ truncate:
137
+ enabled: true
138
+ maxChars: 12000 # 1000–200000
139
+ ```
140
+
141
+ 配置同时注册为 harness 设置文档里的 `dsh-rtk` 命名空间,可以在那里编辑并即时生效、无需重启。组合里的 `config:` 块提供 `base` 层,设置文档在其上叠加用户层。
142
+
143
+ > **为什么 `readCompaction` 默认关。** 过滤或截断 `read` 结果,可能让模型拿到的文本与文件不再一致,编辑就会失配。默认开启的每一项,要么对它归纳的正文是无损的,要么只作用于整个形态都会被替换掉的输出。
144
+
145
+ ## 命令
146
+
147
+ | 命令 | 说明 |
148
+ |---|---|
149
+ | `/rtk` | 显示配置与运行时状态 |
150
+ | `/rtk show` | 同 `/rtk` |
151
+ | `/rtk path` | 配置存放位置 |
152
+ | `/rtk verify` | 检查 rtk 可执行文件是否可用 |
153
+ | `/rtk stats` | 本次会话的压缩收益 |
154
+ | `/rtk clear-stats` | 重置收益计数 |
155
+ | `/rtk reset` | 恢复配置默认值 |
156
+ | `/rtk help` | 用法说明 |
157
+
158
+ ## 工作原理
159
+
160
+ 两部分都挂在工具管线上,而不是包装某个工具,因为 harness 恰好提供两个合适的接缝:
161
+
162
+ - **重写走 `tools/execute`。** `tools/pre-execute` 被刻意设计为不能修改参数——参数在派发前就已记入日志并呈现,在那里改写会让记录下来的调用与实际执行的不一致。around-dispatch 阶段是唯一能让「执行的命令」与「记录的命令」不同的地方,而 `dsh-rtk` 把改动限制在单次调用内:派发一返回就恢复原始参数,后续管线阶段与会话日志看到的仍是模型发出的调用。
163
+
164
+ - **压缩走 `tools/post-execute`**,这是允许替换结果内容的阶段。
165
+
166
+ 两个监听器都注册在该行被挂载的 scope 里。因此 preset 行恰好覆盖它自己的 agent(preset 的 standing scope 是每个加入它的会话的祖先),而 host 行覆盖进程内所有 agent。
167
+
168
+ 硬依赖只有 `tools` 服务。`settings`、`commands`、`systemPrompt` 都用 `ctx.get` 解析,缺少其中任何一个的组合仍然能获得优化。
169
+
170
+ ### 注意:改写所用的接缝不是官方扩展点
171
+
172
+ harness 对 `tools/execute` wrapper 的文档原话是「may change only `exec.signal`」。替换 `arguments` **不是受支持的扩展点**——它能生效,只是因为执行对象要等到结果被通知时才被冻结。而失败的替代方案更糟:`tools/pre-execute` 被刻意设计为不能重写输入(参数在派发前就已记入日志并呈现),同一 layer 内也无法用新工具覆盖已有工具。
173
+
174
+ `dsh-rtk` 是**知情地**用了这条接缝,并在力所能及处做了对冲:替换只作用于单次调用、在 `finally` 里恢复,任何失败路径都退回模型发出的原命令。若未来的 harness 提前冻结执行对象,改写会**静默失效**而不是报错——命令按原样执行,压缩不受影响,因为 `tools/post-execute` 是**有文档记载的内容替换阶段**。
175
+
176
+ ## 与 pi-rtk-optimizer 的差异
177
+
178
+ | pi-rtk-optimizer | dsh-rtk |
179
+ |---|---|
180
+ | 在 `tool_call` 钩子里改 `event.input.command` | 在一次派发期间替换 `exec.arguments`,随后恢复 |
181
+ | 在 `tool_result` 钩子里压缩 | 在 `tools/post-execute` waterfall 里压缩 |
182
+ | `/rtk` 打开 TUI 设置面板 | `/rtk` 输出文本状态;配置存在 harness 设置文档里 |
183
+ | 配置是它自己管的 JSON 文件 | 配置是 harness 设置命名空间,叠加在组合的 `config:` 块之上 |
184
+ | 通过 `tool_execution_*` 钩子清洗流式输出 | harness 没有对应钩子,故不清洗流式输出 |
185
+ | Windows 专用 shell 修正 | 未移植;目标部署是 POSIX |
186
+
187
+ 其余部分——把重写委托给 `rtk rewrite`、退出码契约、rtk 缺失时的守卫、每个压缩开关的默认值、以及各项技术的算法——都是原样移植。
188
+
189
+ ## 开发
190
+
191
+ ```bash
192
+ pnpm run typecheck # 用真实 harness 类型跑 tsc --noEmit
193
+ pnpm test # 先构建,再跑 node --test test/
194
+ pnpm run build # 产出 lib/
195
+ pnpm run check # typecheck + test
196
+ ```
197
+
198
+ 测试 import 的是 `lib/`,所以 `pnpm test` 会先构建;否则只改 `src/` 而没构建的回归会静默通过。
199
+
200
+ `test/integration.test.ts` 用替身上下文驱动真实的 `apply()`,并调用真实的 `rtk` 二进制,所以在没装 rtk 的机器上会失败——这是有意的:重写路径就是这个功能本身。
201
+
202
+ ## Star history
203
+
204
+ <picture>
205
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wings1848/dsh-rtk/output/star-history-dark.svg">
206
+ <img alt="Star history chart" src="https://raw.githubusercontent.com/wings1848/dsh-rtk/output/star-history-light.svg">
207
+ </picture>
208
+
209
+ ## 许可证
210
+
211
+ MIT
@@ -0,0 +1,51 @@
1
+ # Third-party notices
2
+
3
+ `dsh-rtk` is MIT licensed (see [LICENSE](LICENSE)). It is a derivative of an
4
+ MIT-licensed project, whose notice is reproduced below as that license requires.
5
+
6
+ ---
7
+
8
+ ## pi-rtk-optimizer
9
+
10
+ Copyright (c) 2026 MasuRii
11
+
12
+ Licensed under the MIT License.
13
+
14
+ Derived from <https://github.com/MasuRii/pi-rtk-optimizer>, version 0.9.0.
15
+
16
+ What came from it: the decision to delegate rewriting to `rtk rewrite` instead of
17
+ maintaining a rule table; rtk's exit-code contract (`0`/`3` carry a rewrite on
18
+ stdout, `1` means no equivalent, `2` means rtk refused); the runtime guard that
19
+ runs the original command when rtk is unavailable; the output-compaction pipeline
20
+ and each of its techniques (ANSI stripping, build filtering, test aggregation, git
21
+ compaction, linter aggregation, search grouping, source filtering, and both
22
+ truncation stages); the documented default of every configuration field; and the
23
+ `/rtk` command surface.
24
+
25
+ What did not: the seam it hooks (Pi's `tool_call`/`tool_result` events rather than
26
+ this harness's `tools/execute` and `tools/post-execute`), the TUI settings modal,
27
+ streamed-output sanitization, and the Windows-specific shell fixups. Each is
28
+ recorded under "Differences from pi-rtk-optimizer" in [README.md](README.md), and
29
+ [docs/verification.md](docs/verification.md) holds this port's acceptance evidence.
30
+
31
+ ---
32
+
33
+ ## MIT License, as it applies to the project above
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining a copy
36
+ of this software and associated documentation files (the "Software"), to deal
37
+ in the Software without restriction, including without limitation the rights
38
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39
+ copies of the Software, and to permit persons to whom the Software is
40
+ furnished to do so, subject to the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be included in all
43
+ copies or substantial portions of the Software.
44
+
45
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
51
+ SOFTWARE.
@@ -0,0 +1,47 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 840 500" width="840" height="500" role="img" aria-label="dsh-rtk rewrites bash commands to rtk and compacts the result before the model sees it">
2
+ <title>How dsh-rtk works</title>
3
+ <desc>A command flows from the model through a rewrite stage to rtk, then through a compaction stage back to the model. Measured on git status: 1050 characters become 215, an 80% reduction.</desc>
4
+
5
+ <!-- Opaque background: GitHub and npm render on both light and dark themes, so
6
+ a transparent plate designed for one of them smears on the other. -->
7
+ <rect width="840" height="500" fill="#f8fafc"/>
8
+ <rect x="0.5" y="0.5" width="839" height="499" fill="none" stroke="#e2e8f0"/>
9
+
10
+ <text x="32" y="52" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="25" font-weight="700" fill="#0f172a">dsh-rtk</text>
11
+ <text x="32" y="78" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="14" fill="#64748b">rewrites bash commands to rtk, then compacts what comes back</text>
12
+
13
+ <!-- Stage 1: what the model asked for -->
14
+ <rect x="32" y="104" width="776" height="72" rx="10" fill="#ffffff" stroke="#cbd5e1"/>
15
+ <text x="52" y="132" font-family="system-ui, -apple-system, sans-serif" font-size="12" font-weight="600" fill="#94a3b8" letter-spacing="0.6">THE MODEL ASKS FOR</text>
16
+ <text x="52" y="160" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="17" fill="#0f172a">git status</text>
17
+
18
+ <!-- connector -->
19
+ <line x1="120" y1="176" x2="120" y2="212" stroke="#0ea5e9" stroke-width="2"/>
20
+ <path d="M120 220 L115 210 L125 210 Z" fill="#0ea5e9"/>
21
+ <text x="140" y="203" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="#0284c7">tools/execute</text>
22
+ <text x="258" y="203" font-family="system-ui, -apple-system, sans-serif" font-size="13" fill="#64748b">— argument replaced for this call only</text>
23
+
24
+ <!-- Stage 2: what actually runs -->
25
+ <rect x="32" y="228" width="776" height="72" rx="10" fill="#ffffff" stroke="#cbd5e1"/>
26
+ <text x="52" y="256" font-family="system-ui, -apple-system, sans-serif" font-size="12" font-weight="600" fill="#94a3b8" letter-spacing="0.6">WHAT ACTUALLY RUNS</text>
27
+ <text x="52" y="284" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="17" fill="#0f172a">rtk git status</text>
28
+ <text x="330" y="284" font-family="system-ui, -apple-system, sans-serif" font-size="12.5" fill="#94a3b8">rtk stays the source of truth — it decides what it supports</text>
29
+
30
+ <!-- connector -->
31
+ <line x1="120" y1="300" x2="120" y2="336" stroke="#10b981" stroke-width="2"/>
32
+ <path d="M120 344 L115 334 L125 334 Z" fill="#10b981"/>
33
+ <text x="140" y="327" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="600" fill="#059669">tools/post-execute</text>
34
+ <text x="290" y="327" font-family="system-ui, -apple-system, sans-serif" font-size="13" fill="#64748b">— body summarized, status markers kept</text>
35
+
36
+ <!-- Stage 3: what the model sees -->
37
+ <rect x="32" y="352" width="776" height="116" rx="10" fill="#ffffff" stroke="#cbd5e1"/>
38
+ <text x="52" y="380" font-family="system-ui, -apple-system, sans-serif" font-size="12" font-weight="600" fill="#94a3b8" letter-spacing="0.6">THE MODEL SEES</text>
39
+ <text x="52" y="412" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="15" fill="#0f172a">Branch: main</text>
40
+ <text x="52" y="434" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="15" fill="#0f172a">Modified: 2 files</text>
41
+ <text x="52" y="456" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="15" fill="#0f172a">[exit code: 0]</text>
42
+
43
+ <!-- measured outcome -->
44
+ <rect x="470" y="394" width="318" height="56" rx="8" fill="#ecfdf5" stroke="#a7f3d0"/>
45
+ <text x="490" y="418" font-family="system-ui, -apple-system, sans-serif" font-size="13" fill="#065f46">measured on a 40-file repo</text>
46
+ <text x="490" y="440" font-family="system-ui, -apple-system, sans-serif" font-size="15" font-weight="700" fill="#047857">1050 &#8594; 215 chars (&#8722;80%)</text>
47
+ </svg>