supipowers 2.2.0 → 2.2.1
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/README.md +71 -12
- package/package.json +4 -8
- package/skills/ui-design/SKILL.md +2 -2
- package/src/ai/final-message.ts +15 -1
- package/src/ai/schema-text.ts +60 -40
- package/src/ai/schema-validation.ts +88 -0
- package/src/ai/structured-output.ts +19 -19
- package/src/commands/fix-pr.ts +166 -26
- package/src/config/schema.ts +102 -139
- package/src/docs/contracts.ts +13 -23
- package/src/fix-pr/assessment.ts +63 -24
- package/src/fix-pr/contracts.ts +15 -23
- package/src/fix-pr/fetch-comments.ts +119 -0
- package/src/fix-pr/prompt-builder.ts +19 -8
- package/src/git/commit-contract.ts +13 -19
- package/src/git/commit.ts +168 -6
- package/src/lsp/capabilities.ts +9 -12
- package/src/lsp/contracts.ts +15 -23
- package/src/planning/planning-ask-tool.ts +13 -2
- package/src/planning/spec.ts +21 -27
- package/src/planning/system-prompt.ts +1 -1
- package/src/planning/validate.ts +4 -7
- package/src/platform/progress.ts +11 -0
- package/src/quality/contracts.ts +15 -23
- package/src/quality/schemas.ts +40 -67
- package/src/release/contracts.ts +19 -28
- package/src/review/types.ts +142 -186
- package/src/types.ts +5 -2
- package/src/ui-design/session.ts +13 -2
- package/src/ui-design/system-prompt.ts +2 -2
- package/src/ultraplan/contracts.ts +458 -524
package/src/commands/fix-pr.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { Platform } from "../platform/types.js";
|
|
5
5
|
import type { WorkspaceTarget } from "../types.js";
|
|
6
|
-
import { buildWorkspaceTargetOptionLabel, parseTargetArg,
|
|
6
|
+
import { buildWorkspaceTargetOptionLabel, parseTargetArg, stripCliArg, tokenizeCliArgs } from "../workspace/selector.js";
|
|
7
7
|
import { resolvePackageManager } from "../workspace/package-manager.js";
|
|
8
8
|
import { resolveRepoRoot } from "../workspace/repo-root.js";
|
|
9
9
|
import { discoverWorkspaceTargets } from "../workspace/targets.js";
|
|
@@ -31,6 +31,45 @@ import { loadModelConfig } from "../config/model-config.js";
|
|
|
31
31
|
import { detectBotReviewers } from "../fix-pr/bot-detector.js";
|
|
32
32
|
import { runFixPrAssessment, groupAssessmentsIntoBatches } from "../fix-pr/assessment.js";
|
|
33
33
|
import { updateFixPrSession } from "../storage/fix-pr-sessions.js";
|
|
34
|
+
import { createWorkflowProgress } from "../platform/progress.js";
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const FIX_PR_ASSESSMENT_TIMEOUT_MS = 5 * 60 * 1_000;
|
|
38
|
+
const FIX_PR_ASSESSMENT_COMMENTS_PER_BATCH = 12;
|
|
39
|
+
|
|
40
|
+
const FIX_PR_STEPS = [
|
|
41
|
+
{ key: "fetch-comments", label: "Fetch PR comments" },
|
|
42
|
+
{ key: "select-target", label: "Select target" },
|
|
43
|
+
{ key: "prepare-session", label: "Prepare session" },
|
|
44
|
+
{ key: "llm-assessment", label: "LLM assessment" },
|
|
45
|
+
{ key: "send-prompt", label: "Send fix prompt" },
|
|
46
|
+
] as const;
|
|
47
|
+
|
|
48
|
+
function createFixPrProgress(ctx: any) {
|
|
49
|
+
const progress = createWorkflowProgress(ctx.ui, {
|
|
50
|
+
title: "supi:fix-pr",
|
|
51
|
+
statusKey: "supi-fix-pr",
|
|
52
|
+
statusLabel: "Fixing PR...",
|
|
53
|
+
widgetKey: "supi-fix-pr",
|
|
54
|
+
clearStatusKeys: ["supi-model"],
|
|
55
|
+
steps: [...FIX_PR_STEPS],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
activate(stepIndex: number, detail?: string): void {
|
|
60
|
+
progress.activate(FIX_PR_STEPS[stepIndex]!.key, detail);
|
|
61
|
+
},
|
|
62
|
+
complete(stepIndex: number, detail?: string): void {
|
|
63
|
+
progress.complete(FIX_PR_STEPS[stepIndex]!.key, detail);
|
|
64
|
+
},
|
|
65
|
+
fail(stepIndex: number, detail?: string): void {
|
|
66
|
+
progress.fail(FIX_PR_STEPS[stepIndex]!.key, detail);
|
|
67
|
+
},
|
|
68
|
+
dispose(): void {
|
|
69
|
+
progress.dispose();
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
34
73
|
|
|
35
74
|
modelRegistry.register({
|
|
36
75
|
id: "fix-pr",
|
|
@@ -47,6 +86,7 @@ modelRegistry.register({
|
|
|
47
86
|
harnessRoleHint: "default",
|
|
48
87
|
});
|
|
49
88
|
|
|
89
|
+
|
|
50
90
|
function getScriptsDir(): string {
|
|
51
91
|
return path.join(moduleDir(import.meta.url), "..", "fix-pr", "scripts");
|
|
52
92
|
}
|
|
@@ -91,6 +131,12 @@ function formatUnscopedCommentCount(count: number): string {
|
|
|
91
131
|
return `${formatCommentCount(count)} without file path`;
|
|
92
132
|
}
|
|
93
133
|
|
|
134
|
+
type FixPrSelectedTarget =
|
|
135
|
+
| { kind: "workspace"; target: WorkspaceTarget }
|
|
136
|
+
| { kind: "all"; target: WorkspaceTarget };
|
|
137
|
+
|
|
138
|
+
const ALL_FIX_PR_TARGET_ID = "all";
|
|
139
|
+
|
|
94
140
|
function buildCommentTargetOptions(
|
|
95
141
|
targets: readonly WorkspaceTarget[],
|
|
96
142
|
commentsByTargetId: ReadonlyMap<string, readonly PrComment[]>,
|
|
@@ -109,6 +155,43 @@ function buildCommentTargetOptions(
|
|
|
109
155
|
});
|
|
110
156
|
}
|
|
111
157
|
|
|
158
|
+
function createAllCommentsTarget(repoRoot: string, packageManager: WorkspaceTarget["packageManager"]): WorkspaceTarget {
|
|
159
|
+
return {
|
|
160
|
+
id: ALL_FIX_PR_TARGET_ID,
|
|
161
|
+
name: "all",
|
|
162
|
+
kind: "workspace",
|
|
163
|
+
repoRoot,
|
|
164
|
+
packageDir: repoRoot,
|
|
165
|
+
manifestPath: path.join(repoRoot, "package.json"),
|
|
166
|
+
relativeDir: ALL_FIX_PR_TARGET_ID,
|
|
167
|
+
version: "0.0.0",
|
|
168
|
+
private: true,
|
|
169
|
+
packageManager,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function describeFixPrSelectedTarget(selected: FixPrSelectedTarget): string {
|
|
174
|
+
return selected.kind === "all" ? "all targets" : describeTarget(selected.target);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function getSelectedTargetComments(
|
|
178
|
+
selected: FixPrSelectedTarget,
|
|
179
|
+
commentsByTargetId: ReadonlyMap<string, readonly PrComment[]>,
|
|
180
|
+
unscopedComments: readonly PrComment[],
|
|
181
|
+
): readonly PrComment[] {
|
|
182
|
+
if (selected.kind === "workspace") {
|
|
183
|
+
return commentsByTargetId.get(selected.target.id) ?? [];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const comments: PrComment[] = [];
|
|
187
|
+
for (const targetComments of commentsByTargetId.values()) {
|
|
188
|
+
comments.push(...targetComments);
|
|
189
|
+
}
|
|
190
|
+
comments.push(...unscopedComments);
|
|
191
|
+
return comments;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
112
195
|
function countUnresolvedAssessments(assessment: FixPrAssessmentBatch): number {
|
|
113
196
|
return assessment.assessments.filter((item: FixPrAssessmentBatch["assessments"][number]) => item.verdict !== "apply").length;
|
|
114
197
|
}
|
|
@@ -211,14 +294,18 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
211
294
|
return;
|
|
212
295
|
}
|
|
213
296
|
|
|
297
|
+
const progress = createFixPrProgress(ctx);
|
|
214
298
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "supi-fix-pr-"));
|
|
215
299
|
try {
|
|
300
|
+
progress.activate(0, `PR #${prNumber}`);
|
|
216
301
|
const fetchedCommentsPath = path.join(tempDir, "comments.jsonl");
|
|
217
302
|
const fetchError = await fetchPrComments(platform, repo, prNumber, fetchedCommentsPath, repoRoot);
|
|
218
303
|
if (fetchError) {
|
|
304
|
+
progress.fail(0, "failed");
|
|
219
305
|
notifyError(ctx, "Failed to fetch PR comments", fetchError);
|
|
220
306
|
return;
|
|
221
307
|
}
|
|
308
|
+
progress.complete(0, `PR #${prNumber}`);
|
|
222
309
|
|
|
223
310
|
const fetchedComments = fs.readFileSync(fetchedCommentsPath, "utf-8").trim();
|
|
224
311
|
if (!fetchedComments) {
|
|
@@ -238,36 +325,74 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
238
325
|
clusteredComments.commentsByTargetId,
|
|
239
326
|
);
|
|
240
327
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
328
|
+
const allTarget = createAllCommentsTarget(repoRoot, packageManager.id);
|
|
329
|
+
const allOption = {
|
|
330
|
+
target: allTarget,
|
|
331
|
+
changed: true,
|
|
332
|
+
label: buildWorkspaceTargetOptionLabel(
|
|
333
|
+
{ target: allTarget, changed: true },
|
|
334
|
+
[formatCommentCount(parsedComments.length)],
|
|
335
|
+
),
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
if (targetOptions.length === 0 && clusteredComments.unscopedComments.length === 0) {
|
|
339
|
+
notifyWarning(ctx, "No actionable comments found", "PR comments were fetched but could not be assigned to a package or root target");
|
|
246
340
|
return;
|
|
247
341
|
}
|
|
248
342
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
343
|
+
progress.activate(1, `${formatCommentCount(parsedComments.length)}`);
|
|
344
|
+
let selected: FixPrSelectedTarget | null = null;
|
|
345
|
+
if (requestedTarget === ALL_FIX_PR_TARGET_ID) {
|
|
346
|
+
selected = { kind: "all", target: allTarget };
|
|
347
|
+
} else if (requestedTarget) {
|
|
348
|
+
const target = targetOptions
|
|
349
|
+
.map((option) => option.target)
|
|
350
|
+
.find((optionTarget) => optionTarget.id === requestedTarget || optionTarget.name === requestedTarget);
|
|
351
|
+
selected = target ? { kind: "workspace", target } : null;
|
|
352
|
+
} else if (!ctx.hasUI) {
|
|
353
|
+
selected = { kind: "all", target: allTarget };
|
|
354
|
+
} else {
|
|
355
|
+
const labels = [
|
|
356
|
+
allOption.label,
|
|
357
|
+
...targetOptions.map((option) => option.label ?? buildWorkspaceTargetOptionLabel(option)),
|
|
358
|
+
];
|
|
359
|
+
const choice = await ctx.ui.select("Fix-PR target", labels, {
|
|
360
|
+
helpText: "Select all comments or one target to process for this run",
|
|
361
|
+
});
|
|
362
|
+
if (!choice) {
|
|
363
|
+
progress.fail(1, "cancelled");
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const selectedIndex = labels.indexOf(choice);
|
|
367
|
+
selected = selectedIndex === 0
|
|
368
|
+
? { kind: "all", target: allTarget }
|
|
369
|
+
: targetOptions[selectedIndex - 1]
|
|
370
|
+
? { kind: "workspace", target: targetOptions[selectedIndex - 1].target }
|
|
371
|
+
: null;
|
|
252
372
|
}
|
|
253
373
|
|
|
254
|
-
|
|
255
|
-
title: "Fix-PR target",
|
|
256
|
-
helpText: "Select one target to process for this run",
|
|
257
|
-
});
|
|
258
|
-
if (!selectedTarget) {
|
|
374
|
+
if (!selected) {
|
|
259
375
|
if (requestedTarget) {
|
|
260
376
|
notifyError(ctx, "Target has no review comments", buildAvailableTargetDetail(targetOptions, clusteredComments.unscopedComments.length));
|
|
261
377
|
}
|
|
378
|
+
progress.fail(1, "empty");
|
|
262
379
|
return;
|
|
263
380
|
}
|
|
381
|
+
progress.complete(1, describeFixPrSelectedTarget(selected));
|
|
264
382
|
|
|
265
|
-
const
|
|
383
|
+
const selectedTarget = selected.target;
|
|
384
|
+
const selectedComments = getSelectedTargetComments(
|
|
385
|
+
selected,
|
|
386
|
+
clusteredComments.commentsByTargetId,
|
|
387
|
+
clusteredComments.unscopedComments,
|
|
388
|
+
);
|
|
266
389
|
if (selectedComments.length === 0) {
|
|
267
390
|
notifyInfo(ctx, "No comments for selected target", `${selectedTarget.id} has no actionable comments in this PR snapshot`);
|
|
268
391
|
return;
|
|
269
392
|
}
|
|
270
393
|
|
|
394
|
+
progress.activate(2, `${formatCommentCount(selectedComments.length)}`);
|
|
395
|
+
|
|
271
396
|
let activeSession = findActiveFixPrSession(platform.paths, selectedTarget, repo, prNumber);
|
|
272
397
|
if (activeSession && ctx.hasUI) {
|
|
273
398
|
const choice = await ctx.ui.select(
|
|
@@ -278,7 +403,10 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
278
403
|
],
|
|
279
404
|
{ helpText: "Select session · Esc to cancel" },
|
|
280
405
|
);
|
|
281
|
-
if (!choice)
|
|
406
|
+
if (!choice) {
|
|
407
|
+
progress.fail(2, "cancelled");
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
282
410
|
if (choice.startsWith("Start new")) activeSession = null;
|
|
283
411
|
}
|
|
284
412
|
|
|
@@ -328,12 +456,17 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
328
456
|
|
|
329
457
|
const taskResolved = resolveModelForAction("task", modelRegistry, modelConfig, bridge);
|
|
330
458
|
const taskModel = taskResolved.model ?? resolved.model ?? "claude-sonnet-4-6";
|
|
331
|
-
const deferredCommentsSummary =
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
459
|
+
const deferredCommentsSummary = selected.kind === "all"
|
|
460
|
+
? null
|
|
461
|
+
: buildDeferredCommentsSummary(
|
|
462
|
+
targetOptions,
|
|
463
|
+
clusteredComments.commentsByTargetId,
|
|
464
|
+
selectedTarget,
|
|
465
|
+
clusteredComments.unscopedComments.length,
|
|
466
|
+
);
|
|
467
|
+
progress.complete(2, activeSession ? `resume ${ledger.id}` : `new ${ledger.id}`);
|
|
468
|
+
|
|
469
|
+
progress.activate(3, `${formatCommentCount(selectedComments.length)}`);
|
|
337
470
|
|
|
338
471
|
const assessmentResult = await runFixPrAssessment({
|
|
339
472
|
createAgentSession: platform.createAgentSession,
|
|
@@ -342,17 +475,22 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
342
475
|
comments: selectedComments,
|
|
343
476
|
repo,
|
|
344
477
|
prNumber,
|
|
345
|
-
selectedTargetLabel:
|
|
478
|
+
selectedTargetLabel: describeFixPrSelectedTarget(selected),
|
|
346
479
|
model: resolved.model,
|
|
347
480
|
thinkingLevel: resolved.thinkingLevel,
|
|
481
|
+
timeoutMs: FIX_PR_ASSESSMENT_TIMEOUT_MS,
|
|
482
|
+
maxCommentsPerBatch: FIX_PR_ASSESSMENT_COMMENTS_PER_BATCH,
|
|
348
483
|
});
|
|
349
484
|
if (assessmentResult.status === "blocked") {
|
|
485
|
+
progress.fail(3, "blocked");
|
|
350
486
|
notifyError(ctx, "Fix-PR assessment failed", assessmentResult.error);
|
|
351
487
|
return;
|
|
352
488
|
}
|
|
489
|
+
progress.complete(3, `${formatCommentCount(assessmentResult.output.assessments.length)} assessed`);
|
|
353
490
|
const assessment = assessmentResult.output;
|
|
354
491
|
const unresolvedAssessmentCount = countUnresolvedAssessments(assessment);
|
|
355
492
|
const workBatches = groupAssessmentsIntoBatches(assessment);
|
|
493
|
+
progress.activate(4, `${workBatches.length} batch${workBatches.length === 1 ? "" : "es"}`);
|
|
356
494
|
ledger.assessment = assessment;
|
|
357
495
|
updateFixPrSession(platform.paths, selectedTarget, ledger);
|
|
358
496
|
|
|
@@ -360,7 +498,7 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
360
498
|
notifyWarning(
|
|
361
499
|
ctx,
|
|
362
500
|
"Unresolved comments remain",
|
|
363
|
-
`${formatCommentCount(unresolvedAssessmentCount)} for ${
|
|
501
|
+
`${formatCommentCount(unresolvedAssessmentCount)} for ${describeFixPrSelectedTarget(selected)} still need rejection or investigation handling before this run can be considered complete.`,
|
|
364
502
|
);
|
|
365
503
|
}
|
|
366
504
|
|
|
@@ -374,7 +512,7 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
374
512
|
iteration: ledger.iteration,
|
|
375
513
|
skillContent,
|
|
376
514
|
taskModel,
|
|
377
|
-
selectedTargetLabel:
|
|
515
|
+
selectedTargetLabel: describeFixPrSelectedTarget(selected),
|
|
378
516
|
deferredCommentsSummary,
|
|
379
517
|
assessment,
|
|
380
518
|
workBatches,
|
|
@@ -388,14 +526,16 @@ export function registerFixPrCommand(platform: Platform): void {
|
|
|
388
526
|
},
|
|
389
527
|
{ deliverAs: "steer", triggerTurn: true },
|
|
390
528
|
);
|
|
529
|
+
progress.complete(4, "sent");
|
|
391
530
|
|
|
392
|
-
const detailParts = [`${formatCommentCount(selectedComments.length)} for ${
|
|
531
|
+
const detailParts = [`${formatCommentCount(selectedComments.length)} for ${describeFixPrSelectedTarget(selected)}`];
|
|
393
532
|
if (deferredCommentsSummary) {
|
|
394
533
|
detailParts.push(`deferred: ${deferredCommentsSummary}`);
|
|
395
534
|
}
|
|
396
535
|
notifyInfo(ctx, `Fix-PR started: PR #${prNumber}`, `${detailParts.join(" | ")} | session ${ledger.id}`);
|
|
397
536
|
} finally {
|
|
398
537
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
538
|
+
progress.dispose();
|
|
399
539
|
}
|
|
400
540
|
},
|
|
401
541
|
});
|
package/src/config/schema.ts
CHANGED
|
@@ -1,176 +1,142 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { Value } from "@sinclair/typebox/value";
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import type { ZodType } from "zod/v4";
|
|
4
3
|
import type { SupipowersConfig } from "../types.js";
|
|
5
4
|
import { QualityGatesSchema } from "../quality/schemas.js";
|
|
6
5
|
import { UltraPlanConfigSchema } from "../ultraplan/contracts.js";
|
|
6
|
+
import { collectSchemaValidationErrors } from "../ai/schema-validation.js";
|
|
7
7
|
|
|
8
8
|
const TAG_FORMAT_PATTERN = "^(?:(?!\\$\\{version\\}).)*\\$\\{version\\}(?:(?!\\$\\{version\\}).)*$";
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
export const ConfigSchema =
|
|
11
|
+
export const ConfigSchema = z.object(
|
|
12
12
|
{
|
|
13
|
-
version:
|
|
14
|
-
quality:
|
|
13
|
+
version: z.string(),
|
|
14
|
+
quality: z.object(
|
|
15
15
|
{
|
|
16
16
|
gates: QualityGatesSchema,
|
|
17
17
|
},
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
lsp: Type.Object(
|
|
18
|
+
).strict(),
|
|
19
|
+
lsp: z.object(
|
|
21
20
|
{
|
|
22
|
-
setupGuide:
|
|
21
|
+
setupGuide: z.boolean(),
|
|
23
22
|
},
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
qa: Type.Object(
|
|
23
|
+
).strict(),
|
|
24
|
+
qa: z.object(
|
|
27
25
|
{
|
|
28
|
-
framework:
|
|
29
|
-
e2e:
|
|
26
|
+
framework: z.string().nullable(),
|
|
27
|
+
e2e: z.boolean(),
|
|
30
28
|
},
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
release: Type.Object(
|
|
29
|
+
).strict(),
|
|
30
|
+
release: z.object(
|
|
34
31
|
{
|
|
35
|
-
channels:
|
|
36
|
-
tagFormat:
|
|
37
|
-
customChannels:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
),
|
|
46
|
-
),
|
|
32
|
+
channels: z.array(z.string()),
|
|
33
|
+
tagFormat: z.string().regex(new RegExp(TAG_FORMAT_PATTERN)),
|
|
34
|
+
customChannels: z.record(
|
|
35
|
+
z.string(),
|
|
36
|
+
z.object({
|
|
37
|
+
label: z.string(),
|
|
38
|
+
publishCommand: z.string(),
|
|
39
|
+
detectCommand: z.string().optional(),
|
|
40
|
+
}),
|
|
41
|
+
).optional(),
|
|
47
42
|
},
|
|
48
|
-
|
|
49
|
-
),
|
|
43
|
+
).strict(),
|
|
50
44
|
ultraplan: UltraPlanConfigSchema,
|
|
51
|
-
contextMode:
|
|
45
|
+
contextMode: z.object(
|
|
52
46
|
{
|
|
53
|
-
enabled:
|
|
54
|
-
compressionThreshold:
|
|
55
|
-
blockHttpCommands:
|
|
56
|
-
routingInstructions:
|
|
57
|
-
eventTracking:
|
|
58
|
-
compaction:
|
|
59
|
-
llmSummarization:
|
|
60
|
-
llmThreshold:
|
|
61
|
-
enforceRouting:
|
|
62
|
-
lazyTools:
|
|
47
|
+
enabled: z.boolean(),
|
|
48
|
+
compressionThreshold: z.number().min(1024),
|
|
49
|
+
blockHttpCommands: z.boolean(),
|
|
50
|
+
routingInstructions: z.boolean(),
|
|
51
|
+
eventTracking: z.boolean(),
|
|
52
|
+
compaction: z.boolean(),
|
|
53
|
+
llmSummarization: z.boolean(),
|
|
54
|
+
llmThreshold: z.number().min(4096),
|
|
55
|
+
enforceRouting: z.boolean(),
|
|
56
|
+
lazyTools: z.object(
|
|
63
57
|
{
|
|
64
|
-
enabled:
|
|
65
|
-
mode:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
]),
|
|
70
|
-
alwaysKeep: Type.Array(Type.String()),
|
|
71
|
-
commandAllowlist: Type.Record(Type.String(), Type.Array(Type.String())),
|
|
72
|
-
keywordTools: Type.Record(Type.String(), Type.Array(Type.String())),
|
|
58
|
+
enabled: z.boolean(),
|
|
59
|
+
mode: z.enum(["conservative", "balanced", "aggressive"]),
|
|
60
|
+
alwaysKeep: z.array(z.string()),
|
|
61
|
+
commandAllowlist: z.record(z.string(), z.array(z.string())),
|
|
62
|
+
keywordTools: z.record(z.string(), z.array(z.string())),
|
|
73
63
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
processors: Type.Object(
|
|
64
|
+
).strict(),
|
|
65
|
+
processors: z.object(
|
|
77
66
|
{
|
|
78
|
-
enabled:
|
|
79
|
-
disable:
|
|
80
|
-
|
|
81
|
-
Type.Literal("git"),
|
|
82
|
-
Type.Literal("test"),
|
|
83
|
-
Type.Literal("lint"),
|
|
84
|
-
Type.Literal("build"),
|
|
85
|
-
Type.Literal("k8s"),
|
|
86
|
-
Type.Literal("docker"),
|
|
87
|
-
Type.Literal("log"),
|
|
88
|
-
Type.Literal("json"),
|
|
89
|
-
]),
|
|
67
|
+
enabled: z.boolean(),
|
|
68
|
+
disable: z.array(
|
|
69
|
+
z.enum(["git", "test", "lint", "build", "k8s", "docker", "log", "json"]),
|
|
90
70
|
),
|
|
91
71
|
},
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
cacheHandles: Type.Object(
|
|
72
|
+
).strict(),
|
|
73
|
+
cacheHandles: z.object(
|
|
95
74
|
{
|
|
96
|
-
enabled:
|
|
97
|
-
spillThresholdBytes:
|
|
98
|
-
previewBytes:
|
|
75
|
+
enabled: z.boolean(),
|
|
76
|
+
spillThresholdBytes: z.number().min(1024),
|
|
77
|
+
previewBytes: z.number().min(256),
|
|
99
78
|
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
repomap: Type.Object(
|
|
79
|
+
).strict(),
|
|
80
|
+
repomap: z.object(
|
|
103
81
|
{
|
|
104
|
-
enabled:
|
|
105
|
-
tokenBudget:
|
|
106
|
-
maxFiles:
|
|
82
|
+
enabled: z.boolean(),
|
|
83
|
+
tokenBudget: z.number().min(100),
|
|
84
|
+
maxFiles: z.number().min(1),
|
|
107
85
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
memory: Type.Object(
|
|
86
|
+
).strict(),
|
|
87
|
+
memory: z.object(
|
|
111
88
|
{
|
|
112
|
-
enabled:
|
|
113
|
-
byteBudget:
|
|
114
|
-
maxRows:
|
|
115
|
-
retentionDays:
|
|
116
|
-
focusChainCadence:
|
|
89
|
+
enabled: z.boolean(),
|
|
90
|
+
byteBudget: z.number().min(256),
|
|
91
|
+
maxRows: z.number().min(1),
|
|
92
|
+
retentionDays: z.number().min(1),
|
|
93
|
+
focusChainCadence: z.number().int().min(1),
|
|
117
94
|
},
|
|
118
|
-
|
|
119
|
-
),
|
|
95
|
+
).strict(),
|
|
120
96
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
mempalace: Type.Object(
|
|
97
|
+
).strict(),
|
|
98
|
+
mempalace: z.object(
|
|
124
99
|
{
|
|
125
|
-
enabled:
|
|
126
|
-
packageVersion:
|
|
127
|
-
managedVenvPath:
|
|
128
|
-
palacePath:
|
|
129
|
-
defaultWingStrategy:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
explicitWing: Type.Union([Type.String(), Type.Null()]),
|
|
135
|
-
defaultAgentName: Type.String({ minLength: 1 }),
|
|
136
|
-
autoSetup: Type.Boolean(),
|
|
137
|
-
hooks: Type.Object(
|
|
100
|
+
enabled: z.boolean(),
|
|
101
|
+
packageVersion: z.string().min(1),
|
|
102
|
+
managedVenvPath: z.string().min(1),
|
|
103
|
+
palacePath: z.string().min(1),
|
|
104
|
+
defaultWingStrategy: z.enum(["repo-name", "project-slug", "explicit"]),
|
|
105
|
+
explicitWing: z.string().nullable(),
|
|
106
|
+
defaultAgentName: z.string().min(1),
|
|
107
|
+
autoSetup: z.boolean(),
|
|
108
|
+
hooks: z.object(
|
|
138
109
|
{
|
|
139
|
-
wakeUp:
|
|
140
|
-
searchGuidance:
|
|
141
|
-
autoSearchOnPrompt:
|
|
142
|
-
compactionCheckpoint:
|
|
143
|
-
shutdownDiary:
|
|
110
|
+
wakeUp: z.boolean(),
|
|
111
|
+
searchGuidance: z.boolean(),
|
|
112
|
+
autoSearchOnPrompt: z.boolean(),
|
|
113
|
+
compactionCheckpoint: z.boolean(),
|
|
114
|
+
shutdownDiary: z.boolean(),
|
|
144
115
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
budgets: Type.Object(
|
|
116
|
+
).strict(),
|
|
117
|
+
budgets: z.object(
|
|
148
118
|
{
|
|
149
|
-
wakeUpTokens:
|
|
150
|
-
searchResultChars:
|
|
151
|
-
listResultChars:
|
|
152
|
-
diaryChars:
|
|
153
|
-
autoSearchTokens:
|
|
154
|
-
wakeUpInjectionEvery:
|
|
155
|
-
autoSearchSimilarityFloor:
|
|
156
|
-
autoSearchBm25Floor:
|
|
119
|
+
wakeUpTokens: z.number().int().min(1),
|
|
120
|
+
searchResultChars: z.number().int().min(1),
|
|
121
|
+
listResultChars: z.number().int().min(1),
|
|
122
|
+
diaryChars: z.number().int().min(1),
|
|
123
|
+
autoSearchTokens: z.number().int().min(1),
|
|
124
|
+
wakeUpInjectionEvery: z.number().int().min(1),
|
|
125
|
+
autoSearchSimilarityFloor: z.number().min(0).max(1),
|
|
126
|
+
autoSearchBm25Floor: z.number().min(0),
|
|
157
127
|
},
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
timeouts: Type.Object(
|
|
128
|
+
).strict(),
|
|
129
|
+
timeouts: z.object(
|
|
161
130
|
{
|
|
162
|
-
setupMs:
|
|
163
|
-
bridgeMs:
|
|
164
|
-
hookMs:
|
|
131
|
+
setupMs: z.number().int().min(1),
|
|
132
|
+
bridgeMs: z.number().int().min(1),
|
|
133
|
+
hookMs: z.number().int().min(1),
|
|
165
134
|
},
|
|
166
|
-
|
|
167
|
-
),
|
|
135
|
+
).strict(),
|
|
168
136
|
},
|
|
169
|
-
|
|
170
|
-
),
|
|
137
|
+
).strict(),
|
|
171
138
|
},
|
|
172
|
-
|
|
173
|
-
);
|
|
139
|
+
).strict();
|
|
174
140
|
|
|
175
141
|
export interface ConfigParseError {
|
|
176
142
|
source: "global" | "root";
|
|
@@ -190,13 +156,10 @@ export interface InspectionLoadResult {
|
|
|
190
156
|
validationErrors: ConfigValidationError[];
|
|
191
157
|
}
|
|
192
158
|
|
|
193
|
-
function normalizeErrorPath(path: string): string {
|
|
194
|
-
return path.replace(/^\//, "").replace(/\//g, ".") || "(root)";
|
|
195
|
-
}
|
|
196
159
|
|
|
197
|
-
function collectValidationErrors(schema:
|
|
198
|
-
return
|
|
199
|
-
path:
|
|
160
|
+
function collectValidationErrors(schema: ZodType, data: unknown): ConfigValidationError[] {
|
|
161
|
+
return collectSchemaValidationErrors(schema, data).map((error) => ({
|
|
162
|
+
path: error.path,
|
|
200
163
|
message: error.message,
|
|
201
164
|
}));
|
|
202
165
|
}
|
package/src/docs/contracts.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// retry loop (runWithOutputValidation) will hand validation errors back to
|
|
6
6
|
// the model rather than letting a silent regex heuristic invent findings.
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { z } from "zod/v4"
|
|
9
9
|
|
|
10
10
|
export const DOC_DRIFT_SEVERITIES = ["info", "warning", "error"] as const;
|
|
11
11
|
export const DOC_DRIFT_STATUSES = ["ok", "drifted"] as const;
|
|
@@ -13,27 +13,17 @@ export const DOC_DRIFT_STATUSES = ["ok", "drifted"] as const;
|
|
|
13
13
|
export type DocDriftSeverity = (typeof DOC_DRIFT_SEVERITIES)[number];
|
|
14
14
|
export type DocDriftStatus = (typeof DOC_DRIFT_STATUSES)[number];
|
|
15
15
|
|
|
16
|
-
export const DocDriftFindingSchema =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
),
|
|
23
|
-
relatedFiles: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
24
|
-
},
|
|
25
|
-
{ additionalProperties: false },
|
|
26
|
-
);
|
|
16
|
+
export const DocDriftFindingSchema = z.object({
|
|
17
|
+
file: z.string().min(1),
|
|
18
|
+
description: z.string().min(1),
|
|
19
|
+
severity: z.enum(DOC_DRIFT_SEVERITIES),
|
|
20
|
+
relatedFiles: z.array(z.string().min(1)).optional(),
|
|
21
|
+
}).strict();
|
|
27
22
|
|
|
28
|
-
export const DocDriftOutputSchema =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
DOC_DRIFT_STATUSES.map((value) => Type.Literal(value)),
|
|
33
|
-
),
|
|
34
|
-
},
|
|
35
|
-
{ additionalProperties: false },
|
|
36
|
-
);
|
|
23
|
+
export const DocDriftOutputSchema = z.object({
|
|
24
|
+
findings: z.array(DocDriftFindingSchema),
|
|
25
|
+
status: z.enum(DOC_DRIFT_STATUSES),
|
|
26
|
+
}).strict();
|
|
37
27
|
|
|
38
|
-
export type DocDriftFinding =
|
|
39
|
-
export type DocDriftOutput =
|
|
28
|
+
export type DocDriftFinding = z.infer<typeof DocDriftFindingSchema>;
|
|
29
|
+
export type DocDriftOutput = z.infer<typeof DocDriftOutputSchema>;
|