@diegopetrucci/pi-extensions 0.1.78 → 0.1.80

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 (46) hide show
  1. package/README.md +1 -1
  2. package/extensions/agent-workflow-audit/index.ts +3 -20
  3. package/extensions/agent-workflow-audit/package.json +1 -1
  4. package/extensions/annotate-git-diff/glimpseui.d.ts +5 -65
  5. package/extensions/annotate-git-diff/package.json +1 -1
  6. package/extensions/annotate-last-message/glimpseui.d.ts +5 -65
  7. package/extensions/annotate-last-message/package.json +1 -1
  8. package/extensions/annotate-last-message/prompt.ts +2 -8
  9. package/extensions/annotate-last-message/web/index.html +0 -27
  10. package/extensions/code-reviewer/index.ts +2 -46
  11. package/extensions/code-reviewer/package.json +1 -1
  12. package/extensions/context-cap/README.md +9 -9
  13. package/extensions/context-cap/index.ts +3 -3
  14. package/extensions/context-cap/package.json +2 -2
  15. package/extensions/context-inspector/index.ts +2 -46
  16. package/extensions/context-inspector/package.json +1 -1
  17. package/extensions/contrarian/index.ts +5 -55
  18. package/extensions/contrarian/package.json +1 -1
  19. package/extensions/dynamic-context-pruning/README.md +24 -7
  20. package/extensions/dynamic-context-pruning/index.ts +21 -130
  21. package/extensions/dynamic-context-pruning/package.json +1 -1
  22. package/extensions/fast/README.md +16 -9
  23. package/extensions/fast/index.ts +9 -5
  24. package/extensions/fast/package.json +1 -1
  25. package/extensions/git-footer/index.ts +2 -16
  26. package/extensions/git-footer/package.json +1 -1
  27. package/extensions/librarian/index.ts +8 -46
  28. package/extensions/librarian/package.json +1 -1
  29. package/extensions/minimal-footer/README.md +5 -5
  30. package/extensions/minimal-footer/index.ts +3 -17
  31. package/extensions/minimal-footer/minimal-footer.example.json +1 -1
  32. package/extensions/minimal-footer/openai-usage.ts +2 -11
  33. package/extensions/minimal-footer/package.json +1 -1
  34. package/extensions/oracle/index.ts +5 -55
  35. package/extensions/oracle/package.json +1 -1
  36. package/extensions/permission-gate/index.ts +31 -63
  37. package/extensions/permission-gate/package.json +1 -1
  38. package/extensions/project-mcp-json/config.ts +2 -1
  39. package/extensions/project-mcp-json/package.json +1 -1
  40. package/extensions/quiet-tools/index.ts +15 -6
  41. package/extensions/quiet-tools/package.json +1 -1
  42. package/extensions/review/README.md +2 -0
  43. package/extensions/review/package.json +1 -1
  44. package/extensions/triage-comments/index.ts +1 -22
  45. package/extensions/triage-comments/package.json +1 -1
  46. package/package.json +4 -4
@@ -3,7 +3,7 @@
3
3
  "showPercent": true,
4
4
  "dumbZone": {
5
5
  "enabled": true,
6
- "thresholdTokens": 300000,
6
+ "thresholdTokens": 350000,
7
7
  "label": "DUMB ZONE",
8
8
  "color": "error"
9
9
  }
@@ -15,7 +15,6 @@ export type UsageAvailability = "unsupported" | "unavailable";
15
15
 
16
16
  interface WhamUsageWindow {
17
17
  limit_window_seconds?: number;
18
- reset_at?: number;
19
18
  used_percent?: number;
20
19
  }
21
20
 
@@ -29,7 +28,6 @@ interface WhamUsageResponse {
29
28
  export interface UsageWindow {
30
29
  usedPercent?: number;
31
30
  windowSeconds?: number;
32
- resetAt?: number;
33
31
  }
34
32
 
35
33
  export interface UsageSnapshot {
@@ -59,19 +57,12 @@ function normalizeWindowSeconds(value?: number): number | undefined {
59
57
  return value;
60
58
  }
61
59
 
62
- function normalizeResetAt(value?: number): number | undefined {
63
- if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
64
- return value * 1000;
65
- }
66
-
67
60
  function parseUsageWindow(window?: WhamUsageWindow): UsageWindow | undefined {
68
61
  if (!window) return undefined;
69
62
  const usedPercent = normalizeUsedPercent(window.used_percent);
70
63
  const windowSeconds = normalizeWindowSeconds(window.limit_window_seconds);
71
- const resetAt = normalizeResetAt(window.reset_at);
72
- if (usedPercent === undefined && windowSeconds === undefined && resetAt === undefined)
73
- return undefined;
74
- return { usedPercent, windowSeconds, resetAt };
64
+ if (usedPercent === undefined && windowSeconds === undefined) return undefined;
65
+ return { usedPercent, windowSeconds };
75
66
  }
76
67
 
77
68
  function parseUsageSnapshot(data: WhamUsageResponse): Omit<UsageSnapshot, "fetchedAt"> {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-minimal-footer",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "A minimal custom footer for pi.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -202,23 +202,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
202
202
  "azure-openai-responses": AZURE_MODEL_PREFERENCES,
203
203
  baseten: [
204
204
  "moonshotai/Kimi-K3",
205
- "moonshotai/Kimi-K2.7-Code ",
206
205
  "zai-org/GLM-5.3 ",
207
206
  "deepseek-ai/DeepSeek-V4-Pro-0813",
208
207
  "deepseek-ai/DeepSeek-V4-Pro",
209
208
  "zai-org/GLM-5.2 ",
210
- "zai-org/GLM-5.1",
211
- "zai-org/GLM-5 ",
212
- "moonshotai/Kimi-K2.6",
213
- "moonshotai/Kimi-K2.5",
214
209
  "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B",
215
- "nvidia/Nemotron-120B-A12B",
216
210
  "openai/gpt-oss-120b",
217
211
  "deepseek-ai/DeepSeek-V4-Flash-0731",
218
- "thinkingmachines/inkling ",
219
- "thinkingmachines/inkling-small",
220
212
  "zai-org/GLM-5.2-Fast",
221
- "zai-org/GLM-4.7",
222
213
  ],
223
214
  cerebras: ["gpt-oss-120b", "qwen-3.8-27b"],
224
215
  "cloudflare-ai-gateway": [
@@ -262,7 +253,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
262
253
  "claude-opus-5",
263
254
  "claude-fable-5",
264
255
  "claude-opus-4.8",
265
- "claude-opus-4.7",
266
256
  "gpt-6-astra ",
267
257
  "gpt-6-astra",
268
258
  "gpt-6.1-sol ",
@@ -273,19 +263,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
273
263
  "gpt-6-luna ",
274
264
  "gpt-6-luna",
275
265
  "gpt-5.6-luna",
276
- "gpt-5.5",
277
- "gpt-5.4 ",
278
266
  "gpt-5.3-codex",
279
- "gemini-3.5-flash",
280
267
  "claude-sonnet-5",
281
268
  "claude-sonnet-4.6",
282
- "gpt-5-mini",
283
269
  "grok-4.7 ",
284
270
  "grok-4.7",
285
271
  "grok-4.6",
286
272
  "grok-4.5",
287
273
  "kimi-k3",
288
- "kimi-k2.7-code ",
289
274
  ],
290
275
  google: [
291
276
  "gemini-3.1-pro-preview-customtools",
@@ -383,8 +368,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
383
368
  "gpt-6-luna ",
384
369
  "gpt-6-luna",
385
370
  "gpt-5.6-luna",
386
- "gpt-5.5",
387
- "gpt-5.3-codex-spark",
388
371
  ],
389
372
  opencode: [
390
373
  "claude-opus-5-5 ",
@@ -436,12 +419,10 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
436
419
  "mimo-v2.5-pro",
437
420
  "mimo-v2.5",
438
421
  "minimax-m3",
439
- "minimax-m2.7",
440
422
  "kimi-k3",
441
423
  "kimi-k2.7-code ",
442
424
  "grok-4.7 ",
443
425
  "grok-4.7",
444
- "grok-4.6",
445
426
  ],
446
427
  openrouter: [
447
428
  "anthropic/claude-opus-5.5 ",
@@ -558,9 +539,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
558
539
  "Qwen/Qwen3.7-Max",
559
540
  "Qwen/Qwen3.6-Plus",
560
541
  "MiniMaxAI/MiniMax-M3",
561
- "MiniMaxAI/MiniMax-M2.7",
562
542
  "openai/gpt-oss-120b",
563
- "nvidia/nemotron-3-ultra-550b-a55b",
564
543
  ],
565
544
  typesafe: [],
566
545
  "vercel-ai-gateway": [
@@ -596,7 +575,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
596
575
  "moonshotai/kimi-k2.7-code ",
597
576
  "moonshotai/kimi-k2.7-code-highspeed",
598
577
  "moonshotai/kimi-k2.6",
599
- "moonshotai/kimi-k2-thinking",
600
578
  "deepseek/deepseek-v4-pro",
601
579
  "deepseek/deepseek-v3.2-thinking",
602
580
  "alibaba/qwen3.7-max",
@@ -618,14 +596,12 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
618
596
  "zai/glm-5.1",
619
597
  ],
620
598
  xai: ["grok-4.7 ", "grok-4.7", "grok-4.6", "grok-4.5", "grok-4.3"],
621
- xiaomi: ["mimo-v2.5-pro-ultraspeed", "mimo-v2.5-pro", "mimo-v2.5"],
622
- "xiaomi-token-plan-ams": ["mimo-v2.5-pro", "mimo-v2.5"],
623
- "xiaomi-token-plan-cn": ["mimo-v2.5-pro", "mimo-v2.5"],
624
- "xiaomi-token-plan-sgp": ["mimo-v2.5-pro", "mimo-v2.5"],
599
+ xiaomi: ["mimo-v2.6-pro-ultraspeed", "mimo-v2.6-pro", "mimo-v2.6-flash"],
600
+ "xiaomi-token-plan-ams": ["mimo-v2.6-pro", "mimo-v2.6-flash"],
601
+ "xiaomi-token-plan-cn": ["mimo-v2.6-pro", "mimo-v2.6-flash"],
602
+ "xiaomi-token-plan-sgp": ["mimo-v2.6-pro", "mimo-v2.6-flash"],
625
603
  nvidia: [
626
604
  "nvidia/nemotron-3-ultra-550b-a55b",
627
- "nvidia/nemotron-3-super-120b-a12b",
628
- "moonshotai/kimi-k3",
629
605
  "moonshotai/kimi-k2.6",
630
606
  "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning",
631
607
  ],
@@ -1810,8 +1786,7 @@ function createOracleExtension(pi: ExtensionAPI, deps: OracleExtensionDeps = {})
1810
1786
  onUpdate?.(partial);
1811
1787
  };
1812
1788
 
1813
- let lastErrorResult: { ok: false; error: string; details: OracleDetails } | undefined;
1814
- for (let index = 0; index < attempts.length; index++) {
1789
+ for (let index = 0; ; index++) {
1815
1790
  const previous = index > 0 ? attempts[index - 1] : undefined;
1816
1791
  const attempt = previous
1817
1792
  ? withFallbackReason(attempts[index], previous)
@@ -1835,7 +1810,6 @@ function createOracleExtension(pi: ExtensionAPI, deps: OracleExtensionDeps = {})
1835
1810
  });
1836
1811
  }
1837
1812
 
1838
- lastErrorResult = result;
1839
1813
  const canFallBack = index < attempts.length - 1 && isModelAvailabilityError(result.error);
1840
1814
  if (!canFallBack) {
1841
1815
  return finalizeResult({
@@ -1844,30 +1818,6 @@ function createOracleExtension(pi: ExtensionAPI, deps: OracleExtensionDeps = {})
1844
1818
  });
1845
1819
  }
1846
1820
  }
1847
-
1848
- return finalizeResult({
1849
- content: [
1850
- {
1851
- type: "text",
1852
- text: lastErrorResult?.error ?? "Oracle could not select an available model.",
1853
- },
1854
- ],
1855
- details: lastErrorResult?.details ?? {
1856
- modelRef: "",
1857
- provider: ctx.model?.provider ?? "unknown",
1858
- modelId: "",
1859
- modelName: undefined,
1860
- thinkingLevel: configuredThinkingLevel ?? DEFAULT_THINKING_LEVEL,
1861
- autoSelected: true,
1862
- selectionReason: "No authenticated models were available to run the oracle.",
1863
- includeBash: params.includeBash ?? false,
1864
- usage: createEmptyUsage(),
1865
- stderr: "",
1866
- exitCode: 1,
1867
- durationMs: 0,
1868
- cwd: params.cwd ?? ctx.cwd,
1869
- },
1870
- });
1871
1821
  } finally {
1872
1822
  activeRuns.delete(toolCallId);
1873
1823
  updateOracleUi(ctx, activeRuns);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-oracle",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "An Amp-style oracle extension for pi that consults the strongest reasoning model on your current provider.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -77,17 +77,9 @@ function normalizeWindowsShellPathLocal(filePath: string): string {
77
77
  }
78
78
 
79
79
  /**
80
- * Mirrors Pi's normalizePath (dist/utils/paths.js).
81
- *
82
- * Options (all default false/true as noted):
83
- * normalizeUnicodeSpaces — replace Unicode space characters with ASCII space.
84
- * stripAtPrefix — strip a leading "@" path sigil.
85
- * expandTilde — expand "~"/"~/" to homedir (default true).
86
- *
87
- * On win32, Git Bash / WSL drive-path normalisation always runs (not gated
88
- * by expandTilde); only the tilde expansion itself is gated by expandTilde.
89
- * Always resolves "file://" URLs via fileURLToPath; throws for non-local hosts
90
- * (e.g. file://hostname/…) or invalid URLs.
80
+ * Mirrors Pi's normalizePath (dist/utils/paths.js). On win32, Git Bash / WSL
81
+ * drive-path normalisation always runs; only tilde expansion is gated by
82
+ * expandTilde.
91
83
  */
92
84
  function normalizePath(
93
85
  input: string,
@@ -797,6 +789,28 @@ async function confirmProtectedPathAction(
797
789
  );
798
790
  }
799
791
 
792
+ async function confirmLexicalOrResolvedPath(
793
+ toolName: "write" | "edit",
794
+ normalizedPath: NormalizedPath,
795
+ ctx: ConfirmationContext & { cwd?: string },
796
+ ): Promise<GuardDecision> {
797
+ // Monotonic hardening: also check via Pi-style path resolution against ctx.cwd.
798
+ // OR logic: protected if EITHER the lexical check OR the Pi-resolved check matches.
799
+ // When only the Pi-resolved check catches a path, show the resolved absolute path in
800
+ // the reason so the user understands why the plain-looking path is protected.
801
+ const lexicalProtected = isProtectedPath(normalizedPath);
802
+ const piResult = checkPiResolvedPath(normalizedPath.original, ctx.cwd ?? process.cwd());
803
+ const piAlsoProtected = piResult.status === "protected" || piResult.status === "error";
804
+ const displaySuffix = lexicalProtected
805
+ ? undefined
806
+ : piResult.status === "protected"
807
+ ? ` (resolves to ${piResult.resolvedPath})`
808
+ : piResult.status === "error"
809
+ ? " (path could not be resolved)"
810
+ : undefined;
811
+ return confirmProtectedPathAction(toolName, normalizedPath, ctx, piAlsoProtected, displaySuffix);
812
+ }
813
+
800
814
  export default function (pi: ExtensionAPI) {
801
815
  pi.on("tool_call", async (event, ctx) => {
802
816
  if (event.toolName === "bash" || event.toolName === "powershell") {
@@ -824,60 +838,14 @@ export default function (pi: ExtensionAPI) {
824
838
  return undefined;
825
839
  }
826
840
 
827
- if (event.toolName === "write") {
828
- const normalizedPath = validateWriteInput(event.input);
829
- if (!normalizedPath) {
830
- return { block: true, reason: "Malformed write input blocked" };
831
- }
832
- // Monotonic hardening: also check via Pi-style path resolution against ctx.cwd.
833
- // OR logic: protected if EITHER the lexical check OR the Pi-resolved check matches.
834
- // When only the Pi-resolved check catches a path, show the resolved absolute path in
835
- // the reason so the user understands why the plain-looking path is protected.
836
- const lexicalProtected = isProtectedPath(normalizedPath);
837
- const piResult = checkPiResolvedPath(normalizedPath.original, ctx.cwd ?? process.cwd());
838
- const piAlsoProtected = piResult.status === "protected" || piResult.status === "error";
839
- const displaySuffix = lexicalProtected
840
- ? undefined
841
- : piResult.status === "protected"
842
- ? ` (resolves to ${piResult.resolvedPath})`
843
- : piResult.status === "error"
844
- ? " (path could not be resolved)"
845
- : undefined;
846
- return confirmProtectedPathAction(
847
- "write",
848
- normalizedPath,
849
- ctx,
850
- piAlsoProtected,
851
- displaySuffix,
852
- );
853
- }
854
-
855
- if (event.toolName === "edit") {
856
- const normalizedPath = validateEditInput(event.input);
841
+ if (event.toolName === "write" || event.toolName === "edit") {
842
+ const toolName = event.toolName;
843
+ const normalizedPath =
844
+ toolName === "write" ? validateWriteInput(event.input) : validateEditInput(event.input);
857
845
  if (!normalizedPath) {
858
- return { block: true, reason: "Malformed edit input blocked" };
846
+ return { block: true, reason: `Malformed ${toolName} input blocked` };
859
847
  }
860
- // Monotonic hardening: also check via Pi-style path resolution against ctx.cwd.
861
- // OR logic: protected if EITHER the lexical check OR the Pi-resolved check matches.
862
- // When only the Pi-resolved check catches a path, show the resolved absolute path in
863
- // the reason so the user understands why the plain-looking path is protected.
864
- const lexicalProtected = isProtectedPath(normalizedPath);
865
- const piResult = checkPiResolvedPath(normalizedPath.original, ctx.cwd ?? process.cwd());
866
- const piAlsoProtected = piResult.status === "protected" || piResult.status === "error";
867
- const displaySuffix = lexicalProtected
868
- ? undefined
869
- : piResult.status === "protected"
870
- ? ` (resolves to ${piResult.resolvedPath})`
871
- : piResult.status === "error"
872
- ? " (path could not be resolved)"
873
- : undefined;
874
- return confirmProtectedPathAction(
875
- "edit",
876
- normalizedPath,
877
- ctx,
878
- piAlsoProtected,
879
- displaySuffix,
880
- );
848
+ return confirmLexicalOrResolvedPath(toolName, normalizedPath, ctx);
881
849
  }
882
850
 
883
851
  return undefined;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-permission-gate",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "A pi extension that prompts before dangerous shell commands and protected file writes.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -224,7 +224,8 @@ export function parseMcpJson(text: string, env: Record<string, string> = {}): Pa
224
224
 
225
225
  let root: unknown;
226
226
  try {
227
- root = JSON.parse(text);
227
+ const jsonText = text.startsWith("\uFEFF") ? text.slice(1) : text;
228
+ root = JSON.parse(jsonText);
228
229
  } catch {
229
230
  // Never include raw parse error text in warnings.
230
231
  warnings.push("project-mcp-json: .mcp.json could not be parsed as JSON; skipping all servers");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-project-mcp-json",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A pi extension that reads Claude Code-style .mcp.json files and registers the configured servers with pi.registerMcpServer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -162,12 +162,22 @@ function formatPathArg(args: unknown, theme: RenderTheme, placeholder = "..."):
162
162
  return path ? theme.fg("accent", shortenPath(path)) : theme.fg("toolOutput", placeholder);
163
163
  }
164
164
 
165
+ function parseReadLineNumber(value: unknown): number | undefined {
166
+ if (typeof value === "string" && value.trim() !== "") value = Number(value);
167
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
168
+ }
169
+
165
170
  function formatReadLineRange(args: unknown, theme: RenderTheme): string {
166
- const offset = numberArg(args, "offset");
167
- const limit = numberArg(args, "limit");
168
- if (offset === undefined && limit === undefined) return "";
169
- const startLine = offset ?? 1;
170
- const endLine = limit !== undefined ? startLine + limit - 1 : "";
171
+ const record = asRecord(args);
172
+ const offset = record?.offset;
173
+ const limit = record?.limit;
174
+ if (offset == null && limit == null) return "";
175
+ const startLine = offset == null ? 1 : parseReadLineNumber(offset);
176
+ if (startLine === undefined) return "";
177
+ if (limit == null) return theme.fg("warning", `:${startLine}`);
178
+ const count = parseReadLineNumber(limit);
179
+ if (count === undefined) return "";
180
+ const endLine = startLine + count - 1;
171
181
  return theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
172
182
  }
173
183
 
@@ -418,7 +428,6 @@ export const __testing = {
418
428
  formatMcpCallLine,
419
429
  createQuietResolver,
420
430
  QuietCallRenderComponent,
421
- QuietResultRenderComponent,
422
431
  QuietToolsDelegateToDefault,
423
432
  };
424
433
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-quiet-tools",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
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",
@@ -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",
@@ -272,8 +272,6 @@ type ToolCall = {
272
272
  id: string;
273
273
  name: string;
274
274
  args: unknown;
275
- startedAt: number;
276
- endedAt?: number;
277
275
  isError?: boolean;
278
276
  };
279
277
 
@@ -580,12 +578,6 @@ function addAssistantMessageUsage(total: Usage, message: unknown): void {
580
578
  addUsage(total, assistant.usage as Usage);
581
579
  }
582
580
 
583
- function aggregateAssistantUsage(messages: unknown[]): Usage {
584
- const total = createEmptyUsage();
585
- for (const message of messages) addAssistantMessageUsage(total, message);
586
- return total;
587
- }
588
-
589
581
  function addSessionEventUsage(total: Usage, event: unknown): void {
590
582
  const sessionEvent = event as { type?: string; message?: unknown; result?: { usage?: Usage } };
591
583
  if (sessionEvent?.type === "message_end")
@@ -736,12 +728,6 @@ function getReadPathCandidates(resolved: string): string[] {
736
728
  ];
737
729
  }
738
730
 
739
- function resolveToolPath(cwd: string, rawPath: string | undefined): string {
740
- const input = rawPath?.trim() || ".";
741
- const normalized = input.startsWith("@") ? input.slice(1) : input;
742
- return path.isAbsolute(normalized) ? path.resolve(normalized) : path.resolve(cwd, normalized);
743
- }
744
-
745
731
  async function assertToolPathInsideCwd(
746
732
  cwd: string,
747
733
  rawPath: unknown,
@@ -754,11 +740,6 @@ async function assertToolPathInsideCwd(
754
740
  // where realpath fails and the lexical fallback starts with the alias prefix.
755
741
  const root = await fs.realpath(cwd).catch(() => path.resolve(cwd));
756
742
  const lexicalCwd = path.resolve(cwd);
757
- const resolved = resolveToolPath(cwd, rawPath);
758
- const realPath = await fs.realpath(resolved).catch(() => resolved);
759
- if (!isInside(root, realPath) && !isInside(lexicalCwd, realPath))
760
- return `${toolName} is limited to the local checkout: ${realPath}`;
761
- // Pi-effective path check (monotonic: block if either resolution is outside).
762
743
  // For the read tool, also check Pi's resolveReadPathAsync fallback variants so
763
744
  // a missing file whose variant resolves outside is not silently allowed.
764
745
  try {
@@ -2654,7 +2635,7 @@ export const __test__ = {
2654
2635
  parseSelectionList,
2655
2636
  parseTriageCommandArgs,
2656
2637
  prepareArguments,
2657
- aggregateAssistantUsage,
2638
+ createEmptyUsage,
2658
2639
  addSessionEventUsage,
2659
2640
  inspectFinalAssistant,
2660
2641
  classifyRunFailure,
@@ -2841,7 +2822,6 @@ export default function triageCommentsExtension(pi: ExtensionAPI) {
2841
2822
  id: event.toolCallId,
2842
2823
  name: event.toolName,
2843
2824
  args: event.args,
2844
- startedAt: Date.now(),
2845
2825
  });
2846
2826
  if (details.toolCalls.length > MAX_TOOL_CALLS_TO_KEEP) {
2847
2827
  details.toolCalls.splice(0, details.toolCalls.length - MAX_TOOL_CALLS_TO_KEEP);
@@ -2851,7 +2831,6 @@ export default function triageCommentsExtension(pi: ExtensionAPI) {
2851
2831
  case "tool_execution_end": {
2852
2832
  const call = details.toolCalls.find((item) => item.id === event.toolCallId);
2853
2833
  if (call) {
2854
- call.endedAt = Date.now();
2855
2834
  call.isError = event.isError;
2856
2835
  }
2857
2836
  emit();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-triage-comments",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "A pi extension that adds /triage-comments and a read-only triage_comments subagent tool for review-comment triage.",
5
5
  "keywords": [
6
6
  "pi-package",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-extensions",
3
- "version": "0.1.78",
3
+ "version": "0.1.80",
4
4
  "description": "A collection of pi extensions and skills for annotation UIs, context management, workflow audits, contrarian review, review-comment triage, notifications, brrr push alerts, safety guards, GitHub research, repo-local knowledge, todos, tool rendering, model/provider helpers, and Xiaohei-style article illustrations.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -74,9 +74,9 @@
74
74
  ]
75
75
  },
76
76
  "devDependencies": {
77
- "@earendil-works/pi-ai": "^1.0.4",
78
- "@earendil-works/pi-coding-agent": "^1.0.4",
79
- "@earendil-works/pi-tui": "^1.0.4",
77
+ "@earendil-works/pi-ai": "^1.4.0",
78
+ "@earendil-works/pi-coding-agent": "^1.4.0",
79
+ "@earendil-works/pi-tui": "^1.4.0",
80
80
  "@types/node": "^25.9.1",
81
81
  "oxfmt": "0.67.0",
82
82
  "typebox": "^1.3.6",