@mrclrchtr/supi-review 2.7.0 → 3.0.0

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 (49) hide show
  1. package/README.md +75 -163
  2. package/node_modules/@mrclrchtr/supi-core/README.md +1 -1
  3. package/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/api.ts +2 -0
  5. package/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +40 -0
  6. package/node_modules/@mrclrchtr/supi-core/src/settings/scoped-settings-list.ts +1 -0
  7. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +14 -0
  8. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-submenus.ts +31 -14
  9. package/node_modules/@mrclrchtr/supi-core/src/settings.ts +1 -0
  10. package/package.json +3 -3
  11. package/src/config.ts +64 -0
  12. package/src/git-command.ts +78 -0
  13. package/src/git.ts +306 -505
  14. package/src/history/collect.ts +43 -120
  15. package/src/model.ts +35 -0
  16. package/src/review-path.ts +85 -0
  17. package/src/review-result.ts +43 -92
  18. package/src/review.ts +154 -288
  19. package/src/session/review-plan-store.ts +20 -51
  20. package/src/target/packet.ts +46 -369
  21. package/src/tool/agent-review-schemas.ts +101 -104
  22. package/src/tool/agent-review-tools.ts +269 -301
  23. package/src/tool/child-failure-diagnostics.ts +1 -1
  24. package/src/tool/child-resource-loader.ts +37 -0
  25. package/src/tool/child-session-runner.ts +83 -0
  26. package/src/tool/output-page.ts +47 -0
  27. package/src/tool/planner-runner.ts +69 -0
  28. package/src/tool/review-runner.ts +42 -257
  29. package/src/tool/review-system-prompt.ts +12 -110
  30. package/src/tool/review-tools.ts +119 -0
  31. package/src/tool/review-workflow.ts +309 -0
  32. package/src/tool/runner-helpers.ts +3 -8
  33. package/src/tool/schemas.ts +75 -62
  34. package/src/tui/common.ts +175 -0
  35. package/src/tui/prepare.ts +132 -0
  36. package/src/tui/run.ts +160 -0
  37. package/src/types.ts +153 -275
  38. package/src/history/synthesize.ts +0 -121
  39. package/src/tool/agent-review-workflow.ts +0 -398
  40. package/src/tool/brief-runner.ts +0 -180
  41. package/src/tool/guidance.ts +0 -21
  42. package/src/tool/review-handlers.ts +0 -314
  43. package/src/tool/snapshot-tools.ts +0 -117
  44. package/src/ui/flow.ts +0 -189
  45. package/src/ui/format-content.ts +0 -143
  46. package/src/ui/renderer.ts +0 -274
  47. package/src/ui/review-plan-inspector.ts +0 -391
  48. package/src/ui/review-tool-format.ts +0 -138
  49. package/src/ui/review-tool-renderer.ts +0 -234
@@ -1,143 +0,0 @@
1
- import {
2
- formatChildFailureCopy,
3
- formatChildFailureDiagnostics,
4
- } from "../tool/child-failure-diagnostics.ts";
5
- import type {
6
- BriefSynthesisRunResult,
7
- ChildFailureDiagnostics,
8
- ReviewItem,
9
- ReviewResult,
10
- } from "../types.ts";
11
-
12
- /** Format a `impact` or `effort` level value. */
13
- export function formatLevel(value: ReviewItem["impact"] | ReviewItem["effort"]): string {
14
- return `${value[0]?.toUpperCase() ?? ""}${value.slice(1)}`;
15
- }
16
-
17
- /** Format a code location as a human-readable `file:line` or `file:start-end`. */
18
- export function formatLocation(file: string, startLine: number, endLine: number): string {
19
- return `${file}:${startLine === endLine ? startLine : `${startLine}-${endLine}`}`;
20
- }
21
-
22
- /** Format one non-success brief-synthesis result for parent-facing custom-message content. */
23
- export function formatBriefSynthesisFailureContent(
24
- result: Exclude<BriefSynthesisRunResult, { kind: "success" }>,
25
- ): string {
26
- const parts = [formatBriefSynthesisFailureCopy(result)];
27
- appendChildFailureDiagnostics(parts, result.diagnostics);
28
- return parts.join("\n");
29
- }
30
-
31
- /** Generate short static parent-facing copy for a brief-synthesis outcome. */
32
- export function formatBriefSynthesisFailureCopy(
33
- result: Exclude<BriefSynthesisRunResult, { kind: "success" }>,
34
- ): string {
35
- switch (result.kind) {
36
- case "failed":
37
- return formatChildFailureCopy("brief-synthesis", result.failureCode);
38
- case "canceled":
39
- return "Brief synthesis was canceled.";
40
- case "timeout":
41
- return "Brief synthesis timed out.";
42
- }
43
- }
44
-
45
- /** Format review results for the LLM-visible custom message content. */
46
- export function formatReviewContent(result: ReviewResult): string {
47
- switch (result.kind) {
48
- case "success":
49
- return formatSuccessContent(result);
50
- case "failed":
51
- return formatFailureContent(result);
52
- case "canceled":
53
- return formatCanceledContent(result);
54
- case "timeout":
55
- return formatTimeoutContent(result);
56
- }
57
- }
58
-
59
- function formatFailureContent(result: Extract<ReviewResult, { kind: "failed" }>): string {
60
- const parts = [formatChildFailureCopy("reviewer", result.failureCode)];
61
- appendChildFailureDiagnostics(parts, result.diagnostics);
62
- return parts.join("\n");
63
- }
64
-
65
- function formatCanceledContent(result: Extract<ReviewResult, { kind: "canceled" }>): string {
66
- const parts = ["Reviewer was canceled."];
67
- appendChildFailureDiagnostics(parts, result.diagnostics);
68
- return parts.join("\n");
69
- }
70
-
71
- function formatTimeoutContent(result: Extract<ReviewResult, { kind: "timeout" }>): string {
72
- const parts = ["Reviewer timed out."];
73
- appendChildFailureDiagnostics(parts, result.diagnostics);
74
- return parts.join("\n");
75
- }
76
-
77
- function appendChildFailureDiagnostics(
78
- parts: string[],
79
- diagnostics: ChildFailureDiagnostics | undefined,
80
- ): void {
81
- if (!diagnostics) return;
82
- parts.push("", "Diagnostics:", ...formatChildFailureDiagnostics(diagnostics));
83
- }
84
-
85
- function formatSuccessContent(result: Extract<ReviewResult, { kind: "success" }>): string {
86
- const { output } = result;
87
- const confidencePercent = Math.round(output.overall_confidence_score * 100);
88
- const lines: string[] = ["## Code Review Result", "", `**Model:** ${result.modelId}`];
89
-
90
- if (result.brief) {
91
- lines.push("", "### Session-derived Brief", "");
92
- lines.push(`**Summary:** ${result.brief.summary}`);
93
- lines.push(`**Intended outcome:** ${result.brief.intendedOutcome}`);
94
- if (result.brief.constraints.length > 0) {
95
- lines.push("**Constraints:**");
96
- lines.push(...result.brief.constraints.map((item) => `- ${item}`));
97
- }
98
- if (result.brief.focusAreas.length > 0) {
99
- lines.push("**Focus areas:**");
100
- lines.push(...result.brief.focusAreas.map((item) => `- ${item}`));
101
- }
102
- if (result.brief.riskyFiles.length > 0) {
103
- lines.push("**Risky files:**");
104
- lines.push(...result.brief.riskyFiles.map((item) => `- ${item}`));
105
- }
106
- }
107
-
108
- lines.push("", `Verdict: ${output.overall_correctness} (confidence: ${confidencePercent}%)`);
109
- lines.push(
110
- `Summary: ${output.summary.actions.mustFix} must-fix, ${output.summary.actions.shouldFix} should-fix, ${output.summary.actions.consider} consider`,
111
- );
112
-
113
- if (output.items.length > 0) {
114
- lines.push("", "### Review Items", "", ...formatReviewItems(output.items));
115
- }
116
-
117
- lines.push("", `Overall: ${output.overall_explanation}`);
118
- return lines.join("\n");
119
- }
120
-
121
- function formatReviewItems(items: ReviewItem[]): string[] {
122
- return items.flatMap((item, index) => {
123
- const location = item.code_location
124
- ? ` ${formatLocation(item.code_location.absolute_file_path, item.code_location.line_range.start, item.code_location.line_range.end)}`
125
- : undefined;
126
-
127
- const lines = [
128
- ` #${index + 1} ${item.title} [${item.recommended_action}]`,
129
- ` Category: ${item.category}`,
130
- ` Impact: ${formatLevel(item.impact)}`,
131
- ` Effort: ${formatLevel(item.effort)}`,
132
- ];
133
-
134
- if (location) {
135
- lines.push(location);
136
- }
137
-
138
- lines.push(` ${item.body}`);
139
- lines.push(` Suggested fix: ${item.suggested_fix}`);
140
- lines.push(` Verification: ${item.verification_hint}`);
141
- return lines;
142
- });
143
- }
@@ -1,274 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { Box, Container, Spacer, Text } from "@earendil-works/pi-tui";
3
- import {
4
- formatChildFailureCopy,
5
- formatChildFailureDiagnostics,
6
- } from "../tool/child-failure-diagnostics.ts";
7
- import type {
8
- BriefSynthesisRunResult,
9
- ChildFailureDiagnostics,
10
- ReviewItem,
11
- ReviewResult,
12
- ReviewSnapshot,
13
- } from "../types.ts";
14
- import { formatBriefSynthesisFailureCopy, formatLevel, formatLocation } from "./format-content.ts";
15
-
16
- /** Register the custom TUI renderer for `supi-review` messages. */
17
- interface BriefSynthesisFailureMessage {
18
- result: Exclude<BriefSynthesisRunResult, { kind: "success" }>;
19
- snapshot: ReviewSnapshot;
20
- modelId: string;
21
- }
22
-
23
- interface ReviewMessageDetails {
24
- result?: ReviewResult;
25
- briefSynthesisFailure?: BriefSynthesisFailureMessage;
26
- }
27
-
28
- export function registerReviewRenderer(pi: ExtensionAPI): void {
29
- pi.registerMessageRenderer("supi-review", (message, { expanded }, theme) => {
30
- const details = message.details as ReviewMessageDetails | undefined;
31
- if (details?.briefSynthesisFailure) {
32
- return renderBriefSynthesisFailure(details.briefSynthesisFailure, theme);
33
- }
34
-
35
- const result = details?.result;
36
- if (!result) {
37
- return new Text(theme.fg("dim", "No review data"), 1, 0);
38
- }
39
-
40
- switch (result.kind) {
41
- case "success":
42
- return renderSuccess(result, theme, expanded);
43
- case "failed":
44
- return renderFailed(result, theme);
45
- case "canceled":
46
- return renderCanceled(result, theme);
47
- case "timeout":
48
- return renderTimeout(result, theme);
49
- default:
50
- return new Text(theme.fg("dim", "Unknown review state"), 1, 0);
51
- }
52
- });
53
- }
54
-
55
- function renderBriefSynthesisFailure(
56
- failure: BriefSynthesisFailureMessage,
57
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
58
- ): Container {
59
- const container = new Container();
60
- const title =
61
- failure.result.kind === "failed"
62
- ? "◆ Brief Synthesis Failed"
63
- : failure.result.kind === "timeout"
64
- ? "◆ Brief Synthesis Timed Out"
65
- : "◆ Brief Synthesis Canceled";
66
- const color = failure.result.kind === "failed" ? "error" : "warning";
67
-
68
- container.addChild(new Text(theme.fg(color, title), 1, 0));
69
- container.addChild(new Spacer(1));
70
- container.addChild(new Text(theme.fg("muted", `Model: ${failure.modelId}`), 1, 0));
71
- container.addChild(new Text(theme.fg("muted", `Snapshot: ${failure.snapshot.title}`), 1, 0));
72
- container.addChild(new Spacer(1));
73
- container.addChild(
74
- new Text(theme.fg(color, formatBriefSynthesisFailureCopy(failure.result)), 1, 0),
75
- );
76
- renderChildFailureDiagnostics(container, failure.result.diagnostics, theme);
77
- return container;
78
- }
79
-
80
- function renderSuccess(
81
- result: Extract<ReviewResult, { kind: "success" }>,
82
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
83
- expanded: boolean,
84
- ): Container {
85
- const container = new Container();
86
- const output = result.output;
87
-
88
- container.addChild(new Text(theme.fg("accent", "◆ Code Review Results"), 1, 0));
89
- container.addChild(new Text(theme.fg("muted", `Model: ${result.modelId}`), 1, 0));
90
- container.addChild(new Text(theme.fg("muted", `Snapshot: ${result.snapshot.title}`), 1, 0));
91
- container.addChild(new Spacer(1));
92
-
93
- if (result.brief) {
94
- renderBriefContext(container, result.brief, theme);
95
- container.addChild(new Spacer(1));
96
- }
97
-
98
- const normalizedVerdict = output.overall_correctness.toLowerCase();
99
- const verdictColor = normalizedVerdict.includes("issues") ? "warning" : "success";
100
- container.addChild(
101
- new Text(
102
- `${theme.fg(verdictColor, "●")} ${theme.fg(verdictColor, output.overall_correctness)}` +
103
- theme.fg("dim", ` (confidence: ${(output.overall_confidence_score * 100).toFixed(0)}%)`),
104
- 1,
105
- 0,
106
- ),
107
- );
108
- container.addChild(
109
- new Text(
110
- theme.fg(
111
- "dim",
112
- `Summary: ${output.summary.actions.mustFix} must-fix, ${output.summary.actions.shouldFix} should-fix, ${output.summary.actions.consider} consider`,
113
- ),
114
- 1,
115
- 0,
116
- ),
117
- );
118
-
119
- if (output.overall_explanation) {
120
- container.addChild(new Spacer(1));
121
- container.addChild(new Text(theme.fg("dim", output.overall_explanation), 1, 0));
122
- }
123
-
124
- if (expanded) {
125
- container.addChild(new Spacer(1));
126
-
127
- if (output.items.length === 0) {
128
- container.addChild(new Text(theme.fg("success", "✓ No review items"), 1, 0));
129
- } else {
130
- container.addChild(
131
- new Text(theme.fg("accent", `Review Items (${output.items.length})`), 1, 0),
132
- );
133
- output.items.forEach((item, index) => {
134
- container.addChild(renderReviewItem(item, index, theme));
135
- });
136
- }
137
- }
138
-
139
- return container;
140
- }
141
-
142
- function renderBriefContext(
143
- container: Container,
144
- brief: NonNullable<Extract<ReviewResult, { kind: "success" }>["brief"]>,
145
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
146
- ): void {
147
- container.addChild(new Text(theme.fg("muted", `Summary: ${brief.summary}`), 1, 0));
148
- container.addChild(new Text(theme.fg("muted", `Outcome: ${brief.intendedOutcome}`), 1, 0));
149
- if (brief.focusAreas.length > 0) {
150
- container.addChild(
151
- new Text(theme.fg("muted", `Focus: ${brief.focusAreas.slice(0, 3).join(", ")}`), 1, 0),
152
- );
153
- }
154
- if (brief.riskyFiles.length > 0) {
155
- container.addChild(
156
- new Text(theme.fg("muted", `Risky files: ${brief.riskyFiles.slice(0, 3).join(", ")}`), 1, 0),
157
- );
158
- }
159
- }
160
-
161
- function renderReviewItem(
162
- item: ReviewItem,
163
- index: number,
164
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
165
- ): Container {
166
- const container = new Container();
167
- const actionColor = actionColorName(item.recommended_action);
168
- const locationText = item.code_location
169
- ? formatLocation(
170
- item.code_location.absolute_file_path,
171
- item.code_location.line_range.start,
172
- item.code_location.line_range.end,
173
- )
174
- : undefined;
175
-
176
- container.addChild(new Spacer(1));
177
- container.addChild(
178
- new Text(
179
- `${theme.fg("text", `#${index + 1} ${item.title}`)} ${theme.fg(actionColor, `[${item.recommended_action}]`)}`,
180
- 1,
181
- 0,
182
- ),
183
- );
184
- container.addChild(new Text(theme.fg("dim", `Category: ${item.category}`), 2, 0));
185
- container.addChild(new Text(theme.fg("dim", `Impact: ${formatLevel(item.impact)}`), 2, 0));
186
- container.addChild(new Text(theme.fg("dim", `Effort: ${formatLevel(item.effort)}`), 2, 0));
187
-
188
- if (locationText) {
189
- container.addChild(new Text(theme.fg("dim", locationText), 2, 0));
190
- }
191
-
192
- if (item.body) {
193
- const body = new Box(2, 0);
194
- body.addChild(new Text(theme.fg("text", item.body), 0, 0));
195
- container.addChild(body);
196
- }
197
-
198
- container.addChild(new Text(theme.fg("dim", `Suggested fix: ${item.suggested_fix}`), 2, 0));
199
- container.addChild(new Text(theme.fg("dim", `Verification: ${item.verification_hint}`), 2, 0));
200
-
201
- return container;
202
- }
203
-
204
- function renderFailed(
205
- result: Extract<ReviewResult, { kind: "failed" }>,
206
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
207
- ): Container {
208
- const container = new Container();
209
- container.addChild(new Text(theme.fg("error", "◆ Review Failed"), 1, 0));
210
- container.addChild(new Spacer(1));
211
- container.addChild(new Text(theme.fg("muted", `Model: ${result.modelId}`), 1, 0));
212
- container.addChild(new Text(theme.fg("muted", `Snapshot: ${result.snapshot.title}`), 1, 0));
213
- container.addChild(new Spacer(1));
214
- container.addChild(
215
- new Text(theme.fg("error", formatChildFailureCopy("reviewer", result.failureCode)), 1, 0),
216
- );
217
- renderChildFailureDiagnostics(container, result.diagnostics, theme);
218
- return container;
219
- }
220
-
221
- function renderTimeout(
222
- result: Extract<ReviewResult, { kind: "timeout" }>,
223
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
224
- ): Container {
225
- const container = new Container();
226
- container.addChild(new Text(theme.fg("warning", "◆ Review Timed Out"), 1, 0));
227
- container.addChild(new Spacer(1));
228
- container.addChild(new Text(theme.fg("muted", `Model: ${result.modelId}`), 1, 0));
229
- container.addChild(new Text(theme.fg("muted", `Snapshot: ${result.snapshot.title}`), 1, 0));
230
- container.addChild(new Spacer(1));
231
- container.addChild(new Text(theme.fg("warning", "Reviewer timed out."), 1, 0));
232
- renderChildFailureDiagnostics(container, result.diagnostics, theme);
233
- return container;
234
- }
235
-
236
- function renderCanceled(
237
- result: Extract<ReviewResult, { kind: "canceled" }>,
238
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
239
- ): Container {
240
- const container = new Container();
241
- container.addChild(new Text(theme.fg("warning", "◆ Review Canceled"), 1, 0));
242
- container.addChild(new Text(theme.fg("warning", "Reviewer was canceled."), 1, 0));
243
- renderChildFailureDiagnostics(container, result.diagnostics, theme);
244
- return container;
245
- }
246
-
247
- function renderChildFailureDiagnostics(
248
- container: Container,
249
- diagnostics: ChildFailureDiagnostics | undefined,
250
- theme: Parameters<Parameters<ExtensionAPI["registerMessageRenderer"]>[1]>[2],
251
- ): void {
252
- if (!diagnostics) return;
253
-
254
- container.addChild(new Spacer(1));
255
- container.addChild(new Text(theme.fg("dim", "Diagnostics:"), 1, 0));
256
- for (const line of formatChildFailureDiagnostics(diagnostics)) {
257
- container.addChild(new Text(theme.fg("dim", line), 1, 0));
258
- }
259
- }
260
-
261
- function actionColorName(
262
- action: ReviewItem["recommended_action"],
263
- ): "success" | "warning" | "error" {
264
- switch (action) {
265
- case "must-fix":
266
- return "error";
267
- case "should-fix":
268
- return "warning";
269
- case "consider":
270
- return "success";
271
- default:
272
- return "success";
273
- }
274
- }