pi-rtk-optimizer 0.5.0 → 0.5.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,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.1] - 2026-03-24
11
+
12
+ ### Fixed
13
+ - RTK_DB_PATH environment variable now correctly scoped to rewritten producer commands only — Windows commands now use subshell scoping `{ RTK_DB_PATH=...; ... }` instead of leaking the prefix into the rewritten command
14
+ - Command rewrite pipeline now applies environment scoping BEFORE shell safety fixups to prevent env prefix stripping
15
+
16
+ ### Added
17
+ - `shell-env-prefix.ts` module for splitting leading environment variable assignments from commands
18
+ - `splitLeadingEnvAssignments()` function to properly extract `ENV=value` prefixes before command analysis
19
+
20
+ ### Changed
21
+ - Refactored `rtk-command-environment.ts` to use the new `splitLeadingEnvAssignments` utility
22
+ - Refactored `rewrite-pipeline-safety.ts` to preserve env prefixes when analyzing and rewriting rtk commands
23
+
24
+ ### Tests
25
+ - Added test coverage for RTK_DB_PATH scoping on Windows vs Unix platforms
26
+ - Verified env prefix is preserved through the rewrite pipeline
27
+
10
28
  ## [0.5.0] - 2026-03-23
11
29
 
12
30
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-rtk-optimizer",
3
- "version": "0.5.0",
3
+ "version": "0.5.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",
@@ -14,6 +14,7 @@ import { matchesCommandPatterns, normalizeCommandForDetection } from "./techniqu
14
14
  import { compactPath } from "./techniques/path-utils.ts";
15
15
  import { applyWindowsBashCompatibilityFixes } from "./windows-command-helpers.ts";
16
16
  import { applyRewrittenCommandShellSafetyFixups } from "./rewrite-pipeline-safety.ts";
17
+ import { applyRtkCommandEnvironment } from "./rtk-command-environment.ts";
17
18
  import { sanitizeStreamingBashExecutionResult } from "./tool-execution-sanitizer.ts";
18
19
  import { sanitizeRtkEmojiOutput } from "./techniques/emoji.ts";
19
20
  import { stripRtkHookWarnings } from "./techniques/rtk.ts";
@@ -197,6 +198,28 @@ runTest("rewrite pipeline safety buffers rewritten Windows producer commands", (
197
198
  assert.equal(applyRewrittenCommandShellSafetyFixups("git diff | grep TODO"), "git diff | grep TODO");
198
199
  });
199
200
 
201
+ runTest("rewrite pipeline safety keeps RTK_DB_PATH scoped to rewritten producer commands", () => {
202
+ const rewritten = applyRewrittenCommandShellSafetyFixups(
203
+ applyRtkCommandEnvironment("rtk git diff agent/extensions/pi-multi-auth/account-manager.ts | head -200"),
204
+ );
205
+
206
+ if (process.platform === "win32") {
207
+ assert.ok(rewritten.startsWith("{"));
208
+ assert.equal(rewritten.startsWith("RTK_DB_PATH="), false);
209
+ assert.ok(rewritten.includes("RTK_DB_PATH="));
210
+ assert.ok(
211
+ rewritten.includes('rtk git diff agent/extensions/pi-multi-auth/account-manager.ts > "$__pi_rtk_pipe_tmp"'),
212
+ );
213
+ assert.ok(rewritten.includes('(head -200) < "$__pi_rtk_pipe_tmp"'));
214
+ } else {
215
+ assert.ok(rewritten.startsWith("RTK_DB_PATH="));
216
+ assert.equal(
217
+ rewritten,
218
+ applyRtkCommandEnvironment("rtk git diff agent/extensions/pi-multi-auth/account-manager.ts | head -200"),
219
+ );
220
+ }
221
+ });
222
+
200
223
  runTest("stripRtkHookWarnings handles bare, prefixed, and already-sanitized hook notices", () => {
201
224
  assert.equal(
202
225
  stripRtkHookWarnings("No hook installed — run `rtk init -g` for automatic token savings\n\nready\n", null),
package/src/index.ts CHANGED
@@ -363,8 +363,8 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
363
363
  if (config.showRewriteNotifications && ctx.hasUI) {
364
364
  ctx.ui.notify(formatRewriteNotice(decision.originalCommand, decision.rewrittenCommand), "info");
365
365
  }
366
- const safeRewrittenCommand = applyRewrittenCommandShellSafetyFixups(decision.rewrittenCommand);
367
- event.input.command = applyRtkCommandEnvironment(safeRewrittenCommand);
366
+ const envScopedRewrittenCommand = applyRtkCommandEnvironment(decision.rewrittenCommand);
367
+ event.input.command = applyRewrittenCommandShellSafetyFixups(envScopedRewrittenCommand);
368
368
  return {};
369
369
  }
370
370
 
@@ -1,3 +1,5 @@
1
+ import { splitLeadingEnvAssignments } from "./shell-env-prefix.js";
2
+
1
3
  interface ParsedPipeline {
2
4
  segments: string[];
3
5
  separators: string[];
@@ -88,18 +90,19 @@ function parseSimpleTopLevelPipeline(command: string): ParsedPipeline | null {
88
90
 
89
91
  function extractProducerRewritePlan(segment: string, firstSeparator: string): ProducerRewritePlan | null {
90
92
  const trimmed = segment.trim();
91
- if (!/^rtk\s+/i.test(trimmed)) {
93
+ const { envPrefix, command: commandWithOptionalRedirect } = splitLeadingEnvAssignments(trimmed);
94
+ if (!/^rtk\s+/i.test(commandWithOptionalRedirect)) {
92
95
  return null;
93
96
  }
94
97
 
95
- const stderrMergeMatch = trimmed.match(/^(.*?)(?:\s+)?2>\s*&1\s*$/u);
98
+ const stderrMergeMatch = commandWithOptionalRedirect.match(/^(.*?)(?:\s+)?2>\s*&1\s*$/u);
96
99
  if (stderrMergeMatch) {
97
100
  const command = stderrMergeMatch[1]?.trimEnd() ?? "";
98
- return command ? { command, captureStderr: true } : null;
101
+ return command ? { command: `${envPrefix}${command}`.trim(), captureStderr: true } : null;
99
102
  }
100
103
 
101
104
  return {
102
- command: trimmed,
105
+ command: `${envPrefix}${commandWithOptionalRedirect}`.trim(),
103
106
  captureStderr: firstSeparator === "|&",
104
107
  };
105
108
  }
@@ -1,7 +1,8 @@
1
1
  import { join } from "node:path";
2
2
 
3
+ import { splitLeadingEnvAssignments } from "./shell-env-prefix.js";
4
+
3
5
  const RTK_DB_PATH_ENV_NAME = "RTK_DB_PATH";
4
- const LEADING_ENV_ASSIGNMENT_PATTERN = /^((?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|[^\s]+)\s+)*)/;
5
6
  const RTK_DB_PATH_ASSIGNMENT_PATTERN = /(?:^|\s)RTK_DB_PATH=(?:"[^"]*"|'[^']*'|[^\s]+)(?=\s|$)/;
6
7
 
7
8
  function resolveTemporaryDirectory(): string {
@@ -47,8 +48,7 @@ function quoteForShellEnv(value: string): string {
47
48
  }
48
49
 
49
50
  function hasLeadingRtkDbPathAssignment(command: string): boolean {
50
- const leadingAssignments = command.match(LEADING_ENV_ASSIGNMENT_PATTERN)?.[1] ?? "";
51
- return RTK_DB_PATH_ASSIGNMENT_PATTERN.test(leadingAssignments);
51
+ return RTK_DB_PATH_ASSIGNMENT_PATTERN.test(splitLeadingEnvAssignments(command).envPrefix);
52
52
  }
53
53
 
54
54
  export function applyRtkCommandEnvironment(command: string): string {
@@ -0,0 +1,14 @@
1
+ const LEADING_ENV_ASSIGNMENT_PATTERN = /^((?:[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|[^\s]+)\s+)*)/;
2
+
3
+ export interface LeadingEnvAssignmentSplit {
4
+ envPrefix: string;
5
+ command: string;
6
+ }
7
+
8
+ export function splitLeadingEnvAssignments(input: string): LeadingEnvAssignmentSplit {
9
+ const envPrefix = input.match(LEADING_ENV_ASSIGNMENT_PATTERN)?.[1] ?? "";
10
+ return {
11
+ envPrefix,
12
+ command: input.slice(envPrefix.length),
13
+ };
14
+ }