pi-rtk-optimizer 0.5.4 → 0.6.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 -6
- package/README.md +34 -49
- package/config/config.example.json +0 -9
- package/package.json +64 -64
- package/src/additional-coverage-test.ts +13 -14
- package/src/command-rewriter-test.ts +118 -160
- package/src/command-rewriter.ts +43 -594
- package/src/config-modal-test.ts +3 -0
- package/src/config-modal.ts +1 -105
- package/src/config-store.ts +0 -24
- package/src/index-test.ts +80 -1
- package/src/index.ts +21 -6
- package/src/output-compactor-test.ts +4 -4
- 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 +0 -18
- 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}, sourceFilterEnabled=${config.outputCompaction.sourceCodeFilteringEnabled}, preserveSkillReads=${config.outputCompaction.preserveExactSkillReads}, sourceFilter=${config.outputCompaction.sourceCodeFiltering}, ${runtime}`;
|
|
76
62
|
}
|
|
77
63
|
|
|
78
64
|
function buildSettingItems(config: RtkIntegrationConfig): SettingItem[] {
|
|
@@ -210,69 +196,6 @@ function buildSettingItems(config: RtkIntegrationConfig): SettingItem[] {
|
|
|
210
196
|
currentValue: toOnOff(config.outputCompaction.trackSavings),
|
|
211
197
|
values: ON_OFF,
|
|
212
198
|
},
|
|
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
199
|
];
|
|
277
200
|
}
|
|
278
201
|
|
|
@@ -418,24 +341,6 @@ function applySetting(config: RtkIntegrationConfig, id: string, value: string):
|
|
|
418
341
|
trackSavings: value === "on",
|
|
419
342
|
},
|
|
420
343
|
};
|
|
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
344
|
default:
|
|
440
345
|
return config;
|
|
441
346
|
}
|
|
@@ -461,15 +366,6 @@ function syncSettingValues(settingsList: SettingValueSyncTarget, config: RtkInte
|
|
|
461
366
|
settingsList.updateValue("outputAggregateLinterOutput", toOnOff(config.outputCompaction.aggregateLinterOutput));
|
|
462
367
|
settingsList.updateValue("outputGroupSearchOutput", toOnOff(config.outputCompaction.groupSearchOutput));
|
|
463
368
|
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
369
|
}
|
|
474
370
|
|
|
475
371
|
async function openSettingsModal(ctx: ExtensionCommandContext, controller: RtkIntegrationController): Promise<void> {
|
package/src/config-store.ts
CHANGED
|
@@ -60,30 +60,6 @@ export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfi
|
|
|
60
60
|
source.showRewriteNotifications,
|
|
61
61
|
DEFAULT_RTK_INTEGRATION_CONFIG.showRewriteNotifications,
|
|
62
62
|
),
|
|
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
63
|
outputCompaction: {
|
|
88
64
|
enabled: toBoolean(
|
|
89
65
|
outputCompactionSource.enabled,
|
package/src/index-test.ts
CHANGED
|
@@ -28,7 +28,38 @@ mock.module("@mariozechner/pi-tui", () => ({
|
|
|
28
28
|
visibleWidth: (text: string) => text.length,
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
|
-
const { createBoundedNoticeTracker } = await import("./index.ts");
|
|
31
|
+
const { createBoundedNoticeTracker, shouldInjectSourceFilterTroubleshootingNote } = await import("./index.ts");
|
|
32
|
+
const { DEFAULT_RTK_INTEGRATION_CONFIG } = await import("./types.ts");
|
|
33
|
+
|
|
34
|
+
function configWith(overrides: {
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
compactionEnabled?: boolean;
|
|
37
|
+
sourceFilteringEnabled?: boolean;
|
|
38
|
+
sourceFilteringLevel?: "none" | "minimal" | "aggressive";
|
|
39
|
+
smartTruncateEnabled?: boolean;
|
|
40
|
+
truncateEnabled?: boolean;
|
|
41
|
+
}): typeof DEFAULT_RTK_INTEGRATION_CONFIG {
|
|
42
|
+
const base = DEFAULT_RTK_INTEGRATION_CONFIG;
|
|
43
|
+
return {
|
|
44
|
+
...base,
|
|
45
|
+
enabled: overrides.enabled ?? base.enabled,
|
|
46
|
+
outputCompaction: {
|
|
47
|
+
...base.outputCompaction,
|
|
48
|
+
enabled: overrides.compactionEnabled ?? base.outputCompaction.enabled,
|
|
49
|
+
sourceCodeFilteringEnabled:
|
|
50
|
+
overrides.sourceFilteringEnabled ?? base.outputCompaction.sourceCodeFilteringEnabled,
|
|
51
|
+
sourceCodeFiltering: overrides.sourceFilteringLevel ?? base.outputCompaction.sourceCodeFiltering,
|
|
52
|
+
smartTruncate: {
|
|
53
|
+
...base.outputCompaction.smartTruncate,
|
|
54
|
+
enabled: overrides.smartTruncateEnabled ?? base.outputCompaction.smartTruncate.enabled,
|
|
55
|
+
},
|
|
56
|
+
truncate: {
|
|
57
|
+
...base.outputCompaction.truncate,
|
|
58
|
+
enabled: overrides.truncateEnabled ?? base.outputCompaction.truncate.enabled,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
32
63
|
|
|
33
64
|
runTest("bounded notice tracker evicts old entries and supports reset", () => {
|
|
34
65
|
const tracker = createBoundedNoticeTracker(2);
|
|
@@ -52,4 +83,52 @@ runTest("bounded notice tracker coerces invalid limits to a safe minimum", () =>
|
|
|
52
83
|
assert.equal(tracker.remember("alpha"), true);
|
|
53
84
|
});
|
|
54
85
|
|
|
86
|
+
runTest("source-filter note injected when source filtering is active", () => {
|
|
87
|
+
assert.equal(
|
|
88
|
+
shouldInjectSourceFilterTroubleshootingNote(
|
|
89
|
+
configWith({ sourceFilteringEnabled: true, sourceFilteringLevel: "minimal" }),
|
|
90
|
+
),
|
|
91
|
+
true,
|
|
92
|
+
);
|
|
93
|
+
assert.equal(
|
|
94
|
+
shouldInjectSourceFilterTroubleshootingNote(
|
|
95
|
+
configWith({ sourceFilteringEnabled: true, sourceFilteringLevel: "aggressive" }),
|
|
96
|
+
),
|
|
97
|
+
true,
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
runTest("source-filter note skipped when extension is disabled", () => {
|
|
102
|
+
assert.equal(shouldInjectSourceFilterTroubleshootingNote(configWith({ enabled: false })), false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
runTest("source-filter note skipped when compaction is disabled", () => {
|
|
106
|
+
assert.equal(shouldInjectSourceFilterTroubleshootingNote(configWith({ compactionEnabled: false })), false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
runTest("source-filter note skipped when source filtering flag is off", () => {
|
|
110
|
+
assert.equal(
|
|
111
|
+
shouldInjectSourceFilterTroubleshootingNote(configWith({ sourceFilteringEnabled: false })),
|
|
112
|
+
false,
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
runTest("source-filter note skipped when filtering level is 'none'", () => {
|
|
117
|
+
assert.equal(
|
|
118
|
+
shouldInjectSourceFilterTroubleshootingNote(
|
|
119
|
+
configWith({ sourceFilteringEnabled: true, sourceFilteringLevel: "none" }),
|
|
120
|
+
),
|
|
121
|
+
false,
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
runTest("source-filter note skipped when all read filtering safeguards are disabled", () => {
|
|
126
|
+
assert.equal(
|
|
127
|
+
shouldInjectSourceFilterTroubleshootingNote(
|
|
128
|
+
configWith({ smartTruncateEnabled: false, truncateEnabled: false }),
|
|
129
|
+
),
|
|
130
|
+
false,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
55
134
|
console.log("All index tests passed.");
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,17 @@ function trimMessage(raw: string, maxLength = 220): string {
|
|
|
30
30
|
const SOURCE_FILTER_TROUBLESHOOTING_NOTE =
|
|
31
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 source filtering enabled', re-read the file, apply the edit, then ask the user to manually re-enable it in the Pi TUI.";
|
|
32
32
|
|
|
33
|
+
export function shouldInjectSourceFilterTroubleshootingNote(config: RtkIntegrationConfig): boolean {
|
|
34
|
+
const compaction = config.outputCompaction;
|
|
35
|
+
return (
|
|
36
|
+
config.enabled &&
|
|
37
|
+
compaction.enabled &&
|
|
38
|
+
compaction.sourceCodeFilteringEnabled &&
|
|
39
|
+
compaction.sourceCodeFiltering !== "none" &&
|
|
40
|
+
(compaction.smartTruncate.enabled || compaction.truncate.enabled)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
function mergeCompactionDetails(
|
|
34
45
|
existingDetails: unknown,
|
|
35
46
|
compaction: ToolResultCompactionMetadata,
|
|
@@ -112,7 +123,6 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
112
123
|
return;
|
|
113
124
|
}
|
|
114
125
|
|
|
115
|
-
console.warn(`[${EXTENSION_NAME}] ${message}`);
|
|
116
126
|
if (ctx.hasUI) {
|
|
117
127
|
ctx.ui.notify(message, level);
|
|
118
128
|
}
|
|
@@ -211,7 +221,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
211
221
|
};
|
|
212
222
|
|
|
213
223
|
const maybeWarnRtkMissing = (ctx: ExtensionContext): void => {
|
|
214
|
-
if (!config.enabled ||
|
|
224
|
+
if (!config.enabled || !config.guardWhenRtkMissing) {
|
|
215
225
|
return;
|
|
216
226
|
}
|
|
217
227
|
|
|
@@ -226,7 +236,8 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
226
236
|
|
|
227
237
|
missingRtkWarningShown = true;
|
|
228
238
|
const reason = runtimeStatus.lastError ? ` (${runtimeStatus.lastError})` : "";
|
|
229
|
-
|
|
239
|
+
const handling = config.mode === "suggest" ? "rewrite suggestions" : "command rewrite";
|
|
240
|
+
warnOnce(ctx, `${EXTENSION_NAME}: rtk binary unavailable, ${handling} bypassed${reason}.`);
|
|
230
241
|
};
|
|
231
242
|
|
|
232
243
|
const ensureRuntimeStatusFresh = async (): Promise<void> => {
|
|
@@ -316,6 +327,10 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
316
327
|
await ensureRuntimeStatusFresh();
|
|
317
328
|
maybeWarnRtkMissing(ctx);
|
|
318
329
|
|
|
330
|
+
if (!shouldInjectSourceFilterTroubleshootingNote(config)) {
|
|
331
|
+
return {};
|
|
332
|
+
}
|
|
333
|
+
|
|
319
334
|
if (event.systemPrompt.includes(SOURCE_FILTER_TROUBLESHOOTING_NOTE)) {
|
|
320
335
|
return {};
|
|
321
336
|
}
|
|
@@ -346,8 +361,8 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
346
361
|
return {};
|
|
347
362
|
}
|
|
348
363
|
|
|
349
|
-
const decision = computeRewriteDecision(event.input.command, config);
|
|
350
|
-
if (!decision.changed
|
|
364
|
+
const decision = await computeRewriteDecision(event.input.command, config, pi);
|
|
365
|
+
if (!decision.changed) {
|
|
351
366
|
return {};
|
|
352
367
|
}
|
|
353
368
|
|
|
@@ -361,7 +376,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
|
|
|
361
376
|
}
|
|
362
377
|
|
|
363
378
|
if (config.mode === "suggest") {
|
|
364
|
-
const suggestionKey = `${decision.
|
|
379
|
+
const suggestionKey = `${decision.originalCommand}:${decision.rewrittenCommand}`;
|
|
365
380
|
if (suggestionNotices.remember(suggestionKey) && ctx.hasUI) {
|
|
366
381
|
ctx.ui.notify(`RTK suggestion: ${decision.rewrittenCommand}`, "info");
|
|
367
382
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { mock } from "bun:test";
|
|
3
2
|
import { join } from "node:path";
|
|
3
|
+
import { mock } from "bun:test";
|
|
4
4
|
|
|
5
5
|
import { cloneDefaultConfig, runTest } from "./test-helpers.ts";
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const TEST_AGENT_DIR = "/tmp/.pi/agent";
|
|
8
8
|
|
|
9
9
|
mock.module("@mariozechner/pi-coding-agent", () => ({
|
|
10
|
-
getAgentDir: () =>
|
|
10
|
+
getAgentDir: () => TEST_AGENT_DIR,
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
13
|
const { compactToolResult } = await import("./output-compactor.ts");
|
|
@@ -223,7 +223,7 @@ runTest("skill reads stay exact when preserveExactSkillReads is enabled for user
|
|
|
223
223
|
const result = compactToolResult(
|
|
224
224
|
{
|
|
225
225
|
toolName: "read",
|
|
226
|
-
input: { path: join(
|
|
226
|
+
input: { path: join(TEST_AGENT_DIR, "skills", "example", "SKILL.md") },
|
|
227
227
|
content: [{ type: "text", text: content }],
|
|
228
228
|
},
|
|
229
229
|
config,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
export interface RtkRewriteProviderResult {
|
|
4
|
+
changed: boolean;
|
|
5
|
+
originalCommand: string;
|
|
6
|
+
rewrittenCommand: string;
|
|
7
|
+
exitCode: number;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isAlreadyRtk(command: string): boolean {
|
|
12
|
+
const trimmed = command.trimStart();
|
|
13
|
+
return trimmed === "rtk" || trimmed.startsWith("rtk ");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function resolveRtkRewrite(
|
|
17
|
+
pi: ExtensionAPI,
|
|
18
|
+
command: string,
|
|
19
|
+
timeoutMs = 3000,
|
|
20
|
+
): Promise<RtkRewriteProviderResult> {
|
|
21
|
+
if (!command || !command.trim()) {
|
|
22
|
+
return { changed: false, originalCommand: command, rewrittenCommand: command, exitCode: 1 };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (isAlreadyRtk(command)) {
|
|
26
|
+
return { changed: false, originalCommand: command, rewrittenCommand: command, exitCode: 1 };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const result = await pi.exec("rtk", ["rewrite", command], { timeout: timeoutMs });
|
|
31
|
+
|
|
32
|
+
if (result.code === 1) {
|
|
33
|
+
return { changed: false, originalCommand: command, rewrittenCommand: command, exitCode: 1 };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (result.code === 2) {
|
|
37
|
+
return {
|
|
38
|
+
changed: false,
|
|
39
|
+
originalCommand: command,
|
|
40
|
+
rewrittenCommand: command,
|
|
41
|
+
exitCode: 2,
|
|
42
|
+
error: result.stderr?.trim() || "rtk denied rewrite",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (result.code === 0 || result.code === 3) {
|
|
47
|
+
const rewritten = result.stdout?.trim();
|
|
48
|
+
if (!rewritten) {
|
|
49
|
+
return {
|
|
50
|
+
changed: false,
|
|
51
|
+
originalCommand: command,
|
|
52
|
+
rewrittenCommand: command,
|
|
53
|
+
exitCode: result.code,
|
|
54
|
+
error: "rtk returned empty output",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (rewritten === command) {
|
|
58
|
+
return {
|
|
59
|
+
changed: false,
|
|
60
|
+
originalCommand: command,
|
|
61
|
+
rewrittenCommand: command,
|
|
62
|
+
exitCode: result.code,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
changed: true,
|
|
67
|
+
originalCommand: command,
|
|
68
|
+
rewrittenCommand: rewritten,
|
|
69
|
+
exitCode: result.code,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
changed: false,
|
|
75
|
+
originalCommand: command,
|
|
76
|
+
rewrittenCommand: command,
|
|
77
|
+
exitCode: result.code,
|
|
78
|
+
error: `unexpected exit code ${result.code}`,
|
|
79
|
+
};
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
return {
|
|
83
|
+
changed: false,
|
|
84
|
+
originalCommand: command,
|
|
85
|
+
rewrittenCommand: command,
|
|
86
|
+
exitCode: -1,
|
|
87
|
+
error: message,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -21,13 +21,14 @@ runTest("rewrite mode still requires RTK availability when guard is enabled", ()
|
|
|
21
21
|
assert.equal(shouldSkipCommandHandlingWhenRtkMissing(config, runtimeStatus(true)), false);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
runTest("suggest mode
|
|
24
|
+
runTest("suggest mode uses RTK availability guard to avoid repeated missing-binary rewrite probes", () => {
|
|
25
25
|
const config = cloneDefaultConfig();
|
|
26
26
|
config.mode = "suggest";
|
|
27
27
|
config.guardWhenRtkMissing = true;
|
|
28
28
|
|
|
29
|
-
assert.equal(shouldRequireRtkAvailabilityForCommandHandling(config),
|
|
30
|
-
assert.equal(shouldSkipCommandHandlingWhenRtkMissing(config, runtimeStatus(false)),
|
|
29
|
+
assert.equal(shouldRequireRtkAvailabilityForCommandHandling(config), true);
|
|
30
|
+
assert.equal(shouldSkipCommandHandlingWhenRtkMissing(config, runtimeStatus(false)), true);
|
|
31
|
+
assert.equal(shouldSkipCommandHandlingWhenRtkMissing(config, runtimeStatus(true)), false);
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
runTest("guard disabled never blocks command handling", () => {
|
package/src/runtime-guard.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { RtkIntegrationConfig, RuntimeStatus } from "./types.js";
|
|
|
3
3
|
export function shouldRequireRtkAvailabilityForCommandHandling(
|
|
4
4
|
config: Pick<RtkIntegrationConfig, "mode" | "guardWhenRtkMissing">,
|
|
5
5
|
): boolean {
|
|
6
|
-
return config.
|
|
6
|
+
return config.guardWhenRtkMissing;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function shouldSkipCommandHandlingWhenRtkMissing(
|
package/src/types.ts
CHANGED
|
@@ -31,15 +31,6 @@ export interface RtkIntegrationConfig {
|
|
|
31
31
|
mode: RtkMode;
|
|
32
32
|
guardWhenRtkMissing: boolean;
|
|
33
33
|
showRewriteNotifications: boolean;
|
|
34
|
-
rewriteGitGithub: boolean;
|
|
35
|
-
rewriteFilesystem: boolean;
|
|
36
|
-
rewriteRust: boolean;
|
|
37
|
-
rewriteJavaScript: boolean;
|
|
38
|
-
rewritePython: boolean;
|
|
39
|
-
rewriteGo: boolean;
|
|
40
|
-
rewriteContainers: boolean;
|
|
41
|
-
rewriteNetwork: boolean;
|
|
42
|
-
rewritePackageManagers: boolean;
|
|
43
34
|
outputCompaction: RtkOutputCompactionConfig;
|
|
44
35
|
}
|
|
45
36
|
|
|
@@ -48,15 +39,6 @@ export const DEFAULT_RTK_INTEGRATION_CONFIG: RtkIntegrationConfig = {
|
|
|
48
39
|
mode: "rewrite",
|
|
49
40
|
guardWhenRtkMissing: true,
|
|
50
41
|
showRewriteNotifications: true,
|
|
51
|
-
rewriteGitGithub: true,
|
|
52
|
-
rewriteFilesystem: true,
|
|
53
|
-
rewriteRust: true,
|
|
54
|
-
rewriteJavaScript: true,
|
|
55
|
-
rewritePython: true,
|
|
56
|
-
rewriteGo: true,
|
|
57
|
-
rewriteContainers: true,
|
|
58
|
-
rewriteNetwork: true,
|
|
59
|
-
rewritePackageManagers: true,
|
|
60
42
|
outputCompaction: {
|
|
61
43
|
enabled: true,
|
|
62
44
|
stripAnsi: true,
|