@diegopetrucci/pi-extensions 0.1.53 → 0.1.55

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 (76) hide show
  1. package/.pi-fleet-tested-version +1 -1
  2. package/README.md +37 -15
  3. package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
  4. package/extensions/agent-workflow-audit/index.ts +12 -0
  5. package/extensions/agent-workflow-audit/package.json +1 -1
  6. package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
  7. package/extensions/annotate-git-diff/git.ts +1 -0
  8. package/extensions/annotate-git-diff/index.ts +1 -1
  9. package/extensions/annotate-git-diff/package.json +1 -1
  10. package/extensions/annotate-git-diff/prompt.ts +2 -1
  11. package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
  12. package/extensions/annotate-last-message/index.ts +1 -1
  13. package/extensions/annotate-last-message/package.json +1 -1
  14. package/extensions/brrr/.pi-fleet-tested-version +1 -1
  15. package/extensions/brrr/index.ts +4 -3
  16. package/extensions/brrr/package.json +1 -1
  17. package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
  18. package/extensions/claude-fast/package.json +1 -1
  19. package/extensions/code-reviewer/.pi-fleet-tested-version +1 -0
  20. package/extensions/code-reviewer/README.md +84 -0
  21. package/extensions/code-reviewer/index.ts +1280 -0
  22. package/extensions/code-reviewer/package.json +35 -0
  23. package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
  24. package/extensions/confirm-destructive/package.json +1 -1
  25. package/extensions/context-cap/.pi-fleet-tested-version +1 -1
  26. package/extensions/context-cap/package.json +1 -1
  27. package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
  28. package/extensions/context-inspector/index.ts +5 -0
  29. package/extensions/context-inspector/package.json +1 -1
  30. package/extensions/contrarian/.pi-fleet-tested-version +1 -1
  31. package/extensions/contrarian/index.ts +35 -3
  32. package/extensions/contrarian/package.json +1 -1
  33. package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
  34. package/extensions/dirty-repo-guard/package.json +1 -1
  35. package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -0
  36. package/extensions/dynamic-context-pruning/README.md +435 -0
  37. package/extensions/dynamic-context-pruning/index.ts +2546 -0
  38. package/extensions/dynamic-context-pruning/package.json +34 -0
  39. package/extensions/git-footer/.pi-fleet-tested-version +1 -1
  40. package/extensions/git-footer/index.ts +42 -14
  41. package/extensions/git-footer/package.json +1 -1
  42. package/extensions/gnosis/.pi-fleet-tested-version +1 -1
  43. package/extensions/gnosis/package.json +1 -1
  44. package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
  45. package/extensions/illustrations-to-explain-things/package.json +1 -1
  46. package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
  47. package/extensions/inline-bash/package.json +1 -1
  48. package/extensions/librarian/.pi-fleet-tested-version +1 -1
  49. package/extensions/librarian/index.ts +7 -0
  50. package/extensions/librarian/package.json +1 -1
  51. package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
  52. package/extensions/minimal-footer/index.ts +132 -72
  53. package/extensions/minimal-footer/package.json +1 -1
  54. package/extensions/notify/.pi-fleet-tested-version +1 -1
  55. package/extensions/notify/package.json +1 -1
  56. package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
  57. package/extensions/openai-fast/package.json +1 -1
  58. package/extensions/oracle/.pi-fleet-tested-version +1 -1
  59. package/extensions/oracle/index.ts +21 -2
  60. package/extensions/oracle/package.json +1 -1
  61. package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
  62. package/extensions/permission-gate/index.ts +5 -1
  63. package/extensions/permission-gate/package.json +1 -1
  64. package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
  65. package/extensions/quiet-tools/index.ts +6 -0
  66. package/extensions/quiet-tools/package.json +1 -1
  67. package/extensions/review/.pi-fleet-tested-version +1 -1
  68. package/extensions/review/index.ts +251 -197
  69. package/extensions/review/package.json +1 -1
  70. package/extensions/todo/.pi-fleet-tested-version +1 -1
  71. package/extensions/todo/index.ts +1 -1
  72. package/extensions/todo/package.json +1 -1
  73. package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
  74. package/extensions/triage-comments/index.ts +25 -3
  75. package/extensions/triage-comments/package.json +1 -1
  76. package/package.json +9 -6
@@ -307,6 +307,12 @@ function createQuietToolDefinitions(cwd: string, enabled: boolean): ToolDefiniti
307
307
  return enabled ? baseDefinitions.map(createQuietToolDefinition) : baseDefinitions;
308
308
  }
309
309
 
310
+ export const __testing = {
311
+ sanitizeInlineText,
312
+ formatQuietCallLine,
313
+ createQuietToolDefinition,
314
+ };
315
+
310
316
  export default function quietToolsExtension(pi: ExtensionAPI) {
311
317
  let enabled = true;
312
318
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-quiet-tools",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A pi extension that visually compacts collapsed built-in tool rows in the TUI without changing tool results sent to the model.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.79.10
1
+ 0.80.3
@@ -60,6 +60,24 @@ let reviewLoopFixingEnabled = false;
60
60
  let reviewCustomInstructions: string | undefined = undefined;
61
61
  let reviewLoopInProgress = false;
62
62
 
63
+ function getReviewRuntimeState() {
64
+ return {
65
+ reviewOriginId,
66
+ endReviewInProgress,
67
+ reviewLoopFixingEnabled,
68
+ reviewCustomInstructions,
69
+ reviewLoopInProgress,
70
+ };
71
+ }
72
+
73
+ function resetReviewRuntimeState() {
74
+ reviewOriginId = undefined;
75
+ endReviewInProgress = false;
76
+ reviewLoopFixingEnabled = false;
77
+ reviewCustomInstructions = undefined;
78
+ reviewLoopInProgress = false;
79
+ }
80
+
63
81
  const REVIEW_STATE_TYPE = "review-session";
64
82
  const REVIEW_ANCHOR_TYPE = "review-anchor";
65
83
  const REVIEW_SETTINGS_TYPE = "review-settings";
@@ -88,7 +106,7 @@ function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
88
106
 
89
107
  function setReviewWidget(ctx: ExtensionContext, active: boolean) {
90
108
  if (!ctx.hasUI) return;
91
- if (!active) {
109
+ if (!active || ctx.mode !== "tui") {
92
110
  ctx.ui.setWidget("review", undefined);
93
111
  return;
94
112
  }
@@ -630,18 +648,20 @@ async function hasPendingChanges(pi: ExtensionAPI): Promise<boolean> {
630
648
  function parsePrReference(ref: string): number | null {
631
649
  const trimmed = ref.trim();
632
650
 
633
- // Try as a number first
634
- const num = parseInt(trimmed, 10);
635
- if (!isNaN(num) && num > 0) {
636
- return num;
651
+ if (/^\d+$/.test(trimmed)) {
652
+ const num = Number(trimmed);
653
+ if (Number.isSafeInteger(num) && num > 0) {
654
+ return num;
655
+ }
637
656
  }
638
657
 
639
658
  // Try to extract from GitHub URL
640
659
  // Formats: https://github.com/owner/repo/pull/123
641
660
  // github.com/owner/repo/pull/123
642
- const urlMatch = trimmed.match(/github\.com\/[^/]+\/[^/]+\/pull\/(\d+)/);
661
+ const urlMatch = trimmed.match(/^(?:https?:\/\/)?(?:www\.)?github\.com\/[^/]+\/[^/]+\/pull\/(\d+)(?:[/?#]|$)/i);
643
662
  if (urlMatch) {
644
- return parseInt(urlMatch[1], 10);
663
+ const num = Number(urlMatch[1]);
664
+ return Number.isSafeInteger(num) && num > 0 ? num : null;
645
665
  }
646
666
 
647
667
  return null;
@@ -763,6 +783,30 @@ async function buildReviewPrompt(
763
783
  /**
764
784
  * Get user-facing hint for the review target
765
785
  */
786
+ type ComposeReviewPromptOptions = {
787
+ customInstructions?: string;
788
+ extraInstruction?: string;
789
+ projectGuidelines?: string | null;
790
+ };
791
+
792
+ function composeReviewPrompt(prompt: string, options: ComposeReviewPromptOptions = {}): string {
793
+ let fullPrompt = `${REVIEW_RUBRIC}\n\n---\n\nPlease perform a code review with the following focus:\n\n${prompt}`;
794
+
795
+ if (options.customInstructions?.trim()) {
796
+ fullPrompt += `\n\nShared custom review instructions (applies to all reviews):\n\n${options.customInstructions.trim()}`;
797
+ }
798
+
799
+ if (options.extraInstruction?.trim()) {
800
+ fullPrompt += `\n\nAdditional user-provided review instruction:\n\n${options.extraInstruction.trim()}`;
801
+ }
802
+
803
+ if (options.projectGuidelines?.trim()) {
804
+ fullPrompt += `\n\nThis project has additional instructions for code reviews:\n\n${options.projectGuidelines.trim()}`;
805
+ }
806
+
807
+ return fullPrompt;
808
+ }
809
+
766
810
  function getUserFacingHint(target: ReviewTarget): string {
767
811
  switch (target.type) {
768
812
  case "uncommitted":
@@ -833,6 +877,130 @@ function sleep(ms: number): Promise<void> {
833
877
  return new Promise((resolve) => setTimeout(resolve, ms));
834
878
  }
835
879
 
880
+ function tokenizeArgs(value: string): string[] {
881
+ const tokens: string[] = [];
882
+ let current = "";
883
+ let quote: '"' | "'" | null = null;
884
+
885
+ for (let i = 0; i < value.length; i++) {
886
+ const char = value[i];
887
+
888
+ if (quote) {
889
+ if (char === "\\" && i + 1 < value.length) {
890
+ current += value[i + 1];
891
+ i += 1;
892
+ continue;
893
+ }
894
+ if (char === quote) {
895
+ quote = null;
896
+ continue;
897
+ }
898
+ current += char;
899
+ continue;
900
+ }
901
+
902
+ if (char === '"' || char === "'") {
903
+ quote = char;
904
+ continue;
905
+ }
906
+
907
+ if (/\s/.test(char)) {
908
+ if (current.length > 0) {
909
+ tokens.push(current);
910
+ current = "";
911
+ }
912
+ continue;
913
+ }
914
+
915
+ current += char;
916
+ }
917
+
918
+ if (current.length > 0) {
919
+ tokens.push(current);
920
+ }
921
+
922
+ return tokens;
923
+ }
924
+
925
+ function parseReviewPaths(value: string): string[] {
926
+ return tokenizeArgs(value)
927
+ .map((item) => item.trim())
928
+ .filter((item) => item.length > 0);
929
+ }
930
+
931
+ type ParsedReviewArgs = {
932
+ target: ReviewTarget | { type: "pr"; ref: string } | null;
933
+ extraInstruction?: string;
934
+ error?: string;
935
+ };
936
+
937
+ function parseArgs(args: string | undefined): ParsedReviewArgs {
938
+ if (!args?.trim()) return { target: null };
939
+
940
+ const rawParts = tokenizeArgs(args.trim());
941
+ const parts: string[] = [];
942
+ let extraInstruction: string | undefined;
943
+
944
+ for (let i = 0; i < rawParts.length; i++) {
945
+ const part = rawParts[i];
946
+ if (part === "--extra") {
947
+ const next = rawParts[i + 1];
948
+ if (!next) {
949
+ return { target: null, error: "Missing value for --extra" };
950
+ }
951
+ extraInstruction = next;
952
+ i += 1;
953
+ continue;
954
+ }
955
+
956
+ if (part.startsWith("--extra=")) {
957
+ extraInstruction = part.slice("--extra=".length);
958
+ continue;
959
+ }
960
+
961
+ parts.push(part);
962
+ }
963
+
964
+ if (parts.length === 0) {
965
+ return { target: null, extraInstruction };
966
+ }
967
+
968
+ const subcommand = parts[0]?.toLowerCase();
969
+
970
+ switch (subcommand) {
971
+ case "uncommitted":
972
+ return { target: { type: "uncommitted" }, extraInstruction };
973
+
974
+ case "branch": {
975
+ const branch = parts[1];
976
+ if (!branch) return { target: null, extraInstruction };
977
+ return { target: { type: "baseBranch", branch }, extraInstruction };
978
+ }
979
+
980
+ case "commit": {
981
+ const sha = parts[1];
982
+ if (!sha) return { target: null, extraInstruction };
983
+ const title = parts.slice(2).join(" ") || undefined;
984
+ return { target: { type: "commit", sha, title }, extraInstruction };
985
+ }
986
+
987
+ case "folder": {
988
+ const paths = parts.slice(1).map((item) => item.trim()).filter((item) => item.length > 0);
989
+ if (paths.length === 0) return { target: null, extraInstruction };
990
+ return { target: { type: "folder", paths }, extraInstruction };
991
+ }
992
+
993
+ case "pr": {
994
+ const ref = parts[1];
995
+ if (!ref) return { target: null, extraInstruction };
996
+ return { target: { type: "pr", ref }, extraInstruction };
997
+ }
998
+
999
+ default:
1000
+ return { target: null, extraInstruction };
1001
+ }
1002
+ }
1003
+
836
1004
  async function waitForLoopTurnToStart(ctx: ExtensionContext, previousAssistantId?: string): Promise<boolean> {
837
1005
  const deadline = Date.now() + REVIEW_LOOP_START_TIMEOUT_MS;
838
1006
 
@@ -863,6 +1031,68 @@ type ReviewPresetValue =
863
1031
  | typeof TOGGLE_LOOP_FIXING_VALUE
864
1032
  | typeof TOGGLE_CUSTOM_INSTRUCTIONS_VALUE;
865
1033
 
1034
+ const REVIEW_SUMMARY_PROMPT = `We are leaving a code-review branch and returning to the main coding branch.
1035
+ Create a structured handoff that can be used immediately to implement fixes.
1036
+
1037
+ You MUST summarize the review that happened in this branch so findings can be acted on.
1038
+ Do not omit findings: include every actionable issue that was identified.
1039
+
1040
+ Required sections (in order):
1041
+
1042
+ ## Review Scope
1043
+ - What was reviewed (files/paths, changes, and scope)
1044
+
1045
+ ## Verdict
1046
+ - "correct" or "needs attention"
1047
+
1048
+ ## Findings
1049
+ For EACH finding, include:
1050
+ - Priority tag ([P0]..[P3]) and short title
1051
+ - File location (\`path/to/file.ext:line\`)
1052
+ - Why it matters (brief)
1053
+ - What should change (brief, actionable)
1054
+
1055
+ ## Fix Queue
1056
+ 1. Ordered implementation checklist (highest priority first)
1057
+
1058
+ ## Constraints & Preferences
1059
+ - Any constraints or preferences mentioned during review
1060
+ - Or "(none)"
1061
+
1062
+ ## Human Reviewer Callouts (Non-Blocking)
1063
+ Include only applicable callouts (no yes/no lines):
1064
+ - **This change adds a database migration:** <files/details>
1065
+ - **This change introduces a new dependency:** <package(s)/details>
1066
+ - **This change changes a dependency (or the lockfile):** <files/package(s)/details>
1067
+ - **This change modifies auth/permission behavior:** <what changed and where>
1068
+ - **This change introduces backwards-incompatible public schema/API/contract changes:** <what changed and where>
1069
+ - **This change includes irreversible or destructive operations:** <operation and scope>
1070
+
1071
+ If none apply, write "- (none)".
1072
+
1073
+ These are informational callouts for humans and are not fix items by themselves.
1074
+
1075
+ Preserve exact file paths, function names, and error messages where available.`;
1076
+
1077
+ export const __test__ = {
1078
+ applyReviewSettings,
1079
+ applyReviewState,
1080
+ buildReviewPrompt,
1081
+ composeReviewPrompt,
1082
+ getReviewRuntimeState,
1083
+ getReviewSettings,
1084
+ getReviewState,
1085
+ hasBlockingReviewFindings,
1086
+ hasNeedsAttentionVerdict,
1087
+ getUserFacingHint,
1088
+ parseArgs,
1089
+ parsePrReference,
1090
+ parseReviewPaths,
1091
+ REVIEW_SUMMARY_PROMPT,
1092
+ resetReviewRuntimeState,
1093
+ tokenizeArgs,
1094
+ };
1095
+
866
1096
  export default function reviewExtension(pi: ExtensionAPI) {
867
1097
  function persistReviewSettings() {
868
1098
  pi.appendEntry(REVIEW_SETTINGS_TYPE, {
@@ -1276,13 +1506,6 @@ export default function reviewExtension(pi: ExtensionAPI) {
1276
1506
  }
1277
1507
 
1278
1508
 
1279
- function parseReviewPaths(value: string): string[] {
1280
- return value
1281
- .split(/\s+/)
1282
- .map((item) => item.trim())
1283
- .filter((item) => item.length > 0);
1284
- }
1285
-
1286
1509
  /**
1287
1510
  * Show folder input
1288
1511
  */
@@ -1433,20 +1656,11 @@ export default function reviewExtension(pi: ExtensionAPI) {
1433
1656
  const hint = getUserFacingHint(target);
1434
1657
  const projectGuidelines = canReadProjectConfig(ctx) ? await loadProjectReviewGuidelines(ctx.cwd) : null;
1435
1658
 
1436
- // Combine the review rubric with the specific prompt
1437
- let fullPrompt = `${REVIEW_RUBRIC}\n\n---\n\nPlease perform a code review with the following focus:\n\n${prompt}`;
1438
-
1439
- if (reviewCustomInstructions) {
1440
- fullPrompt += `\n\nShared custom review instructions (applies to all reviews):\n\n${reviewCustomInstructions}`;
1441
- }
1442
-
1443
- if (options?.extraInstruction?.trim()) {
1444
- fullPrompt += `\n\nAdditional user-provided review instruction:\n\n${options.extraInstruction.trim()}`;
1445
- }
1446
-
1447
- if (projectGuidelines) {
1448
- fullPrompt += `\n\nThis project has additional instructions for code reviews:\n\n${projectGuidelines}`;
1449
- }
1659
+ const fullPrompt = composeReviewPrompt(prompt, {
1660
+ customInstructions: reviewCustomInstructions,
1661
+ extraInstruction: options?.extraInstruction,
1662
+ projectGuidelines,
1663
+ });
1450
1664
 
1451
1665
  const modeHint = useFreshSession ? " (fresh session)" : "";
1452
1666
  ctx.ui.notify(`Starting review: ${hint}${modeHint}`, "info");
@@ -1456,129 +1670,6 @@ export default function reviewExtension(pi: ExtensionAPI) {
1456
1670
  return true;
1457
1671
  }
1458
1672
 
1459
- /**
1460
- * Parse command arguments for direct invocation
1461
- * Returns the target or a special marker for PR that needs async handling
1462
- */
1463
- type ParsedReviewArgs = {
1464
- target: ReviewTarget | { type: "pr"; ref: string } | null;
1465
- extraInstruction?: string;
1466
- error?: string;
1467
- };
1468
-
1469
- function tokenizeArgs(value: string): string[] {
1470
- const tokens: string[] = [];
1471
- let current = "";
1472
- let quote: '"' | "'" | null = null;
1473
-
1474
- for (let i = 0; i < value.length; i++) {
1475
- const char = value[i];
1476
-
1477
- if (quote) {
1478
- if (char === "\\" && i + 1 < value.length) {
1479
- current += value[i + 1];
1480
- i += 1;
1481
- continue;
1482
- }
1483
- if (char === quote) {
1484
- quote = null;
1485
- continue;
1486
- }
1487
- current += char;
1488
- continue;
1489
- }
1490
-
1491
- if (char === '"' || char === "'") {
1492
- quote = char;
1493
- continue;
1494
- }
1495
-
1496
- if (/\s/.test(char)) {
1497
- if (current.length > 0) {
1498
- tokens.push(current);
1499
- current = "";
1500
- }
1501
- continue;
1502
- }
1503
-
1504
- current += char;
1505
- }
1506
-
1507
- if (current.length > 0) {
1508
- tokens.push(current);
1509
- }
1510
-
1511
- return tokens;
1512
- }
1513
-
1514
- function parseArgs(args: string | undefined): ParsedReviewArgs {
1515
- if (!args?.trim()) return { target: null };
1516
-
1517
- const rawParts = tokenizeArgs(args.trim());
1518
- const parts: string[] = [];
1519
- let extraInstruction: string | undefined;
1520
-
1521
- for (let i = 0; i < rawParts.length; i++) {
1522
- const part = rawParts[i];
1523
- if (part === "--extra") {
1524
- const next = rawParts[i + 1];
1525
- if (!next) {
1526
- return { target: null, error: "Missing value for --extra" };
1527
- }
1528
- extraInstruction = next;
1529
- i += 1;
1530
- continue;
1531
- }
1532
-
1533
- if (part.startsWith("--extra=")) {
1534
- extraInstruction = part.slice("--extra=".length);
1535
- continue;
1536
- }
1537
-
1538
- parts.push(part);
1539
- }
1540
-
1541
- if (parts.length === 0) {
1542
- return { target: null, extraInstruction };
1543
- }
1544
-
1545
- const subcommand = parts[0]?.toLowerCase();
1546
-
1547
- switch (subcommand) {
1548
- case "uncommitted":
1549
- return { target: { type: "uncommitted" }, extraInstruction };
1550
-
1551
- case "branch": {
1552
- const branch = parts[1];
1553
- if (!branch) return { target: null, extraInstruction };
1554
- return { target: { type: "baseBranch", branch }, extraInstruction };
1555
- }
1556
-
1557
- case "commit": {
1558
- const sha = parts[1];
1559
- if (!sha) return { target: null, extraInstruction };
1560
- const title = parts.slice(2).join(" ") || undefined;
1561
- return { target: { type: "commit", sha, title }, extraInstruction };
1562
- }
1563
-
1564
-
1565
- case "folder": {
1566
- const paths = parseReviewPaths(parts.slice(1).join(" "));
1567
- if (paths.length === 0) return { target: null, extraInstruction };
1568
- return { target: { type: "folder", paths }, extraInstruction };
1569
- }
1570
-
1571
- case "pr": {
1572
- const ref = parts[1];
1573
- if (!ref) return { target: null, extraInstruction };
1574
- return { target: { type: "pr", ref }, extraInstruction };
1575
- }
1576
-
1577
- default:
1578
- return { target: null, extraInstruction };
1579
- }
1580
- }
1581
-
1582
1673
  /**
1583
1674
  * Handle PR checkout and return a ReviewTarget (or null on failure)
1584
1675
  */
@@ -1754,7 +1845,7 @@ export default function reviewExtension(pi: ExtensionAPI) {
1754
1845
  pi.registerCommand("review", {
1755
1846
  description: "Review code changes (PR, uncommitted, branch, commit, or folder)",
1756
1847
  handler: async (args, ctx) => {
1757
- if (!ctx.hasUI) {
1848
+ if (ctx.mode !== "tui") {
1758
1849
  ctx.ui.notify("Review requires interactive mode", "error");
1759
1850
  return;
1760
1851
  }
@@ -1859,50 +1950,6 @@ export default function reviewExtension(pi: ExtensionAPI) {
1859
1950
  },
1860
1951
  });
1861
1952
 
1862
- // Custom prompt for review summaries - focuses on preserving actionable findings
1863
- const REVIEW_SUMMARY_PROMPT = `We are leaving a code-review branch and returning to the main coding branch.
1864
- Create a structured handoff that can be used immediately to implement fixes.
1865
-
1866
- You MUST summarize the review that happened in this branch so findings can be acted on.
1867
- Do not omit findings: include every actionable issue that was identified.
1868
-
1869
- Required sections (in order):
1870
-
1871
- ## Review Scope
1872
- - What was reviewed (files/paths, changes, and scope)
1873
-
1874
- ## Verdict
1875
- - "correct" or "needs attention"
1876
-
1877
- ## Findings
1878
- For EACH finding, include:
1879
- - Priority tag ([P0]..[P3]) and short title
1880
- - File location (\`path/to/file.ext:line\`)
1881
- - Why it matters (brief)
1882
- - What should change (brief, actionable)
1883
-
1884
- ## Fix Queue
1885
- 1. Ordered implementation checklist (highest priority first)
1886
-
1887
- ## Constraints & Preferences
1888
- - Any constraints or preferences mentioned during review
1889
- - Or "(none)"
1890
-
1891
- ## Human Reviewer Callouts (Non-Blocking)
1892
- Include only applicable callouts (no yes/no lines):
1893
- - **This change adds a database migration:** <files/details>
1894
- - **This change introduces a new dependency:** <package(s)/details>
1895
- - **This change changes a dependency (or the lockfile):** <files/package(s)/details>
1896
- - **This change modifies auth/permission behavior:** <what changed and where>
1897
- - **This change introduces backwards-incompatible public schema/API/contract changes:** <what changed and where>
1898
- - **This change includes irreversible or destructive operations:** <operation and scope>
1899
-
1900
- If none apply, write "- (none)".
1901
-
1902
- These are informational callouts for humans and are not fix items by themselves.
1903
-
1904
- Preserve exact file paths, function names, and error messages where available.`;
1905
-
1906
1953
  const REVIEW_FIX_FINDINGS_PROMPT = `Use the latest review summary in this session and implement the review findings now.
1907
1954
 
1908
1955
  Instructions:
@@ -1954,7 +2001,7 @@ Instructions:
1954
2001
  originId: string,
1955
2002
  showLoader: boolean,
1956
2003
  ): Promise<{ cancelled: boolean; error?: string } | null> {
1957
- if (showLoader && ctx.hasUI) {
2004
+ if (showLoader && ctx.mode === "tui") {
1958
2005
  return ctx.ui.custom<{ cancelled: boolean; error?: string } | null>((tui, theme, _kb, done) => {
1959
2006
  const loader = new BorderedLoader(tui, theme, "Returning and summarizing review branch...");
1960
2007
  loader.onAbort = () => done(null);
@@ -2062,6 +2109,13 @@ Instructions:
2062
2109
  return;
2063
2110
  }
2064
2111
 
2112
+ if (!getActiveReviewOrigin(ctx)) {
2113
+ if (!getReviewState(ctx)?.active) {
2114
+ ctx.ui.notify("Not in a review branch (use /review first, or review was started in current session mode)", "info");
2115
+ }
2116
+ return;
2117
+ }
2118
+
2065
2119
  if (endReviewInProgress) {
2066
2120
  ctx.ui.notify("/end-review is already running", "info");
2067
2121
  return;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-review",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A standalone pi extension that adds /review and /end-review commands adapted from mitsuhiko/agent-stuff.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.79.10
1
+ 0.80.3
@@ -288,7 +288,7 @@ export default function (pi: ExtensionAPI) {
288
288
  pi.registerCommand("todos", {
289
289
  description: "Show all todos on the current branch",
290
290
  handler: async (_args, ctx) => {
291
- if (!ctx.hasUI) {
291
+ if (ctx.mode !== "tui") {
292
292
  ctx.ui.notify("/todos requires interactive mode", "error");
293
293
  return;
294
294
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-todo",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A pi extension that adds a branch-aware todo tool and /todos viewer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.79.10
1
+ 0.80.3
@@ -227,14 +227,14 @@ function normalizeComment(raw: unknown, index: number): NormalizedComment | unde
227
227
 
228
228
  return {
229
229
  index,
230
- id: asTrimmedString(record.id ?? record.databaseId ?? record.nodeId),
230
+ id: asStableIdPart(record.id, record.databaseId, record.nodeId),
231
231
  body,
232
232
  path: asTrimmedString(record.path ?? record.file ?? record.filePath),
233
233
  line: asFiniteNumber(record.line ?? record.position),
234
234
  startLine: asFiniteNumber(record.startLine ?? record.start_line),
235
235
  side: asTrimmedString(record.side),
236
236
  diffHunk: asTrimmedString(record.diffHunk ?? record.diff_hunk ?? record.hunk),
237
- author: asTrimmedString(record.author ?? record.user ?? record.login),
237
+ author: githubLogin(record.author) ?? githubLogin(record.user) ?? asTrimmedString(record.login),
238
238
  url: asTrimmedString(record.url ?? record.htmlUrl ?? record.html_url),
239
239
  createdAt: asTrimmedString(record.createdAt ?? record.created_at),
240
240
  context: asTrimmedString(record.context ?? record.extraContext),
@@ -670,7 +670,11 @@ function normalizeFlagValue(value: string | undefined): string | undefined {
670
670
  }
671
671
 
672
672
  function getBlockedGhApiReason(tokens: string[]): string | undefined {
673
- if (getGhCommand(tokens).command !== "api") return undefined;
673
+ const parsed = getGhCommand(tokens);
674
+ if (parsed.command !== "api") return undefined;
675
+ if (tokens.find((token) => token.toLowerCase() === "graphql")) {
676
+ return "Triage bash blocks gh api graphql because it uses POST/body fields.";
677
+ }
674
678
  for (let index = 1; index < tokens.length; index += 1) {
675
679
  const token = tokens[index];
676
680
  const lowerToken = token.toLowerCase();
@@ -1877,6 +1881,24 @@ async function runPrMode(pi: ExtensionAPI, ctx: ExtensionCommandContext, target?
1877
1881
  }
1878
1882
  }
1879
1883
 
1884
+ export const __test__ = {
1885
+ assertToolPathInsideCwd,
1886
+ buildCommandPayload,
1887
+ buildSystemPrompt,
1888
+ buildUserPrompt,
1889
+ createTriageRuntimeGuardExtension,
1890
+ ensureImplementationNote,
1891
+ formatFetchedCommentsForSelection,
1892
+ formatInlineFilterContext,
1893
+ formatToolCall,
1894
+ getBlockedBashReason,
1895
+ normalizeComment,
1896
+ normalizeInput,
1897
+ parseSelectionList,
1898
+ parseTriageCommandArgs,
1899
+ prepareArguments,
1900
+ };
1901
+
1880
1902
  export default function triageCommentsExtension(pi: ExtensionAPI) {
1881
1903
  pi.registerCommand("triage-comments", {
1882
1904
  description: "Collect pasted feedback or PR comments, then start a triage_comments investigation",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-triage-comments",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
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",