@diegopetrucci/pi-extensions 0.1.76 → 0.1.79

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.
Files changed (63) hide show
  1. package/README.md +8 -8
  2. package/extensions/agent-workflow-audit/README.md +1 -1
  3. package/extensions/agent-workflow-audit/index.ts +275 -59
  4. package/extensions/agent-workflow-audit/package.json +1 -1
  5. package/extensions/annotate-git-diff/git.ts +0 -8
  6. package/extensions/annotate-git-diff/glimpseui.d.ts +5 -65
  7. package/extensions/annotate-git-diff/package.json +1 -1
  8. package/extensions/annotate-last-message/glimpseui.d.ts +5 -65
  9. package/extensions/annotate-last-message/package.json +1 -1
  10. package/extensions/annotate-last-message/web/index.html +3 -30
  11. package/extensions/code-reviewer/README.md +5 -5
  12. package/extensions/code-reviewer/index.ts +252 -82
  13. package/extensions/code-reviewer/package.json +1 -1
  14. package/extensions/confirm-destructive/index.ts +2 -8
  15. package/extensions/confirm-destructive/package.json +1 -1
  16. package/extensions/context-cap/README.md +9 -9
  17. package/extensions/context-cap/index.ts +3 -3
  18. package/extensions/context-cap/package.json +2 -2
  19. package/extensions/context-inspector/index.ts +2 -46
  20. package/extensions/context-inspector/package.json +1 -1
  21. package/extensions/contrarian/README.md +4 -3
  22. package/extensions/contrarian/index.ts +48 -67
  23. package/extensions/contrarian/package.json +1 -1
  24. package/extensions/dirty-repo-guard/index.ts +0 -4
  25. package/extensions/dirty-repo-guard/package.json +1 -1
  26. package/extensions/dynamic-context-pruning/README.md +20 -11
  27. package/extensions/dynamic-context-pruning/index.ts +118 -341
  28. package/extensions/dynamic-context-pruning/package.json +1 -1
  29. package/extensions/fast/README.md +18 -11
  30. package/extensions/fast/index.ts +9 -5
  31. package/extensions/fast/package.json +1 -1
  32. package/extensions/git-footer/index.ts +2 -16
  33. package/extensions/git-footer/package.json +1 -1
  34. package/extensions/inline-bash/index.ts +0 -16
  35. package/extensions/inline-bash/package.json +1 -1
  36. package/extensions/librarian/README.md +1 -1
  37. package/extensions/librarian/index.ts +246 -70
  38. package/extensions/librarian/package.json +1 -1
  39. package/extensions/minimal-footer/README.md +5 -5
  40. package/extensions/minimal-footer/index.ts +5 -26
  41. package/extensions/minimal-footer/minimal-footer.example.json +1 -1
  42. package/extensions/minimal-footer/openai-usage.ts +2 -11
  43. package/extensions/minimal-footer/package.json +1 -1
  44. package/extensions/oracle/README.md +4 -3
  45. package/extensions/oracle/index.ts +46 -65
  46. package/extensions/oracle/package.json +1 -1
  47. package/extensions/permission-gate/README.md +3 -0
  48. package/extensions/permission-gate/index.ts +127 -36
  49. package/extensions/permission-gate/package.json +1 -1
  50. package/extensions/project-mcp-json/README.md +2 -2
  51. package/extensions/project-mcp-json/package.json +1 -1
  52. package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
  53. package/extensions/quiet-tools/README.md +30 -19
  54. package/extensions/quiet-tools/index.ts +233 -166
  55. package/extensions/quiet-tools/package.json +2 -2
  56. package/extensions/review/README.md +2 -0
  57. package/extensions/review/package.json +1 -1
  58. package/extensions/todo/index.ts +0 -19
  59. package/extensions/todo/package.json +1 -1
  60. package/extensions/triage-comments/README.md +4 -2
  61. package/extensions/triage-comments/index.ts +344 -39
  62. package/extensions/triage-comments/package.json +1 -1
  63. package/package.json +4 -4
@@ -1,53 +1,114 @@
1
1
  import { homedir } from "node:os";
2
2
 
3
3
  import {
4
- createBashToolDefinition,
5
- createEditToolDefinition,
6
- createFindToolDefinition,
7
- createGrepToolDefinition,
8
- createLsToolDefinition,
9
- createReadToolDefinition,
10
- createWriteToolDefinition,
11
4
  keyHint,
12
- SettingsManager,
13
5
  type ExtensionAPI,
14
- type ToolDefinition as PiToolDefinition,
15
- type ToolsOptions,
6
+ type ToolRendererResolver,
7
+ type ToolRenderers,
16
8
  } from "@earendil-works/pi-coding-agent";
17
- import { Container, Text, truncateToWidth } from "@earendil-works/pi-tui";
9
+ import { Box, Container, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
18
10
 
19
- const QUIET_CALL_TOOL_NAMES = new Set(["bash", "edit", "find", "grep", "ls", "read", "write"]);
11
+ const QUIET_TOOL_NAMES = new Set(["bash", "edit", "find", "grep", "ls", "read", "write"]);
12
+ const MAX_MCP_ARG_LENGTH = 2000;
20
13
 
21
- type ToolDefinition = PiToolDefinition<any, any, any>;
22
- type ToolRenderCall = NonNullable<ToolDefinition["renderCall"]>;
23
- type ToolRenderResult = NonNullable<ToolDefinition["renderResult"]>;
14
+ type ToolRenderCall = NonNullable<ToolRenderers["renderCall"]>;
15
+ type ToolRenderResult = NonNullable<ToolRenderers["renderResult"]>;
24
16
  type ToolRenderCallParams = Parameters<ToolRenderCall>;
25
17
  type ToolRenderResultParams = Parameters<ToolRenderResult>;
26
- type RenderTheme = ToolRenderResultParams[2];
27
- type ToolRenderContext = ToolRenderResultParams[3];
28
-
29
- type TimerRenderState = {
18
+ type RenderTheme = ToolRenderCallParams[1];
19
+ type ToolRenderContext = ToolRenderCallParams[2];
20
+ type QuietToolRenderContext = ToolRenderContext & {
21
+ durationMs?: number;
22
+ outputPad?: number;
23
+ };
24
+ type BashRenderState = {
30
25
  startedAt?: number;
31
26
  endedAt?: number;
32
27
  interval?: ReturnType<typeof setInterval>;
33
28
  };
34
29
 
30
+ type QuietLinesRenderer = (width: number) => string[];
31
+
35
32
  class QuietLinesRenderComponent extends Container {
36
- private linesRenderer: ((width: number) => string[]) | undefined;
33
+ private linesRenderer: QuietLinesRenderer | undefined;
37
34
 
38
- setLinesRenderer(linesRenderer: (width: number) => string[]): void {
35
+ setLinesRenderer(linesRenderer: QuietLinesRenderer): void {
39
36
  this.linesRenderer = linesRenderer;
40
37
  this.invalidate();
41
38
  }
42
39
 
43
- render(width: number): string[] {
40
+ protected renderLines(width: number): string[] {
44
41
  return this.linesRenderer?.(width).filter((line) => line) ?? [];
45
42
  }
43
+
44
+ render(width: number): string[] {
45
+ return this.renderLines(width);
46
+ }
47
+ }
48
+
49
+ class QuietCallRenderComponent extends QuietLinesRenderComponent {
50
+ private frame: Box | undefined;
51
+ private framePaddingX: number | undefined;
52
+
53
+ private readonly frameBody = {
54
+ render: (width: number) => this.renderLines(width),
55
+ invalidate: () => {},
56
+ };
57
+
58
+ setNativeFrame(theme: RenderTheme, context: QuietToolRenderContext): void {
59
+ const outputPad = getOutputPad(context);
60
+ if (!this.frame || this.framePaddingX !== outputPad) {
61
+ this.frame = new Box(outputPad, 1);
62
+ this.framePaddingX = outputPad;
63
+ }
64
+
65
+ const themeWithBackground = theme as RenderTheme & {
66
+ bg?: (kind: string, text: string) => string;
67
+ };
68
+ const backgroundKind = context.isPartial
69
+ ? "toolPendingBg"
70
+ : context.isError
71
+ ? "toolErrorBg"
72
+ : "toolSuccessBg";
73
+ const background = themeWithBackground.bg;
74
+ this.frame.setBgFn(
75
+ background ? (text) => background.call(themeWithBackground, backgroundKind, text) : undefined,
76
+ );
77
+ }
78
+
79
+ clearNativeFrame(): void {
80
+ this.frame = undefined;
81
+ this.framePaddingX = undefined;
82
+ }
83
+
84
+ render(width: number): string[] {
85
+ if (!this.frame) return super.render(width);
86
+
87
+ this.frame.clear();
88
+ this.frame.addChild(this.frameBody);
89
+ return this.frame.render(width);
90
+ }
91
+
92
+ invalidate(): void {
93
+ super.invalidate();
94
+ this.frame?.invalidate();
95
+ }
46
96
  }
47
97
 
48
- class QuietCallRenderComponent extends QuietLinesRenderComponent {}
49
98
  class QuietResultRenderComponent extends QuietLinesRenderComponent {}
50
99
 
100
+ /**
101
+ * Thrown from a delegating renderCall/renderResult slot when next() did not supply that slot.
102
+ * Pi's ToolExecutionComponent catches renderer errors and falls back to its native
103
+ * createCallFallback/createResultFallback, which preserve the host's expanded behavior.
104
+ */
105
+ class QuietToolsDelegateToDefault extends Error {
106
+ constructor() {
107
+ super("quiet-tools: delegate to Pi default renderer");
108
+ this.name = "QuietToolsDelegateToDefault";
109
+ }
110
+ }
111
+
51
112
  function sanitizeInlineText(text: string): string {
52
113
  return text
53
114
  .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "")
@@ -195,30 +256,69 @@ function formatQuietCallLine(toolName: string, args: unknown, theme: RenderTheme
195
256
  }
196
257
  }
197
258
 
259
+ function formatMcpCallLine(toolName: string, args: unknown, theme: RenderTheme): string {
260
+ let argsStr = "";
261
+ if (args != null) {
262
+ let raw: string | undefined;
263
+ try {
264
+ raw = JSON.stringify(args);
265
+ } catch {
266
+ // circular references, BigInt, etc.
267
+ }
268
+ if (raw === undefined) {
269
+ argsStr = "[\u2026]";
270
+ } else {
271
+ const capped = raw.length > MAX_MCP_ARG_LENGTH ? raw.slice(0, MAX_MCP_ARG_LENGTH) : raw;
272
+ argsStr = sanitizeInlineText(capped);
273
+ }
274
+ }
275
+ return `${formatToolTitle(toolName, theme)}${argsStr ? ` ${theme.fg("toolOutput", argsStr)}` : ""}`;
276
+ }
277
+
198
278
  function formatExpandHint(theme: RenderTheme): string {
199
279
  return `${theme.fg("muted", "(")}${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
200
280
  }
201
281
 
202
- function markToolTiming(options: ToolRenderResultParams[1], context: ToolRenderContext): void {
203
- const state = context.state as TimerRenderState;
282
+ function formatFailedMarker(theme: RenderTheme): string {
283
+ return theme.fg("error", " [failed]");
284
+ }
204
285
 
205
- if ((!options.isPartial || context.isError) && state.startedAt !== undefined) {
206
- state.endedAt ??= Date.now();
207
- }
286
+ function truncateQuietCallLine(line: string, status: string, width: number): string {
287
+ if (!status) return truncateToWidth(line, width, "...");
288
+ const statusWidth = visibleWidth(status);
289
+ if (statusWidth >= width) return truncateToWidth(status, width, "");
290
+ return `${truncateToWidth(line, width - statusWidth, "...")}${status}`;
291
+ }
292
+
293
+ function getOutputPad(context: QuietToolRenderContext): number {
294
+ const outputPad = context.outputPad;
295
+ if (typeof outputPad !== "number" || !Number.isFinite(outputPad)) return 1;
296
+ return Math.max(0, Math.floor(outputPad));
297
+ }
208
298
 
209
- if ((!options.isPartial || context.isError) && state.interval) {
210
- clearInterval(state.interval);
211
- state.interval = undefined;
299
+ function finishBashTiming(
300
+ options: ToolRenderResultParams[1],
301
+ context: QuietToolRenderContext,
302
+ ): void {
303
+ const state = context.state as BashRenderState;
304
+
305
+ if (!options.isPartial || context.isError) {
306
+ state.endedAt ??= Date.now();
307
+ if (state.interval) {
308
+ clearInterval(state.interval);
309
+ state.interval = undefined;
310
+ }
212
311
  }
213
312
  }
214
313
 
215
314
  function renderQuietCollapsedResult(
315
+ toolName: string,
216
316
  _result: ToolRenderResultParams[0],
217
317
  options: ToolRenderResultParams[1],
218
318
  _theme: RenderTheme,
219
- context: ToolRenderContext,
319
+ context: QuietToolRenderContext,
220
320
  ): QuietResultRenderComponent {
221
- markToolTiming(options, context);
321
+ if (toolName === "bash") finishBashTiming(options, context);
222
322
  const component =
223
323
  context.lastComponent instanceof QuietResultRenderComponent
224
324
  ? context.lastComponent
@@ -227,169 +327,136 @@ function renderQuietCollapsedResult(
227
327
  return component;
228
328
  }
229
329
 
230
- function renderQuietCall(
231
- toolName: string,
232
- base: ToolDefinition,
330
+ function delegateCall(
331
+ nextRenderers: ToolRenderers | undefined,
233
332
  args: ToolRenderCallParams[0],
234
- theme: ToolRenderCallParams[1],
235
- context: ToolRenderCallParams[2],
236
- ) {
237
- const state = context.state as TimerRenderState;
238
- if (context.executionStarted && state.startedAt === undefined) {
239
- state.startedAt = Date.now();
240
- state.endedAt = undefined;
241
- }
242
-
243
- if (context.expanded || !QUIET_CALL_TOOL_NAMES.has(toolName)) {
244
- const delegateContext =
245
- context.lastComponent instanceof QuietCallRenderComponent
246
- ? { ...context, lastComponent: undefined }
247
- : context;
248
- return (
249
- base.renderCall?.(args, theme, delegateContext) ??
250
- new Text(theme.fg("toolTitle", theme.bold(toolName)), 0, 0)
251
- );
252
- }
253
-
254
- const component =
333
+ theme: RenderTheme,
334
+ context: ToolRenderContext,
335
+ ): ReturnType<ToolRenderCall> {
336
+ if (!nextRenderers?.renderCall) throw new QuietToolsDelegateToDefault();
337
+ const delegateContext =
255
338
  context.lastComponent instanceof QuietCallRenderComponent
256
- ? context.lastComponent
257
- : new QuietCallRenderComponent();
258
- const line = formatQuietCallLine(toolName, args, theme);
259
- const hint = formatExpandHint(theme);
260
- component.setLinesRenderer((width) => [
261
- truncateToWidth(line, width, "..."),
262
- truncateToWidth(hint, width, "..."),
263
- ]);
264
- return component;
339
+ ? { ...context, lastComponent: undefined }
340
+ : context;
341
+ return nextRenderers.renderCall(args, theme, delegateContext);
342
+ }
343
+
344
+ function delegateResult(
345
+ nextRenderers: ToolRenderers | undefined,
346
+ result: ToolRenderResultParams[0],
347
+ options: ToolRenderResultParams[1],
348
+ theme: RenderTheme,
349
+ context: ToolRenderContext,
350
+ ): ReturnType<ToolRenderResult> {
351
+ if (!nextRenderers?.renderResult) throw new QuietToolsDelegateToDefault();
352
+ const delegateContext =
353
+ context.lastComponent instanceof QuietResultRenderComponent
354
+ ? { ...context, lastComponent: undefined }
355
+ : context;
356
+ return nextRenderers.renderResult(result, options, theme, delegateContext);
265
357
  }
266
358
 
267
- function createQuietToolDefinition(base: ToolDefinition): ToolDefinition {
268
- const baseRenderResult = base.renderResult;
359
+ export function createQuietResolver(): ToolRendererResolver {
360
+ return (toolName, next) => {
361
+ const isBuiltin = QUIET_TOOL_NAMES.has(toolName);
362
+ const isMcp = toolName.startsWith("mcp__");
269
363
 
270
- return {
271
- ...base,
272
- renderCall(args, theme, context) {
273
- return renderQuietCall(base.name, base, args, theme, context);
274
- },
275
- renderResult(result, options, theme, context) {
276
- if (options.expanded && baseRenderResult) {
277
- const delegateContext =
278
- context.lastComponent instanceof QuietResultRenderComponent
279
- ? { ...context, lastComponent: undefined }
280
- : context;
281
- return baseRenderResult(result, options, theme, delegateContext);
282
- }
364
+ if (!isBuiltin && !isMcp) return next();
283
365
 
284
- return renderQuietCollapsedResult(result, options, theme, context);
285
- },
286
- };
287
- }
366
+ const nextRenderers = next();
367
+ const usesNativeShell = nextRenderers?.renderShell === "self";
288
368
 
289
- function createBaseToolOptions(cwd: string): ToolsOptions | undefined {
290
- try {
291
- const settings = SettingsManager.create(cwd);
292
369
  return {
293
- read: { autoResizeImages: settings.getImageAutoResize() },
294
- bash: {
295
- commandPrefix: settings.getShellCommandPrefix(),
296
- shellPath: settings.getShellPath(),
370
+ renderShell: nextRenderers?.renderShell,
371
+ renderCall(args, theme, context) {
372
+ if (context.expanded) return delegateCall(nextRenderers, args, theme, context);
373
+
374
+ const quietContext = context as QuietToolRenderContext;
375
+ if (toolName === "bash") {
376
+ const state = quietContext.state as BashRenderState;
377
+ if (quietContext.executionStarted && state.startedAt === undefined) {
378
+ state.startedAt = Date.now();
379
+ state.endedAt = undefined;
380
+ }
381
+ }
382
+
383
+ const component =
384
+ context.lastComponent instanceof QuietCallRenderComponent
385
+ ? context.lastComponent
386
+ : new QuietCallRenderComponent();
387
+ if (usesNativeShell) component.setNativeFrame(theme, quietContext);
388
+ else component.clearNativeFrame();
389
+
390
+ const line = isBuiltin
391
+ ? formatQuietCallLine(toolName, args, theme)
392
+ : formatMcpCallLine(toolName, args, theme);
393
+ const status = quietContext.isError ? formatFailedMarker(theme) : "";
394
+ const hint = formatExpandHint(theme);
395
+ component.setLinesRenderer((width) => [
396
+ truncateQuietCallLine(line, status, width),
397
+ truncateToWidth(hint, width, "..."),
398
+ ]);
399
+ return component;
400
+ },
401
+ renderResult(result, options, theme, context) {
402
+ if (options.expanded) return delegateResult(nextRenderers, result, options, theme, context);
403
+ return renderQuietCollapsedResult(
404
+ toolName,
405
+ result,
406
+ options,
407
+ theme,
408
+ context as QuietToolRenderContext,
409
+ );
297
410
  },
298
411
  };
299
- } catch {
300
- return undefined;
301
- }
302
- }
303
-
304
- function createBaseToolDefinitions(cwd: string): ToolDefinition[] {
305
- const options = createBaseToolOptions(cwd);
306
- return [
307
- createReadToolDefinition(cwd, options?.read),
308
- createBashToolDefinition(cwd, options?.bash),
309
- createEditToolDefinition(cwd, options?.edit),
310
- createWriteToolDefinition(cwd, options?.write),
311
- createGrepToolDefinition(cwd, options?.grep),
312
- createFindToolDefinition(cwd, options?.find),
313
- createLsToolDefinition(cwd, options?.ls),
314
- ] as ToolDefinition[];
315
- }
316
-
317
- function createQuietToolDefinitions(cwd: string, enabled: boolean): ToolDefinition[] {
318
- const baseDefinitions = createBaseToolDefinitions(cwd);
319
- return enabled ? baseDefinitions.map(createQuietToolDefinition) : baseDefinitions;
412
+ };
320
413
  }
321
414
 
322
415
  export const __testing = {
323
416
  sanitizeInlineText,
324
417
  formatQuietCallLine,
325
- createQuietToolDefinition,
418
+ formatMcpCallLine,
419
+ createQuietResolver,
420
+ QuietCallRenderComponent,
421
+ QuietResultRenderComponent,
422
+ QuietToolsDelegateToDefault,
326
423
  };
327
424
 
425
+ const STATUS_MESSAGE =
426
+ "Quiet tool previews are always on. Collapsed built-in and MCP tool rows show a one-line invocation and hide output until expanded. Model-visible tool results are unchanged.";
427
+ const FALLBACK_STATUS_MESSAGE =
428
+ "Quiet tool previews are inactive: this Pi host lacks registerToolRenderer (requires Pi >=1.0.1).";
429
+ const STATUS_USAGE = "Usage: /quiet-tools status";
430
+
328
431
  export default function quietToolsExtension(pi: ExtensionAPI) {
329
- let enabled = true;
432
+ let notifiedFallback = false;
330
433
 
331
- function registerTools(cwd: string): void {
332
- for (const tool of createQuietToolDefinitions(cwd, enabled)) {
333
- pi.registerTool(tool);
334
- }
335
- }
434
+ const hasRenderer = typeof (pi as any).registerToolRenderer === "function";
435
+
436
+ if (hasRenderer) pi.registerToolRenderer(createQuietResolver());
336
437
 
337
438
  pi.on("session_start", async (_event, ctx) => {
338
- registerTools(ctx.cwd);
439
+ if (!hasRenderer && !notifiedFallback) {
440
+ notifiedFallback = true;
441
+ ctx.ui.notify(
442
+ "quiet-tools needs Pi >=1.0.1 to customise tool renderers. Quiet rendering is inactive.",
443
+ "warning",
444
+ );
445
+ }
339
446
  });
340
447
 
341
448
  pi.registerCommand("quiet-tools", {
342
- description: "Toggle one-line collapsed invocations for built-in tool rows",
449
+ description: "Show always-on one-line collapsed invocations for built-in and MCP tool rows",
343
450
  getArgumentCompletions: (prefix) => {
344
- const commands = ["on", "off", "toggle", "status"];
345
451
  const query = prefix.trim().toLowerCase();
346
- const matches = commands.filter((command) => command.startsWith(query));
347
- return matches.length > 0 ? matches.map((value) => ({ value, label: value })) : null;
452
+ return "status".startsWith(query) ? [{ value: "status", label: "status" }] : null;
348
453
  },
349
454
  handler: async (args, ctx) => {
350
- const action = args.trim().toLowerCase() || "toggle";
351
-
352
- if (action === "on" || action === "enable") {
353
- enabled = true;
354
- registerTools(ctx.cwd);
355
- ctx.ui.notify(
356
- "Quiet tool previews enabled: collapsed built-in tool rows show a one-line invocation plus an expand hint.",
357
- "info",
358
- );
455
+ if (!args.trim() || args.trim().toLowerCase() === "status") {
456
+ ctx.ui.notify(hasRenderer ? STATUS_MESSAGE : FALLBACK_STATUS_MESSAGE, "info");
359
457
  return;
360
458
  }
361
-
362
- if (action === "off" || action === "disable") {
363
- enabled = false;
364
- registerTools(ctx.cwd);
365
- ctx.ui.notify(
366
- "Quiet tool previews disabled: restored pi's standard built-in tool renderers.",
367
- "info",
368
- );
369
- return;
370
- }
371
-
372
- if (action === "toggle") {
373
- enabled = !enabled;
374
- registerTools(ctx.cwd);
375
- ctx.ui.notify(
376
- enabled
377
- ? "Quiet tool previews enabled: collapsed built-in tool rows show a one-line invocation plus an expand hint."
378
- : "Quiet tool previews disabled: restored pi's standard built-in tool renderers.",
379
- "info",
380
- );
381
- return;
382
- }
383
-
384
- if (action === "status") {
385
- ctx.ui.notify(
386
- `Quiet tool previews are ${enabled ? "enabled" : "disabled"}. Collapsed tool rows ${enabled ? "show a one-line invocation and hide output until expanded" : "use pi's default rendering"}. Model-visible tool results are unchanged.`,
387
- "info",
388
- );
389
- return;
390
- }
391
-
392
- ctx.ui.notify("Usage: /quiet-tools on | off | toggle | status", "warning");
459
+ ctx.ui.notify(STATUS_USAGE, "warning");
393
460
  },
394
461
  });
395
462
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-quiet-tools",
3
- "version": "0.1.13",
4
- "description": "A pi extension that visually compacts collapsed built-in tool rows in the TUI without changing tool results sent to the model.",
3
+ "version": "0.1.14",
4
+ "description": "A pi extension that visually compacts collapsed built-in tool rows and MCP tool rows in the TUI without changing tool results sent to the model.",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "pi",
@@ -29,7 +29,9 @@ Then reload pi:
29
29
 
30
30
  ## Notes
31
31
 
32
+ - `/review` requires interactive mode (TUI); `/end-review` requires a UI.
32
33
  - This fork adds loop fixing, custom review instructions persisted in the session, a blocking-findings parser, and `/end-review` handoff modes (return only, return and summarize, or return and fix findings). Loop fixing cycles until the parser reports no blocking findings: tagged P0–P2 items, or a "needs attention" verdict when no priority tags are present. Commit review is excluded from loop fixing.
34
+ - Loop fixing always uses Empty branch mode and skips the Empty branch / Current session choice.
33
35
  - PR review requires `gh` access and a clean working tree for tracked files.
34
36
  - If the project is trusted and a `REVIEW_GUIDELINES.md` file exists next to the repo's `<pi-config-dir>` directory, its contents are appended to the review prompt. `<pi-config-dir>` is Pi's runtime config directory name (`CONFIG_DIR_NAME`; `.pi` by default).
35
37
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-review",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "A standalone pi extension that adds /review and /end-review commands adapted from mitsuhiko/agent-stuff.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1,15 +1,3 @@
1
- /**
2
- * Todo Extension - Demonstrates state management via session entries
3
- *
4
- * This extension:
5
- * - Registers a `todo` tool for the LLM to manage todos
6
- * - Registers a `/todos` command for users to view the list
7
- *
8
- * State is stored in tool result details (not external files), which allows
9
- * proper branching - when you branch, the todo state is automatically
10
- * correct for that point in history.
11
- */
12
-
13
1
  import { StringEnum } from "@earendil-works/pi-ai";
14
2
  import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
15
3
  import { matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
@@ -36,9 +24,6 @@ const TodoParams = Type.Object({
36
24
  id: Type.Optional(Type.Number({ description: "Todo ID (for toggle)" })),
37
25
  });
38
26
 
39
- /**
40
- * UI component for the /todos command
41
- */
42
27
  class TodoListComponent {
43
28
  private todos: Todo[];
44
29
  private theme: Theme;
@@ -109,7 +94,6 @@ class TodoListComponent {
109
94
  }
110
95
 
111
96
  export default function (pi: ExtensionAPI) {
112
- // In-memory state (reconstructed from session on load)
113
97
  let todos: Todo[] = [];
114
98
  let nextId = 1;
115
99
 
@@ -134,11 +118,9 @@ export default function (pi: ExtensionAPI) {
134
118
  }
135
119
  };
136
120
 
137
- // Reconstruct state on session events
138
121
  pi.on("session_start", async (_event, ctx) => reconstructState(ctx));
139
122
  pi.on("session_tree", async (_event, ctx) => reconstructState(ctx));
140
123
 
141
- // Register the todo tool for the LLM
142
124
  pi.registerTool({
143
125
  name: "todo",
144
126
  label: "Todo",
@@ -303,7 +285,6 @@ export default function (pi: ExtensionAPI) {
303
285
  },
304
286
  });
305
287
 
306
- // Register the /todos command for users
307
288
  pi.registerCommand("todos", {
308
289
  description: "Show all todos on the current branch",
309
290
  handler: async (_args, ctx) => {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-todo",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "A pi extension that adds a branch-aware todo tool and /todos viewer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1,6 +1,6 @@
1
1
  # triage-comments
2
2
 
3
- Adds `/triage-comments` plus a read-only `triage_comments` subagent tool for evidence-based review-comment triage.
3
+ Adds `/triage-comments` plus a `triage_comments` subagent tool with read-only built-in tools and configured MCP tools for evidence-based review-comment triage.
4
4
 
5
5
  Use it when you want `/triage-comments` to inspect selected PR feedback, classify whether each comment is valid, cite local evidence, draft review-thread responses, and propose handling options. It does **not** implement changes; after triage, the main agent should ask which handling option to take before editing anything.
6
6
 
@@ -71,7 +71,8 @@ The tool accepts selected comments and optional PR, base, diff, and caller conte
71
71
  When called, it launches an isolated in-memory subagent with:
72
72
 
73
73
  - no inherited extensions, skills, prompt templates, themes, context files, or agents files;
74
- - read-only tools only: `read`, `grep`, `find`, `ls`, and guarded `bash`;
74
+ - read-only built-in tools: `read`, `grep`, `find`, `ls`, and guarded `bash`;
75
+ - configured MCP tools (run without confirmation in the subagent; may have side effects; use for evidence gathering only; requires host Pi >=1.0.4; older hosts degrade to no MCP, no error); the global `mcp.json` always applies; a project `.pi/mcp.json` is loaded only when the host session has already trusted the project;
75
76
  - a local-checkout path guard for file inspection;
76
77
  - a bash guard that allows only direct read-only `git`, `gh`, or `pwd` invocations and blocks write/edit tools, shell pipelines, redirection, destructive git commands, and mutating `gh`/GitHub API calls;
77
78
  - a fixed triage output format with verdicts, evidence, reasoning, suggested responses, handling options, and a list of read-only checks performed.
@@ -83,6 +84,7 @@ Verdicts are one of `valid`, `invalid`, `partially valid`, `subjective`, or `nee
83
84
  - The slash command only collects/selects comments and asks the main agent to run triage.
84
85
  - The subagent is explicitly instructed not to implement changes.
85
86
  - Runtime guards block write tools, filesystem mutation through shell syntax, mutating git commands, and mutating GitHub CLI/API calls.
87
+ - Runtime guards apply to built-in tools only; configured MCP tools pass the guard, run without confirmation, and are limited only by prompt guidance.
86
88
  - The generated prompt tells the main agent to summarize findings and ask which handling option to take before implementation.
87
89
  - Suggested responses are drafts for the user/agent to adapt; the extension does not post them to GitHub.
88
90