@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.
- package/.pi-fleet-tested-version +1 -1
- package/README.md +37 -15
- package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
- package/extensions/agent-workflow-audit/index.ts +12 -0
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-git-diff/git.ts +1 -0
- package/extensions/annotate-git-diff/index.ts +1 -1
- package/extensions/annotate-git-diff/package.json +1 -1
- package/extensions/annotate-git-diff/prompt.ts +2 -1
- package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-last-message/index.ts +1 -1
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/brrr/.pi-fleet-tested-version +1 -1
- package/extensions/brrr/index.ts +4 -3
- package/extensions/brrr/package.json +1 -1
- package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
- package/extensions/claude-fast/package.json +1 -1
- package/extensions/code-reviewer/.pi-fleet-tested-version +1 -0
- package/extensions/code-reviewer/README.md +84 -0
- package/extensions/code-reviewer/index.ts +1280 -0
- package/extensions/code-reviewer/package.json +35 -0
- package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/.pi-fleet-tested-version +1 -1
- package/extensions/context-cap/package.json +1 -1
- package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
- package/extensions/context-inspector/index.ts +5 -0
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/.pi-fleet-tested-version +1 -1
- package/extensions/contrarian/index.ts +35 -3
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
- package/extensions/dirty-repo-guard/package.json +1 -1
- package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -0
- package/extensions/dynamic-context-pruning/README.md +435 -0
- package/extensions/dynamic-context-pruning/index.ts +2546 -0
- package/extensions/dynamic-context-pruning/package.json +34 -0
- package/extensions/git-footer/.pi-fleet-tested-version +1 -1
- package/extensions/git-footer/index.ts +42 -14
- package/extensions/git-footer/package.json +1 -1
- package/extensions/gnosis/.pi-fleet-tested-version +1 -1
- package/extensions/gnosis/package.json +1 -1
- package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
- package/extensions/illustrations-to-explain-things/package.json +1 -1
- package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/.pi-fleet-tested-version +1 -1
- package/extensions/librarian/index.ts +7 -0
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
- package/extensions/minimal-footer/index.ts +132 -72
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/notify/.pi-fleet-tested-version +1 -1
- package/extensions/notify/package.json +1 -1
- package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
- package/extensions/openai-fast/package.json +1 -1
- package/extensions/oracle/.pi-fleet-tested-version +1 -1
- package/extensions/oracle/index.ts +21 -2
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
- package/extensions/permission-gate/index.ts +5 -1
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/index.ts +6 -0
- package/extensions/quiet-tools/package.json +1 -1
- package/extensions/review/.pi-fleet-tested-version +1 -1
- package/extensions/review/index.ts +251 -197
- package/extensions/review/package.json +1 -1
- package/extensions/todo/.pi-fleet-tested-version +1 -1
- package/extensions/todo/index.ts +1 -1
- package/extensions/todo/package.json +1 -1
- package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
- package/extensions/triage-comments/index.ts +25 -3
- package/extensions/triage-comments/package.json +1 -1
- 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 +1 @@
|
|
|
1
|
-
0.
|
|
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
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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(
|
|
661
|
+
const urlMatch = trimmed.match(/^(?:https?:\/\/)?(?:www\.)?github\.com\/[^/]+\/[^/]+\/pull\/(\d+)(?:[/?#]|$)/i);
|
|
643
662
|
if (urlMatch) {
|
|
644
|
-
|
|
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
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
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 (
|
|
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.
|
|
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 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.80.3
|
package/extensions/todo/index.ts
CHANGED
|
@@ -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 (
|
|
291
|
+
if (ctx.mode !== "tui") {
|
|
292
292
|
ctx.ui.notify("/todos requires interactive mode", "error");
|
|
293
293
|
return;
|
|
294
294
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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",
|