pi-rtk-optimizer 0.7.0 → 0.7.1

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 CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.1] - 2026-05-04
11
+
12
+ ### Changed
13
+ - Clarified the README architecture inventory for delegated `rtk rewrite` ownership and documented Bun as a development verification prerequisite.
14
+ - Pinned TypeScript and esbuild as dev dependencies so build and bundle checks use locked local tooling.
15
+ - Added RTK executable path visibility to runtime verification output and documented audit/debug config expectations.
16
+
17
+ ### Fixed
18
+ - Hardened `RTK_DB_PATH` shell quoting against inherited temp paths containing command-substitution syntax.
19
+ - Made the custom test helper await async tests before reporting pass.
20
+ - Preserved RTK rewrite error details through the extension's existing UI warning path.
21
+ - Expanded Windows command and rewritten-pipeline fixups for leading compound-command cases.
22
+ - Normalized compaction technique return handling while preserving existing output behavior.
23
+ - Added lifecycle and vendored modal regression coverage for high-risk extension event paths.
24
+
10
25
  ## [0.7.0] - 2026-04-30
11
26
 
12
27
  ### Added
@@ -14,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
29
 
15
30
  ### Changed
16
31
  - 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.70.6.
32
+ - Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to ^0.72.0.
18
33
 
19
34
  ## [0.6.0] - 2026-04-27
20
35
 
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
@@ -107,6 +108,8 @@ Actual global path: $PI_CODING_AGENT_DIR/extensions/pi-rtk-optimizer/config.json
107
108
 
108
109
  A starter template is included at `config/config.example.json`.
109
110
 
111
+ 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.
112
+
110
113
  ### Configuration Options
111
114
 
112
115
  #### Top-Level Settings
@@ -207,9 +210,8 @@ src/
207
210
  ├── index.ts # Extension bootstrap and event wiring
208
211
  ├── config-store.ts # Config load/save with normalization
209
212
  ├── config-modal.ts # TUI settings modal and /rtk handler
210
- ├── command-rewriter.ts # Command tokenization and rewrite logic
211
- ├── rewrite-bypass.ts # Rewrite safety bypass rules for interactive/structured commands
212
- ├── rewrite-rules.ts # Rewrite rule catalog
213
+ ├── command-rewriter.ts # Command rewrite decision adapter for RTK delegation
214
+ ├── rtk-rewrite-provider.ts # Calls `rtk rewrite` as the rewrite source of truth
213
215
  ├── rewrite-pipeline-safety.ts # Shell-safety fixups for rewritten commands
214
216
  ├── rtk-command-environment.ts # RTK_DB_PATH scoping for rewritten commands
215
217
  ├── shell-env-prefix.ts # Environment assignment parsing helpers
@@ -252,17 +254,18 @@ Automatic fixes applied on Windows:
252
254
 
253
255
  - **Peer dependencies:** `@mariozechner/pi-coding-agent`, `@mariozechner/pi-tui`
254
256
  - **Runtime:** Node.js ≥20, optional `rtk` binary for command rewriting
257
+ - **Development verification:** Node.js ≥20, npm, and Bun for the test scripts
255
258
 
256
259
  ## Development
257
260
 
258
261
  ```bash
259
- # Build
262
+ # Transpile-only TypeScript build check
260
263
  npm run build
261
264
 
262
265
  # Full typecheck
263
266
  npm run typecheck
264
267
 
265
- # Run tests
268
+ # Run Bun-based tests
266
269
  npm run test
267
270
 
268
271
  # Full verification
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-rtk-optimizer",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Pi extension that optimizes RTK command rewriting and tool output compaction for the coding agent.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -16,12 +16,11 @@
16
16
  "LICENSE"
17
17
  ],
18
18
  "scripts": {
19
- "build": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json --noCheck",
20
- "lint": "npm run build",
21
- "typecheck": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json",
19
+ "build": "tsc -p tsconfig.json --noCheck",
20
+ "typecheck": "tsc -p tsconfig.json",
22
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",
23
- "check": "npm run lint && npm run typecheck && npm run test",
24
- "build:check": "bunx esbuild ./index.ts --bundle --platform=node --format=esm --outfile=./.pi-rtk-optimizer-check.mjs --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-tui && bun -e \"import { unlinkSync } from 'node:fs'; unlinkSync('./.pi-rtk-optimizer-check.mjs');\""
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');\""
25
24
  },
26
25
  "keywords": [
27
26
  "pi-package",
@@ -52,13 +51,17 @@
52
51
  "publishConfig": {
53
52
  "access": "public"
54
53
  },
54
+ "devDependencies": {
55
+ "esbuild": "0.28.0",
56
+ "typescript": "5.7.3"
57
+ },
55
58
  "pi": {
56
59
  "extensions": [
57
60
  "./index.ts"
58
61
  ]
59
62
  },
60
63
  "peerDependencies": {
61
- "@mariozechner/pi-coding-agent": "^0.70.6",
62
- "@mariozechner/pi-tui": "^0.70.6"
64
+ "@mariozechner/pi-coding-agent": "^0.72.0",
65
+ "@mariozechner/pi-tui": "^0.72.0"
63
66
  }
64
67
  }
@@ -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
- if (process.platform === "win32") {
200
- assert.deepEqual(fixed.applied, ["cd-/d", "python-utf8"]);
201
- assert.equal(
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
- const alreadyUtf8 = applyWindowsBashCompatibilityFixes("PYTHONIOENCODING=utf-8 python script.py");
207
- assert.deepEqual(alreadyUtf8.applied, []);
208
- assert.equal(alreadyUtf8.command, "PYTHONIOENCODING=utf-8 python script.py");
209
- } else {
210
- assert.deepEqual(fixed.applied, []);
211
- assert.equal(fixed.command, command);
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
- if (process.platform === "win32") {
219
- assert.ok(rewritten.includes('mktemp'));
220
- assert.ok(rewritten.includes('trap'));
221
- assert.ok(rewritten.includes('rtk git diff > "$__pi_rtk_pipe_tmp"'));
222
- assert.ok(rewritten.includes('(grep TODO) < "$__pi_rtk_pipe_tmp"'));
223
- } else {
224
- assert.equal(rewritten, "rtk git diff | grep TODO");
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
- assert.equal(applyRewrittenCommandShellSafetyFixups("git diff | grep TODO"), "git diff | grep TODO");
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 rewritten = applyRewrittenCommandShellSafetyFixups(
232
- applyRtkCommandEnvironment("rtk git diff agent/extensions/pi-multi-auth/account-manager.ts | head -200"),
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
- if (process.platform === "win32") {
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
- if (process.platform === "win32") {
257
- assert.ok(rewritten.startsWith('export RTK_DB_PATH="/custom/history.db"; {'));
258
- assert.ok(rewritten.includes('rtk git diff > "$__pi_rtk_pipe_tmp"'));
259
- assert.ok(rewritten.includes('(head -200) < "$__pi_rtk_pipe_tmp"'));
260
- } else {
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
- assert.equal(sanitizeStreamingBashExecutionResult(hookNoticeResult, "rtk git status"), true);
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
- assert.equal(sanitizeStreamingBashExecutionResult(emojiResult, "rtk git diff -- src/file.ts"), true);
331
- assert.equal((emojiResult.content[0] as { text: string }).text, "> src/file.ts\n[OK] Files are identical\n");
332
- assert.deepEqual(emojiResult.content[1], { type: "image", url: "ignored" });
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
- assert.equal(sanitizeStreamingBashExecutionResult(parseWarningResult, "rtk git status"), false);
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",