pi-rtk-optimizer 0.5.5 → 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 +17 -0
- package/README.md +20 -41
- package/config/config.example.json +6 -12
- package/package.json +64 -64
- package/src/additional-coverage-test.ts +48 -6
- package/src/command-rewriter-test.ts +118 -160
- package/src/command-rewriter.ts +43 -594
- package/src/config-modal-test.ts +2 -0
- package/src/config-modal.ts +17 -105
- package/src/config-store.ts +32 -29
- package/src/index-test.ts +31 -2
- package/src/index.ts +8 -7
- 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/rtk-rewrite-provider.ts +90 -0
- package/src/runtime-guard-test.ts +4 -3
- package/src/runtime-guard.ts +1 -1
- package/src/types.ts +9 -21
- package/src/rewrite-bypass.ts +0 -332
- package/src/rewrite-rules.ts +0 -255
package/src/config-modal.ts
CHANGED
|
@@ -54,25 +54,11 @@ function parseIntegerInRange(value: string, min: number, max: number): number |
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function summarizeConfig(config: RtkIntegrationConfig, runtimeStatus: RuntimeStatus): string {
|
|
57
|
-
const categories = [
|
|
58
|
-
config.rewriteGitGithub ? "git" : "",
|
|
59
|
-
config.rewriteFilesystem ? "files" : "",
|
|
60
|
-
config.rewriteRust ? "rust" : "",
|
|
61
|
-
config.rewriteJavaScript ? "js" : "",
|
|
62
|
-
config.rewritePython ? "python" : "",
|
|
63
|
-
config.rewriteGo ? "go" : "",
|
|
64
|
-
config.rewriteContainers ? "containers" : "",
|
|
65
|
-
config.rewriteNetwork ? "network" : "",
|
|
66
|
-
config.rewritePackageManagers ? "packages" : "",
|
|
67
|
-
]
|
|
68
|
-
.filter((value) => value.length > 0)
|
|
69
|
-
.join(",");
|
|
70
|
-
|
|
71
57
|
const runtime = runtimeStatus.rtkAvailable
|
|
72
58
|
? "rtk=available"
|
|
73
59
|
: `rtk=missing${runtimeStatus.lastError ? ` (${runtimeStatus.lastError})` : ""}`;
|
|
74
60
|
|
|
75
|
-
return `enabled=${config.enabled}, mode=${config.mode}, rewriteNotice=${config.showRewriteNotifications}, compaction=${config.outputCompaction.enabled}, sourceFilterEnabled=${config.outputCompaction.sourceCodeFilteringEnabled}, preserveSkillReads=${config.outputCompaction.preserveExactSkillReads}, sourceFilter=${config.outputCompaction.sourceCodeFiltering},
|
|
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}`;
|
|
76
62
|
}
|
|
77
63
|
|
|
78
64
|
function buildSettingItems(config: RtkIntegrationConfig): SettingItem[] {
|
|
@@ -119,6 +105,13 @@ function buildSettingItems(config: RtkIntegrationConfig): SettingItem[] {
|
|
|
119
105
|
currentValue: toOnOff(config.outputCompaction.stripAnsi),
|
|
120
106
|
values: ON_OFF,
|
|
121
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
|
+
},
|
|
122
115
|
{
|
|
123
116
|
id: "outputTruncateEnabled",
|
|
124
117
|
label: "Hard truncation enabled",
|
|
@@ -210,69 +203,6 @@ function buildSettingItems(config: RtkIntegrationConfig): SettingItem[] {
|
|
|
210
203
|
currentValue: toOnOff(config.outputCompaction.trackSavings),
|
|
211
204
|
values: ON_OFF,
|
|
212
205
|
},
|
|
213
|
-
{
|
|
214
|
-
id: "rewriteGitGithub",
|
|
215
|
-
label: "Rewrite git / gh",
|
|
216
|
-
description: "git status/log/diff and gh pr/issue/run/api/release",
|
|
217
|
-
currentValue: toOnOff(config.rewriteGitGithub),
|
|
218
|
-
values: ON_OFF,
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
id: "rewriteFilesystem",
|
|
222
|
-
label: "Rewrite filesystem commands",
|
|
223
|
-
description: "cat, head, rg/grep, ls, tree, find, diff",
|
|
224
|
-
currentValue: toOnOff(config.rewriteFilesystem),
|
|
225
|
-
values: ON_OFF,
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
id: "rewriteRust",
|
|
229
|
-
label: "Rewrite Rust commands",
|
|
230
|
-
description: "cargo test/build/clippy/check/install/nextest/fmt",
|
|
231
|
-
currentValue: toOnOff(config.rewriteRust),
|
|
232
|
-
values: ON_OFF,
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
id: "rewriteJavaScript",
|
|
236
|
-
label: "Rewrite JavaScript/TypeScript",
|
|
237
|
-
description: "vitest, npm/npx/next, tsc, lint, prettier, playwright, prisma",
|
|
238
|
-
currentValue: toOnOff(config.rewriteJavaScript),
|
|
239
|
-
values: ON_OFF,
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
id: "rewritePython",
|
|
243
|
-
label: "Rewrite Python",
|
|
244
|
-
description: "pytest, ruff, pip, uv pip",
|
|
245
|
-
currentValue: toOnOff(config.rewritePython),
|
|
246
|
-
values: ON_OFF,
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
id: "rewriteGo",
|
|
250
|
-
label: "Rewrite Go",
|
|
251
|
-
description: "go test/build/vet and golangci-lint",
|
|
252
|
-
currentValue: toOnOff(config.rewriteGo),
|
|
253
|
-
values: ON_OFF,
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
id: "rewriteContainers",
|
|
257
|
-
label: "Rewrite containers",
|
|
258
|
-
description: "docker compose/ps/images/logs/run/build/exec and kubectl core ops",
|
|
259
|
-
currentValue: toOnOff(config.rewriteContainers),
|
|
260
|
-
values: ON_OFF,
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
id: "rewriteNetwork",
|
|
264
|
-
label: "Rewrite network",
|
|
265
|
-
description: "curl and wget",
|
|
266
|
-
currentValue: toOnOff(config.rewriteNetwork),
|
|
267
|
-
values: ON_OFF,
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
id: "rewritePackageManagers",
|
|
271
|
-
label: "Rewrite package managers",
|
|
272
|
-
description: "pnpm list/ls/outdated/build/typecheck and npm list/outdated",
|
|
273
|
-
currentValue: toOnOff(config.rewritePackageManagers),
|
|
274
|
-
values: ON_OFF,
|
|
275
|
-
},
|
|
276
206
|
];
|
|
277
207
|
}
|
|
278
208
|
|
|
@@ -296,6 +226,14 @@ function applySetting(config: RtkIntegrationConfig, id: string, value: string):
|
|
|
296
226
|
...config,
|
|
297
227
|
outputCompaction: { ...config.outputCompaction, stripAnsi: value === "on" },
|
|
298
228
|
};
|
|
229
|
+
case "outputReadCompactionEnabled":
|
|
230
|
+
return {
|
|
231
|
+
...config,
|
|
232
|
+
outputCompaction: {
|
|
233
|
+
...config.outputCompaction,
|
|
234
|
+
readCompaction: { enabled: value === "on" },
|
|
235
|
+
},
|
|
236
|
+
};
|
|
299
237
|
case "outputTruncateEnabled":
|
|
300
238
|
return {
|
|
301
239
|
...config,
|
|
@@ -418,24 +356,6 @@ function applySetting(config: RtkIntegrationConfig, id: string, value: string):
|
|
|
418
356
|
trackSavings: value === "on",
|
|
419
357
|
},
|
|
420
358
|
};
|
|
421
|
-
case "rewriteGitGithub":
|
|
422
|
-
return { ...config, rewriteGitGithub: value === "on" };
|
|
423
|
-
case "rewriteFilesystem":
|
|
424
|
-
return { ...config, rewriteFilesystem: value === "on" };
|
|
425
|
-
case "rewriteRust":
|
|
426
|
-
return { ...config, rewriteRust: value === "on" };
|
|
427
|
-
case "rewriteJavaScript":
|
|
428
|
-
return { ...config, rewriteJavaScript: value === "on" };
|
|
429
|
-
case "rewritePython":
|
|
430
|
-
return { ...config, rewritePython: value === "on" };
|
|
431
|
-
case "rewriteGo":
|
|
432
|
-
return { ...config, rewriteGo: value === "on" };
|
|
433
|
-
case "rewriteContainers":
|
|
434
|
-
return { ...config, rewriteContainers: value === "on" };
|
|
435
|
-
case "rewriteNetwork":
|
|
436
|
-
return { ...config, rewriteNetwork: value === "on" };
|
|
437
|
-
case "rewritePackageManagers":
|
|
438
|
-
return { ...config, rewritePackageManagers: value === "on" };
|
|
439
359
|
default:
|
|
440
360
|
return config;
|
|
441
361
|
}
|
|
@@ -448,6 +368,7 @@ function syncSettingValues(settingsList: SettingValueSyncTarget, config: RtkInte
|
|
|
448
368
|
settingsList.updateValue("guardWhenRtkMissing", toOnOff(config.guardWhenRtkMissing));
|
|
449
369
|
settingsList.updateValue("outputCompactionEnabled", toOnOff(config.outputCompaction.enabled));
|
|
450
370
|
settingsList.updateValue("outputStripAnsi", toOnOff(config.outputCompaction.stripAnsi));
|
|
371
|
+
settingsList.updateValue("outputReadCompactionEnabled", toOnOff(config.outputCompaction.readCompaction.enabled));
|
|
451
372
|
settingsList.updateValue("outputTruncateEnabled", toOnOff(config.outputCompaction.truncate.enabled));
|
|
452
373
|
settingsList.updateValue("outputTruncateMaxChars", String(config.outputCompaction.truncate.maxChars));
|
|
453
374
|
settingsList.updateValue("outputSourceFilteringEnabled", toOnOff(config.outputCompaction.sourceCodeFilteringEnabled));
|
|
@@ -461,15 +382,6 @@ function syncSettingValues(settingsList: SettingValueSyncTarget, config: RtkInte
|
|
|
461
382
|
settingsList.updateValue("outputAggregateLinterOutput", toOnOff(config.outputCompaction.aggregateLinterOutput));
|
|
462
383
|
settingsList.updateValue("outputGroupSearchOutput", toOnOff(config.outputCompaction.groupSearchOutput));
|
|
463
384
|
settingsList.updateValue("outputTrackSavings", toOnOff(config.outputCompaction.trackSavings));
|
|
464
|
-
settingsList.updateValue("rewriteGitGithub", toOnOff(config.rewriteGitGithub));
|
|
465
|
-
settingsList.updateValue("rewriteFilesystem", toOnOff(config.rewriteFilesystem));
|
|
466
|
-
settingsList.updateValue("rewriteRust", toOnOff(config.rewriteRust));
|
|
467
|
-
settingsList.updateValue("rewriteJavaScript", toOnOff(config.rewriteJavaScript));
|
|
468
|
-
settingsList.updateValue("rewritePython", toOnOff(config.rewritePython));
|
|
469
|
-
settingsList.updateValue("rewriteGo", toOnOff(config.rewriteGo));
|
|
470
|
-
settingsList.updateValue("rewriteContainers", toOnOff(config.rewriteContainers));
|
|
471
|
-
settingsList.updateValue("rewriteNetwork", toOnOff(config.rewriteNetwork));
|
|
472
|
-
settingsList.updateValue("rewritePackageManagers", toOnOff(config.rewritePackageManagers));
|
|
473
385
|
}
|
|
474
386
|
|
|
475
387
|
async function openSettingsModal(ctx: ExtensionCommandContext, controller: RtkIntegrationController): Promise<void> {
|
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),
|
|
@@ -60,30 +76,6 @@ export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfi
|
|
|
60
76
|
source.showRewriteNotifications,
|
|
61
77
|
DEFAULT_RTK_INTEGRATION_CONFIG.showRewriteNotifications,
|
|
62
78
|
),
|
|
63
|
-
rewriteGitGithub: toBoolean(
|
|
64
|
-
source.rewriteGitGithub,
|
|
65
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteGitGithub,
|
|
66
|
-
),
|
|
67
|
-
rewriteFilesystem: toBoolean(
|
|
68
|
-
source.rewriteFilesystem,
|
|
69
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteFilesystem,
|
|
70
|
-
),
|
|
71
|
-
rewriteRust: toBoolean(source.rewriteRust, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteRust),
|
|
72
|
-
rewriteJavaScript: toBoolean(
|
|
73
|
-
source.rewriteJavaScript,
|
|
74
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteJavaScript,
|
|
75
|
-
),
|
|
76
|
-
rewritePython: toBoolean(source.rewritePython, DEFAULT_RTK_INTEGRATION_CONFIG.rewritePython),
|
|
77
|
-
rewriteGo: toBoolean(source.rewriteGo, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteGo),
|
|
78
|
-
rewriteContainers: toBoolean(
|
|
79
|
-
source.rewriteContainers,
|
|
80
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteContainers,
|
|
81
|
-
),
|
|
82
|
-
rewriteNetwork: toBoolean(source.rewriteNetwork, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteNetwork),
|
|
83
|
-
rewritePackageManagers: toBoolean(
|
|
84
|
-
source.rewritePackageManagers,
|
|
85
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewritePackageManagers,
|
|
86
|
-
),
|
|
87
79
|
outputCompaction: {
|
|
88
80
|
enabled: toBoolean(
|
|
89
81
|
outputCompactionSource.enabled,
|
|
@@ -93,9 +85,17 @@ export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfi
|
|
|
93
85
|
outputCompactionSource.stripAnsi,
|
|
94
86
|
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.stripAnsi,
|
|
95
87
|
),
|
|
88
|
+
readCompaction: {
|
|
89
|
+
enabled: hasReadCompaction
|
|
90
|
+
? toBoolean(
|
|
91
|
+
readCompactionSource.enabled,
|
|
92
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.readCompaction.enabled,
|
|
93
|
+
)
|
|
94
|
+
: true,
|
|
95
|
+
},
|
|
96
96
|
sourceCodeFilteringEnabled: toBoolean(
|
|
97
97
|
outputCompactionSource.sourceCodeFilteringEnabled,
|
|
98
|
-
|
|
98
|
+
sourceFilteringFallback,
|
|
99
99
|
),
|
|
100
100
|
preserveExactSkillReads: toBoolean(
|
|
101
101
|
outputCompactionSource.preserveExactSkillReads,
|
|
@@ -113,11 +113,14 @@ export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfi
|
|
|
113
113
|
200_000,
|
|
114
114
|
),
|
|
115
115
|
},
|
|
116
|
-
sourceCodeFiltering: toSourceFilterLevel(
|
|
116
|
+
sourceCodeFiltering: toSourceFilterLevel(
|
|
117
|
+
outputCompactionSource.sourceCodeFiltering,
|
|
118
|
+
sourceFilterLevelFallback,
|
|
119
|
+
),
|
|
117
120
|
smartTruncate: {
|
|
118
121
|
enabled: toBoolean(
|
|
119
122
|
smartTruncateSource.enabled,
|
|
120
|
-
|
|
123
|
+
smartTruncateEnabledFallback,
|
|
121
124
|
),
|
|
122
125
|
maxLines: toInteger(
|
|
123
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)
|
|
@@ -123,7 +124,6 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
123
124
|
return;
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
console.warn(`[${EXTENSION_NAME}] ${message}`);
|
|
127
127
|
if (ctx.hasUI) {
|
|
128
128
|
ctx.ui.notify(message, level);
|
|
129
129
|
}
|
|
@@ -222,7 +222,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
222
222
|
};
|
|
223
223
|
|
|
224
224
|
const maybeWarnRtkMissing = (ctx: ExtensionContext): void => {
|
|
225
|
-
if (!config.enabled ||
|
|
225
|
+
if (!config.enabled || !config.guardWhenRtkMissing) {
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
228
|
|
|
@@ -237,7 +237,8 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
237
237
|
|
|
238
238
|
missingRtkWarningShown = true;
|
|
239
239
|
const reason = runtimeStatus.lastError ? ` (${runtimeStatus.lastError})` : "";
|
|
240
|
-
|
|
240
|
+
const handling = config.mode === "suggest" ? "rewrite suggestions" : "command rewrite";
|
|
241
|
+
warnOnce(ctx, `${EXTENSION_NAME}: rtk binary unavailable, ${handling} bypassed${reason}.`);
|
|
241
242
|
};
|
|
242
243
|
|
|
243
244
|
const ensureRuntimeStatusFresh = async (): Promise<void> => {
|
|
@@ -361,8 +362,8 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
361
362
|
return {};
|
|
362
363
|
}
|
|
363
364
|
|
|
364
|
-
const decision = computeRewriteDecision(event.input.command, config);
|
|
365
|
-
if (!decision.changed
|
|
365
|
+
const decision = await computeRewriteDecision(event.input.command, config, pi);
|
|
366
|
+
if (!decision.changed) {
|
|
366
367
|
return {};
|
|
367
368
|
}
|
|
368
369
|
|
|
@@ -376,7 +377,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
if (config.mode === "suggest") {
|
|
379
|
-
const suggestionKey = `${decision.
|
|
380
|
+
const suggestionKey = `${decision.originalCommand}:${decision.rewrittenCommand}`;
|
|
380
381
|
if (suggestionNotices.remember(suggestionKey) && ctx.hasUI) {
|
|
381
382
|
ctx.ui.notify(`RTK suggestion: ${decision.rewrittenCommand}`, "info");
|
|
382
383
|
}
|
|
@@ -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
|
}
|