@diegopetrucci/pi-extensions 0.1.57 → 0.1.59
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/.pi-fleet-tested-version +1 -1
- package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
- package/extensions/agent-workflow-audit/index.ts +8 -1
- package/extensions/agent-workflow-audit/package.json +5 -1
- package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-git-diff/package.json +5 -2
- package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-last-message/package.json +5 -2
- package/extensions/brrr/.pi-fleet-tested-version +1 -1
- package/extensions/brrr/README.md +2 -2
- package/extensions/brrr/package.json +5 -1
- package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
- package/extensions/claude-fast/package.json +5 -1
- package/extensions/code-reviewer/.pi-fleet-tested-version +1 -1
- package/extensions/code-reviewer/index.ts +129 -39
- package/extensions/code-reviewer/package.json +5 -1
- package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
- package/extensions/confirm-destructive/README.md +1 -1
- package/extensions/confirm-destructive/index.ts +20 -5
- package/extensions/confirm-destructive/package.json +5 -1
- package/extensions/context-cap/.pi-fleet-tested-version +1 -1
- package/extensions/context-cap/package.json +5 -1
- package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
- package/extensions/context-inspector/package.json +5 -1
- package/extensions/contrarian/.pi-fleet-tested-version +1 -1
- package/extensions/contrarian/README.md +1 -1
- package/extensions/contrarian/index.ts +199 -40
- package/extensions/contrarian/package.json +5 -1
- package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
- package/extensions/dirty-repo-guard/README.md +1 -1
- package/extensions/dirty-repo-guard/package.json +5 -1
- package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -1
- package/extensions/dynamic-context-pruning/package.json +4 -1
- package/extensions/git-footer/.pi-fleet-tested-version +1 -1
- package/extensions/git-footer/package.json +5 -1
- package/extensions/gnosis/.pi-fleet-tested-version +1 -1
- package/extensions/gnosis/package.json +5 -1
- package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
- package/extensions/illustrations-to-explain-things/package.json +4 -1
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/01-two-breakpoints.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/02-minimum-loop.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/03-sort-by-purpose.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/04-one-fish-many-uses.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/05-handoff-path.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/06-three-sources.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/07-three-content-jobs.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/08-handoff-copy-toolbox.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/09-common-pits-no-title.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/10-information-well.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/11-idea-press.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/12-content-fermentation.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/13-system-bearing.webp +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/14-trust-bridge.webp +0 -0
- package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
- package/extensions/inline-bash/README.md +1 -1
- package/extensions/inline-bash/package.json +5 -1
- package/extensions/librarian/.pi-fleet-tested-version +1 -1
- package/extensions/librarian/index.ts +393 -107
- package/extensions/librarian/package.json +5 -1
- package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
- package/extensions/minimal-footer/index.ts +2 -5
- package/extensions/minimal-footer/openai-usage.ts +58 -11
- package/extensions/minimal-footer/package.json +5 -1
- package/extensions/notify/.pi-fleet-tested-version +1 -1
- package/extensions/notify/README.md +3 -3
- package/extensions/notify/package.json +5 -1
- package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
- package/extensions/openai-fast/package.json +5 -1
- package/extensions/oracle/.pi-fleet-tested-version +1 -1
- package/extensions/oracle/README.md +1 -1
- package/extensions/oracle/index.ts +200 -41
- package/extensions/oracle/package.json +5 -1
- package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
- package/extensions/permission-gate/README.md +1 -1
- package/extensions/permission-gate/index.ts +453 -2
- package/extensions/permission-gate/package.json +5 -1
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/package.json +5 -1
- package/extensions/review/.pi-fleet-tested-version +1 -1
- package/extensions/review/index.ts +53 -19
- package/extensions/review/package.json +5 -1
- package/extensions/todo/.pi-fleet-tested-version +1 -1
- package/extensions/todo/README.md +1 -1
- package/extensions/todo/package.json +5 -1
- package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
- package/extensions/triage-comments/index.ts +115 -34
- package/extensions/triage-comments/package.json +5 -1
- package/package.json +10 -6
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/01-two-breakpoints.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/02-minimum-loop.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/03-sort-by-purpose.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/04-one-fish-many-uses.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/05-handoff-path.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/06-three-sources.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/07-three-content-jobs.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/08-handoff-copy-toolbox.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/09-common-pits-no-title.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/10-information-well.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/11-idea-press.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/12-content-fermentation.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/13-system-bearing.png +0 -0
- package/extensions/illustrations-to-explain-things/skills/illustrations-to-explain-things/assets/examples/14-trust-bridge.png +0 -0
package/.pi-fleet-tested-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -69,6 +69,13 @@ type ParsedArgs = {
|
|
|
69
69
|
error?: string;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
type CreateAgentSessionOptions = NonNullable<Parameters<typeof createAgentSession>[0]>;
|
|
73
|
+
|
|
74
|
+
function getModelRuntimeOption(ctx: { modelRegistry?: unknown }): Pick<CreateAgentSessionOptions, "modelRuntime"> {
|
|
75
|
+
const modelRuntime = (ctx.modelRegistry as { runtime?: CreateAgentSessionOptions["modelRuntime"] } | undefined)?.runtime;
|
|
76
|
+
return modelRuntime ? { modelRuntime } : {};
|
|
77
|
+
}
|
|
78
|
+
|
|
72
79
|
function parseArgs(args: string): ParsedArgs {
|
|
73
80
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
74
81
|
const focusParts: string[] = [];
|
|
@@ -1013,7 +1020,7 @@ async function runAudit(
|
|
|
1013
1020
|
const tools = options.planOnly ? ["read", "grep", "find", "ls"] : ["read", "grep", "find", "ls", "bash"];
|
|
1014
1021
|
const created = await createAgentSession({
|
|
1015
1022
|
cwd,
|
|
1016
|
-
|
|
1023
|
+
...getModelRuntimeOption(ctx),
|
|
1017
1024
|
resourceLoader,
|
|
1018
1025
|
settingsManager: isolatedSettingsManager,
|
|
1019
1026
|
sessionManager: SessionManager.inMemory(cwd),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-agent-workflow-audit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "A pi extension that runs an isolated repo workflow audit and returns only the final report to the main session.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -32,5 +32,9 @@
|
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@earendil-works/pi-coding-agent": "*",
|
|
34
34
|
"@earendil-works/pi-tui": "*"
|
|
35
|
+
},
|
|
36
|
+
"type": "module",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22.19.0"
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-annotate-git-diff",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "A standalone pi extension that adds /annotate-git-diff, a native Glimpse UI for annotating git diffs and sending feedback to the editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -46,5 +46,8 @@
|
|
|
46
46
|
"glimpseui": "^0.8.1",
|
|
47
47
|
"monaco-editor": "0.52.2"
|
|
48
48
|
},
|
|
49
|
-
"type": "module"
|
|
49
|
+
"type": "module",
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22.19.0"
|
|
52
|
+
}
|
|
50
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-annotate-last-message",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "A standalone pi extension that adds /annotate-last-message, a native Glimpse UI for annotating the latest assistant reply.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -41,5 +41,8 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"glimpseui": "^0.8.1"
|
|
43
43
|
},
|
|
44
|
-
"type": "module"
|
|
44
|
+
"type": "module",
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=22.19.0"
|
|
47
|
+
}
|
|
45
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# brrr
|
|
2
2
|
|
|
3
|
-
A pi extension that sends [brrr](https://brrr.now) push notifications when pi
|
|
3
|
+
A pi extension that sends [brrr](https://brrr.now) push notifications when pi has fully settled and is ready for input.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -81,6 +81,6 @@ Example:
|
|
|
81
81
|
|
|
82
82
|
## Notes
|
|
83
83
|
|
|
84
|
-
- Hooks the `
|
|
84
|
+
- Hooks the `agent_settled` event so automatic retries, compaction retries, and queued follow-ups do not trigger intermediate notifications.
|
|
85
85
|
- The extension sends directly to the brrr webhook; it does not require the `brrr` CLI at runtime.
|
|
86
86
|
- By default, notifications are skipped unless the Mac has been idle for at least 20 seconds.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-brrr",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "A pi extension that sends brrr push notifications when pi is ready for input.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -31,5 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@earendil-works/pi-coding-agent": "*"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.19.0"
|
|
34
38
|
}
|
|
35
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-claude-fast",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "A pi extension that enables Anthropic Claude Fast mode for supported Claude Opus models by injecting speed=fast.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -31,5 +31,9 @@
|
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@earendil-works/pi-coding-agent": "*"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.19.0"
|
|
34
38
|
}
|
|
35
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
} from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { Type } from "typebox";
|
|
13
13
|
|
|
14
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
15
|
+
|
|
14
16
|
const MAX_TOOL_CALLS_TO_KEEP = 80;
|
|
15
17
|
const MAX_TURNS = 8;
|
|
16
18
|
const MAX_RUN_MS = 8 * 60 * 1000;
|
|
@@ -220,7 +222,13 @@ type ModelRegistryContext = {
|
|
|
220
222
|
modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> };
|
|
221
223
|
};
|
|
222
224
|
|
|
223
|
-
type
|
|
225
|
+
type CreateAgentSessionOptions = NonNullable<Parameters<typeof createAgentSession>[0]>;
|
|
226
|
+
type CreateAgentSessionModel = CreateAgentSessionOptions["model"];
|
|
227
|
+
|
|
228
|
+
function getModelRuntimeOption(ctx: { modelRegistry?: unknown }): Pick<CreateAgentSessionOptions, "modelRuntime"> {
|
|
229
|
+
const modelRuntime = (ctx.modelRegistry as { runtime?: CreateAgentSessionOptions["modelRuntime"] } | undefined)?.runtime;
|
|
230
|
+
return modelRuntime ? { modelRuntime } : {};
|
|
231
|
+
}
|
|
224
232
|
|
|
225
233
|
const MODEL_AVAILABILITY_ERROR_PATTERN =
|
|
226
234
|
/\b(?:404|403|not[_ ]?found(?:[_ ]?error)?|model[_ ]?not[_ ]?found(?:[_ ]?error)?|no such model|unknown model|does not exist|is not available|not available|model[_ ]?not[_ ]?available|unsupported model|invalid model|forbidden|access[ _-]?denied|permission[ _-]?denied|not[ _-]?entitled|do(?:es)? not have access)\b/i;
|
|
@@ -457,26 +465,72 @@ function applyModelAttemptDetails(details: ReviewDetails, model: PiModel, thinki
|
|
|
457
465
|
details.thinkingLevelNote = thinking.note;
|
|
458
466
|
}
|
|
459
467
|
|
|
460
|
-
function
|
|
468
|
+
function createEmptyUsage(): Usage {
|
|
469
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } };
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function addUsage(total: Usage, usage: Usage): void {
|
|
473
|
+
total.input += usage.input || 0;
|
|
474
|
+
total.output += usage.output || 0;
|
|
475
|
+
total.cacheRead += usage.cacheRead || 0;
|
|
476
|
+
total.cacheWrite += usage.cacheWrite || 0;
|
|
477
|
+
if (usage.cacheWrite1h !== undefined) total.cacheWrite1h = (total.cacheWrite1h ?? 0) + usage.cacheWrite1h;
|
|
478
|
+
if (usage.reasoning !== undefined) total.reasoning = (total.reasoning ?? 0) + usage.reasoning;
|
|
479
|
+
total.totalTokens += usage.totalTokens || 0;
|
|
480
|
+
total.cost.input += usage.cost?.input || 0;
|
|
481
|
+
total.cost.output += usage.cost?.output || 0;
|
|
482
|
+
total.cost.cacheRead += usage.cost?.cacheRead || 0;
|
|
483
|
+
total.cost.cacheWrite += usage.cost?.cacheWrite || 0;
|
|
484
|
+
total.cost.total += usage.cost?.total || 0;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
type AssistantLikeMessage = { role?: string; content?: unknown; stopReason?: unknown; errorMessage?: unknown; usage?: unknown };
|
|
488
|
+
|
|
489
|
+
function addAssistantMessageUsage(total: Usage, message: unknown): void {
|
|
490
|
+
const assistant = message as AssistantLikeMessage;
|
|
491
|
+
if (assistant?.role !== "assistant" || !assistant.usage || typeof assistant.usage !== "object") return;
|
|
492
|
+
addUsage(total, assistant.usage as Usage);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function aggregateAssistantUsage(messages: unknown[]): Usage {
|
|
496
|
+
const total = createEmptyUsage();
|
|
497
|
+
for (const message of messages) addAssistantMessageUsage(total, message);
|
|
498
|
+
return total;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function addSessionEventUsage(total: Usage, event: unknown): void {
|
|
502
|
+
const sessionEvent = event as { type?: string; message?: unknown; result?: { usage?: Usage } };
|
|
503
|
+
if (sessionEvent?.type === "message_end") return void addAssistantMessageUsage(total, sessionEvent.message);
|
|
504
|
+
if (sessionEvent?.type === "compaction_end" && sessionEvent.result?.usage) addUsage(total, sessionEvent.result.usage);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function inspectFinalAssistant(messages: unknown[]):
|
|
508
|
+
| { ok: true; answer: string; stopReason?: string }
|
|
509
|
+
| { ok: false; reason: string; stopReason?: string; errorMessage?: string } {
|
|
461
510
|
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
462
|
-
const message = messages[i] as
|
|
463
|
-
if (message?.role !== "assistant"
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
511
|
+
const message = messages[i] as AssistantLikeMessage;
|
|
512
|
+
if (message?.role !== "assistant") continue;
|
|
513
|
+
const stopReason = typeof message.stopReason === "string" ? message.stopReason.trim() : "";
|
|
514
|
+
const errorMessage = typeof message.errorMessage === "string" ? message.errorMessage.trim() : "";
|
|
515
|
+
const answer = Array.isArray(message.content)
|
|
516
|
+
? message.content
|
|
517
|
+
.map((part) => (part && typeof part === "object" && (part as { type?: string }).type === "text" && typeof (part as { text?: unknown }).text === "string"
|
|
518
|
+
? (part as { text: string }).text
|
|
519
|
+
: ""))
|
|
520
|
+
.join("")
|
|
521
|
+
.trim()
|
|
522
|
+
: "";
|
|
523
|
+
if (stopReason === "error") return { ok: false, reason: `code_reviewer subagent error: ${errorMessage || "provider/model error"}`, stopReason, errorMessage };
|
|
524
|
+
if (stopReason === "aborted") return { ok: false, reason: "code_reviewer subagent aborted before producing a usable answer", stopReason };
|
|
525
|
+
if (answer) return { ok: true, answer, stopReason: stopReason || undefined };
|
|
526
|
+
return { ok: false, reason: stopReason ? `code_reviewer subagent produced no final assistant text (stopReason: ${stopReason})` : "code_reviewer subagent produced no final assistant text", stopReason: stopReason || undefined, errorMessage: errorMessage || undefined };
|
|
472
527
|
}
|
|
473
|
-
return "";
|
|
528
|
+
return { ok: false, reason: "code_reviewer subagent produced no assistant message" };
|
|
474
529
|
}
|
|
475
530
|
|
|
476
|
-
function
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return /aborted|cancelled|canceled/i.test(message);
|
|
531
|
+
function classifyRunFailure(error: unknown, aborted: boolean): { status: "aborted" | "error"; message: string; error?: string } {
|
|
532
|
+
const message = aborted ? "Aborted" : error instanceof Error ? error.message : String(error);
|
|
533
|
+
return { status: aborted ? "aborted" : "error", message, error: aborted ? undefined : message };
|
|
480
534
|
}
|
|
481
535
|
|
|
482
536
|
function isInside(parent: string, child: string): boolean {
|
|
@@ -1076,12 +1130,25 @@ export const __test__ = {
|
|
|
1076
1130
|
buildSafeGitCommand,
|
|
1077
1131
|
formatToolCall,
|
|
1078
1132
|
getBlockedBashReason,
|
|
1133
|
+
isModelAvailabilityError,
|
|
1079
1134
|
normalizeThinkingLevel,
|
|
1080
1135
|
resolveThinkingLevel,
|
|
1081
1136
|
selectCodeReviewerModel,
|
|
1137
|
+
aggregateAssistantUsage,
|
|
1138
|
+
addAssistantMessageUsage,
|
|
1139
|
+
addSessionEventUsage,
|
|
1140
|
+
inspectFinalAssistant,
|
|
1141
|
+
classifyRunFailure,
|
|
1082
1142
|
};
|
|
1083
1143
|
|
|
1084
1144
|
export default function codeReviewerExtension(pi: ExtensionAPI) {
|
|
1145
|
+
pi.on("tool_result", async (event) => {
|
|
1146
|
+
if (event.toolName !== "code_reviewer") return undefined;
|
|
1147
|
+
const details = event.details as ReviewDetails | undefined;
|
|
1148
|
+
if (details?.status !== "error") return undefined;
|
|
1149
|
+
return { isError: true };
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1085
1152
|
pi.registerTool({
|
|
1086
1153
|
name: "code_reviewer",
|
|
1087
1154
|
label: "Code reviewer",
|
|
@@ -1119,24 +1186,29 @@ export default function codeReviewerExtension(pi: ExtensionAPI) {
|
|
|
1119
1186
|
let unsubscribe: (() => void) | undefined;
|
|
1120
1187
|
let runTimeout: NodeJS.Timeout | undefined;
|
|
1121
1188
|
let abortListenerAdded = false;
|
|
1122
|
-
let
|
|
1189
|
+
let callerAborted = Boolean(signal?.aborted);
|
|
1190
|
+
const usage = createEmptyUsage();
|
|
1123
1191
|
|
|
1124
1192
|
const emit = () => {
|
|
1125
1193
|
onUpdate?.({ content: [{ type: "text", text: lastContent }], details });
|
|
1126
1194
|
};
|
|
1127
1195
|
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1196
|
+
const abortForCleanup = () => {
|
|
1197
|
+
void session?.abort();
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
const abortFromCaller = () => {
|
|
1201
|
+
callerAborted = true;
|
|
1130
1202
|
details.status = "aborted";
|
|
1131
1203
|
details.endedAt = Date.now();
|
|
1132
1204
|
lastContent = "Aborted";
|
|
1133
1205
|
emit();
|
|
1134
|
-
|
|
1206
|
+
abortForCleanup();
|
|
1135
1207
|
};
|
|
1136
1208
|
|
|
1137
|
-
if (signal?.aborted)
|
|
1209
|
+
if (signal?.aborted) abortFromCaller();
|
|
1138
1210
|
if (signal && !signal.aborted) {
|
|
1139
|
-
signal.addEventListener("abort",
|
|
1211
|
+
signal.addEventListener("abort", abortFromCaller);
|
|
1140
1212
|
abortListenerAdded = true;
|
|
1141
1213
|
}
|
|
1142
1214
|
|
|
@@ -1181,7 +1253,7 @@ export default function codeReviewerExtension(pi: ExtensionAPI) {
|
|
|
1181
1253
|
try {
|
|
1182
1254
|
const created = await createAgentSession({
|
|
1183
1255
|
cwd,
|
|
1184
|
-
|
|
1256
|
+
...getModelRuntimeOption(ctx),
|
|
1185
1257
|
resourceLoader,
|
|
1186
1258
|
settingsManager: isolatedSettingsManager,
|
|
1187
1259
|
sessionManager: SessionManager.inMemory(cwd),
|
|
@@ -1192,6 +1264,7 @@ export default function codeReviewerExtension(pi: ExtensionAPI) {
|
|
|
1192
1264
|
attemptSession = created.session;
|
|
1193
1265
|
session = attemptSession;
|
|
1194
1266
|
attemptUnsubscribe = attemptSession.subscribe((event) => {
|
|
1267
|
+
addSessionEventUsage(usage, event);
|
|
1195
1268
|
switch (event.type) {
|
|
1196
1269
|
case "turn_end":
|
|
1197
1270
|
details.turns += 1;
|
|
@@ -1228,20 +1301,29 @@ export default function codeReviewerExtension(pi: ExtensionAPI) {
|
|
|
1228
1301
|
});
|
|
1229
1302
|
unsubscribe = attemptUnsubscribe;
|
|
1230
1303
|
|
|
1231
|
-
if (!
|
|
1304
|
+
if (!callerAborted) {
|
|
1232
1305
|
const promptPromise = attemptSession.prompt(buildUserPrompt(input, cwd), { expandPromptTemplates: false });
|
|
1233
1306
|
const timeoutPromise = new Promise<never>((_resolve, reject) => {
|
|
1234
1307
|
runTimeout = setTimeout(() => {
|
|
1235
|
-
|
|
1308
|
+
abortForCleanup();
|
|
1236
1309
|
reject(new Error(`code_reviewer timed out after ${Math.round(MAX_RUN_MS / 1000)} seconds.`));
|
|
1237
1310
|
}, MAX_RUN_MS);
|
|
1238
1311
|
});
|
|
1239
1312
|
await Promise.race([promptPromise, timeoutPromise]);
|
|
1240
1313
|
}
|
|
1241
1314
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1315
|
+
const outcome = inspectFinalAssistant(attemptSession.state.messages);
|
|
1316
|
+
if (outcome.ok) {
|
|
1317
|
+
answer = outcome.answer;
|
|
1318
|
+
succeeded = true;
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
lastAttemptError = outcome.reason;
|
|
1322
|
+
const canFallBack =
|
|
1323
|
+
index < modelSelection.ordered.length - 1 &&
|
|
1324
|
+
outcome.stopReason === "error" &&
|
|
1325
|
+
isModelAvailabilityError(outcome.errorMessage || outcome.reason);
|
|
1326
|
+
if (!canFallBack) break;
|
|
1245
1327
|
} catch (error) {
|
|
1246
1328
|
const message = error instanceof Error ? error.message : String(error);
|
|
1247
1329
|
lastAttemptError = message;
|
|
@@ -1260,26 +1342,34 @@ export default function codeReviewerExtension(pi: ExtensionAPI) {
|
|
|
1260
1342
|
}
|
|
1261
1343
|
}
|
|
1262
1344
|
}
|
|
1263
|
-
if (!answer
|
|
1264
|
-
|
|
1265
|
-
|
|
1345
|
+
if (!answer) {
|
|
1346
|
+
if (callerAborted) {
|
|
1347
|
+
lastContent = "Aborted";
|
|
1348
|
+
details.status = "aborted";
|
|
1349
|
+
} else {
|
|
1350
|
+
throw new Error(lastAttemptError || "the isolated code_reviewer session finished without a usable answer");
|
|
1351
|
+
}
|
|
1352
|
+
} else {
|
|
1353
|
+
lastContent = answer;
|
|
1354
|
+
details.status = "done";
|
|
1355
|
+
}
|
|
1266
1356
|
details.endedAt = Date.now();
|
|
1267
1357
|
const report = appendRunDetails(lastContent, details);
|
|
1268
1358
|
lastContent = report;
|
|
1269
1359
|
emit();
|
|
1270
|
-
return { content: [{ type: "text", text: report }], details };
|
|
1360
|
+
return { content: [{ type: "text", text: report }], details, usage };
|
|
1271
1361
|
} catch (error) {
|
|
1272
|
-
const
|
|
1273
|
-
const message =
|
|
1274
|
-
details.status =
|
|
1275
|
-
details.error =
|
|
1362
|
+
const failure = classifyRunFailure(error, callerAborted);
|
|
1363
|
+
const { message } = failure;
|
|
1364
|
+
details.status = failure.status;
|
|
1365
|
+
details.error = failure.error;
|
|
1276
1366
|
details.endedAt = Date.now();
|
|
1277
1367
|
lastContent = appendRunDetails(`## Verdict\nReview failed.\n\n## Findings\n- major — Review execution failed. Evidence: ${message}. Why it matters: the requested read-only review did not complete.\n\n## Validation\n- none\n\n## Scope check\nReview could not be completed.\n\n## Run details\n- failure: ${message}`, details);
|
|
1278
1368
|
emit();
|
|
1279
|
-
return { content: [{ type: "text", text: lastContent }], details };
|
|
1369
|
+
return { content: [{ type: "text", text: lastContent }], details, usage };
|
|
1280
1370
|
} finally {
|
|
1281
1371
|
if (runTimeout) clearTimeout(runTimeout);
|
|
1282
|
-
if (signal && abortListenerAdded) signal.removeEventListener("abort",
|
|
1372
|
+
if (signal && abortListenerAdded) signal.removeEventListener("abort", abortFromCaller);
|
|
1283
1373
|
unsubscribe?.();
|
|
1284
1374
|
session?.dispose();
|
|
1285
1375
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-code-reviewer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "A standalone pi extension that adds a read-only code_reviewer subagent tool for isolated code reviews.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -31,5 +31,9 @@
|
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@earendil-works/pi-coding-agent": "*",
|
|
33
33
|
"typebox": "*"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.19.0"
|
|
34
38
|
}
|
|
35
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A small pi extension that asks for confirmation before destructive session actions.
|
|
4
4
|
|
|
5
|
-
This is adapted from the original `confirm-destructive.ts` example in [`earendil-works/pi
|
|
5
|
+
This is adapted from the original `confirm-destructive.ts` example in [`earendil-works/pi`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/confirm-destructive.ts) and kept basically the same.
|
|
6
6
|
|
|
7
7
|
## What it checks
|
|
8
8
|
|
|
@@ -5,7 +5,24 @@
|
|
|
5
5
|
* Demonstrates how to cancel session events using the before_* events.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { ExtensionAPI, SessionBeforeSwitchEvent,
|
|
8
|
+
import type { ExtensionAPI, SessionBeforeSwitchEvent, SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
function hasUnsavedUserWork(entries: SessionEntry[]): boolean {
|
|
11
|
+
let sawAssistant = false;
|
|
12
|
+
|
|
13
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
14
|
+
const entry = entries[index];
|
|
15
|
+
if (entry.type !== "message") continue;
|
|
16
|
+
if (entry.message.role === "assistant") {
|
|
17
|
+
sawAssistant = true;
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
if (entry.message.role === "user") return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (sawAssistant) return false;
|
|
24
|
+
return entries.some((entry) => entry.type === "message" && entry.message.role === "user");
|
|
25
|
+
}
|
|
9
26
|
|
|
10
27
|
export default function (pi: ExtensionAPI) {
|
|
11
28
|
pi.on("session_before_switch", async (event: SessionBeforeSwitchEvent, ctx) => {
|
|
@@ -24,11 +41,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
24
41
|
return;
|
|
25
42
|
}
|
|
26
43
|
|
|
27
|
-
// reason === "resume" - check if there are unsaved changes (messages
|
|
44
|
+
// reason === "resume" - check if there are unsaved changes (user messages newer than the last assistant response)
|
|
28
45
|
const entries = ctx.sessionManager.getEntries();
|
|
29
|
-
const hasUnsavedWork = entries
|
|
30
|
-
(e): e is SessionMessageEntry => e.type === "message" && e.message.role === "user",
|
|
31
|
-
);
|
|
46
|
+
const hasUnsavedWork = hasUnsavedUserWork(entries);
|
|
32
47
|
|
|
33
48
|
if (hasUnsavedWork) {
|
|
34
49
|
const confirmed = await ctx.ui.confirm(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-confirm-destructive",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "A pi extension that confirms destructive session actions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -29,5 +29,9 @@
|
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@earendil-works/pi-coding-agent": "*"
|
|
32
|
+
},
|
|
33
|
+
"type": "module",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.19.0"
|
|
32
36
|
}
|
|
33
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-context-cap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "A pi extension that caps effective model context windows at 200k tokens for earlier auto-compaction.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -30,5 +30,9 @@
|
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@earendil-works/pi-ai": "*",
|
|
32
32
|
"@earendil-works/pi-coding-agent": "*"
|
|
33
|
+
},
|
|
34
|
+
"type": "module",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22.19.0"
|
|
33
37
|
}
|
|
34
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-context-inspector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "A pi extension that opens a local HTML dashboard explaining where the current session context is going.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -30,5 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@earendil-works/pi-coding-agent": "*"
|
|
33
|
+
},
|
|
34
|
+
"type": "module",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22.19.0"
|
|
33
37
|
}
|
|
34
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.81.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# contrarian
|
|
2
2
|
|
|
3
|
-
A read-only contrarian subagent for [pi](https://github.com/earendil-works/pi
|
|
3
|
+
A read-only contrarian subagent for [pi](https://github.com/earendil-works/pi).
|
|
4
4
|
|
|
5
5
|
It adds a `contrarian` tool that spins up a separate pi subprocess to stress-test plans, designs, assumptions, bug hypotheses, review conclusions, and product directions by steelmanning the strongest credible opposing case.
|
|
6
6
|
|