pi-rtk-optimizer 0.7.0 → 0.8.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.
- package/CHANGELOG.md +25 -1
- package/README.md +14 -8
- package/package.json +67 -64
- package/src/additional-coverage-test.ts +117 -58
- package/src/command-completions.ts +49 -49
- package/src/command-rewriter-test.ts +187 -118
- package/src/command-rewriter.ts +46 -43
- package/src/config-modal-test.ts +97 -31
- package/src/config-modal.ts +91 -12
- package/src/constants.ts +1 -1
- package/src/index-test.ts +198 -3
- package/src/index.ts +49 -5
- package/src/output-compactor-test.ts +208 -3
- package/src/output-compactor.ts +316 -16
- package/src/rewrite-pipeline-safety.ts +203 -178
- package/src/rtk-command-environment.ts +73 -69
- package/src/rtk-executable-resolver.ts +97 -0
- package/src/rtk-rewrite-provider.ts +126 -90
- package/src/shell-env-prefix.ts +5 -1
- package/src/test-helpers.ts +23 -10
- package/src/tool-execution-sanitizer.ts +80 -69
- package/src/types-shims.d.ts +4 -2
- package/src/types.ts +4 -0
- package/src/windows-command-helpers.ts +92 -16
- package/src/zellij-modal.ts +137 -30
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.0] - 2026-05-22
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Added tabbed `/rtk` settings modal groups with left/right tab navigation and context-aware help for search and value changes.
|
|
14
|
+
- Added anchor-safe `read` compaction that detects hashline/anchored read output and preserves complete edit anchors during source filtering, smart truncation, and hard truncation.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Updated package metadata and lockfile version to `0.8.0` and migrated Pi peer dependency metadata to the `@earendil-works` scope.
|
|
18
|
+
|
|
19
|
+
## [0.7.1] - 2026-05-04
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Clarified the README architecture inventory for delegated `rtk rewrite` ownership and documented Bun as a development verification prerequisite.
|
|
23
|
+
- Pinned TypeScript and esbuild as dev dependencies so build and bundle checks use locked local tooling.
|
|
24
|
+
- Added RTK executable path visibility to runtime verification output and documented audit/debug config expectations.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- Hardened `RTK_DB_PATH` shell quoting against inherited temp paths containing command-substitution syntax.
|
|
28
|
+
- Made the custom test helper await async tests before reporting pass.
|
|
29
|
+
- Preserved RTK rewrite error details through the extension's existing UI warning path.
|
|
30
|
+
- Expanded Windows command and rewritten-pipeline fixups for leading compound-command cases.
|
|
31
|
+
- Normalized compaction technique return handling while preserving existing output behavior.
|
|
32
|
+
- Added lifecycle and vendored modal regression coverage for high-risk extension event paths.
|
|
33
|
+
|
|
10
34
|
## [0.7.0] - 2026-04-30
|
|
11
35
|
|
|
12
36
|
### Added
|
|
@@ -14,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
14
38
|
|
|
15
39
|
### Changed
|
|
16
40
|
- Updated README and example configuration defaults for safer read-compaction behavior and troubleshooting guidance.
|
|
17
|
-
- Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to ^0.
|
|
41
|
+
- Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to ^0.72.0.
|
|
18
42
|
|
|
19
43
|
## [0.6.0] - 2026-04-27
|
|
20
44
|
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
- **Automatic rewriting** or **suggestion-only** mode for common development workflows
|
|
17
17
|
- Delegates bash command rewrite decisions to the installed `rtk rewrite` command, keeping RTK as the source of truth for supported commands, shell parsing, bypasses, and compound-command behavior
|
|
18
18
|
- Runtime guard when `rtk` binary is unavailable (raw commands run unchanged and repeated missing-binary rewrite probes are avoided)
|
|
19
|
+
- `/rtk show` and `/rtk verify` surface the resolved `rtk` executable path when the host can resolve it
|
|
19
20
|
- Pi-specific shell safety fixups for rewritten commands on Windows
|
|
20
21
|
|
|
21
22
|
### Output Compaction Pipeline
|
|
@@ -32,11 +33,12 @@ Multi-stage pipeline to reduce token consumption:
|
|
|
32
33
|
| Search Grouping | Groups `grep`/`rg` results by file |
|
|
33
34
|
| Source Code Filtering | `none`, `minimal`, or `aggressive` comment/whitespace removal with userscript metadata preservation |
|
|
34
35
|
| Smart Truncation | Preserves file boundaries and important lines while keeping 80-line reads exact |
|
|
36
|
+
| Anchor-Safe Read Compaction | Detects hashline/anchored `read` output and preserves complete edit anchors when filtering or truncating anchored lines |
|
|
35
37
|
| Hard Truncation | Final character limit enforcement |
|
|
36
38
|
|
|
37
39
|
### Interactive Settings
|
|
38
40
|
|
|
39
|
-
- TUI settings modal via `/rtk` command
|
|
41
|
+
- Tabbed TUI settings modal via `/rtk` command
|
|
40
42
|
- Real-time configuration changes without restart
|
|
41
43
|
- Command completions for all subcommands
|
|
42
44
|
|
|
@@ -81,7 +83,7 @@ Open the interactive settings modal:
|
|
|
81
83
|
/rtk
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
Use
|
|
86
|
+
Use ←/→ to switch tabs, ↑/↓ to navigate settings in the active tab, type to search, Enter/Space to cycle values, and Escape to close.
|
|
85
87
|
|
|
86
88
|
### Subcommands
|
|
87
89
|
|
|
@@ -107,6 +109,8 @@ Actual global path: $PI_CODING_AGENT_DIR/extensions/pi-rtk-optimizer/config.json
|
|
|
107
109
|
|
|
108
110
|
A starter template is included at `config/config.example.json`.
|
|
109
111
|
|
|
112
|
+
For audit or debugging sessions, keep `showRewriteNotifications` enabled and disable lossy `read` compaction/source filtering before gathering evidence. Existing `config.json` files are user-owned runtime state; do not overwrite local choices unless you intentionally want to change live extension behavior.
|
|
113
|
+
|
|
110
114
|
### Configuration Options
|
|
111
115
|
|
|
112
116
|
#### Top-Level Settings
|
|
@@ -143,6 +147,8 @@ Bash command support is intentionally resolved by the installed `rtk` binary thr
|
|
|
143
147
|
|
|
144
148
|
Skill-read preservation covers the global Pi skills directory (`~/.pi/agent/skills` by default, or `$PI_CODING_AGENT_DIR/skills` when set), `~/.agents/skills`, project `.pi/skills`, and ancestor `.agents/skills` directories.
|
|
145
149
|
|
|
150
|
+
When `read` output uses Pi hashline/anchor prefixes, the compactor treats each anchored line as an indivisible edit anchor. Source filtering and truncation may omit anchored lines, but retained lines keep their complete anchor prefixes; hard truncation inserts an anchor-safe marker instead of cutting through an anchor.
|
|
151
|
+
|
|
146
152
|
#### Truncation Settings
|
|
147
153
|
|
|
148
154
|
| Option | Type | Default | Range | Description |
|
|
@@ -207,9 +213,8 @@ src/
|
|
|
207
213
|
├── index.ts # Extension bootstrap and event wiring
|
|
208
214
|
├── config-store.ts # Config load/save with normalization
|
|
209
215
|
├── config-modal.ts # TUI settings modal and /rtk handler
|
|
210
|
-
├── command-rewriter.ts # Command
|
|
211
|
-
├── rewrite-
|
|
212
|
-
├── rewrite-rules.ts # Rewrite rule catalog
|
|
216
|
+
├── command-rewriter.ts # Command rewrite decision adapter for RTK delegation
|
|
217
|
+
├── rtk-rewrite-provider.ts # Calls `rtk rewrite` as the rewrite source of truth
|
|
213
218
|
├── rewrite-pipeline-safety.ts # Shell-safety fixups for rewritten commands
|
|
214
219
|
├── rtk-command-environment.ts # RTK_DB_PATH scoping for rewritten commands
|
|
215
220
|
├── shell-env-prefix.ts # Environment assignment parsing helpers
|
|
@@ -250,19 +255,20 @@ Automatic fixes applied on Windows:
|
|
|
250
255
|
|
|
251
256
|
### Dependencies
|
|
252
257
|
|
|
253
|
-
- **Peer dependencies:** `@
|
|
258
|
+
- **Peer dependencies:** `@earendil-works/pi-coding-agent`, `@earendil-works/pi-tui`
|
|
254
259
|
- **Runtime:** Node.js ≥20, optional `rtk` binary for command rewriting
|
|
260
|
+
- **Development verification:** Node.js ≥20, npm, and Bun for the test scripts
|
|
255
261
|
|
|
256
262
|
## Development
|
|
257
263
|
|
|
258
264
|
```bash
|
|
259
|
-
#
|
|
265
|
+
# Transpile-only TypeScript build check
|
|
260
266
|
npm run build
|
|
261
267
|
|
|
262
268
|
# Full typecheck
|
|
263
269
|
npm run typecheck
|
|
264
270
|
|
|
265
|
-
# Run tests
|
|
271
|
+
# Run Bun-based tests
|
|
266
272
|
npm run test
|
|
267
273
|
|
|
268
274
|
# Full verification
|
package/package.json
CHANGED
|
@@ -1,64 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-rtk-optimizer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pi extension that optimizes RTK command rewriting and tool output compaction for the coding agent.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./index.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./index.ts"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"index.ts",
|
|
12
|
-
"src",
|
|
13
|
-
"config/config.example.json",
|
|
14
|
-
"README.md",
|
|
15
|
-
"CHANGELOG.md",
|
|
16
|
-
"LICENSE"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"check": "
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"pi
|
|
28
|
-
"pi",
|
|
29
|
-
"pi-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-rtk-optimizer",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Pi extension that optimizes RTK command rewriting and tool output compaction for the coding agent.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.ts",
|
|
12
|
+
"src",
|
|
13
|
+
"config/config.example.json",
|
|
14
|
+
"README.md",
|
|
15
|
+
"CHANGELOG.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json --noCheck",
|
|
20
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
21
|
+
"test": "bun ./src/output-compactor-test.ts && bun ./src/command-rewriter-test.ts && bun ./src/runtime-guard-test.ts && bun ./src/additional-coverage-test.ts && bun ./src/config-modal-test.ts && bun ./src/index-test.ts",
|
|
22
|
+
"check": "npm run typecheck && npm run test && npm run build:check",
|
|
23
|
+
"build:check": "esbuild ./index.ts --bundle --platform=node --format=esm --outfile=./.pi-rtk-optimizer-check.mjs --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-tui && node -e \"import { unlinkSync } from 'node:fs'; unlinkSync('./.pi-rtk-optimizer-check.mjs');\""
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"pi-package",
|
|
27
|
+
"pi",
|
|
28
|
+
"pi-extension",
|
|
29
|
+
"pi-coding-agent",
|
|
30
|
+
"coding-agent",
|
|
31
|
+
"rtk",
|
|
32
|
+
"token-optimization",
|
|
33
|
+
"tool-compaction",
|
|
34
|
+
"output-compaction",
|
|
35
|
+
"command-rewrite",
|
|
36
|
+
"optimization"
|
|
37
|
+
],
|
|
38
|
+
"author": "MasuRii",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/MasuRii/pi-rtk-optimizer.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/MasuRii/pi-rtk-optimizer/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/MasuRii/pi-rtk-optimizer#readme",
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=20"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"esbuild": "0.28.0",
|
|
56
|
+
"typescript": "6.0.3"
|
|
57
|
+
},
|
|
58
|
+
"pi": {
|
|
59
|
+
"extensions": [
|
|
60
|
+
"./index.ts"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"@earendil-works/pi-coding-agent": "^0.75.4",
|
|
65
|
+
"@earendil-works/pi-tui": "^0.75.4"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -13,7 +13,7 @@ import { sanitizeStreamingBashExecutionResult } from "./tool-execution-sanitizer
|
|
|
13
13
|
import { sanitizeRtkEmojiOutput } from "./techniques/emoji.ts";
|
|
14
14
|
import { stripRtkHookWarnings } from "./techniques/rtk.ts";
|
|
15
15
|
|
|
16
|
-
mock.module("@
|
|
16
|
+
mock.module("@earendil-works/pi-coding-agent", () => ({
|
|
17
17
|
getAgentDir: () => "/tmp/.pi/agent",
|
|
18
18
|
}));
|
|
19
19
|
|
|
@@ -172,10 +172,36 @@ runTest("RTK command environment preserves explicit leading RTK_DB_PATH override
|
|
|
172
172
|
const command = 'RTK_DB_PATH="/custom/history.db" rtk git diff';
|
|
173
173
|
assert.equal(applyRtkCommandEnvironment(command), command);
|
|
174
174
|
|
|
175
|
+
const singleQuotedCommand = "RTK_DB_PATH='/custom/it'\\''s/history.db' rtk git diff";
|
|
176
|
+
assert.equal(applyRtkCommandEnvironment(singleQuotedCommand), singleQuotedCommand);
|
|
177
|
+
|
|
175
178
|
const exportedCommand = 'export RTK_DB_PATH="/custom/history.db"; rtk git diff';
|
|
176
179
|
assert.equal(applyRtkCommandEnvironment(exportedCommand), exportedCommand);
|
|
177
180
|
});
|
|
178
181
|
|
|
182
|
+
runTest("RTK command environment single-quotes hostile temp paths", () => {
|
|
183
|
+
const previousTmpDir = process.env.TMPDIR;
|
|
184
|
+
const previousTmp = process.env.TMP;
|
|
185
|
+
const previousTemp = process.env.TEMP;
|
|
186
|
+
const hostilePath = process.platform === "win32" ? "C:\\Temp\\$(touch owned)`bad`'dir" : "/tmp/$(touch owned)`bad`'dir";
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
process.env.TMPDIR = hostilePath;
|
|
190
|
+
process.env.TMP = hostilePath;
|
|
191
|
+
process.env.TEMP = hostilePath;
|
|
192
|
+
|
|
193
|
+
const rewritten = applyRtkCommandEnvironment("rtk git status");
|
|
194
|
+
assert.ok(rewritten.startsWith("export RTK_DB_PATH='"));
|
|
195
|
+
assert.ok(rewritten.includes("$(touch owned)`bad`'\\''dir"));
|
|
196
|
+
assert.ok(rewritten.endsWith("; rtk git status"));
|
|
197
|
+
assert.equal(/^export RTK_DB_PATH=\"/.test(rewritten), false);
|
|
198
|
+
} finally {
|
|
199
|
+
process.env.TMPDIR = previousTmpDir;
|
|
200
|
+
process.env.TMP = previousTmp;
|
|
201
|
+
process.env.TEMP = previousTemp;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
179
205
|
runTest("path compaction preserves the tail and handles Windows separators", () => {
|
|
180
206
|
const unixPath = "/Users/example/projects/pi-rtk-optimizer/src/techniques/path-utils.ts";
|
|
181
207
|
const compactUnixPath = compactPath(unixPath, 28);
|
|
@@ -194,72 +220,93 @@ runTest("path compaction preserves the tail and handles Windows separators", ()
|
|
|
194
220
|
|
|
195
221
|
runTest("windows bash compatibility rewrites only when the runtime is Windows", () => {
|
|
196
222
|
const command = "cd /d C:\\Users\\Administrator\\project && python script.py";
|
|
197
|
-
const fixed = applyWindowsBashCompatibilityFixes(command);
|
|
223
|
+
const fixed = applyWindowsBashCompatibilityFixes(command, "win32");
|
|
224
|
+
assert.deepEqual(fixed.applied, ["cd-/d", "python-utf8"]);
|
|
225
|
+
assert.equal(
|
|
226
|
+
fixed.command,
|
|
227
|
+
'PYTHONIOENCODING=utf-8 cd "C:/Users/Administrator/project" && python script.py',
|
|
228
|
+
);
|
|
198
229
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
fixed.command,
|
|
203
|
-
'PYTHONIOENCODING=utf-8 cd "C:/Users/Administrator/project" && python script.py',
|
|
204
|
-
);
|
|
230
|
+
const unchanged = applyWindowsBashCompatibilityFixes(command, "linux");
|
|
231
|
+
assert.deepEqual(unchanged.applied, []);
|
|
232
|
+
assert.equal(unchanged.command, command);
|
|
205
233
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
234
|
+
const alreadyUtf8 = applyWindowsBashCompatibilityFixes("PYTHONIOENCODING=utf-8 python script.py", "win32");
|
|
235
|
+
assert.deepEqual(alreadyUtf8.applied, []);
|
|
236
|
+
assert.equal(alreadyUtf8.command, "PYTHONIOENCODING=utf-8 python script.py");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
runTest("windows bash compatibility rewrites compound cd slash-d operators", () => {
|
|
240
|
+
assert.equal(
|
|
241
|
+
applyWindowsBashCompatibilityFixes("cd /d C:\\work || echo failed", "win32").command,
|
|
242
|
+
'cd "C:/work" || echo failed',
|
|
243
|
+
);
|
|
244
|
+
assert.equal(
|
|
245
|
+
applyWindowsBashCompatibilityFixes("cd /d C:\\work ; echo done", "win32").command,
|
|
246
|
+
'cd "C:/work" ; echo done',
|
|
247
|
+
);
|
|
248
|
+
assert.equal(
|
|
249
|
+
applyWindowsBashCompatibilityFixes("cd /d C:\\work | cat", "win32").command,
|
|
250
|
+
'cd "C:/work" | cat',
|
|
251
|
+
);
|
|
252
|
+
assert.equal(
|
|
253
|
+
applyWindowsBashCompatibilityFixes('cd /d "C:\\work space" || echo failed', "win32").command,
|
|
254
|
+
'cd "C:/work space" || echo failed',
|
|
255
|
+
);
|
|
213
256
|
});
|
|
214
257
|
|
|
215
258
|
runTest("rewrite pipeline safety buffers rewritten Windows producer commands", () => {
|
|
216
|
-
const rewritten = applyRewrittenCommandShellSafetyFixups("rtk git diff | grep TODO");
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
259
|
+
const rewritten = applyRewrittenCommandShellSafetyFixups("rtk git diff | grep TODO", "win32");
|
|
260
|
+
assert.ok(rewritten.includes('mktemp'));
|
|
261
|
+
assert.ok(rewritten.includes('trap'));
|
|
262
|
+
assert.ok(rewritten.includes('rtk git diff > "$__pi_rtk_pipe_tmp"'));
|
|
263
|
+
assert.ok(rewritten.includes('(grep TODO) < "$__pi_rtk_pipe_tmp"'));
|
|
264
|
+
|
|
265
|
+
assert.equal(
|
|
266
|
+
applyRewrittenCommandShellSafetyFixups("rtk git diff | grep TODO", "linux"),
|
|
267
|
+
"rtk git diff | grep TODO",
|
|
268
|
+
);
|
|
269
|
+
assert.equal(applyRewrittenCommandShellSafetyFixups("git diff | grep TODO", "win32"), "git diff | grep TODO");
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
runTest("rewrite pipeline safety buffers leading pipelines before compound suffixes", () => {
|
|
273
|
+
const andCommand = applyRewrittenCommandShellSafetyFixups("rtk git diff | grep TODO && echo done", "win32");
|
|
274
|
+
assert.ok(andCommand.includes('(grep TODO) < "$__pi_rtk_pipe_tmp"'));
|
|
275
|
+
assert.ok(andCommand.endsWith("&& echo done"));
|
|
276
|
+
|
|
277
|
+
const orCommand = applyRewrittenCommandShellSafetyFixups("rtk git diff | grep TODO || echo none", "win32");
|
|
278
|
+
assert.ok(orCommand.includes('(grep TODO) < "$__pi_rtk_pipe_tmp"'));
|
|
279
|
+
assert.ok(orCommand.endsWith("|| echo none"));
|
|
226
280
|
|
|
227
|
-
|
|
281
|
+
const semicolonCommand = applyRewrittenCommandShellSafetyFixups("rtk git diff | grep TODO; echo done", "win32");
|
|
282
|
+
assert.ok(semicolonCommand.includes('(grep TODO) < "$__pi_rtk_pipe_tmp"'));
|
|
283
|
+
assert.ok(semicolonCommand.endsWith("; echo done"));
|
|
228
284
|
});
|
|
229
285
|
|
|
230
286
|
runTest("rewrite pipeline safety keeps exported RTK_DB_PATH on rewritten producer commands", () => {
|
|
231
|
-
const
|
|
232
|
-
|
|
287
|
+
const envScopedCommand = applyRtkCommandEnvironment("rtk git diff agent/extensions/pi-multi-auth/account-manager.ts | head -200");
|
|
288
|
+
const rewritten = applyRewrittenCommandShellSafetyFixups(envScopedCommand, "win32");
|
|
289
|
+
|
|
290
|
+
assert.ok(rewritten.startsWith("export RTK_DB_PATH="));
|
|
291
|
+
assert.equal(rewritten.startsWith("RTK_DB_PATH="), false);
|
|
292
|
+
assert.ok(rewritten.includes("; {"));
|
|
293
|
+
assert.ok(
|
|
294
|
+
rewritten.includes('rtk git diff agent/extensions/pi-multi-auth/account-manager.ts > "$__pi_rtk_pipe_tmp"'),
|
|
233
295
|
);
|
|
296
|
+
assert.ok(rewritten.includes('(head -200) < "$__pi_rtk_pipe_tmp"'));
|
|
234
297
|
|
|
235
|
-
|
|
236
|
-
assert.ok(rewritten.startsWith("export RTK_DB_PATH="));
|
|
237
|
-
assert.equal(rewritten.startsWith("RTK_DB_PATH="), false);
|
|
238
|
-
assert.ok(rewritten.includes("; {"));
|
|
239
|
-
assert.ok(
|
|
240
|
-
rewritten.includes('rtk git diff agent/extensions/pi-multi-auth/account-manager.ts > "$__pi_rtk_pipe_tmp"'),
|
|
241
|
-
);
|
|
242
|
-
assert.ok(rewritten.includes('(head -200) < "$__pi_rtk_pipe_tmp"'));
|
|
243
|
-
} else {
|
|
244
|
-
assert.ok(rewritten.startsWith("export RTK_DB_PATH="));
|
|
245
|
-
assert.equal(
|
|
246
|
-
rewritten,
|
|
247
|
-
applyRtkCommandEnvironment("rtk git diff agent/extensions/pi-multi-auth/account-manager.ts | head -200"),
|
|
248
|
-
);
|
|
249
|
-
}
|
|
298
|
+
assert.equal(applyRewrittenCommandShellSafetyFixups(envScopedCommand, "linux"), envScopedCommand);
|
|
250
299
|
});
|
|
251
300
|
|
|
252
301
|
runTest("rewrite pipeline safety buffers explicit RTK_DB_PATH export preludes", () => {
|
|
253
302
|
const command = 'export RTK_DB_PATH="/custom/history.db"; rtk git diff | head -200';
|
|
254
|
-
const rewritten = applyRewrittenCommandShellSafetyFixups(command);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
assert.equal(rewritten, command);
|
|
262
|
-
}
|
|
303
|
+
const rewritten = applyRewrittenCommandShellSafetyFixups(command, "win32");
|
|
304
|
+
|
|
305
|
+
assert.ok(rewritten.startsWith('export RTK_DB_PATH="/custom/history.db"; {'));
|
|
306
|
+
assert.ok(rewritten.includes('rtk git diff > "$__pi_rtk_pipe_tmp"'));
|
|
307
|
+
assert.ok(rewritten.includes('(head -200) < "$__pi_rtk_pipe_tmp"'));
|
|
308
|
+
|
|
309
|
+
assert.equal(applyRewrittenCommandShellSafetyFixups(command, "linux"), command);
|
|
263
310
|
});
|
|
264
311
|
|
|
265
312
|
runTest("RTK command environment uses export prelude for shell compound commands", () => {
|
|
@@ -315,11 +362,16 @@ runTest("streaming sanitizer strips hook notices, sanitizes emoji output, and pr
|
|
|
315
362
|
},
|
|
316
363
|
],
|
|
317
364
|
};
|
|
318
|
-
|
|
365
|
+
const hookNoticeSanitization = sanitizeStreamingBashExecutionResult(hookNoticeResult, "rtk git status");
|
|
366
|
+
assert.equal(hookNoticeSanitization.changed, true);
|
|
319
367
|
assert.equal(
|
|
320
|
-
(hookNoticeResult.content[0] as { text: string }).text,
|
|
368
|
+
((hookNoticeSanitization.result as typeof hookNoticeResult).content[0] as { text: string }).text,
|
|
321
369
|
"working tree clean\n",
|
|
322
370
|
);
|
|
371
|
+
assert.equal(
|
|
372
|
+
(hookNoticeResult.content[0] as { text: string }).text,
|
|
373
|
+
"[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n\nworking tree clean\n",
|
|
374
|
+
);
|
|
323
375
|
|
|
324
376
|
const emojiResult = {
|
|
325
377
|
content: [
|
|
@@ -327,9 +379,14 @@ runTest("streaming sanitizer strips hook notices, sanitizes emoji output, and pr
|
|
|
327
379
|
{ type: "image", url: "ignored" },
|
|
328
380
|
],
|
|
329
381
|
};
|
|
330
|
-
|
|
331
|
-
assert.equal(
|
|
332
|
-
assert.
|
|
382
|
+
const emojiSanitization = sanitizeStreamingBashExecutionResult(emojiResult, "rtk git diff -- src/file.ts");
|
|
383
|
+
assert.equal(emojiSanitization.changed, true);
|
|
384
|
+
assert.equal(
|
|
385
|
+
((emojiSanitization.result as typeof emojiResult).content[0] as { text: string }).text,
|
|
386
|
+
"> src/file.ts\n[OK] Files are identical\n",
|
|
387
|
+
);
|
|
388
|
+
assert.equal((emojiResult.content[0] as { text: string }).text, "📄 src/file.ts\n✅ Files are identical\n");
|
|
389
|
+
assert.deepEqual((emojiSanitization.result as typeof emojiResult).content[1], { type: "image", url: "ignored" });
|
|
333
390
|
|
|
334
391
|
const parseWarningResult = {
|
|
335
392
|
content: [
|
|
@@ -339,7 +396,9 @@ runTest("streaming sanitizer strips hook notices, sanitizes emoji output, and pr
|
|
|
339
396
|
},
|
|
340
397
|
],
|
|
341
398
|
};
|
|
342
|
-
|
|
399
|
+
const parseWarningSanitization = sanitizeStreamingBashExecutionResult(parseWarningResult, "rtk git status");
|
|
400
|
+
assert.equal(parseWarningSanitization.changed, false);
|
|
401
|
+
assert.equal(parseWarningSanitization.result, parseWarningResult);
|
|
343
402
|
assert.equal(
|
|
344
403
|
(parseWarningResult.content[0] as { text: string }).text,
|
|
345
404
|
"[rtk] warning: builtin filters: parse failure\n\nworking tree clean\n",
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import type { AutocompleteItem } from "@
|
|
2
|
-
|
|
3
|
-
interface CompletionDefinition {
|
|
4
|
-
name: string;
|
|
5
|
-
description: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const TOP_LEVEL_SUBCOMMANDS: CompletionDefinition[] = [
|
|
9
|
-
{ name: "show", description: "Show current RTK config + runtime summary" },
|
|
10
|
-
{ name: "path", description: "Show RTK config file path" },
|
|
11
|
-
{ name: "verify", description: "Check whether rtk binary is available" },
|
|
12
|
-
{ name: "stats", description: "Show output compaction metrics" },
|
|
13
|
-
{ name: "clear-stats", description: "Clear output compaction metrics" },
|
|
14
|
-
{ name: "reset", description: "Reset RTK settings to defaults" },
|
|
15
|
-
{ name: "help", description: "Show usage help" },
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
function startsWithFilter(value: string, prefix: string): boolean {
|
|
19
|
-
if (!prefix) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
return value.startsWith(prefix);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function mapCompletions(values: CompletionDefinition[]): AutocompleteItem[] {
|
|
26
|
-
return values.map((entry) => ({
|
|
27
|
-
value: entry.name,
|
|
28
|
-
label: entry.name,
|
|
29
|
-
description: entry.description,
|
|
30
|
-
}));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function getRtkArgumentCompletions(argumentPrefix: string): AutocompleteItem[] | null {
|
|
34
|
-
const normalized = argumentPrefix.trimStart().toLowerCase();
|
|
35
|
-
if (!normalized) {
|
|
36
|
-
return mapCompletions(TOP_LEVEL_SUBCOMMANDS);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (normalized.includes(" ")) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const filtered = TOP_LEVEL_SUBCOMMANDS.filter((entry) => startsWithFilter(entry.name, normalized));
|
|
44
|
-
if (filtered.length === 0) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return mapCompletions(filtered);
|
|
49
|
-
}
|
|
1
|
+
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
2
|
+
|
|
3
|
+
interface CompletionDefinition {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const TOP_LEVEL_SUBCOMMANDS: CompletionDefinition[] = [
|
|
9
|
+
{ name: "show", description: "Show current RTK config + runtime summary" },
|
|
10
|
+
{ name: "path", description: "Show RTK config file path" },
|
|
11
|
+
{ name: "verify", description: "Check whether rtk binary is available" },
|
|
12
|
+
{ name: "stats", description: "Show output compaction metrics" },
|
|
13
|
+
{ name: "clear-stats", description: "Clear output compaction metrics" },
|
|
14
|
+
{ name: "reset", description: "Reset RTK settings to defaults" },
|
|
15
|
+
{ name: "help", description: "Show usage help" },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function startsWithFilter(value: string, prefix: string): boolean {
|
|
19
|
+
if (!prefix) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return value.startsWith(prefix);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function mapCompletions(values: CompletionDefinition[]): AutocompleteItem[] {
|
|
26
|
+
return values.map((entry) => ({
|
|
27
|
+
value: entry.name,
|
|
28
|
+
label: entry.name,
|
|
29
|
+
description: entry.description,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getRtkArgumentCompletions(argumentPrefix: string): AutocompleteItem[] | null {
|
|
34
|
+
const normalized = argumentPrefix.trimStart().toLowerCase();
|
|
35
|
+
if (!normalized) {
|
|
36
|
+
return mapCompletions(TOP_LEVEL_SUBCOMMANDS);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (normalized.includes(" ")) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const filtered = TOP_LEVEL_SUBCOMMANDS.filter((entry) => startsWithFilter(entry.name, normalized));
|
|
44
|
+
if (filtered.length === 0) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return mapCompletions(filtered);
|
|
49
|
+
}
|