@diegopetrucci/pi-extensions 0.1.58 → 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.
Files changed (68) hide show
  1. package/.pi-fleet-tested-version +1 -1
  2. package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
  3. package/extensions/agent-workflow-audit/package.json +1 -1
  4. package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
  5. package/extensions/annotate-git-diff/package.json +1 -1
  6. package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
  7. package/extensions/annotate-last-message/package.json +1 -1
  8. package/extensions/brrr/.pi-fleet-tested-version +1 -1
  9. package/extensions/brrr/package.json +1 -1
  10. package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
  11. package/extensions/claude-fast/package.json +1 -1
  12. package/extensions/code-reviewer/.pi-fleet-tested-version +1 -1
  13. package/extensions/code-reviewer/index.ts +120 -37
  14. package/extensions/code-reviewer/package.json +1 -1
  15. package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
  16. package/extensions/confirm-destructive/README.md +1 -1
  17. package/extensions/confirm-destructive/index.ts +20 -5
  18. package/extensions/confirm-destructive/package.json +1 -1
  19. package/extensions/context-cap/.pi-fleet-tested-version +1 -1
  20. package/extensions/context-cap/package.json +1 -1
  21. package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
  22. package/extensions/context-inspector/package.json +1 -1
  23. package/extensions/contrarian/.pi-fleet-tested-version +1 -1
  24. package/extensions/contrarian/README.md +1 -1
  25. package/extensions/contrarian/index.ts +175 -31
  26. package/extensions/contrarian/package.json +1 -1
  27. package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
  28. package/extensions/dirty-repo-guard/README.md +1 -1
  29. package/extensions/dirty-repo-guard/package.json +1 -1
  30. package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -1
  31. package/extensions/dynamic-context-pruning/package.json +1 -1
  32. package/extensions/git-footer/.pi-fleet-tested-version +1 -1
  33. package/extensions/git-footer/package.json +1 -1
  34. package/extensions/gnosis/.pi-fleet-tested-version +1 -1
  35. package/extensions/gnosis/package.json +1 -1
  36. package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
  37. package/extensions/illustrations-to-explain-things/package.json +1 -1
  38. package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
  39. package/extensions/inline-bash/README.md +1 -1
  40. package/extensions/inline-bash/package.json +1 -1
  41. package/extensions/librarian/.pi-fleet-tested-version +1 -1
  42. package/extensions/librarian/index.ts +385 -106
  43. package/extensions/librarian/package.json +1 -1
  44. package/extensions/notify/.pi-fleet-tested-version +1 -1
  45. package/extensions/notify/README.md +1 -1
  46. package/extensions/notify/package.json +1 -1
  47. package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
  48. package/extensions/openai-fast/package.json +1 -1
  49. package/extensions/oracle/.pi-fleet-tested-version +1 -1
  50. package/extensions/oracle/README.md +1 -1
  51. package/extensions/oracle/index.ts +176 -32
  52. package/extensions/oracle/package.json +1 -1
  53. package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
  54. package/extensions/permission-gate/README.md +1 -1
  55. package/extensions/permission-gate/index.ts +453 -2
  56. package/extensions/permission-gate/package.json +1 -1
  57. package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
  58. package/extensions/quiet-tools/package.json +1 -1
  59. package/extensions/review/.pi-fleet-tested-version +1 -1
  60. package/extensions/review/index.ts +53 -19
  61. package/extensions/review/package.json +1 -1
  62. package/extensions/todo/.pi-fleet-tested-version +1 -1
  63. package/extensions/todo/README.md +1 -1
  64. package/extensions/todo/package.json +1 -1
  65. package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
  66. package/extensions/triage-comments/index.ts +107 -33
  67. package/extensions/triage-comments/package.json +1 -1
  68. package/package.json +1 -1
@@ -2,8 +2,8 @@ import { spawn } from "node:child_process";
2
2
  import { existsSync } from "node:fs";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path from "node:path";
5
- import { StringEnum } from "@earendil-works/pi-ai";
6
- import { getAgentDir, getMarkdownTheme, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
5
+ import { StringEnum, type Usage } from "@earendil-works/pi-ai";
6
+ import { getAgentDir, getMarkdownTheme, type AgentToolResult, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
7
7
  import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
8
8
  import { Type } from "typebox";
9
9
 
@@ -25,7 +25,14 @@ interface UsageStats {
25
25
  output: number;
26
26
  cacheRead: number;
27
27
  cacheWrite: number;
28
+ cacheWrite1h?: number;
29
+ reasoning?: number;
30
+ totalTokens: number;
28
31
  cost: number;
32
+ costInput: number;
33
+ costOutput: number;
34
+ costCacheRead: number;
35
+ costCacheWrite: number;
29
36
  turns: number;
30
37
  contextTokens: number;
31
38
  }
@@ -537,12 +544,91 @@ function createEmptyUsage(): UsageStats {
537
544
  output: 0,
538
545
  cacheRead: 0,
539
546
  cacheWrite: 0,
547
+ totalTokens: 0,
540
548
  cost: 0,
549
+ costInput: 0,
550
+ costOutput: 0,
551
+ costCacheRead: 0,
552
+ costCacheWrite: 0,
541
553
  turns: 0,
542
554
  contextTokens: 0,
543
555
  };
544
556
  }
545
557
 
558
+ function cloneUsageStats(stats: UsageStats): UsageStats {
559
+ return { ...stats };
560
+ }
561
+
562
+ function mergeUsageStats(target: UsageStats, source: UsageStats | undefined): UsageStats {
563
+ if (!source) return target;
564
+ target.input += source.input;
565
+ target.output += source.output;
566
+ target.cacheRead += source.cacheRead;
567
+ target.cacheWrite += source.cacheWrite;
568
+ if (source.cacheWrite1h !== undefined) {
569
+ target.cacheWrite1h = (target.cacheWrite1h ?? 0) + source.cacheWrite1h;
570
+ }
571
+ if (source.reasoning !== undefined) {
572
+ target.reasoning = (target.reasoning ?? 0) + source.reasoning;
573
+ }
574
+ target.totalTokens += source.totalTokens;
575
+ target.cost += source.cost;
576
+ target.costInput += source.costInput;
577
+ target.costOutput += source.costOutput;
578
+ target.costCacheRead += source.costCacheRead;
579
+ target.costCacheWrite += source.costCacheWrite;
580
+ target.turns += source.turns;
581
+ target.contextTokens = source.contextTokens || target.contextTokens;
582
+ return target;
583
+ }
584
+
585
+ function usageStatsToToolUsage(stats: UsageStats): Usage {
586
+ return {
587
+ input: stats.input,
588
+ output: stats.output,
589
+ cacheRead: stats.cacheRead,
590
+ cacheWrite: stats.cacheWrite,
591
+ ...(stats.cacheWrite1h !== undefined ? { cacheWrite1h: stats.cacheWrite1h } : {}),
592
+ ...(stats.reasoning !== undefined ? { reasoning: stats.reasoning } : {}),
593
+ totalTokens: stats.totalTokens,
594
+ cost: {
595
+ input: stats.costInput,
596
+ output: stats.costOutput,
597
+ cacheRead: stats.costCacheRead,
598
+ cacheWrite: stats.costCacheWrite,
599
+ total: stats.cost,
600
+ },
601
+ };
602
+ }
603
+
604
+ function addUsageStats(
605
+ target: UsageStats,
606
+ source: Usage | undefined,
607
+ options: { countTurn?: boolean; updateContextTokens?: boolean } = {},
608
+ ): void {
609
+ if (!source) return;
610
+ if (options.countTurn) target.turns += 1;
611
+ target.input += source.input || 0;
612
+ target.output += source.output || 0;
613
+ target.cacheRead += source.cacheRead || 0;
614
+ target.cacheWrite += source.cacheWrite || 0;
615
+ if (source.cacheWrite1h !== undefined) {
616
+ target.cacheWrite1h = (target.cacheWrite1h ?? 0) + source.cacheWrite1h;
617
+ }
618
+ if (source.reasoning !== undefined) {
619
+ target.reasoning = (target.reasoning ?? 0) + source.reasoning;
620
+ }
621
+ target.totalTokens += source.totalTokens || 0;
622
+ target.cost += source.cost?.total || 0;
623
+ target.costInput += source.cost?.input || 0;
624
+ target.costOutput += source.cost?.output || 0;
625
+ target.costCacheRead += source.cost?.cacheRead || 0;
626
+ target.costCacheWrite += source.cost?.cacheWrite || 0;
627
+ if (options.updateContextTokens) {
628
+ target.contextTokens = source.totalTokens || target.contextTokens;
629
+ }
630
+ }
631
+
546
632
  function getContrarianConfigPath(): string {
547
633
  return path.join(getAgentDir(), "extensions", CONTRARIAN_CONFIG_FILE);
548
634
  }
@@ -631,6 +717,7 @@ function formatUsage(stats: UsageStats): string {
631
717
  if (stats.output) parts.push(`↓${formatTokens(stats.output)}`);
632
718
  if (stats.cacheRead) parts.push(`R${formatTokens(stats.cacheRead)}`);
633
719
  if (stats.cacheWrite) parts.push(`W${formatTokens(stats.cacheWrite)}`);
720
+ if (stats.reasoning) parts.push(`T${formatTokens(stats.reasoning)}`);
634
721
  if (stats.cost) parts.push(`$${stats.cost.toFixed(4)}`);
635
722
  if (stats.contextTokens) parts.push(`ctx:${formatTokens(stats.contextTokens)}`);
636
723
  return parts.join(" ");
@@ -1034,6 +1121,7 @@ async function runContrarian(
1034
1121
  signal: AbortSignal | undefined,
1035
1122
  onUpdate: ((result: { content: Array<{ type: "text"; text: string }>; details: ContrarianDetails }) => void) | undefined,
1036
1123
  defaultCwd: string,
1124
+ spawnImpl: typeof spawn = spawn,
1037
1125
  ): Promise<{ ok: true; output: string; details: ContrarianDetails } | { ok: false; error: string; details: ContrarianDetails }> {
1038
1126
  const cwd = params.cwd ?? defaultCwd;
1039
1127
  const includeBash = params.includeBash ?? false;
@@ -1085,7 +1173,7 @@ async function runContrarian(
1085
1173
  let wasAborted = false;
1086
1174
 
1087
1175
  const exitCode = await new Promise<number>((resolve) => {
1088
- const proc = spawn(invocation.command, invocation.args, {
1176
+ const proc = spawnImpl(invocation.command, invocation.args, {
1089
1177
  cwd,
1090
1178
  shell: false,
1091
1179
  stdio: ["ignore", "pipe", "pipe"],
@@ -1123,18 +1211,16 @@ async function runContrarian(
1123
1211
  const errorMessageField = event.message.errorMessage;
1124
1212
  lastErrorMessage = typeof errorMessageField === "string" ? errorMessageField : undefined;
1125
1213
 
1126
- const messageUsage = event.message.usage;
1127
- if (messageUsage) {
1128
- usage.turns += 1;
1129
- usage.input += messageUsage.input || 0;
1130
- usage.output += messageUsage.output || 0;
1131
- usage.cacheRead += messageUsage.cacheRead || 0;
1132
- usage.cacheWrite += messageUsage.cacheWrite || 0;
1133
- usage.cost += messageUsage.cost?.total || 0;
1134
- usage.contextTokens = messageUsage.totalTokens || usage.contextTokens;
1135
- }
1136
-
1214
+ addUsageStats(usage, event.message.usage as Usage | undefined, {
1215
+ countTurn: true,
1216
+ updateContextTokens: true,
1217
+ });
1137
1218
  emit();
1219
+ return;
1220
+ }
1221
+
1222
+ if (event.type === "compaction_end") {
1223
+ addUsageStats(usage, event.result?.usage as Usage | undefined);
1138
1224
  }
1139
1225
  };
1140
1226
 
@@ -1150,26 +1236,53 @@ async function runContrarian(
1150
1236
  emit();
1151
1237
  });
1152
1238
 
1239
+ let closed = false;
1240
+ let killTimer: ReturnType<typeof setTimeout> | undefined;
1241
+ let abortListener: (() => void) | undefined;
1242
+
1243
+ const childHasExited = () => proc.exitCode !== null || proc.signalCode !== null;
1244
+ const clearEscalation = () => {
1245
+ if (killTimer) {
1246
+ clearTimeout(killTimer);
1247
+ killTimer = undefined;
1248
+ }
1249
+ };
1250
+ const cleanup = () => {
1251
+ clearEscalation();
1252
+ if (signal && abortListener) {
1253
+ signal.removeEventListener("abort", abortListener);
1254
+ abortListener = undefined;
1255
+ }
1256
+ };
1257
+
1258
+ proc.on("exit", () => {
1259
+ clearEscalation();
1260
+ });
1261
+
1153
1262
  proc.on("close", (code) => {
1263
+ closed = true;
1264
+ cleanup();
1154
1265
  if (buffer.trim()) processLine(buffer);
1155
1266
  resolve(code ?? 0);
1156
1267
  });
1157
1268
 
1158
1269
  proc.on("error", (error) => {
1270
+ cleanup();
1159
1271
  stderr += `${error instanceof Error ? error.message : String(error)}\n`;
1160
1272
  resolve(1);
1161
1273
  });
1162
1274
 
1163
1275
  if (signal) {
1164
- const killProc = () => {
1276
+ abortListener = () => {
1165
1277
  wasAborted = true;
1166
1278
  proc.kill("SIGTERM");
1167
- setTimeout(() => {
1168
- if (!proc.killed) proc.kill("SIGKILL");
1279
+ if (closed || childHasExited()) return;
1280
+ killTimer = setTimeout(() => {
1281
+ if (!closed && !childHasExited()) proc.kill("SIGKILL");
1169
1282
  }, 5000);
1170
1283
  };
1171
- if (signal.aborted) killProc();
1172
- else signal.addEventListener("abort", killProc, { once: true });
1284
+ if (signal.aborted) abortListener();
1285
+ else signal.addEventListener("abort", abortListener, { once: true });
1173
1286
  }
1174
1287
  });
1175
1288
 
@@ -1178,6 +1291,7 @@ async function runContrarian(
1178
1291
  details.durationMs = Date.now() - startedAt;
1179
1292
 
1180
1293
  if (wasAborted) {
1294
+ if (details.exitCode === 0) details.exitCode = 1;
1181
1295
  return { ok: false, error: "Contrarian was aborted.", details };
1182
1296
  }
1183
1297
 
@@ -1187,6 +1301,7 @@ async function runContrarian(
1187
1301
  // so callers can distinguish transient provider errors from a genuinely
1188
1302
  // empty response.
1189
1303
  if (lastStopReason === "error" || lastStopReason === "aborted") {
1304
+ if (details.exitCode === 0) details.exitCode = 1;
1190
1305
  return {
1191
1306
  ok: false,
1192
1307
  error: formatContrarianModelError(lastStopReason, lastErrorMessage),
@@ -1203,6 +1318,7 @@ async function runContrarian(
1203
1318
  }
1204
1319
 
1205
1320
  if (!finalOutput.trim()) {
1321
+ if (details.exitCode === 0) details.exitCode = 1;
1206
1322
  return {
1207
1323
  ok: false,
1208
1324
  error: details.stderr || "Contrarian finished without returning any text.",
@@ -1223,15 +1339,26 @@ function renderCollapsedText(text: string, lineLimit = COLLAPSED_LINE_LIMIT): st
1223
1339
  return [...lines.slice(0, lineLimit), `... (${lines.length - lineLimit} more lines)`].join("\n");
1224
1340
  }
1225
1341
 
1342
+ type ContrarianExtensionDeps = {
1343
+ spawnImpl?: typeof spawn;
1344
+ };
1345
+
1226
1346
  export const __test__ = {
1347
+ createContrarianExtension,
1227
1348
  findAvailableModel,
1228
1349
  isModelAvailabilityError,
1229
1350
  parseModelPreference,
1351
+ runContrarian,
1230
1352
  resolveThinkingLevel,
1231
1353
  selectContrarianModel,
1232
1354
  };
1233
1355
 
1234
1356
  export default function contrarianExtension(pi: ExtensionAPI) {
1357
+ createContrarianExtension(pi);
1358
+ }
1359
+
1360
+ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDeps = {}) {
1361
+ const spawnImpl = deps.spawnImpl ?? spawn;
1235
1362
  const activeRuns = new Map<string, ContrarianUiRun>();
1236
1363
  let preferences: ContrarianPreferences = {};
1237
1364
 
@@ -1377,6 +1504,13 @@ export default function contrarianExtension(pi: ExtensionAPI) {
1377
1504
  },
1378
1505
  });
1379
1506
 
1507
+ pi.on("tool_result", async (event) => {
1508
+ if (event.toolName !== "contrarian") return undefined;
1509
+ const details = event.details as ContrarianDetails | undefined;
1510
+ if ((details?.exitCode ?? 0) === 0) return undefined;
1511
+ return { isError: true };
1512
+ });
1513
+
1380
1514
  pi.registerTool({
1381
1515
  name: "contrarian",
1382
1516
  label: "Contrarian",
@@ -1387,9 +1521,10 @@ export default function contrarianExtension(pi: ExtensionAPI) {
1387
1521
  promptGuidelines: [
1388
1522
  "Use contrarian when you need to stress-test a proposal, implementation plan, design, assumption, bug hypothesis, review conclusion, or product direction.",
1389
1523
  "Ask contrarian to steelman the strongest opposing case and separate confirmed objections, plausible concerns, and unresolved unknowns.",
1390
- "Do not use it for routine low-value work; it is slower than the main agent.",
1391
- "The contrarian is read-only by default. Set includeBash only when shell-based inspection is genuinely useful.",
1392
- "The contrarian requests high by default for reasoning models; defaults and explicit thinkingLevel overrides are clamped to the effective model-supported level when the model is matched.",
1524
+ "Do not use contrarian for routine low-value work; contrarian is slower than the main agent.",
1525
+ "The contrarian tool is read-only by default and only exposes read, grep, find, and ls unless contrarian includeBash is enabled.",
1526
+ "Set contrarian includeBash only when the extra bash inspection tool is genuinely useful; keep contrarian relying on read, grep, find, and ls otherwise.",
1527
+ "The contrarian tool requests high by default for reasoning models; contrarian defaults and explicit thinkingLevel overrides are clamped to the effective model-supported level when the model is matched.",
1393
1528
  ],
1394
1529
  parameters: ContrarianParams,
1395
1530
 
@@ -1414,6 +1549,14 @@ export default function contrarianExtension(pi: ExtensionAPI) {
1414
1549
  try {
1415
1550
  const attempts: ContrarianSelection[] = [];
1416
1551
  const seen = new Set<string>();
1552
+ const aggregateUsage = createEmptyUsage();
1553
+ const finalizeResult = (
1554
+ result: { content: Array<{ type: "text"; text: string }>; details: ContrarianDetails },
1555
+ ): AgentToolResult<ContrarianDetails> => ({
1556
+ ...result,
1557
+ details: { ...result.details, usage: cloneUsageStats(aggregateUsage) },
1558
+ usage: usageStatsToToolUsage(aggregateUsage),
1559
+ });
1417
1560
  const pushAttempt = (candidate: ContrarianSelection | undefined) => {
1418
1561
  if (!candidate) return;
1419
1562
  const key = `${candidate.provider}/${candidate.modelId}`.toLowerCase();
@@ -1455,7 +1598,7 @@ export default function contrarianExtension(pi: ExtensionAPI) {
1455
1598
  } else {
1456
1599
  const selectionResult = await selectContrarianModel(ctx, configuredThinkingLevel);
1457
1600
  if (!selectionResult.ok) {
1458
- return {
1601
+ return finalizeResult({
1459
1602
  content: [{ type: "text", text: selectionResult.error }],
1460
1603
  details: {
1461
1604
  modelRef: "",
@@ -1472,7 +1615,7 @@ export default function contrarianExtension(pi: ExtensionAPI) {
1472
1615
  durationMs: 0,
1473
1616
  cwd: params.cwd ?? ctx.cwd,
1474
1617
  },
1475
- };
1618
+ });
1476
1619
  }
1477
1620
  for (const candidate of selectionResult.ordered) pushAttempt(candidate);
1478
1621
  }
@@ -1496,25 +1639,26 @@ export default function contrarianExtension(pi: ExtensionAPI) {
1496
1639
  uiRun.selection = attempt;
1497
1640
  updateContrarianUi(ctx, activeRuns);
1498
1641
 
1499
- const result = await runContrarian(attempt, { ...params, task }, signal, handleUpdate, ctx.cwd);
1642
+ const result = await runContrarian(attempt, { ...params, task }, signal, handleUpdate, ctx.cwd, spawnImpl);
1643
+ mergeUsageStats(aggregateUsage, result.details.usage);
1500
1644
  if (result.ok) {
1501
- return {
1645
+ return finalizeResult({
1502
1646
  content: [{ type: "text", text: result.output }],
1503
1647
  details: result.details,
1504
- };
1648
+ });
1505
1649
  }
1506
1650
 
1507
1651
  lastErrorResult = result;
1508
1652
  const canFallBack = index < attempts.length - 1 && isModelAvailabilityError(result.error);
1509
1653
  if (!canFallBack) {
1510
- return {
1654
+ return finalizeResult({
1511
1655
  content: [{ type: "text", text: result.error }],
1512
1656
  details: result.details,
1513
- };
1657
+ });
1514
1658
  }
1515
1659
  }
1516
1660
 
1517
- return {
1661
+ return finalizeResult({
1518
1662
  content: [
1519
1663
  { type: "text", text: lastErrorResult?.error ?? "Contrarian could not select an available model." },
1520
1664
  ],
@@ -1534,7 +1678,7 @@ export default function contrarianExtension(pi: ExtensionAPI) {
1534
1678
  durationMs: 0,
1535
1679
  cwd: params.cwd ?? ctx.cwd,
1536
1680
  },
1537
- };
1681
+ });
1538
1682
  } finally {
1539
1683
  activeRuns.delete(toolCallId);
1540
1684
  updateContrarianUi(ctx, activeRuns);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-contrarian",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "A read-only contrarian subagent extension for pi that stress-tests plans, designs, assumptions, and conclusions.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.80.6
1
+ 0.81.1
@@ -2,7 +2,7 @@
2
2
 
3
3
  A small pi extension that prompts before session changes when the current git repo has uncommitted changes.
4
4
 
5
- This is copied from the original `dirty-repo-guard.ts` example in [`earendil-works/pi-mono`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/examples/extensions/dirty-repo-guard.ts) and kept basically the same.
5
+ This is copied from the original `dirty-repo-guard.ts` example in [`earendil-works/pi`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/dirty-repo-guard.ts) and kept basically the same.
6
6
 
7
7
  ## What it checks
8
8
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-dirty-repo-guard",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "A pi extension that prompts before session changes when the current git repo has uncommitted changes.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.80.6
1
+ 0.81.1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-dynamic-context-pruning",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "A pi extension that dynamically prunes stale context to keep sessions lean.",
6
6
  "keywords": [
@@ -1 +1 @@
1
- 0.80.6
1
+ 0.81.1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-git-footer",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A TLH-style git status add-on for pi's built-in footer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.80.6
1
+ 0.81.1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-gnosis",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "A pi extension that exposes the gnosis repo-local knowledge base CLI as an agent tool.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.80.6
1
+ 0.81.1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-illustrations-to-explain-things",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A pi skill for generating clean, absurd Xiaohei-style article illustrations, shot lists, and visual metaphors.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.80.6
1
+ 0.81.1
@@ -2,7 +2,7 @@
2
2
 
3
3
  A pi extension that expands inline bash commands in user prompts before they are sent to the agent.
4
4
 
5
- This started from the original `inline-bash.ts` example in [`earendil-works/pi-mono`](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/examples/extensions/inline-bash.ts), with small packaging and safety tweaks.
5
+ This started from the original `inline-bash.ts` example in [`earendil-works/pi`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/inline-bash.ts), with small packaging and safety tweaks.
6
6
 
7
7
  ## Usage
8
8
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-inline-bash",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "A pi extension that expands inline bash commands in user prompts.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.80.6
1
+ 0.81.1