pi-rtk 0.1.3 → 0.1.4
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/README.md +18 -3
- package/config.ts +8 -3
- package/index.ts +151 -7
- package/package.json +1 -1
- package/techniques/linter.ts +2 -0
- package/techniques/test-output.ts +5 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ Create `~/.pi/agent/rtk-config.json`:
|
|
|
53
53
|
"techniques": {
|
|
54
54
|
"ansiStripping": true,
|
|
55
55
|
"truncation": { "enabled": true, "maxChars": 10000 },
|
|
56
|
-
"sourceCodeFiltering": "minimal",
|
|
56
|
+
"sourceCodeFiltering": { "enabled": true, "level": "minimal" },
|
|
57
57
|
"smartTruncation": { "enabled": true, "maxLines": 200 },
|
|
58
58
|
"testOutputAggregation": true,
|
|
59
59
|
"buildOutputFiltering": true,
|
|
@@ -66,15 +66,30 @@ Create `~/.pi/agent/rtk-config.json`:
|
|
|
66
66
|
|
|
67
67
|
### Filter Levels
|
|
68
68
|
|
|
69
|
-
- `none`: No filtering applied
|
|
70
69
|
- `minimal`: Remove comments, normalize whitespace
|
|
71
70
|
- `aggressive`: Keep only signatures and structure
|
|
72
71
|
|
|
72
|
+
Source code filtering can be toggled independently of its level via commands or the `rtk_configure` tool.
|
|
73
|
+
|
|
73
74
|
## Commands
|
|
74
75
|
|
|
75
76
|
- `/rtk-stats` - Show token savings statistics
|
|
76
|
-
- `/rtk-
|
|
77
|
+
- `/rtk-on` / `/rtk-off` - Enable/disable token reduction
|
|
77
78
|
- `/rtk-clear` - Clear metrics history
|
|
79
|
+
- `/rtk-what` - Show current technique configuration
|
|
80
|
+
- `/rtk-toggle-ansiStripping` - Toggle ANSI stripping
|
|
81
|
+
- `/rtk-toggle-truncation` - Toggle output truncation
|
|
82
|
+
- `/rtk-toggle-sourceCodeFiltering` - Toggle source code filtering
|
|
83
|
+
- `/rtk-toggle-smartTruncation` - Toggle smart truncation
|
|
84
|
+
- `/rtk-toggle-testOutputAggregation` - Toggle test output aggregation
|
|
85
|
+
- `/rtk-toggle-buildOutputFiltering` - Toggle build output filtering
|
|
86
|
+
- `/rtk-toggle-gitCompaction` - Toggle git compaction
|
|
87
|
+
- `/rtk-toggle-searchResultGrouping` - Toggle search result grouping
|
|
88
|
+
- `/rtk-toggle-linterAggregation` - Toggle linter aggregation
|
|
89
|
+
|
|
90
|
+
## Agent Tool
|
|
91
|
+
|
|
92
|
+
The `rtk_configure` tool is registered for use by the AI agent to programmatically adjust any RTK parameter at runtime. This is particularly useful when file edits fail due to text-matching errors: the agent can temporarily disable `sourceCodeFiltering`, re-read the file, apply the edit, and re-enable filtering.
|
|
78
93
|
|
|
79
94
|
## Supported Languages
|
|
80
95
|
|
package/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from "fs/promises";
|
|
2
|
-
import { resolve
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
3
4
|
|
|
4
5
|
export type FilterLevel = "none" | "minimal" | "aggressive";
|
|
5
6
|
|
|
@@ -10,7 +11,7 @@ export interface RtkConfig {
|
|
|
10
11
|
techniques: {
|
|
11
12
|
ansiStripping: boolean;
|
|
12
13
|
truncation: { enabled: boolean; maxChars: number };
|
|
13
|
-
sourceCodeFiltering: FilterLevel;
|
|
14
|
+
sourceCodeFiltering: { enabled: boolean; level: FilterLevel };
|
|
14
15
|
smartTruncation: { enabled: boolean; maxLines: number };
|
|
15
16
|
testOutputAggregation: boolean;
|
|
16
17
|
buildOutputFiltering: boolean;
|
|
@@ -27,7 +28,7 @@ export const DEFAULT_CONFIG: RtkConfig = {
|
|
|
27
28
|
techniques: {
|
|
28
29
|
ansiStripping: true,
|
|
29
30
|
truncation: { enabled: true, maxChars: 10000 },
|
|
30
|
-
sourceCodeFiltering: "minimal",
|
|
31
|
+
sourceCodeFiltering: { enabled: true, level: "minimal" },
|
|
31
32
|
smartTruncation: { enabled: true, maxLines: 200 },
|
|
32
33
|
testOutputAggregation: true,
|
|
33
34
|
buildOutputFiltering: true,
|
|
@@ -55,6 +56,10 @@ export function mergeConfig(base: RtkConfig, override: Partial<RtkConfig>): RtkC
|
|
|
55
56
|
...base.techniques.truncation,
|
|
56
57
|
...(override.techniques?.truncation || {}),
|
|
57
58
|
},
|
|
59
|
+
sourceCodeFiltering: {
|
|
60
|
+
...base.techniques.sourceCodeFiltering,
|
|
61
|
+
...(override.techniques?.sourceCodeFiltering || {}),
|
|
62
|
+
},
|
|
58
63
|
smartTruncation: {
|
|
59
64
|
...base.techniques.smartTruncation,
|
|
60
65
|
...(override.techniques?.smartTruncation || {}),
|
package/index.ts
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
isReadToolResult,
|
|
12
12
|
isGrepToolResult,
|
|
13
13
|
} from "@mariozechner/pi-coding-agent";
|
|
14
|
+
import { Type } from "@sinclair/typebox";
|
|
15
|
+
import { StringEnum } from "@mariozechner/pi-ai";
|
|
14
16
|
import { loadConfig, DEFAULT_CONFIG, type RtkConfig } from "./config";
|
|
15
17
|
import { trackSavings, getMetricsSummary, clearMetrics } from "./metrics";
|
|
16
18
|
import {
|
|
@@ -55,6 +57,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
55
57
|
}
|
|
56
58
|
});
|
|
57
59
|
|
|
60
|
+
pi.on("before_agent_start", async (event, _ctx) => {
|
|
61
|
+
return {
|
|
62
|
+
systemPrompt: `${event.systemPrompt}\n\nIMPORTANT: If you encounter repeated failures to edit a file due to non-matching text or errors like \"The old text must match exactly including all whitespace and newlines\", use the rtk_configure tool to set sourceCodeFilteringEnabled to false, re-read the file, apply your edit, then use rtk_configure again to re-enable sourceCodeFilteringEnabled.`,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
58
66
|
pi.on("tool_result", async (event, ctx) => {
|
|
59
67
|
if (!enabled) return;
|
|
60
68
|
let technique = "";
|
|
@@ -140,8 +148,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
140
148
|
|
|
141
149
|
const originalText = textItem.text;
|
|
142
150
|
|
|
143
|
-
if (config.techniques.sourceCodeFiltering
|
|
144
|
-
let filteredText = filterSourceCode(originalText, language, config.techniques.sourceCodeFiltering);
|
|
151
|
+
if (config.techniques.sourceCodeFiltering.enabled && language !== "unknown") {
|
|
152
|
+
let filteredText = filterSourceCode(originalText, language, config.techniques.sourceCodeFiltering.level);
|
|
145
153
|
|
|
146
154
|
if (
|
|
147
155
|
config.techniques.smartTruncation.enabled &&
|
|
@@ -199,13 +207,73 @@ export default function (pi: ExtensionAPI) {
|
|
|
199
207
|
},
|
|
200
208
|
});
|
|
201
209
|
|
|
202
|
-
pi.registerCommand("rtk-
|
|
203
|
-
description: "
|
|
210
|
+
pi.registerCommand("rtk-on", {
|
|
211
|
+
description: "Enable RTK token reduction",
|
|
212
|
+
handler: async (_args, ctx) => {
|
|
213
|
+
enabled = true;
|
|
214
|
+
ctx.ui.notify("RTK token reduction enabled", "info");
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
pi.registerCommand("rtk-off", {
|
|
219
|
+
description: "Disable RTK token reduction",
|
|
204
220
|
handler: async (_args, ctx) => {
|
|
205
|
-
enabled =
|
|
221
|
+
enabled = false;
|
|
222
|
+
ctx.ui.notify("RTK token reduction disabled", "warning");
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const booleanTechniques = [
|
|
227
|
+
"ansiStripping",
|
|
228
|
+
"testOutputAggregation",
|
|
229
|
+
"buildOutputFiltering",
|
|
230
|
+
"gitCompaction",
|
|
231
|
+
"searchResultGrouping",
|
|
232
|
+
"linterAggregation",
|
|
233
|
+
] as const;
|
|
234
|
+
|
|
235
|
+
for (const technique of booleanTechniques) {
|
|
236
|
+
pi.registerCommand(`rtk-toggle-${technique}`, {
|
|
237
|
+
description: `Toggle the ${technique} technique on/off`,
|
|
238
|
+
handler: async (_args, ctx) => {
|
|
239
|
+
config.techniques[technique] = !config.techniques[technique];
|
|
240
|
+
ctx.ui.notify(
|
|
241
|
+
`RTK ${technique} ${config.techniques[technique] ? "enabled" : "disabled"}`,
|
|
242
|
+
config.techniques[technique] ? "info" : "warning"
|
|
243
|
+
);
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
pi.registerCommand("rtk-toggle-truncation", {
|
|
249
|
+
description: "Toggle output truncation on/off",
|
|
250
|
+
handler: async (_args, ctx) => {
|
|
251
|
+
config.techniques.truncation.enabled = !config.techniques.truncation.enabled;
|
|
206
252
|
ctx.ui.notify(
|
|
207
|
-
`RTK
|
|
208
|
-
enabled ? "info" : "warning"
|
|
253
|
+
`RTK truncation ${config.techniques.truncation.enabled ? "enabled" : "disabled"}`,
|
|
254
|
+
config.techniques.truncation.enabled ? "info" : "warning"
|
|
255
|
+
);
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
pi.registerCommand("rtk-toggle-sourceCodeFiltering", {
|
|
260
|
+
description: "Toggle source code filtering on/off",
|
|
261
|
+
handler: async (_args, ctx) => {
|
|
262
|
+
config.techniques.sourceCodeFiltering.enabled = !config.techniques.sourceCodeFiltering.enabled;
|
|
263
|
+
ctx.ui.notify(
|
|
264
|
+
`RTK sourceCodeFiltering ${config.techniques.sourceCodeFiltering.enabled ? "enabled" : "disabled"}`,
|
|
265
|
+
config.techniques.sourceCodeFiltering.enabled ? "info" : "warning"
|
|
266
|
+
);
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
pi.registerCommand("rtk-toggle-smartTruncation", {
|
|
271
|
+
description: "Toggle smart truncation on/off",
|
|
272
|
+
handler: async (_args, ctx) => {
|
|
273
|
+
config.techniques.smartTruncation.enabled = !config.techniques.smartTruncation.enabled;
|
|
274
|
+
ctx.ui.notify(
|
|
275
|
+
`RTK smartTruncation ${config.techniques.smartTruncation.enabled ? "enabled" : "disabled"}`,
|
|
276
|
+
config.techniques.smartTruncation.enabled ? "info" : "warning"
|
|
209
277
|
);
|
|
210
278
|
},
|
|
211
279
|
});
|
|
@@ -218,4 +286,80 @@ export default function (pi: ExtensionAPI) {
|
|
|
218
286
|
ctx.ui.notify("RTK metrics cleared", "info");
|
|
219
287
|
},
|
|
220
288
|
});
|
|
289
|
+
|
|
290
|
+
pi.registerCommand("rtk-what", {
|
|
291
|
+
description: "Show current RTK technique configuration",
|
|
292
|
+
handler: async (_args, ctx) => {
|
|
293
|
+
const t = config.techniques;
|
|
294
|
+
const summary = [
|
|
295
|
+
`RTK enabled: ${enabled}`,
|
|
296
|
+
`ansiStripping: ${t.ansiStripping}`,
|
|
297
|
+
`truncation: enabled=${t.truncation.enabled}, maxChars=${t.truncation.maxChars}`,
|
|
298
|
+
`sourceCodeFiltering: enabled=${t.sourceCodeFiltering.enabled}, level=${t.sourceCodeFiltering.level}`,
|
|
299
|
+
`smartTruncation: enabled=${t.smartTruncation.enabled}, maxLines=${t.smartTruncation.maxLines}`,
|
|
300
|
+
`testOutputAggregation: ${t.testOutputAggregation}`,
|
|
301
|
+
`buildOutputFiltering: ${t.buildOutputFiltering}`,
|
|
302
|
+
`gitCompaction: ${t.gitCompaction}`,
|
|
303
|
+
`searchResultGrouping: ${t.searchResultGrouping}`,
|
|
304
|
+
`linterAggregation: ${t.linterAggregation}`,
|
|
305
|
+
].join("\n");
|
|
306
|
+
ctx.ui.notify(summary, "info");
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
pi.registerTool({
|
|
311
|
+
name: "rtk_configure",
|
|
312
|
+
label: "RTK Configure",
|
|
313
|
+
description: `Configure RTK token-reduction techniques at runtime. Use this tool to tune filtering behavior when you encounter issues.
|
|
314
|
+
IMPORTANT: If you encounter repeated failures to edit a file due to non-matching text or errors like "The old text must match exactly including all whitespace and newlines", set this to false temporarily, re-read the file, apply your edit, then re-enable it.
|
|
315
|
+
`,
|
|
316
|
+
parameters: Type.Object({
|
|
317
|
+
ansiStripping: Type.Optional(Type.Boolean({ description: "Strip ANSI escape codes from output" })),
|
|
318
|
+
truncationEnabled: Type.Optional(Type.Boolean({ description: "Enable output truncation" })),
|
|
319
|
+
truncationMaxChars: Type.Optional(Type.Number({ description: "Maximum characters before truncation" })),
|
|
320
|
+
sourceCodeFilteringEnabled: Type.Optional(Type.Boolean({ description: "Enable source code filtering on file reads. Disable when edit patch matching fails." })),
|
|
321
|
+
sourceCodeFilteringLevel: Type.Optional(StringEnum(["minimal", "aggressive"] as const, { description: "Source code filtering aggressiveness. Use minimal for most cases, and aggressive when you only need to understand basics like function definitions and imports" })),
|
|
322
|
+
smartTruncationEnabled: Type.Optional(Type.Boolean({ description: "Enable smart truncation of source files" })),
|
|
323
|
+
smartTruncationMaxLines: Type.Optional(Type.Number({ description: "Maximum lines kept by smart truncation" })),
|
|
324
|
+
testOutputAggregation: Type.Optional(Type.Boolean({ description: "Aggregate test runner output into a summary" })),
|
|
325
|
+
buildOutputFiltering: Type.Optional(Type.Boolean({ description: "Filter build output to errors/warnings only" })),
|
|
326
|
+
gitCompaction: Type.Optional(Type.Boolean({ description: "Compact verbose git output" })),
|
|
327
|
+
searchResultGrouping: Type.Optional(Type.Boolean({ description: "Group grep/search results by file" })),
|
|
328
|
+
linterAggregation: Type.Optional(Type.Boolean({ description: "Summarise linter output into an issue table" })),
|
|
329
|
+
}),
|
|
330
|
+
|
|
331
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
332
|
+
const t = config.techniques;
|
|
333
|
+
|
|
334
|
+
if (params.ansiStripping !== undefined) t.ansiStripping = params.ansiStripping;
|
|
335
|
+
if (params.truncationEnabled !== undefined) t.truncation.enabled = params.truncationEnabled;
|
|
336
|
+
if (params.truncationMaxChars !== undefined) t.truncation.maxChars = params.truncationMaxChars;
|
|
337
|
+
if (params.sourceCodeFilteringEnabled !== undefined) t.sourceCodeFiltering.enabled = params.sourceCodeFilteringEnabled;
|
|
338
|
+
if (params.sourceCodeFilteringLevel !== undefined) t.sourceCodeFiltering.level = params.sourceCodeFilteringLevel;
|
|
339
|
+
if (params.smartTruncationEnabled !== undefined) t.smartTruncation.enabled = params.smartTruncationEnabled;
|
|
340
|
+
if (params.smartTruncationMaxLines !== undefined) t.smartTruncation.maxLines = params.smartTruncationMaxLines;
|
|
341
|
+
if (params.testOutputAggregation !== undefined) t.testOutputAggregation = params.testOutputAggregation;
|
|
342
|
+
if (params.buildOutputFiltering !== undefined) t.buildOutputFiltering = params.buildOutputFiltering;
|
|
343
|
+
if (params.gitCompaction !== undefined) t.gitCompaction = params.gitCompaction;
|
|
344
|
+
if (params.searchResultGrouping !== undefined) t.searchResultGrouping = params.searchResultGrouping;
|
|
345
|
+
if (params.linterAggregation !== undefined) t.linterAggregation = params.linterAggregation;
|
|
346
|
+
|
|
347
|
+
const summary = [
|
|
348
|
+
`ansiStripping: ${t.ansiStripping}`,
|
|
349
|
+
`truncation: enabled=${t.truncation.enabled}, maxChars=${t.truncation.maxChars}`,
|
|
350
|
+
`sourceCodeFiltering: enabled=${t.sourceCodeFiltering.enabled}, level=${t.sourceCodeFiltering.level}`,
|
|
351
|
+
`smartTruncation: enabled=${t.smartTruncation.enabled}, maxLines=${t.smartTruncation.maxLines}`,
|
|
352
|
+
`testOutputAggregation: ${t.testOutputAggregation}`,
|
|
353
|
+
`buildOutputFiltering: ${t.buildOutputFiltering}`,
|
|
354
|
+
`gitCompaction: ${t.gitCompaction}`,
|
|
355
|
+
`searchResultGrouping: ${t.searchResultGrouping}`,
|
|
356
|
+
`linterAggregation: ${t.linterAggregation}`,
|
|
357
|
+
].join("\n");
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
content: [{ type: "text", text: `RTK configuration updated:\n${summary}` }],
|
|
361
|
+
details: {},
|
|
362
|
+
};
|
|
363
|
+
},
|
|
364
|
+
});
|
|
221
365
|
}
|
package/package.json
CHANGED
package/techniques/linter.ts
CHANGED
|
@@ -52,9 +52,11 @@ function parseLine(line: string, linterType: string): Issue | null {
|
|
|
52
52
|
{
|
|
53
53
|
pattern: /^(.+):(\d+):(\d+):\s*(.+)$/,
|
|
54
54
|
extract: (match: RegExpMatchArray) => ({
|
|
55
|
+
severity: "ERROR" as "ERROR" | "WARNING",
|
|
55
56
|
file: match[1],
|
|
56
57
|
line: parseInt(match[2], 10),
|
|
57
58
|
content: match[4],
|
|
59
|
+
message: match[4],
|
|
58
60
|
}),
|
|
59
61
|
},
|
|
60
62
|
// error: message at file:line:col
|
|
@@ -40,7 +40,11 @@ export function isTestCommand(command: string | undefined | null): boolean {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const cmdLower = command.toLowerCase();
|
|
43
|
-
return TEST_COMMANDS.some((tc) =>
|
|
43
|
+
return TEST_COMMANDS.some((tc) => {
|
|
44
|
+
const escaped = tc.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45
|
+
// Match the test command only when it appears as a whole token (not as part of a longer word like "latest")
|
|
46
|
+
return new RegExp(`(?:^|[\\s|;&])${escaped}(?:[\\s|;&]|$)`).test(cmdLower);
|
|
47
|
+
});
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
function isFailureStart(line: string): boolean {
|