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 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-toggle` - Enable/disable token reduction
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, homedir } from "path";
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 !== "none" && language !== "unknown") {
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-toggle", {
203
- description: "Toggle RTK token reduction on/off",
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 = !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 token reduction ${enabled ? "enabled" : "disabled"}`,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-rtk",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "RTK token reduction extension for pi-coding-agent - reduces LLM token consumption 60-90% by intelligently filtering tool output",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -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) => cmdLower.includes(tc.toLowerCase()));
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 {