@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.
- package/README.md +8 -8
- package/extensions/agent-workflow-audit/README.md +1 -1
- package/extensions/agent-workflow-audit/index.ts +275 -59
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/git.ts +0 -8
- package/extensions/annotate-git-diff/glimpseui.d.ts +5 -65
- package/extensions/annotate-git-diff/package.json +1 -1
- package/extensions/annotate-last-message/glimpseui.d.ts +5 -65
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/annotate-last-message/web/index.html +3 -30
- package/extensions/code-reviewer/README.md +5 -5
- package/extensions/code-reviewer/index.ts +252 -82
- package/extensions/code-reviewer/package.json +1 -1
- package/extensions/confirm-destructive/index.ts +2 -8
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/README.md +9 -9
- package/extensions/context-cap/index.ts +3 -3
- package/extensions/context-cap/package.json +2 -2
- package/extensions/context-inspector/index.ts +2 -46
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/README.md +4 -3
- package/extensions/contrarian/index.ts +48 -67
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dirty-repo-guard/index.ts +0 -4
- package/extensions/dirty-repo-guard/package.json +1 -1
- package/extensions/dynamic-context-pruning/README.md +20 -11
- package/extensions/dynamic-context-pruning/index.ts +118 -341
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/fast/README.md +18 -11
- package/extensions/fast/index.ts +9 -5
- package/extensions/fast/package.json +1 -1
- package/extensions/git-footer/index.ts +2 -16
- package/extensions/git-footer/package.json +1 -1
- package/extensions/inline-bash/index.ts +0 -16
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/README.md +1 -1
- package/extensions/librarian/index.ts +246 -70
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/README.md +5 -5
- package/extensions/minimal-footer/index.ts +5 -26
- package/extensions/minimal-footer/minimal-footer.example.json +1 -1
- package/extensions/minimal-footer/openai-usage.ts +2 -11
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/oracle/README.md +4 -3
- package/extensions/oracle/index.ts +46 -65
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/README.md +3 -0
- package/extensions/permission-gate/index.ts +127 -36
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/project-mcp-json/README.md +2 -2
- package/extensions/project-mcp-json/package.json +1 -1
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/README.md +30 -19
- package/extensions/quiet-tools/index.ts +233 -166
- package/extensions/quiet-tools/package.json +2 -2
- package/extensions/review/README.md +2 -0
- package/extensions/review/package.json +1 -1
- package/extensions/todo/index.ts +0 -19
- package/extensions/todo/package.json +1 -1
- package/extensions/triage-comments/README.md +4 -2
- package/extensions/triage-comments/index.ts +344 -39
- package/extensions/triage-comments/package.json +1 -1
- 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
|
|
15
|
-
type
|
|
6
|
+
type ToolRendererResolver,
|
|
7
|
+
type ToolRenderers,
|
|
16
8
|
} from "@earendil-works/pi-coding-agent";
|
|
17
|
-
import {
|
|
9
|
+
import { Box, Container, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
18
10
|
|
|
19
|
-
const
|
|
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
|
|
22
|
-
type
|
|
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 =
|
|
27
|
-
type ToolRenderContext =
|
|
28
|
-
|
|
29
|
-
|
|
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:
|
|
33
|
+
private linesRenderer: QuietLinesRenderer | undefined;
|
|
37
34
|
|
|
38
|
-
setLinesRenderer(linesRenderer:
|
|
35
|
+
setLinesRenderer(linesRenderer: QuietLinesRenderer): void {
|
|
39
36
|
this.linesRenderer = linesRenderer;
|
|
40
37
|
this.invalidate();
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
|
|
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
|
|
203
|
-
|
|
282
|
+
function formatFailedMarker(theme: RenderTheme): string {
|
|
283
|
+
return theme.fg("error", " [failed]");
|
|
284
|
+
}
|
|
204
285
|
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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:
|
|
319
|
+
context: QuietToolRenderContext,
|
|
220
320
|
): QuietResultRenderComponent {
|
|
221
|
-
|
|
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
|
|
231
|
-
|
|
232
|
-
base: ToolDefinition,
|
|
330
|
+
function delegateCall(
|
|
331
|
+
nextRenderers: ToolRenderers | undefined,
|
|
233
332
|
args: ToolRenderCallParams[0],
|
|
234
|
-
theme:
|
|
235
|
-
context:
|
|
236
|
-
) {
|
|
237
|
-
|
|
238
|
-
|
|
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
|
|
257
|
-
:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
]
|
|
264
|
-
|
|
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
|
|
268
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
|
432
|
+
let notifiedFallback = false;
|
|
330
433
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
package/extensions/todo/index.ts
CHANGED
|
@@ -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
|
# triage-comments
|
|
2
2
|
|
|
3
|
-
Adds `/triage-comments` plus a
|
|
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
|
|
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
|
|