pi-rtk-optimizer 0.6.0 → 0.7.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 +9 -0
- package/README.md +11 -7
- package/config/config.example.json +6 -3
- package/package.json +64 -64
- package/src/additional-coverage-test.ts +47 -5
- package/src/config-modal.ts +17 -1
- package/src/config-store.ts +32 -5
- package/src/index-test.ts +31 -2
- package/src/index.ts +2 -1
- package/src/output-compactor-test.ts +45 -2
- package/src/output-compactor.ts +4 -0
- package/src/rewrite-pipeline-safety.ts +23 -2
- package/src/rtk-command-environment.ts +69 -64
- package/src/types.ts +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-04-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Added opt-in `readCompaction` controls for `read` output so lossy source filtering and smart truncation stay disabled unless explicitly enabled.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- 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.
|
|
18
|
+
|
|
10
19
|
## [0.6.0] - 2026-04-27
|
|
11
20
|
|
|
12
21
|
### Changed
|
package/README.md
CHANGED
|
@@ -130,9 +130,10 @@ Bash command support is intentionally resolved by the installed `rtk` binary thr
|
|
|
130
130
|
|--------|------|---------|-------------|
|
|
131
131
|
| `outputCompaction.enabled` | boolean | `true` | Enable output compaction pipeline |
|
|
132
132
|
| `outputCompaction.stripAnsi` | boolean | `true` | Remove ANSI escape codes |
|
|
133
|
-
| `outputCompaction.
|
|
133
|
+
| `outputCompaction.readCompaction.enabled` | boolean | `false` | Enable lossy compaction for `read` output; defaults off so code reads stay exact |
|
|
134
|
+
| `outputCompaction.sourceCodeFilteringEnabled` | boolean | `false` | Enable source code filtering for `read` output when read compaction is enabled |
|
|
134
135
|
| `outputCompaction.preserveExactSkillReads` | boolean | `false` | Keep reads under configured Pi/global/project skill directories exact, bypassing read compaction |
|
|
135
|
-
| `outputCompaction.sourceCodeFiltering` | string | `"
|
|
136
|
+
| `outputCompaction.sourceCodeFiltering` | string | `"none"` | Filter level: `"none"`, `"minimal"`, `"aggressive"` |
|
|
136
137
|
| `outputCompaction.aggregateTestOutput` | boolean | `true` | Summarize test runner output |
|
|
137
138
|
| `outputCompaction.filterBuildOutput` | boolean | `true` | Filter build/compile output |
|
|
138
139
|
| `outputCompaction.compactGitOutput` | boolean | `true` | Compact git command output |
|
|
@@ -146,7 +147,7 @@ Skill-read preservation covers the global Pi skills directory (`~/.pi/agent/skil
|
|
|
146
147
|
|
|
147
148
|
| Option | Type | Default | Range | Description |
|
|
148
149
|
|--------|------|---------|-------|-------------|
|
|
149
|
-
| `outputCompaction.smartTruncate.enabled` | boolean | `
|
|
150
|
+
| `outputCompaction.smartTruncate.enabled` | boolean | `false` | — | Enable smart line-based truncation for read output when read compaction is enabled |
|
|
150
151
|
| `outputCompaction.smartTruncate.maxLines` | number | `220` | 40–4000 | Maximum lines after smart truncation |
|
|
151
152
|
| `outputCompaction.truncate.enabled` | boolean | `true` | — | Enable hard character truncation |
|
|
152
153
|
| `outputCompaction.truncate.maxChars` | number | `12000` | 1000–200000 | Maximum characters in final output |
|
|
@@ -159,7 +160,7 @@ Skill-read preservation covers the global Pi skills directory (`~/.pi/agent/skil
|
|
|
159
160
|
| `minimal` | Removes non-doc comments, collapses blank lines |
|
|
160
161
|
| `aggressive` | Also removes imports, keeps only signatures and key logic |
|
|
161
162
|
|
|
162
|
-
> **Note:** When source filtering and read truncation safeguards are active, Pi injects a troubleshooting note for repeated file-edit mismatches. If edits fail because "old text does not match," disable
|
|
163
|
+
> **Note:** When read compaction, source filtering, and read truncation safeguards are active, Pi injects a troubleshooting note for repeated file-edit mismatches. If edits fail because "old text does not match," disable read compaction via `/rtk`, re-read the file, apply the edit, then re-enable compaction.
|
|
163
164
|
|
|
164
165
|
### Example Configuration
|
|
165
166
|
|
|
@@ -172,9 +173,12 @@ Skill-read preservation covers the global Pi skills directory (`~/.pi/agent/skil
|
|
|
172
173
|
"outputCompaction": {
|
|
173
174
|
"enabled": true,
|
|
174
175
|
"stripAnsi": true,
|
|
175
|
-
"
|
|
176
|
+
"readCompaction": {
|
|
177
|
+
"enabled": false
|
|
178
|
+
},
|
|
179
|
+
"sourceCodeFilteringEnabled": false,
|
|
176
180
|
"preserveExactSkillReads": false,
|
|
177
|
-
"sourceCodeFiltering": "
|
|
181
|
+
"sourceCodeFiltering": "none",
|
|
178
182
|
"aggregateTestOutput": true,
|
|
179
183
|
"filterBuildOutput": true,
|
|
180
184
|
"compactGitOutput": true,
|
|
@@ -182,7 +186,7 @@ Skill-read preservation covers the global Pi skills directory (`~/.pi/agent/skil
|
|
|
182
186
|
"groupSearchOutput": true,
|
|
183
187
|
"trackSavings": true,
|
|
184
188
|
"smartTruncate": {
|
|
185
|
-
"enabled":
|
|
189
|
+
"enabled": false,
|
|
186
190
|
"maxLines": 220
|
|
187
191
|
},
|
|
188
192
|
"truncate": {
|
|
@@ -6,15 +6,18 @@
|
|
|
6
6
|
"outputCompaction": {
|
|
7
7
|
"enabled": true,
|
|
8
8
|
"stripAnsi": true,
|
|
9
|
-
"
|
|
9
|
+
"readCompaction": {
|
|
10
|
+
"enabled": false
|
|
11
|
+
},
|
|
12
|
+
"sourceCodeFilteringEnabled": false,
|
|
10
13
|
"preserveExactSkillReads": false,
|
|
11
14
|
"truncate": {
|
|
12
15
|
"enabled": true,
|
|
13
16
|
"maxChars": 12000
|
|
14
17
|
},
|
|
15
|
-
"sourceCodeFiltering": "
|
|
18
|
+
"sourceCodeFiltering": "none",
|
|
16
19
|
"smartTruncate": {
|
|
17
|
-
"enabled":
|
|
20
|
+
"enabled": false,
|
|
18
21
|
"maxLines": 220
|
|
19
22
|
},
|
|
20
23
|
"aggregateTestOutput": true,
|
package/package.json
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
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": "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",
|
|
22
|
-
"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');\""
|
|
25
|
-
},
|
|
26
|
-
"keywords": [
|
|
27
|
-
"pi-package",
|
|
28
|
-
"pi",
|
|
29
|
-
"pi-extension",
|
|
30
|
-
"pi-coding-agent",
|
|
31
|
-
"coding-agent",
|
|
32
|
-
"rtk",
|
|
33
|
-
"token-optimization",
|
|
34
|
-
"tool-compaction",
|
|
35
|
-
"output-compaction",
|
|
36
|
-
"command-rewrite",
|
|
37
|
-
"optimization"
|
|
38
|
-
],
|
|
39
|
-
"author": "MasuRii",
|
|
40
|
-
"license": "MIT",
|
|
41
|
-
"repository": {
|
|
42
|
-
"type": "git",
|
|
43
|
-
"url": "git+https://github.com/MasuRii/pi-rtk-optimizer.git"
|
|
44
|
-
},
|
|
45
|
-
"bugs": {
|
|
46
|
-
"url": "https://github.com/MasuRii/pi-rtk-optimizer/issues"
|
|
47
|
-
},
|
|
48
|
-
"homepage": "https://github.com/MasuRii/pi-rtk-optimizer#readme",
|
|
49
|
-
"engines": {
|
|
50
|
-
"node": ">=20"
|
|
51
|
-
},
|
|
52
|
-
"publishConfig": {
|
|
53
|
-
"access": "public"
|
|
54
|
-
},
|
|
55
|
-
"pi": {
|
|
56
|
-
"extensions": [
|
|
57
|
-
"./index.ts"
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
"peerDependencies": {
|
|
61
|
-
"@mariozechner/pi-coding-agent": "^0.70.
|
|
62
|
-
"@mariozechner/pi-tui": "^0.70.
|
|
63
|
-
}
|
|
64
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-rtk-optimizer",
|
|
3
|
+
"version": "0.7.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": "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",
|
|
22
|
+
"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');\""
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"pi-package",
|
|
28
|
+
"pi",
|
|
29
|
+
"pi-extension",
|
|
30
|
+
"pi-coding-agent",
|
|
31
|
+
"coding-agent",
|
|
32
|
+
"rtk",
|
|
33
|
+
"token-optimization",
|
|
34
|
+
"tool-compaction",
|
|
35
|
+
"output-compaction",
|
|
36
|
+
"command-rewrite",
|
|
37
|
+
"optimization"
|
|
38
|
+
],
|
|
39
|
+
"author": "MasuRii",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/MasuRii/pi-rtk-optimizer.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/MasuRii/pi-rtk-optimizer/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/MasuRii/pi-rtk-optimizer#readme",
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"pi": {
|
|
56
|
+
"extensions": [
|
|
57
|
+
"./index.ts"
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@mariozechner/pi-coding-agent": "^0.70.6",
|
|
62
|
+
"@mariozechner/pi-tui": "^0.70.6"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -66,6 +66,7 @@ runTest("config-store normalizes invalid values and clamps numeric ranges", () =
|
|
|
66
66
|
assert.equal(normalized.mode, "rewrite");
|
|
67
67
|
assert.equal(Object.hasOwn(normalized, "rewriteGitGithub"), false);
|
|
68
68
|
assert.equal(normalized.outputCompaction.stripAnsi, false);
|
|
69
|
+
assert.equal(normalized.outputCompaction.readCompaction.enabled, true);
|
|
69
70
|
assert.equal(normalized.outputCompaction.sourceCodeFilteringEnabled, true);
|
|
70
71
|
assert.equal(normalized.outputCompaction.sourceCodeFiltering, "minimal");
|
|
71
72
|
assert.equal(normalized.outputCompaction.truncate.maxChars, 1_000);
|
|
@@ -73,18 +74,32 @@ runTest("config-store normalizes invalid values and clamps numeric ranges", () =
|
|
|
73
74
|
assert.equal(normalized.outputCompaction.trackSavings, false);
|
|
74
75
|
});
|
|
75
76
|
|
|
77
|
+
runTest("config-store uses safer read defaults when readCompaction is explicit", () => {
|
|
78
|
+
const normalized = normalizeRtkIntegrationConfig({
|
|
79
|
+
outputCompaction: {
|
|
80
|
+
readCompaction: { enabled: false },
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
assert.equal(normalized.outputCompaction.readCompaction.enabled, false);
|
|
85
|
+
assert.equal(normalized.outputCompaction.sourceCodeFilteringEnabled, false);
|
|
86
|
+
assert.equal(normalized.outputCompaction.sourceCodeFiltering, "none");
|
|
87
|
+
assert.equal(normalized.outputCompaction.smartTruncate.enabled, false);
|
|
88
|
+
});
|
|
89
|
+
|
|
76
90
|
runTest("config-store can ensure, save, and reload isolated config files", () => {
|
|
77
91
|
const tempPath = makeTempConfigPath();
|
|
78
92
|
cleanupFile(tempPath);
|
|
79
93
|
|
|
80
94
|
try {
|
|
81
95
|
const ensured = ensureConfigExists(tempPath);
|
|
82
|
-
assert.equal(ensured.
|
|
96
|
+
assert.equal(ensured.error, undefined);
|
|
83
97
|
assert.equal(existsSync(tempPath), true);
|
|
84
98
|
|
|
85
99
|
const defaultLoad = loadRtkIntegrationConfig(tempPath);
|
|
86
100
|
assert.equal(defaultLoad.warning, undefined);
|
|
87
101
|
assert.equal(defaultLoad.config.mode, "rewrite");
|
|
102
|
+
assert.equal(defaultLoad.config.outputCompaction.readCompaction.enabled, false);
|
|
88
103
|
|
|
89
104
|
const saved = saveRtkIntegrationConfig(
|
|
90
105
|
{
|
|
@@ -153,6 +168,14 @@ runTest("command detection ignores env prefixes, blank lines, and chained suffix
|
|
|
153
168
|
assert.equal(matchesCommandPatterns("echo hello", [/^bun test/]), false);
|
|
154
169
|
});
|
|
155
170
|
|
|
171
|
+
runTest("RTK command environment preserves explicit leading RTK_DB_PATH overrides", () => {
|
|
172
|
+
const command = 'RTK_DB_PATH="/custom/history.db" rtk git diff';
|
|
173
|
+
assert.equal(applyRtkCommandEnvironment(command), command);
|
|
174
|
+
|
|
175
|
+
const exportedCommand = 'export RTK_DB_PATH="/custom/history.db"; rtk git diff';
|
|
176
|
+
assert.equal(applyRtkCommandEnvironment(exportedCommand), exportedCommand);
|
|
177
|
+
});
|
|
178
|
+
|
|
156
179
|
runTest("path compaction preserves the tail and handles Windows separators", () => {
|
|
157
180
|
const unixPath = "/Users/example/projects/pi-rtk-optimizer/src/techniques/path-utils.ts";
|
|
158
181
|
const compactUnixPath = compactPath(unixPath, 28);
|
|
@@ -204,21 +227,21 @@ runTest("rewrite pipeline safety buffers rewritten Windows producer commands", (
|
|
|
204
227
|
assert.equal(applyRewrittenCommandShellSafetyFixups("git diff | grep TODO"), "git diff | grep TODO");
|
|
205
228
|
});
|
|
206
229
|
|
|
207
|
-
runTest("rewrite pipeline safety keeps RTK_DB_PATH
|
|
230
|
+
runTest("rewrite pipeline safety keeps exported RTK_DB_PATH on rewritten producer commands", () => {
|
|
208
231
|
const rewritten = applyRewrittenCommandShellSafetyFixups(
|
|
209
232
|
applyRtkCommandEnvironment("rtk git diff agent/extensions/pi-multi-auth/account-manager.ts | head -200"),
|
|
210
233
|
);
|
|
211
234
|
|
|
212
235
|
if (process.platform === "win32") {
|
|
213
|
-
assert.ok(rewritten.startsWith("
|
|
236
|
+
assert.ok(rewritten.startsWith("export RTK_DB_PATH="));
|
|
214
237
|
assert.equal(rewritten.startsWith("RTK_DB_PATH="), false);
|
|
215
|
-
assert.ok(rewritten.includes("
|
|
238
|
+
assert.ok(rewritten.includes("; {"));
|
|
216
239
|
assert.ok(
|
|
217
240
|
rewritten.includes('rtk git diff agent/extensions/pi-multi-auth/account-manager.ts > "$__pi_rtk_pipe_tmp"'),
|
|
218
241
|
);
|
|
219
242
|
assert.ok(rewritten.includes('(head -200) < "$__pi_rtk_pipe_tmp"'));
|
|
220
243
|
} else {
|
|
221
|
-
assert.ok(rewritten.startsWith("RTK_DB_PATH="));
|
|
244
|
+
assert.ok(rewritten.startsWith("export RTK_DB_PATH="));
|
|
222
245
|
assert.equal(
|
|
223
246
|
rewritten,
|
|
224
247
|
applyRtkCommandEnvironment("rtk git diff agent/extensions/pi-multi-auth/account-manager.ts | head -200"),
|
|
@@ -226,6 +249,25 @@ runTest("rewrite pipeline safety keeps RTK_DB_PATH scoped to rewritten producer
|
|
|
226
249
|
}
|
|
227
250
|
});
|
|
228
251
|
|
|
252
|
+
runTest("rewrite pipeline safety buffers explicit RTK_DB_PATH export preludes", () => {
|
|
253
|
+
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
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
runTest("RTK command environment uses export prelude for shell compound commands", () => {
|
|
266
|
+
const rewritten = applyRtkCommandEnvironment('for d in a b; do echo "$d"; done');
|
|
267
|
+
assert.ok(/^export RTK_DB_PATH=/.test(rewritten));
|
|
268
|
+
assert.ok(/; for d in a b; do echo "\$d"; done$/.test(rewritten));
|
|
269
|
+
});
|
|
270
|
+
|
|
229
271
|
runTest("stripRtkHookWarnings handles bare, prefixed, and already-sanitized hook notices", () => {
|
|
230
272
|
assert.equal(
|
|
231
273
|
stripRtkHookWarnings("No hook installed — run `rtk init -g` for automatic token savings\n\nready\n", null),
|
package/src/config-modal.ts
CHANGED
|
@@ -58,7 +58,7 @@ function summarizeConfig(config: RtkIntegrationConfig, runtimeStatus: RuntimeSta
|
|
|
58
58
|
? "rtk=available"
|
|
59
59
|
: `rtk=missing${runtimeStatus.lastError ? ` (${runtimeStatus.lastError})` : ""}`;
|
|
60
60
|
|
|
61
|
-
return `enabled=${config.enabled}, mode=${config.mode}, rewriteSource=rtk, rewriteNotice=${config.showRewriteNotifications}, compaction=${config.outputCompaction.enabled}, sourceFilterEnabled=${config.outputCompaction.sourceCodeFilteringEnabled}, preserveSkillReads=${config.outputCompaction.preserveExactSkillReads}, sourceFilter=${config.outputCompaction.sourceCodeFiltering}, ${runtime}`;
|
|
61
|
+
return `enabled=${config.enabled}, mode=${config.mode}, rewriteSource=rtk, rewriteNotice=${config.showRewriteNotifications}, compaction=${config.outputCompaction.enabled}, readCompaction=${config.outputCompaction.readCompaction.enabled}, sourceFilterEnabled=${config.outputCompaction.sourceCodeFilteringEnabled}, preserveSkillReads=${config.outputCompaction.preserveExactSkillReads}, sourceFilter=${config.outputCompaction.sourceCodeFiltering}, ${runtime}`;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
function buildSettingItems(config: RtkIntegrationConfig): SettingItem[] {
|
|
@@ -105,6 +105,13 @@ function buildSettingItems(config: RtkIntegrationConfig): SettingItem[] {
|
|
|
105
105
|
currentValue: toOnOff(config.outputCompaction.stripAnsi),
|
|
106
106
|
values: ON_OFF,
|
|
107
107
|
},
|
|
108
|
+
{
|
|
109
|
+
id: "outputReadCompactionEnabled",
|
|
110
|
+
label: "Read compaction enabled",
|
|
111
|
+
description: "If off, read tool output stays exact; build/test/git/grep compaction can still run",
|
|
112
|
+
currentValue: toOnOff(config.outputCompaction.readCompaction.enabled),
|
|
113
|
+
values: ON_OFF,
|
|
114
|
+
},
|
|
108
115
|
{
|
|
109
116
|
id: "outputTruncateEnabled",
|
|
110
117
|
label: "Hard truncation enabled",
|
|
@@ -219,6 +226,14 @@ function applySetting(config: RtkIntegrationConfig, id: string, value: string):
|
|
|
219
226
|
...config,
|
|
220
227
|
outputCompaction: { ...config.outputCompaction, stripAnsi: value === "on" },
|
|
221
228
|
};
|
|
229
|
+
case "outputReadCompactionEnabled":
|
|
230
|
+
return {
|
|
231
|
+
...config,
|
|
232
|
+
outputCompaction: {
|
|
233
|
+
...config.outputCompaction,
|
|
234
|
+
readCompaction: { enabled: value === "on" },
|
|
235
|
+
},
|
|
236
|
+
};
|
|
222
237
|
case "outputTruncateEnabled":
|
|
223
238
|
return {
|
|
224
239
|
...config,
|
|
@@ -353,6 +368,7 @@ function syncSettingValues(settingsList: SettingValueSyncTarget, config: RtkInte
|
|
|
353
368
|
settingsList.updateValue("guardWhenRtkMissing", toOnOff(config.guardWhenRtkMissing));
|
|
354
369
|
settingsList.updateValue("outputCompactionEnabled", toOnOff(config.outputCompaction.enabled));
|
|
355
370
|
settingsList.updateValue("outputStripAnsi", toOnOff(config.outputCompaction.stripAnsi));
|
|
371
|
+
settingsList.updateValue("outputReadCompactionEnabled", toOnOff(config.outputCompaction.readCompaction.enabled));
|
|
356
372
|
settingsList.updateValue("outputTruncateEnabled", toOnOff(config.outputCompaction.truncate.enabled));
|
|
357
373
|
settingsList.updateValue("outputTruncateMaxChars", String(config.outputCompaction.truncate.maxChars));
|
|
358
374
|
settingsList.updateValue("outputSourceFilteringEnabled", toOnOff(config.outputCompaction.sourceCodeFilteringEnabled));
|
package/src/config-store.ts
CHANGED
|
@@ -30,10 +30,14 @@ function toMode(value: unknown): RtkIntegrationConfig["mode"] {
|
|
|
30
30
|
: DEFAULT_RTK_INTEGRATION_CONFIG.mode;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function toSourceFilterLevel(value: unknown): RtkSourceFilterLevel {
|
|
33
|
+
function toSourceFilterLevel(value: unknown, fallback: RtkSourceFilterLevel): RtkSourceFilterLevel {
|
|
34
34
|
return RTK_SOURCE_FILTER_LEVELS.includes(value as RtkSourceFilterLevel)
|
|
35
35
|
? (value as RtkSourceFilterLevel)
|
|
36
|
-
:
|
|
36
|
+
: fallback;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function hasOwnProperty(source: Record<string, unknown>, key: string): boolean {
|
|
40
|
+
return Object.prototype.hasOwnProperty.call(source, key);
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
function toObject(value: unknown): Record<string, unknown> {
|
|
@@ -46,8 +50,20 @@ function toObject(value: unknown): Record<string, unknown> {
|
|
|
46
50
|
export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfig {
|
|
47
51
|
const source = toObject(raw);
|
|
48
52
|
const outputCompactionSource = toObject(source.outputCompaction);
|
|
53
|
+
const readCompactionSource = toObject(outputCompactionSource.readCompaction);
|
|
49
54
|
const truncateSource = toObject(outputCompactionSource.truncate);
|
|
50
55
|
const smartTruncateSource = toObject(outputCompactionSource.smartTruncate);
|
|
56
|
+
const hasReadCompaction = hasOwnProperty(outputCompactionSource, "readCompaction");
|
|
57
|
+
const legacyReadCompactionFallback = !hasReadCompaction;
|
|
58
|
+
const sourceFilteringFallback = legacyReadCompactionFallback
|
|
59
|
+
? true
|
|
60
|
+
: DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.sourceCodeFilteringEnabled;
|
|
61
|
+
const sourceFilterLevelFallback = legacyReadCompactionFallback
|
|
62
|
+
? "minimal"
|
|
63
|
+
: DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.sourceCodeFiltering;
|
|
64
|
+
const smartTruncateEnabledFallback = legacyReadCompactionFallback
|
|
65
|
+
? true
|
|
66
|
+
: DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.smartTruncate.enabled;
|
|
51
67
|
|
|
52
68
|
return {
|
|
53
69
|
enabled: toBoolean(source.enabled, DEFAULT_RTK_INTEGRATION_CONFIG.enabled),
|
|
@@ -69,9 +85,17 @@ export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfi
|
|
|
69
85
|
outputCompactionSource.stripAnsi,
|
|
70
86
|
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.stripAnsi,
|
|
71
87
|
),
|
|
88
|
+
readCompaction: {
|
|
89
|
+
enabled: hasReadCompaction
|
|
90
|
+
? toBoolean(
|
|
91
|
+
readCompactionSource.enabled,
|
|
92
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.readCompaction.enabled,
|
|
93
|
+
)
|
|
94
|
+
: true,
|
|
95
|
+
},
|
|
72
96
|
sourceCodeFilteringEnabled: toBoolean(
|
|
73
97
|
outputCompactionSource.sourceCodeFilteringEnabled,
|
|
74
|
-
|
|
98
|
+
sourceFilteringFallback,
|
|
75
99
|
),
|
|
76
100
|
preserveExactSkillReads: toBoolean(
|
|
77
101
|
outputCompactionSource.preserveExactSkillReads,
|
|
@@ -89,11 +113,14 @@ export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfi
|
|
|
89
113
|
200_000,
|
|
90
114
|
),
|
|
91
115
|
},
|
|
92
|
-
sourceCodeFiltering: toSourceFilterLevel(
|
|
116
|
+
sourceCodeFiltering: toSourceFilterLevel(
|
|
117
|
+
outputCompactionSource.sourceCodeFiltering,
|
|
118
|
+
sourceFilterLevelFallback,
|
|
119
|
+
),
|
|
93
120
|
smartTruncate: {
|
|
94
121
|
enabled: toBoolean(
|
|
95
122
|
smartTruncateSource.enabled,
|
|
96
|
-
|
|
123
|
+
smartTruncateEnabledFallback,
|
|
97
124
|
),
|
|
98
125
|
maxLines: toInteger(
|
|
99
126
|
smartTruncateSource.maxLines,
|
package/src/index-test.ts
CHANGED
|
@@ -34,6 +34,7 @@ const { DEFAULT_RTK_INTEGRATION_CONFIG } = await import("./types.ts");
|
|
|
34
34
|
function configWith(overrides: {
|
|
35
35
|
enabled?: boolean;
|
|
36
36
|
compactionEnabled?: boolean;
|
|
37
|
+
readCompactionEnabled?: boolean;
|
|
37
38
|
sourceFilteringEnabled?: boolean;
|
|
38
39
|
sourceFilteringLevel?: "none" | "minimal" | "aggressive";
|
|
39
40
|
smartTruncateEnabled?: boolean;
|
|
@@ -46,6 +47,10 @@ function configWith(overrides: {
|
|
|
46
47
|
outputCompaction: {
|
|
47
48
|
...base.outputCompaction,
|
|
48
49
|
enabled: overrides.compactionEnabled ?? base.outputCompaction.enabled,
|
|
50
|
+
readCompaction: {
|
|
51
|
+
...base.outputCompaction.readCompaction,
|
|
52
|
+
enabled: overrides.readCompactionEnabled ?? base.outputCompaction.readCompaction.enabled,
|
|
53
|
+
},
|
|
49
54
|
sourceCodeFilteringEnabled:
|
|
50
55
|
overrides.sourceFilteringEnabled ?? base.outputCompaction.sourceCodeFilteringEnabled,
|
|
51
56
|
sourceCodeFiltering: overrides.sourceFilteringLevel ?? base.outputCompaction.sourceCodeFiltering,
|
|
@@ -86,13 +91,23 @@ runTest("bounded notice tracker coerces invalid limits to a safe minimum", () =>
|
|
|
86
91
|
runTest("source-filter note injected when source filtering is active", () => {
|
|
87
92
|
assert.equal(
|
|
88
93
|
shouldInjectSourceFilterTroubleshootingNote(
|
|
89
|
-
configWith({
|
|
94
|
+
configWith({
|
|
95
|
+
readCompactionEnabled: true,
|
|
96
|
+
sourceFilteringEnabled: true,
|
|
97
|
+
sourceFilteringLevel: "minimal",
|
|
98
|
+
smartTruncateEnabled: true,
|
|
99
|
+
}),
|
|
90
100
|
),
|
|
91
101
|
true,
|
|
92
102
|
);
|
|
93
103
|
assert.equal(
|
|
94
104
|
shouldInjectSourceFilterTroubleshootingNote(
|
|
95
|
-
configWith({
|
|
105
|
+
configWith({
|
|
106
|
+
readCompactionEnabled: true,
|
|
107
|
+
sourceFilteringEnabled: true,
|
|
108
|
+
sourceFilteringLevel: "aggressive",
|
|
109
|
+
smartTruncateEnabled: true,
|
|
110
|
+
}),
|
|
96
111
|
),
|
|
97
112
|
true,
|
|
98
113
|
);
|
|
@@ -106,6 +121,20 @@ runTest("source-filter note skipped when compaction is disabled", () => {
|
|
|
106
121
|
assert.equal(shouldInjectSourceFilterTroubleshootingNote(configWith({ compactionEnabled: false })), false);
|
|
107
122
|
});
|
|
108
123
|
|
|
124
|
+
runTest("source-filter note skipped when read compaction is disabled", () => {
|
|
125
|
+
assert.equal(
|
|
126
|
+
shouldInjectSourceFilterTroubleshootingNote(
|
|
127
|
+
configWith({
|
|
128
|
+
readCompactionEnabled: false,
|
|
129
|
+
sourceFilteringEnabled: true,
|
|
130
|
+
sourceFilteringLevel: "minimal",
|
|
131
|
+
smartTruncateEnabled: true,
|
|
132
|
+
}),
|
|
133
|
+
),
|
|
134
|
+
false,
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
109
138
|
runTest("source-filter note skipped when source filtering flag is off", () => {
|
|
110
139
|
assert.equal(
|
|
111
140
|
shouldInjectSourceFilterTroubleshootingNote(configWith({ sourceFilteringEnabled: false })),
|
package/src/index.ts
CHANGED
|
@@ -28,13 +28,14 @@ function trimMessage(raw: string, maxLength = 220): string {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const SOURCE_FILTER_TROUBLESHOOTING_NOTE =
|
|
31
|
-
"RTK note: If file edits repeatedly fail because old text does not match, ask the user to manually run '/rtk' in the Pi TUI, disable 'Read
|
|
31
|
+
"RTK note: If file edits repeatedly fail because old text does not match, ask the user to manually run '/rtk' in the Pi TUI, disable 'Read compaction enabled', re-read the file, apply the edit, then ask the user to manually re-enable it in the Pi TUI.";
|
|
32
32
|
|
|
33
33
|
export function shouldInjectSourceFilterTroubleshootingNote(config: RtkIntegrationConfig): boolean {
|
|
34
34
|
const compaction = config.outputCompaction;
|
|
35
35
|
return (
|
|
36
36
|
config.enabled &&
|
|
37
37
|
compaction.enabled &&
|
|
38
|
+
compaction.readCompaction.enabled &&
|
|
38
39
|
compaction.sourceCodeFilteringEnabled &&
|
|
39
40
|
compaction.sourceCodeFiltering !== "none" &&
|
|
40
41
|
(compaction.smartTruncate.enabled || compaction.truncate.enabled)
|
|
@@ -24,6 +24,10 @@ function buildReadContent(lineCount: number): string {
|
|
|
24
24
|
return `${lines.join("\n")}\n`;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function setReadCompaction(config: ReturnType<typeof cloneDefaultConfig>, enabled: boolean): void {
|
|
28
|
+
config.outputCompaction.readCompaction = { enabled };
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
function firstTextBlock(content: unknown[] | undefined): string {
|
|
28
32
|
if (!Array.isArray(content) || content.length === 0) {
|
|
29
33
|
return "";
|
|
@@ -73,6 +77,7 @@ function assertNoOutputEmoji(text: string): void {
|
|
|
73
77
|
|
|
74
78
|
runTest("precision read with offset keeps exact output (no source/smart/hard truncation)", () => {
|
|
75
79
|
const config = cloneDefaultConfig();
|
|
80
|
+
setReadCompaction(config, true);
|
|
76
81
|
config.outputCompaction.truncate.enabled = true;
|
|
77
82
|
config.outputCompaction.truncate.maxChars = 500;
|
|
78
83
|
config.outputCompaction.smartTruncate.enabled = true;
|
|
@@ -94,6 +99,7 @@ runTest("precision read with offset keeps exact output (no source/smart/hard tru
|
|
|
94
99
|
|
|
95
100
|
runTest("precision read with limit keeps exact output", () => {
|
|
96
101
|
const config = cloneDefaultConfig();
|
|
102
|
+
setReadCompaction(config, true);
|
|
97
103
|
config.outputCompaction.truncate.enabled = true;
|
|
98
104
|
config.outputCompaction.truncate.maxChars = 500;
|
|
99
105
|
config.outputCompaction.smartTruncate.enabled = true;
|
|
@@ -113,8 +119,34 @@ runTest("precision read with limit keeps exact output", () => {
|
|
|
113
119
|
assert.deepEqual(result.techniques, []);
|
|
114
120
|
});
|
|
115
121
|
|
|
116
|
-
runTest("
|
|
122
|
+
runTest("default read output stays exact when read compaction is disabled by default", () => {
|
|
123
|
+
const config = cloneDefaultConfig();
|
|
124
|
+
config.outputCompaction.sourceCodeFilteringEnabled = true;
|
|
125
|
+
config.outputCompaction.sourceCodeFiltering = "aggressive";
|
|
126
|
+
config.outputCompaction.smartTruncate.enabled = true;
|
|
127
|
+
config.outputCompaction.smartTruncate.maxLines = 40;
|
|
128
|
+
config.outputCompaction.truncate.enabled = true;
|
|
129
|
+
config.outputCompaction.truncate.maxChars = 500;
|
|
130
|
+
|
|
131
|
+
const content = buildReadContent(220);
|
|
132
|
+
const result = compactToolResult(
|
|
133
|
+
{
|
|
134
|
+
toolName: "read",
|
|
135
|
+
input: { path: "sample.ts" },
|
|
136
|
+
content: [{ type: "text", text: content }],
|
|
137
|
+
},
|
|
138
|
+
config,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
assert.equal(result.changed, false);
|
|
142
|
+
assert.deepEqual(result.techniques, []);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
runTest("normal read compacts and adds banner when read compaction is enabled", () => {
|
|
117
146
|
const config = cloneDefaultConfig();
|
|
147
|
+
setReadCompaction(config, true);
|
|
148
|
+
config.outputCompaction.sourceCodeFilteringEnabled = true;
|
|
149
|
+
config.outputCompaction.sourceCodeFiltering = "minimal";
|
|
118
150
|
config.outputCompaction.smartTruncate.enabled = true;
|
|
119
151
|
config.outputCompaction.smartTruncate.maxLines = 40;
|
|
120
152
|
|
|
@@ -138,6 +170,7 @@ runTest("normal read compacts and adds banner", () => {
|
|
|
138
170
|
|
|
139
171
|
runTest("short read output stays exact below threshold", () => {
|
|
140
172
|
const config = cloneDefaultConfig();
|
|
173
|
+
setReadCompaction(config, true);
|
|
141
174
|
const content = buildReadContent(40);
|
|
142
175
|
|
|
143
176
|
const result = compactToolResult(
|
|
@@ -155,6 +188,7 @@ runTest("short read output stays exact below threshold", () => {
|
|
|
155
188
|
|
|
156
189
|
runTest("read output stays exact at the 80-line boundary with trailing newline", () => {
|
|
157
190
|
const config = cloneDefaultConfig();
|
|
191
|
+
setReadCompaction(config, true);
|
|
158
192
|
config.outputCompaction.smartTruncate.enabled = true;
|
|
159
193
|
config.outputCompaction.smartTruncate.maxLines = 40;
|
|
160
194
|
|
|
@@ -172,8 +206,11 @@ runTest("read output stays exact at the 80-line boundary with trailing newline",
|
|
|
172
206
|
assert.deepEqual(result.techniques, []);
|
|
173
207
|
});
|
|
174
208
|
|
|
175
|
-
runTest("read output compacts once the content exceeds the 80-line exactness threshold", () => {
|
|
209
|
+
runTest("read output compacts once the content exceeds the 80-line exactness threshold when read compaction is enabled", () => {
|
|
176
210
|
const config = cloneDefaultConfig();
|
|
211
|
+
setReadCompaction(config, true);
|
|
212
|
+
config.outputCompaction.sourceCodeFilteringEnabled = true;
|
|
213
|
+
config.outputCompaction.sourceCodeFiltering = "minimal";
|
|
177
214
|
config.outputCompaction.smartTruncate.enabled = true;
|
|
178
215
|
config.outputCompaction.smartTruncate.maxLines = 40;
|
|
179
216
|
|
|
@@ -193,6 +230,9 @@ runTest("read output compacts once the content exceeds the 80-line exactness thr
|
|
|
193
230
|
|
|
194
231
|
runTest("source file reads skip lossy source filtering when truncation safeguards are not needed", () => {
|
|
195
232
|
const config = cloneDefaultConfig();
|
|
233
|
+
setReadCompaction(config, true);
|
|
234
|
+
config.outputCompaction.sourceCodeFilteringEnabled = true;
|
|
235
|
+
config.outputCompaction.sourceCodeFiltering = "minimal";
|
|
196
236
|
config.outputCompaction.smartTruncate.enabled = false;
|
|
197
237
|
config.outputCompaction.truncate.enabled = false;
|
|
198
238
|
|
|
@@ -213,6 +253,7 @@ runTest("source file reads skip lossy source filtering when truncation safeguard
|
|
|
213
253
|
|
|
214
254
|
runTest("skill reads stay exact when preserveExactSkillReads is enabled for user skills", () => {
|
|
215
255
|
const config = cloneDefaultConfig();
|
|
256
|
+
setReadCompaction(config, true);
|
|
216
257
|
config.outputCompaction.preserveExactSkillReads = true;
|
|
217
258
|
config.outputCompaction.truncate.enabled = true;
|
|
218
259
|
config.outputCompaction.truncate.maxChars = 500;
|
|
@@ -235,6 +276,7 @@ runTest("skill reads stay exact when preserveExactSkillReads is enabled for user
|
|
|
235
276
|
|
|
236
277
|
runTest("project .pi skill reads stay exact when preserveExactSkillReads is enabled", () => {
|
|
237
278
|
const config = cloneDefaultConfig();
|
|
279
|
+
setReadCompaction(config, true);
|
|
238
280
|
config.outputCompaction.preserveExactSkillReads = true;
|
|
239
281
|
config.outputCompaction.truncate.enabled = true;
|
|
240
282
|
config.outputCompaction.truncate.maxChars = 500;
|
|
@@ -257,6 +299,7 @@ runTest("project .pi skill reads stay exact when preserveExactSkillReads is enab
|
|
|
257
299
|
|
|
258
300
|
runTest("ancestor .agents skill reads stay exact when preserveExactSkillReads is enabled", () => {
|
|
259
301
|
const config = cloneDefaultConfig();
|
|
302
|
+
setReadCompaction(config, true);
|
|
260
303
|
config.outputCompaction.preserveExactSkillReads = true;
|
|
261
304
|
config.outputCompaction.truncate.enabled = true;
|
|
262
305
|
config.outputCompaction.truncate.maxChars = 500;
|
package/src/output-compactor.ts
CHANGED
|
@@ -140,6 +140,10 @@ function shouldPreserveExactReadOutput(
|
|
|
140
140
|
input: Record<string, unknown>,
|
|
141
141
|
config: RtkIntegrationConfig,
|
|
142
142
|
): boolean {
|
|
143
|
+
if (!config.outputCompaction.readCompaction.enabled) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
143
147
|
if (hasExplicitReadRange(input)) {
|
|
144
148
|
return true;
|
|
145
149
|
}
|
|
@@ -10,6 +10,26 @@ interface ProducerRewritePlan {
|
|
|
10
10
|
captureStderr: boolean;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
interface ShellSafetyTarget {
|
|
14
|
+
environmentPrelude: string;
|
|
15
|
+
command: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const LEADING_RTK_DB_PATH_EXPORT_PRELUDE_PATTERN =
|
|
19
|
+
/^(\s*export\s+RTK_DB_PATH=(?:"(?:\\.|[^"])*"|'[^']*'|[^\s;]+)\s*;\s*)([\s\S]*)$/u;
|
|
20
|
+
|
|
21
|
+
function splitLeadingRtkDbPathExportPrelude(command: string): ShellSafetyTarget {
|
|
22
|
+
const match = command.match(LEADING_RTK_DB_PATH_EXPORT_PRELUDE_PATTERN);
|
|
23
|
+
if (!match) {
|
|
24
|
+
return { environmentPrelude: "", command };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
environmentPrelude: match[1] ?? "",
|
|
29
|
+
command: match[2] ?? "",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
13
33
|
function isTopLevelQuoteCharacter(character: string): character is '"' | "'" | "`" {
|
|
14
34
|
return character === '"' || character === "'" || character === "`";
|
|
15
35
|
}
|
|
@@ -134,7 +154,8 @@ export function applyRewrittenCommandShellSafetyFixups(command: string): string
|
|
|
134
154
|
return command;
|
|
135
155
|
}
|
|
136
156
|
|
|
137
|
-
const
|
|
157
|
+
const target = splitLeadingRtkDbPathExportPrelude(command);
|
|
158
|
+
const parsedPipeline = parseSimpleTopLevelPipeline(target.command);
|
|
138
159
|
if (!parsedPipeline) {
|
|
139
160
|
return command;
|
|
140
161
|
}
|
|
@@ -153,5 +174,5 @@ export function applyRewrittenCommandShellSafetyFixups(command: string): string
|
|
|
153
174
|
return command;
|
|
154
175
|
}
|
|
155
176
|
|
|
156
|
-
return buildBufferedPipelineCommand(producer, remainder)
|
|
177
|
+
return `${target.environmentPrelude}${buildBufferedPipelineCommand(producer, remainder)}`;
|
|
157
178
|
}
|
|
@@ -1,64 +1,69 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
|
|
3
|
-
import { splitLeadingEnvAssignments } from "./shell-env-prefix.js";
|
|
4
|
-
|
|
5
|
-
const RTK_DB_PATH_ENV_NAME = "RTK_DB_PATH";
|
|
6
|
-
const RTK_DB_PATH_ASSIGNMENT_PATTERN = /(?:^|\s)RTK_DB_PATH=(?:"[^"]*"|'[^']*'|[^\s]+)(?=\s|$)/;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { splitLeadingEnvAssignments } from "./shell-env-prefix.js";
|
|
4
|
+
|
|
5
|
+
const RTK_DB_PATH_ENV_NAME = "RTK_DB_PATH";
|
|
6
|
+
const RTK_DB_PATH_ASSIGNMENT_PATTERN = /(?:^|\s)RTK_DB_PATH=(?:"[^"]*"|'[^']*'|[^\s]+)(?=\s|$)/;
|
|
7
|
+
const RTK_DB_PATH_EXPORT_PATTERN = /^export\s+RTK_DB_PATH=(?:"[^"]*"|'[^']*'|[^\s;]+)(?=\s*(?:;|$))/;
|
|
8
|
+
|
|
9
|
+
function resolveTemporaryDirectory(): string {
|
|
10
|
+
if (process.platform === "win32") {
|
|
11
|
+
const windowsTempDir = process.env.TEMP ?? process.env.TMP;
|
|
12
|
+
if (windowsTempDir && windowsTempDir.trim()) {
|
|
13
|
+
return windowsTempDir;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
17
|
+
if (localAppData && localAppData.trim()) {
|
|
18
|
+
return join(localAppData, "Temp");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const userProfile = process.env.USERPROFILE;
|
|
22
|
+
if (userProfile && userProfile.trim()) {
|
|
23
|
+
return join(userProfile, "AppData", "Local", "Temp");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const systemRoot = process.env.SystemRoot ?? process.env.WINDIR;
|
|
27
|
+
if (systemRoot && systemRoot.trim()) {
|
|
28
|
+
return join(systemRoot, "Temp");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return "C:/Windows/Temp";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const posixTempDir = process.env.TMPDIR ?? process.env.TMP;
|
|
35
|
+
if (posixTempDir && posixTempDir.trim()) {
|
|
36
|
+
return posixTempDir;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return "/tmp";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getTemporaryRtkHistoryDbPath(): string {
|
|
43
|
+
return join(resolveTemporaryDirectory(), "pi-rtk-optimizer", "history.db");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function quoteForShellEnv(value: string): string {
|
|
47
|
+
const normalizedValue = process.platform === "win32" ? value.replace(/\\/g, "/") : value;
|
|
48
|
+
return `"${normalizedValue.replace(/"/g, '\\"')}"`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hasLeadingRtkDbPathAssignment(command: string): boolean {
|
|
52
|
+
const trimmed = command.trimStart();
|
|
53
|
+
return (
|
|
54
|
+
RTK_DB_PATH_ASSIGNMENT_PATTERN.test(splitLeadingEnvAssignments(trimmed).envPrefix) ||
|
|
55
|
+
RTK_DB_PATH_EXPORT_PATTERN.test(trimmed)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function applyRtkCommandEnvironment(command: string): string {
|
|
60
|
+
if (!command.trim()) {
|
|
61
|
+
return command;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (hasLeadingRtkDbPathAssignment(command)) {
|
|
65
|
+
return command;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return `export ${RTK_DB_PATH_ENV_NAME}=${quoteForShellEnv(getTemporaryRtkHistoryDbPath())}; ${command}`;
|
|
69
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -7,6 +7,9 @@ export type RtkSourceFilterLevel = (typeof RTK_SOURCE_FILTER_LEVELS)[number];
|
|
|
7
7
|
export interface RtkOutputCompactionConfig {
|
|
8
8
|
enabled: boolean;
|
|
9
9
|
stripAnsi: boolean;
|
|
10
|
+
readCompaction: {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
};
|
|
10
13
|
truncate: {
|
|
11
14
|
enabled: boolean;
|
|
12
15
|
maxChars: number;
|
|
@@ -42,15 +45,18 @@ export const DEFAULT_RTK_INTEGRATION_CONFIG: RtkIntegrationConfig = {
|
|
|
42
45
|
outputCompaction: {
|
|
43
46
|
enabled: true,
|
|
44
47
|
stripAnsi: true,
|
|
48
|
+
readCompaction: {
|
|
49
|
+
enabled: false,
|
|
50
|
+
},
|
|
45
51
|
truncate: {
|
|
46
52
|
enabled: true,
|
|
47
53
|
maxChars: 12_000,
|
|
48
54
|
},
|
|
49
|
-
sourceCodeFilteringEnabled:
|
|
55
|
+
sourceCodeFilteringEnabled: false,
|
|
50
56
|
preserveExactSkillReads: false,
|
|
51
|
-
sourceCodeFiltering: "
|
|
57
|
+
sourceCodeFiltering: "none",
|
|
52
58
|
smartTruncate: {
|
|
53
|
-
enabled:
|
|
59
|
+
enabled: false,
|
|
54
60
|
maxLines: 220,
|
|
55
61
|
},
|
|
56
62
|
aggregateTestOutput: true,
|