@mrclrchtr/supi-review 2.8.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 (44) hide show
  1. package/README.md +71 -180
  2. package/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  3. package/node_modules/@mrclrchtr/supi-core/src/api.ts +2 -0
  4. package/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +40 -0
  5. package/package.json +3 -3
  6. package/src/config.ts +30 -21
  7. package/src/git-command.ts +78 -0
  8. package/src/git.ts +306 -505
  9. package/src/history/collect.ts +43 -120
  10. package/src/model.ts +3 -1
  11. package/src/review-path.ts +85 -0
  12. package/src/review-result.ts +43 -92
  13. package/src/review.ts +154 -290
  14. package/src/session/review-plan-store.ts +20 -51
  15. package/src/target/packet.ts +46 -369
  16. package/src/tool/agent-review-schemas.ts +101 -104
  17. package/src/tool/agent-review-tools.ts +268 -309
  18. package/src/tool/child-failure-diagnostics.ts +1 -1
  19. package/src/tool/child-resource-loader.ts +37 -0
  20. package/src/tool/child-session-runner.ts +83 -0
  21. package/src/tool/output-page.ts +47 -0
  22. package/src/tool/planner-runner.ts +69 -0
  23. package/src/tool/review-runner.ts +42 -257
  24. package/src/tool/review-system-prompt.ts +12 -110
  25. package/src/tool/review-tools.ts +119 -0
  26. package/src/tool/review-workflow.ts +309 -0
  27. package/src/tool/runner-helpers.ts +3 -8
  28. package/src/tool/schemas.ts +75 -62
  29. package/src/tui/common.ts +175 -0
  30. package/src/tui/prepare.ts +132 -0
  31. package/src/tui/run.ts +160 -0
  32. package/src/types.ts +153 -275
  33. package/src/history/synthesize.ts +0 -121
  34. package/src/tool/agent-review-workflow.ts +0 -398
  35. package/src/tool/brief-runner.ts +0 -180
  36. package/src/tool/guidance.ts +0 -21
  37. package/src/tool/review-handlers.ts +0 -314
  38. package/src/tool/snapshot-tools.ts +0 -117
  39. package/src/ui/flow.ts +0 -189
  40. package/src/ui/format-content.ts +0 -143
  41. package/src/ui/renderer.ts +0 -274
  42. package/src/ui/review-plan-inspector.ts +0 -391
  43. package/src/ui/review-tool-format.ts +0 -138
  44. package/src/ui/review-tool-renderer.ts +0 -234
package/src/types.ts CHANGED
@@ -1,292 +1,100 @@
1
1
  import type { Model } from "@earendil-works/pi-ai";
2
2
  import type { ChildLifecycleTrace } from "./tool/child-lifecycle-trace.ts";
3
3
 
4
- /** Closed, host-owned classification for a managed child failure. */
5
- export type ChildFailureCode =
6
- | "session-creation-failed"
7
- | "prompt-rejected"
8
- | "missing-structured-output"
9
- | "unexpected-runner-failure";
10
-
11
- /** Managed child role used for host-owned failure copy. */
12
- export type ChildStage = "brief-synthesis" | "reviewer";
13
-
14
- /** Failed child result fields with diagnostics required exactly when a child was observed. */
15
- export type ChildFailedResult =
16
- | { failureCode: "session-creation-failed"; diagnostics?: never }
17
- | {
18
- failureCode: Exclude<ChildFailureCode, "session-creation-failed">;
19
- diagnostics: ChildFailureDiagnostics;
20
- };
21
-
22
- /** Inclusive 1-based line range reported by the reviewer. */
23
- export interface ReviewLineRange {
24
- start: number;
25
- end: number;
26
- }
27
-
28
- /** File location reported by the reviewer. */
29
- export interface ReviewCodeLocation {
30
- absolute_file_path: string;
31
- line_range: ReviewLineRange;
32
- }
33
-
34
- export type ReviewItemCategory =
35
- | "correctness"
36
- | "security"
37
- | "performance"
38
- | "api"
39
- | "test-gap"
40
- | "docs"
41
- | "cleanup"
42
- | "maintainer";
4
+ /** A full hexadecimal Git commit object id supplied by an agent caller. */
5
+ export type CommitId = string;
43
6
 
44
- export type ReviewItemImpact = "low" | "medium" | "high";
45
- export type ReviewItemEffort = "low" | "medium" | "high";
46
- export type ReviewItemRecommendedAction = "must-fix" | "should-fix" | "consider";
47
- export type ReviewOverallCorrectness = "PATCH IS CORRECT" | "PATCH HAS ISSUES";
48
-
49
- /** Structured review item returned by the reviewer session. */
50
- export interface ReviewItem {
51
- title: string;
52
- body: string;
53
- category: ReviewItemCategory;
54
- impact: ReviewItemImpact;
55
- effort: ReviewItemEffort;
56
- recommended_action: ReviewItemRecommendedAction;
57
- confidence_score: number;
58
- suggested_fix: string;
59
- verification_hint: string;
60
- code_location?: ReviewCodeLocation;
61
- }
62
-
63
- /** Raw review payload submitted by the reviewer child session. */
64
- export interface ReviewOutputEvent {
65
- items: ReviewItem[];
66
- overall_explanation: string;
67
- overall_confidence_score: number;
68
- }
69
-
70
- export interface ReviewSummary {
71
- actions: {
72
- mustFix: number;
73
- shouldFix: number;
74
- consider: number;
75
- };
76
- categories: Partial<Record<ReviewItemCategory, number>>;
77
- }
78
-
79
- /** Host-normalized review payload used for rendering and follow-up flow. */
80
- export interface NormalizedReviewOutput extends ReviewOutputEvent {
81
- overall_correctness: ReviewOverallCorrectness;
82
- summary: ReviewSummary;
83
- }
84
-
85
- /** User-selected review target. */
7
+ /** The Git change selected for review. */
86
8
  export type ReviewTargetSpec =
87
9
  | { kind: "working-tree" }
88
- | { kind: "branch"; base: string }
89
- | { kind: "commit"; sha: string };
10
+ | { kind: "comparison"; baseCommit: CommitId }
11
+ | { kind: "commit"; commit: CommitId };
12
+
13
+ /** Resolved identity of a selected review target. */
14
+ export type ResolvedReviewTarget =
15
+ | { kind: "working-tree"; headCommit: CommitId }
16
+ | {
17
+ kind: "comparison";
18
+ requestedBaseCommit: CommitId;
19
+ mergeBaseCommit: CommitId;
20
+ headCommit: CommitId;
21
+ }
22
+ | { kind: "commit"; commit: CommitId; parentCommit?: CommitId };
90
23
 
91
- /** Diff statistics for a resolved snapshot. */
92
24
  export interface DiffStats {
93
25
  files: number;
94
26
  additions: number;
95
27
  deletions: number;
96
28
  }
97
29
 
98
- /** Concrete git snapshot resolved before synthesis/review starts. */
30
+ /** Concrete target metadata and diff resolved before reviewer execution. */
99
31
  export interface ReviewSnapshot {
100
- target: ReviewTargetSpec;
32
+ requestedTarget: ReviewTargetSpec;
33
+ target: ResolvedReviewTarget;
101
34
  title: string;
102
35
  changedFiles: string[];
103
36
  diffText: string;
104
37
  stats: DiffStats;
105
38
  }
106
39
 
107
- /** Snapshot metadata safe to retain without duplicating bulk diff text. */
108
40
  export type ReviewSnapshotSummary = Omit<ReviewSnapshot, "diffText">;
109
41
 
110
- /** Model picked explicitly for the current review run. */
111
- export type ReviewModelSelection = import("@mrclrchtr/supi-core/model-selection").ModelSelection;
112
-
113
- /** Structured brief synthesized from the current session history. */
114
- export type ReviewInstructionBlockId =
115
- | "public-surface"
116
- | "cross-layer"
117
- | "schema-widening"
118
- | "cleanup";
119
-
120
- export interface SynthesizedReviewBrief {
121
- summary: string;
122
- intendedOutcome: string;
123
- constraints: string[];
124
- focusAreas: string[];
125
- riskyFiles: string[];
126
- unresolvedQuestions: string[];
127
- reviewInstructionBlockIds: ReviewInstructionBlockId[];
128
- note?: string;
129
- }
130
-
131
- /** Brief field evaluated by the main agent before reviewer sessions run. */
132
- export type ReviewBriefField =
133
- | "summary"
134
- | "intendedOutcome"
135
- | "constraints"
136
- | "focusAreas"
137
- | "riskyFiles"
138
- | "unresolvedQuestions"
139
- | "reviewInstructionBlockIds";
140
-
141
- /** Class of defect identified in a generated review brief. */
142
- export type BriefCritiqueFindingKind =
143
- | "omission"
144
- | "unsupported-inference"
145
- | "misprioritized"
146
- | "unclear";
147
-
148
- /** One evidence-backed main-agent criticism of a generated review brief. */
149
- export interface BriefCritiqueFinding {
150
- kind: BriefCritiqueFindingKind;
151
- field: ReviewBriefField;
152
- explanation: string;
153
- evidence: string;
154
- proposedChange: string;
155
- }
156
-
157
- /** Structured quality gate completed by the main agent before review execution. */
158
- export interface BriefCritique {
159
- verdict: "accept" | "revise";
160
- summary: string;
161
- findings: BriefCritiqueFinding[];
162
- }
163
-
164
- /** One independent reviewer child-session assignment. */
165
- export interface ReviewerAssignment {
42
+ /** One independent caller-defined review objective. */
43
+ export interface ReviewTask {
166
44
  id: string;
167
- focus: string;
45
+ instructions: string;
168
46
  }
169
47
 
170
- /** Generated/effective brief pair retained for synthesis-prompt evaluation. */
171
- export interface BriefEvaluation {
172
- planId: string;
173
- briefPromptVersion: string;
174
- generatedBrief: SynthesizedReviewBrief;
175
- critique: BriefCritique;
176
- effectiveBrief: SynthesizedReviewBrief;
177
- synthesizerModelId: string;
178
- snapshotFingerprint: string;
48
+ /** Semantic input shared with the reviewer tasks in one run. */
49
+ export interface ReviewInput {
50
+ sharedContext?: string;
51
+ tasks: ReviewTask[];
179
52
  }
180
53
 
181
- /** Normalized reviewer result without repeated snapshot or brief payloads. */
182
- export type AgentReviewerResult =
183
- | {
184
- kind: "success";
185
- output: NormalizedReviewOutput;
186
- modelId: string;
187
- }
188
- | ({ kind: "failed"; modelId: string } & ChildFailedResult)
189
- | {
190
- kind: "canceled";
191
- modelId: string;
192
- diagnostics: ChildFailureDiagnostics;
193
- }
194
- | {
195
- kind: "timeout";
196
- timeoutMs: number;
197
- modelId: string;
198
- diagnostics: ChildFailureDiagnostics;
199
- };
54
+ /** Advisory review input generated by the optional lightweight Planner. */
55
+ export type PlannerDraft = ReviewInput;
200
56
 
201
- /** One normalized result from a focused reviewer child session. */
202
- export interface ReviewerAssignmentResult {
203
- assignment: ReviewerAssignment;
204
- result: AgentReviewerResult;
205
- }
57
+ export type FindingImpact = "low" | "medium" | "high";
58
+ export type FindingEffort = "small" | "medium" | "large";
59
+ export type TaskVerdict = "pass" | "issues";
206
60
 
207
- /** Structured details retained on a completed agent-driven review batch. */
208
- export interface AgentReviewBatchDetails {
209
- kind: "review-batch";
210
- evaluation: BriefEvaluation;
211
- snapshot: ReviewSnapshotSummary;
212
- results: ReviewerAssignmentResult[];
61
+ export interface ReviewLocation {
62
+ path: string;
63
+ startLine: number;
64
+ endLine: number;
213
65
  }
214
66
 
215
- /** Structured details returned when an agent-driven review plan is prepared. */
216
- export interface PreparedAgentReviewDetails {
217
- kind: "review-prepared";
218
- planId: string;
219
- briefPromptVersion: string;
220
- generatedBrief: SynthesizedReviewBrief;
221
- snapshot: ReviewSnapshotSummary;
222
- snapshotFingerprint: string;
223
- modelId: string;
67
+ /** One structured issue submitted by a reviewer task. */
68
+ export interface ReviewFinding {
69
+ title: string;
70
+ description: string;
71
+ blocksAcceptance: boolean;
72
+ impact: FindingImpact;
73
+ effort: FindingEffort;
74
+ confidence: number;
75
+ location?: ReviewLocation;
224
76
  }
225
77
 
226
- /** Final prompt packet passed to the reviewer child session. */
227
- export interface ReviewPacket {
228
- prompt: string;
229
- includedFiles: string[];
230
- omittedFiles: string[];
231
- charBudget: number;
78
+ /** Fixed Review Engine-owned submission grammar. */
79
+ export interface ReviewSubmission {
80
+ summary: string;
81
+ findings: ReviewFinding[];
232
82
  }
233
83
 
234
- /** Fully prepared review run. */
235
- export interface ReviewPlan {
236
- model: ReviewModelSelection;
237
- snapshot: ReviewSnapshot;
238
- brief: SynthesizedReviewBrief;
239
- packet: ReviewPacket;
84
+ export interface NormalizedReviewSubmission extends ReviewSubmission {
85
+ verdict: TaskVerdict;
240
86
  }
241
87
 
242
- /** Raw result of the review child session. */
243
- export type RawReviewResult =
244
- | {
245
- kind: "success";
246
- output: ReviewOutputEvent;
247
- snapshot: ReviewSnapshot;
248
- brief?: SynthesizedReviewBrief;
249
- modelId: string;
250
- }
251
- | ({
252
- kind: "failed";
253
- snapshot: ReviewSnapshot;
254
- brief?: SynthesizedReviewBrief;
255
- modelId: string;
256
- } & ChildFailedResult)
257
- | {
258
- kind: "canceled";
259
- snapshot: ReviewSnapshot;
260
- brief?: SynthesizedReviewBrief;
261
- modelId: string;
262
- diagnostics: ChildFailureDiagnostics;
263
- }
264
- | {
265
- kind: "timeout";
266
- snapshot: ReviewSnapshot;
267
- timeoutMs: number;
268
- brief?: SynthesizedReviewBrief;
269
- modelId: string;
270
- diagnostics: ChildFailureDiagnostics;
271
- };
88
+ export type ReviewModelSelection = import("@mrclrchtr/supi-core/model-selection").ModelSelection;
272
89
 
273
- /** Normalized result used by rendering and follow-up logic. */
274
- export type ReviewResult =
275
- | {
276
- kind: "success";
277
- output: NormalizedReviewOutput;
278
- snapshot: ReviewSnapshot;
279
- brief?: SynthesizedReviewBrief;
280
- modelId: string;
281
- }
282
- | Extract<RawReviewResult, { kind: "failed" | "canceled" | "timeout" }>;
90
+ export type ChildStage = "planner" | "reviewer";
91
+
92
+ export type ChildFailureCode =
93
+ | "session-creation-failed"
94
+ | "prompt-rejected"
95
+ | "missing-structured-output"
96
+ | "unexpected-runner-failure";
283
97
 
284
- /**
285
- * Safe bounded diagnostics attached only to a non-success managed child run.
286
- *
287
- * The trace and Recent Activity lane contain only allowlisted control metadata;
288
- * no child-generated text, caught error, or raw SDK event is retained.
289
- */
290
98
  export interface ChildFailureDiagnostics {
291
99
  lifecycleTrace: ChildLifecycleTrace;
292
100
  turns: number;
@@ -303,13 +111,16 @@ export interface ChildFailureDiagnostics {
303
111
  lastAssistantToolCalls?: string[];
304
112
  }
305
113
 
306
- /** Progress state exposed by review/synthesis runners for widget integration. */
114
+ export type ChildFailedResult =
115
+ | { failureCode: "session-creation-failed"; diagnostics?: never }
116
+ | {
117
+ failureCode: Exclude<ChildFailureCode, "session-creation-failed">;
118
+ diagnostics: ChildFailureDiagnostics;
119
+ };
120
+
307
121
  export interface ReviewProgress {
308
- /** Number of agent turns completed. */
309
122
  turns: number;
310
- /** Number of tool executions started. */
311
123
  toolUses: number;
312
- /** Token usage stats, if available. */
313
124
  tokens?: {
314
125
  input: number;
315
126
  output: number;
@@ -317,42 +128,109 @@ export interface ReviewProgress {
317
128
  cacheRead?: number;
318
129
  cacheWrite?: number;
319
130
  };
320
- /** Per-tool execution counts keyed by short display label (e.g. "diffs", "reads", "greps"). */
321
131
  toolCounts?: Record<string, number>;
322
- /** Number of distinct files inspected so far (via read_snapshot_diff / read_snapshot_file). */
323
- filesInspected?: number;
324
- /** Total files in the review snapshot. */
325
- filesTotal?: number;
326
- /** Current tool + context for the progress narrative line. */
327
132
  currentFocus?: { label: string; detail: string };
328
- /** Elapsed time in milliseconds since the operation started. */
329
133
  elapsedMs?: number;
330
134
  }
331
135
 
332
- export type BriefSynthesisRunResult =
333
- | { kind: "success"; brief: SynthesizedReviewBrief }
334
- | ({ kind: "failed" } & ChildFailedResult)
335
- | { kind: "canceled"; diagnostics: ChildFailureDiagnostics }
336
- | { kind: "timeout"; timeoutMs: number; diagnostics: ChildFailureDiagnostics };
337
-
338
- export interface BriefSynthesisInvocation {
136
+ export interface PlannerInvocation {
339
137
  prompt: string;
340
- // biome-ignore lint/suspicious/noExplicitAny: Model<any> is pi's canonical type
138
+ // biome-ignore lint/suspicious/noExplicitAny: Model<any> is Pi's canonical type
341
139
  model: Model<any>;
342
140
  cwd: string;
343
141
  signal?: AbortSignal;
344
- timeoutMs?: number;
345
142
  onProgress?: (progress: ReviewProgress) => void;
346
143
  }
347
144
 
348
- export interface ReviewInvocation {
145
+ export type PlannerRunResult =
146
+ | { kind: "success"; draft: PlannerDraft }
147
+ | ({ kind: "failed" } & ChildFailedResult)
148
+ | { kind: "canceled"; diagnostics: ChildFailureDiagnostics }
149
+ | { kind: "timeout"; timeoutMs: number; diagnostics: ChildFailureDiagnostics };
150
+
151
+ export interface ReviewerInvocation {
349
152
  prompt: string;
153
+ task: ReviewTask;
350
154
  model: ReviewModelSelection;
351
155
  cwd: string;
352
- signal?: AbortSignal;
353
156
  snapshot: ReviewSnapshot;
354
- brief: SynthesizedReviewBrief;
355
- timeoutMs?: number;
356
- onToolActivity?: (event: { toolName: string; phase: "start" | "end" }) => void;
157
+ signal?: AbortSignal;
357
158
  onProgress?: (progress: ReviewProgress) => void;
358
159
  }
160
+
161
+ export type ReviewerRunResult =
162
+ | { kind: "success"; submission: ReviewSubmission; modelId: string }
163
+ | ({ kind: "failed"; modelId: string } & ChildFailedResult)
164
+ | { kind: "canceled"; modelId: string; diagnostics: ChildFailureDiagnostics }
165
+ | {
166
+ kind: "timeout";
167
+ timeoutMs: number;
168
+ modelId: string;
169
+ diagnostics: ChildFailureDiagnostics;
170
+ };
171
+
172
+ interface ReviewTaskResultIdentity {
173
+ taskId: string;
174
+ modelId: string;
175
+ /** SHA-256 of the exact reviewer packet bytes. */
176
+ packetHash: string;
177
+ }
178
+
179
+ export type ReviewTaskResult = ReviewTaskResultIdentity &
180
+ (
181
+ | {
182
+ status: "completed";
183
+ verdict: TaskVerdict;
184
+ summary: string;
185
+ findings: ReviewFinding[];
186
+ }
187
+ | {
188
+ status: "failed";
189
+ failureCode: ChildFailureCode;
190
+ diagnostics?: ChildFailureDiagnostics;
191
+ }
192
+ | {
193
+ status: "canceled";
194
+ diagnostics: ChildFailureDiagnostics;
195
+ }
196
+ | {
197
+ status: "timeout";
198
+ timeoutMs: number;
199
+ diagnostics: ChildFailureDiagnostics;
200
+ }
201
+ );
202
+
203
+ export interface PlanningRecord {
204
+ promptVersion: string;
205
+ modelId: string;
206
+ draft: PlannerDraft;
207
+ effectiveReview: ReviewInput;
208
+ decision: "accept-draft" | "use-review";
209
+ }
210
+
211
+ export interface ReviewBatchDetails {
212
+ kind: "review-batch";
213
+ mode: "direct" | "prepared";
214
+ provenance: "caller-supplied" | "planner-assisted";
215
+ snapshot: ReviewSnapshotSummary;
216
+ review: ReviewInput;
217
+ planning?: PlanningRecord;
218
+ results: ReviewTaskResult[];
219
+ }
220
+
221
+ export interface PreparedReviewDetails {
222
+ kind: "review-prepared";
223
+ planId: string;
224
+ snapshot: ReviewSnapshotSummary;
225
+ reviewerModelId: string;
226
+ plannerDraft?: PlannerDraft;
227
+ plannerModelId?: string;
228
+ plannerPromptVersion?: string;
229
+ }
230
+
231
+ export interface ReviewPacket {
232
+ prompt: string;
233
+ /** SHA-256 of prompt encoded as UTF-8, used to verify adapter parity. */
234
+ packetHash: string;
235
+ taskId: string;
236
+ }
@@ -1,121 +0,0 @@
1
- import { listReviewInstructionBlocks } from "../target/packet.ts";
2
- import { runBriefSynthesis } from "../tool/brief-runner.ts";
3
- import type {
4
- BriefSynthesisRunResult,
5
- ReviewModelSelection,
6
- ReviewProgress,
7
- ReviewSnapshot,
8
- } from "../types.ts";
9
-
10
- const DIFF_EXCERPT_CHAR_BUDGET = 12_000;
11
-
12
- /** Version of the brief-synthesis prompt contract retained in evaluation artifacts. */
13
- export const BRIEF_SYNTHESIS_PROMPT_VERSION = "1";
14
-
15
- export interface SynthesizeReviewBriefOptions {
16
- model: ReviewModelSelection;
17
- cwd: string;
18
- snapshot: ReviewSnapshot;
19
- serializedContext: string;
20
- note?: string;
21
- signal?: AbortSignal;
22
- onProgress?: (progress: ReviewProgress) => void;
23
- }
24
-
25
- /** Synthesize a structured review brief from the current snapshot and session context. */
26
- export function synthesizeReviewBrief(
27
- options: SynthesizeReviewBriefOptions,
28
- ): Promise<BriefSynthesisRunResult> {
29
- const { model, cwd, snapshot, serializedContext, note, signal, onProgress } = options;
30
-
31
- return runBriefSynthesis({
32
- prompt: buildBriefSynthesisPrompt(snapshot, serializedContext, note),
33
- model: model.model,
34
- cwd,
35
- signal,
36
- onProgress,
37
- });
38
- }
39
-
40
- export function buildBriefSynthesisPrompt(
41
- snapshot: ReviewSnapshot,
42
- serializedContext: string,
43
- note?: string,
44
- ): string {
45
- const diffExcerpt = buildDiffExcerpt(snapshot.diffText);
46
- const instructionBlocks = listReviewInstructionBlocks();
47
- const parts: string[] = [
48
- "# Review Brief Synthesis Input",
49
- "",
50
- "You are preparing a review brief for a second code-reviewing agent.",
51
- "Infer the likely goal, constraints, and focus areas from the active session history.",
52
- "Be concise, evidence-based, and avoid inventing requirements that are not supported by the input.",
53
- "",
54
- "## Snapshot",
55
- `Target: ${snapshot.title}`,
56
- `Files changed: ${snapshot.changedFiles.length}`,
57
- `Diff stats: +${snapshot.stats.additions} / -${snapshot.stats.deletions}`,
58
- "",
59
- "### Changed files",
60
- ...snapshot.changedFiles.map((file) => `- ${file}`),
61
- ];
62
-
63
- if (diffExcerpt.text) {
64
- parts.push("", "### Diff excerpt", "```diff", diffExcerpt.text, "```");
65
- if (diffExcerpt.truncated) {
66
- parts.push(`> Note: diff excerpt truncated to ${diffExcerpt.text.length} characters.`);
67
- }
68
- }
69
-
70
- if (instructionBlocks.length > 0) {
71
- parts.push(
72
- "",
73
- "## Available review instruction blocks",
74
- ...instructionBlocks.map((block) => `- ${block.id}: ${block.title} — ${block.instruction}`),
75
- );
76
- }
77
-
78
- if (note?.trim()) {
79
- parts.push("", "## User note", note.trim());
80
- }
81
-
82
- parts.push("", "## Serialized session context");
83
- if (serializedContext.trim()) {
84
- parts.push(serializedContext.trim());
85
- } else {
86
- parts.push("No session context was available. Derive the brief from the snapshot only.");
87
- }
88
-
89
- parts.push(
90
- "",
91
- "## Output requirements",
92
- "Call submit_review_brief with:",
93
- "- summary: one-sentence summary of the likely change",
94
- "- intendedOutcome: what the session seems to be trying to achieve",
95
- "- constraints: invariants or requirements to preserve",
96
- "- focusAreas: what the reviewer should inspect carefully",
97
- "- riskyFiles: changed files that seem especially important or risky.",
98
- " Prioritize files touching auth, data handling, core logic, error handling, or public APIs.",
99
- " Include only files from the changed-files list above.",
100
- "- unresolvedQuestions: ambiguities or concerns that remain unclear",
101
- "- reviewInstructionBlockIds: zero or more IDs from the available review instruction block catalog",
102
- " Select only blocks that are clearly supported by the supplied session/snapshot evidence.",
103
- " Prefer omission over guessing. Do not invent new IDs.",
104
- );
105
-
106
- return parts.join("\n");
107
- }
108
-
109
- function buildDiffExcerpt(diffText: string): { text: string; truncated: boolean } {
110
- const trimmed = diffText.trim();
111
- if (!trimmed) {
112
- return { text: "", truncated: false };
113
- }
114
- if (trimmed.length <= DIFF_EXCERPT_CHAR_BUDGET) {
115
- return { text: trimmed, truncated: false };
116
- }
117
- return {
118
- text: `${trimmed.slice(0, DIFF_EXCERPT_CHAR_BUDGET)}\n[... diff excerpt truncated ...]`,
119
- truncated: true,
120
- };
121
- }