sdd-cli 0.1.29 → 0.1.31
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 +1 -1
- package/dist/cli.js +4 -2
- package/dist/commands/digital-reviewers.d.ts +12 -0
- package/dist/commands/digital-reviewers.js +153 -0
- package/dist/commands/hello.js +49 -27
- package/dist/context/flags.d.ts +1 -0
- package/dist/context/flags.js +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ sdd-cli hello "create a calculator app"
|
|
|
55
55
|
## Global Flags
|
|
56
56
|
|
|
57
57
|
- `--approve`, `--improve`, `--parallel`
|
|
58
|
-
- `--non-interactive`, `--dry-run`, `--beginner`, `--from-step`
|
|
58
|
+
- `--non-interactive`, `--dry-run`, `--beginner`, `--from-step`, `--iterations`
|
|
59
59
|
- `--project`, `--output`, `--scope`, `--metrics-local`
|
|
60
60
|
- `--provider`, `--gemini`, `--model`
|
|
61
61
|
|
package/dist/cli.js
CHANGED
|
@@ -85,6 +85,7 @@ program
|
|
|
85
85
|
.option("--metrics-local", "Enable local opt-in telemetry snapshots in workspace/metrics")
|
|
86
86
|
.option("--provider <name>", "AI provider: gemini|codex|auto", (0, providers_1.defaultProviderPreference)())
|
|
87
87
|
.option("--model <name>", "AI model id (for providers that support model override)")
|
|
88
|
+
.option("--iterations <n>", "Autopilot improvement iterations (1-10)", "1")
|
|
88
89
|
.option("--gemini", "Shortcut for --provider gemini");
|
|
89
90
|
program.hook("preAction", (thisCommand, actionCommand) => {
|
|
90
91
|
const config = (0, config_1.ensureConfig)();
|
|
@@ -109,7 +110,8 @@ program.hook("preAction", (thisCommand, actionCommand) => {
|
|
|
109
110
|
: typeof opts.provider === "string"
|
|
110
111
|
? opts.provider
|
|
111
112
|
: config.ai.preferred_cli,
|
|
112
|
-
model: typeof opts.model === "string" ? opts.model : config.ai.model
|
|
113
|
+
model: typeof opts.model === "string" ? opts.model : config.ai.model,
|
|
114
|
+
iterations: Number.parseInt(typeof opts.iterations === "string" ? opts.iterations : "1", 10)
|
|
113
115
|
});
|
|
114
116
|
process.env.SDD_GEMINI_MODEL = typeof opts.model === "string" ? opts.model : config.ai.model;
|
|
115
117
|
const commandPath = typeof actionCommand.name === "function"
|
|
@@ -492,7 +494,7 @@ function normalizeArgv(argv) {
|
|
|
492
494
|
if (args.length === 0) {
|
|
493
495
|
return argv;
|
|
494
496
|
}
|
|
495
|
-
const valueFlags = new Set(["--from-step", "--project", "--output", "--scope", "--provider", "--model"]);
|
|
497
|
+
const valueFlags = new Set(["--from-step", "--project", "--output", "--scope", "--provider", "--model", "--iterations"]);
|
|
496
498
|
let positionalIndex = -1;
|
|
497
499
|
for (let i = 0; i < args.length; i += 1) {
|
|
498
500
|
const token = args[i];
|
|
@@ -4,6 +4,14 @@ type ReviewerFinding = {
|
|
|
4
4
|
severity: "high" | "medium";
|
|
5
5
|
message: string;
|
|
6
6
|
};
|
|
7
|
+
export type UserStory = {
|
|
8
|
+
id: string;
|
|
9
|
+
priority: "P0" | "P1";
|
|
10
|
+
persona: string;
|
|
11
|
+
story: string;
|
|
12
|
+
acceptanceCriteria: string[];
|
|
13
|
+
sourceReviewer: string;
|
|
14
|
+
};
|
|
7
15
|
export type DigitalReviewResult = {
|
|
8
16
|
passed: boolean;
|
|
9
17
|
findings: ReviewerFinding[];
|
|
@@ -13,5 +21,9 @@ export type DigitalReviewResult = {
|
|
|
13
21
|
summary: string;
|
|
14
22
|
};
|
|
15
23
|
export declare function runDigitalHumanReview(appDir: string, context?: LifecycleContext): DigitalReviewResult;
|
|
24
|
+
export declare function convertFindingsToUserStories(findings: ReviewerFinding[]): UserStory[];
|
|
25
|
+
export declare function storiesToDiagnostics(stories: UserStory[]): string[];
|
|
26
|
+
export declare function writeUserStoriesBacklog(appDir: string, stories: UserStory[]): string | null;
|
|
27
|
+
export declare function appendDigitalReviewRound(appDir: string, round: number, review: DigitalReviewResult, stories: UserStory[]): string | null;
|
|
16
28
|
export declare function writeDigitalReviewReport(appDir: string, review: DigitalReviewResult): string | null;
|
|
17
29
|
export {};
|
|
@@ -4,6 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.runDigitalHumanReview = runDigitalHumanReview;
|
|
7
|
+
exports.convertFindingsToUserStories = convertFindingsToUserStories;
|
|
8
|
+
exports.storiesToDiagnostics = storiesToDiagnostics;
|
|
9
|
+
exports.writeUserStoriesBacklog = writeUserStoriesBacklog;
|
|
10
|
+
exports.appendDigitalReviewRound = appendDigitalReviewRound;
|
|
7
11
|
exports.writeDigitalReviewReport = writeDigitalReviewReport;
|
|
8
12
|
const fs_1 = __importDefault(require("fs"));
|
|
9
13
|
const path_1 = __importDefault(require("path"));
|
|
@@ -90,6 +94,30 @@ function hasUserFlowDocs(root, readme) {
|
|
|
90
94
|
}
|
|
91
95
|
return Boolean(findDoc(root, ["user-flow.md", "ux-notes.md", "experience.md"]));
|
|
92
96
|
}
|
|
97
|
+
function hasAccessibilityEvidence(root, readme) {
|
|
98
|
+
if (/\ba11y\b|\baccessibility\b|\bwcag\b|\bkeyboard\b/.test(readme)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return Boolean(findDoc(root, ["accessibility.md", "a11y.md"]));
|
|
102
|
+
}
|
|
103
|
+
function hasPerformanceEvidence(root, readme) {
|
|
104
|
+
if (/\bperformance\b|\blatency\b|\bthroughput\b|\bp95\b|\bp99\b/.test(readme)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return Boolean(findDoc(root, ["performance.md", "performance-budget.md", "scalability.md"]));
|
|
108
|
+
}
|
|
109
|
+
function hasSupportEvidence(root, readme) {
|
|
110
|
+
if (/\btroubleshoot\b|\bsupport\b|\bfaq\b/.test(readme)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return Boolean(findDoc(root, ["troubleshooting.md", "support.md", "faq.md"]));
|
|
114
|
+
}
|
|
115
|
+
function hasApiContracts(root) {
|
|
116
|
+
return Boolean(findDoc(root, ["openapi.yaml", "openapi.yml", "api-contract.md", "api.md"]));
|
|
117
|
+
}
|
|
118
|
+
function hasReleaseNotes(root) {
|
|
119
|
+
return Boolean(findDoc(root, ["release-notes.md", "changelog.md"]));
|
|
120
|
+
}
|
|
93
121
|
function parseThreshold() {
|
|
94
122
|
const raw = Number.parseInt(process.env.SDD_DIGITAL_REVIEW_MIN_SCORE ?? "", 10);
|
|
95
123
|
if (!Number.isFinite(raw)) {
|
|
@@ -179,6 +207,41 @@ function runDigitalHumanReview(appDir, context) {
|
|
|
179
207
|
message: "User experience flow is unclear. Add user-flow/UX notes and acceptance of critical journeys."
|
|
180
208
|
});
|
|
181
209
|
}
|
|
210
|
+
if (!hasAccessibilityEvidence(appDir, readme)) {
|
|
211
|
+
findings.push({
|
|
212
|
+
reviewer: "accessibility_tester",
|
|
213
|
+
severity: "medium",
|
|
214
|
+
message: "Accessibility evidence missing. Add keyboard/contrast/screen-reader validation notes."
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (!hasPerformanceEvidence(appDir, readme)) {
|
|
218
|
+
findings.push({
|
|
219
|
+
reviewer: "performance_engineer",
|
|
220
|
+
severity: "medium",
|
|
221
|
+
message: "Performance expectations are unclear. Add performance budget and baseline measurements."
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (!hasSupportEvidence(appDir, readme)) {
|
|
225
|
+
findings.push({
|
|
226
|
+
reviewer: "support_agent",
|
|
227
|
+
severity: "medium",
|
|
228
|
+
message: "Support/troubleshooting guidance is missing for operators and end users."
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (!hasApiContracts(appDir)) {
|
|
232
|
+
findings.push({
|
|
233
|
+
reviewer: "integrator_partner",
|
|
234
|
+
severity: "medium",
|
|
235
|
+
message: "API contract/documentation missing. Add OpenAPI or API contract document for integrators."
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
if (!hasReleaseNotes(appDir)) {
|
|
239
|
+
findings.push({
|
|
240
|
+
reviewer: "release_manager",
|
|
241
|
+
severity: "medium",
|
|
242
|
+
message: "Release notes/changelog missing. Add release documentation for change visibility."
|
|
243
|
+
});
|
|
244
|
+
}
|
|
182
245
|
if (hasSecretLeak(appDir)) {
|
|
183
246
|
findings.push({
|
|
184
247
|
reviewer: "security_reviewer",
|
|
@@ -227,6 +290,96 @@ function runDigitalHumanReview(appDir, context) {
|
|
|
227
290
|
summary
|
|
228
291
|
};
|
|
229
292
|
}
|
|
293
|
+
function slugReviewer(reviewer) {
|
|
294
|
+
return reviewer.replace(/[^a-z0-9]+/gi, "_").toLowerCase();
|
|
295
|
+
}
|
|
296
|
+
function acceptanceCriteriaFromFinding(finding) {
|
|
297
|
+
const base = finding.message.replace(/\.$/, "");
|
|
298
|
+
return [
|
|
299
|
+
`Given the generated app, when quality review runs, then ${base.toLowerCase()}.`,
|
|
300
|
+
"Given CI validation, when documentation/tests are checked, then evidence is discoverable and actionable."
|
|
301
|
+
];
|
|
302
|
+
}
|
|
303
|
+
function convertFindingsToUserStories(findings) {
|
|
304
|
+
const deduped = new Map();
|
|
305
|
+
for (const finding of findings) {
|
|
306
|
+
const key = `${finding.reviewer}::${finding.message}`.toLowerCase();
|
|
307
|
+
if (!deduped.has(key)) {
|
|
308
|
+
deduped.set(key, finding);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
let index = 1;
|
|
312
|
+
return [...deduped.values()].map((finding) => {
|
|
313
|
+
const id = `US-${String(index).padStart(3, "0")}`;
|
|
314
|
+
index += 1;
|
|
315
|
+
const persona = slugReviewer(finding.reviewer);
|
|
316
|
+
const priority = finding.severity === "high" ? "P0" : "P1";
|
|
317
|
+
return {
|
|
318
|
+
id,
|
|
319
|
+
priority,
|
|
320
|
+
persona,
|
|
321
|
+
sourceReviewer: finding.reviewer,
|
|
322
|
+
story: `As a ${persona}, I need ${finding.message.toLowerCase()} so that the delivery is production-ready.`,
|
|
323
|
+
acceptanceCriteria: acceptanceCriteriaFromFinding(finding)
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function storiesToDiagnostics(stories) {
|
|
328
|
+
return stories.map((story) => `[UserStory:${story.id}][${story.priority}] ${story.story}`);
|
|
329
|
+
}
|
|
330
|
+
function writeUserStoriesBacklog(appDir, stories) {
|
|
331
|
+
if (!fs_1.default.existsSync(appDir)) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const deployDir = path_1.default.join(appDir, "deploy");
|
|
335
|
+
fs_1.default.mkdirSync(deployDir, { recursive: true });
|
|
336
|
+
const jsonPath = path_1.default.join(deployDir, "digital-review-user-stories.json");
|
|
337
|
+
fs_1.default.writeFileSync(jsonPath, JSON.stringify({
|
|
338
|
+
generatedAt: new Date().toISOString(),
|
|
339
|
+
count: stories.length,
|
|
340
|
+
stories
|
|
341
|
+
}, null, 2), "utf-8");
|
|
342
|
+
const mdPath = path_1.default.join(deployDir, "digital-review-user-stories.md");
|
|
343
|
+
const lines = [
|
|
344
|
+
"# Digital Review User Stories",
|
|
345
|
+
"",
|
|
346
|
+
...stories.flatMap((story) => [
|
|
347
|
+
`## ${story.id} (${story.priority})`,
|
|
348
|
+
`- Persona: ${story.persona}`,
|
|
349
|
+
`- Source reviewer: ${story.sourceReviewer}`,
|
|
350
|
+
`- Story: ${story.story}`,
|
|
351
|
+
"- Acceptance criteria:",
|
|
352
|
+
...story.acceptanceCriteria.map((criterion) => ` - ${criterion}`),
|
|
353
|
+
""
|
|
354
|
+
])
|
|
355
|
+
];
|
|
356
|
+
fs_1.default.writeFileSync(mdPath, `${lines.join("\n")}\n`, "utf-8");
|
|
357
|
+
return jsonPath;
|
|
358
|
+
}
|
|
359
|
+
function appendDigitalReviewRound(appDir, round, review, stories) {
|
|
360
|
+
if (!fs_1.default.existsSync(appDir)) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
const deployDir = path_1.default.join(appDir, "deploy");
|
|
364
|
+
fs_1.default.mkdirSync(deployDir, { recursive: true });
|
|
365
|
+
const reportPath = path_1.default.join(deployDir, "digital-review-rounds.json");
|
|
366
|
+
const existing = fs_1.default.existsSync(reportPath)
|
|
367
|
+
? JSON.parse(fs_1.default.readFileSync(reportPath, "utf-8"))
|
|
368
|
+
: { rounds: [] };
|
|
369
|
+
const rounds = Array.isArray(existing.rounds) ? existing.rounds : [];
|
|
370
|
+
rounds.push({
|
|
371
|
+
round,
|
|
372
|
+
generatedAt: new Date().toISOString(),
|
|
373
|
+
summary: review.summary,
|
|
374
|
+
passed: review.passed,
|
|
375
|
+
score: review.score,
|
|
376
|
+
threshold: review.threshold,
|
|
377
|
+
findings: review.findings,
|
|
378
|
+
stories
|
|
379
|
+
});
|
|
380
|
+
fs_1.default.writeFileSync(reportPath, JSON.stringify({ rounds }, null, 2), "utf-8");
|
|
381
|
+
return reportPath;
|
|
382
|
+
}
|
|
230
383
|
function writeDigitalReviewReport(appDir, review) {
|
|
231
384
|
if (!fs_1.default.existsSync(appDir)) {
|
|
232
385
|
return null;
|
package/dist/commands/hello.js
CHANGED
|
@@ -128,6 +128,11 @@ async function runHello(input, runQuestions) {
|
|
|
128
128
|
const dryRun = runtimeFlags.dryRun;
|
|
129
129
|
const beginnerMode = runtimeFlags.beginner;
|
|
130
130
|
const provider = runtimeFlags.provider;
|
|
131
|
+
const iterations = runtimeFlags.iterations;
|
|
132
|
+
if (!Number.isInteger(iterations) || iterations < 1 || iterations > 10) {
|
|
133
|
+
(0, errors_1.printError)("SDD-1005", "Invalid --iterations value. Use an integer between 1 and 10.");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
131
136
|
console.log("Hello from sdd-cli.");
|
|
132
137
|
console.log(`Workspace: ${workspace.root}`);
|
|
133
138
|
if (beginnerMode) {
|
|
@@ -136,6 +141,7 @@ async function runHello(input, runQuestions) {
|
|
|
136
141
|
if (autoGuidedMode) {
|
|
137
142
|
printWhy("Auto-guided mode active: using current workspace defaults.");
|
|
138
143
|
printWhy(`AI provider preference: ${provider ?? "gemini"}`);
|
|
144
|
+
printWhy(`Iterations configured: ${iterations}`);
|
|
139
145
|
}
|
|
140
146
|
else {
|
|
141
147
|
const useWorkspace = await (0, prompt_1.confirm)("Use this workspace path? (y/n) ");
|
|
@@ -449,40 +455,50 @@ async function runHello(input, runQuestions) {
|
|
|
449
455
|
const digitalReviewDisabled = lifecycleDisabled || process.env.SDD_DISABLE_AI_AUTOPILOT === "1" || process.env.SDD_DISABLE_DIGITAL_REVIEW === "1";
|
|
450
456
|
if (!digitalReviewDisabled) {
|
|
451
457
|
const appDir = path_1.default.join(projectRoot, "generated-app");
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
458
|
+
let deliveryApproved = false;
|
|
459
|
+
for (let round = 1; round <= iterations; round += 1) {
|
|
460
|
+
printWhy(`Iteration ${round}/${iterations}: running multi-persona digital review.`);
|
|
461
|
+
let review = (0, digital_reviewers_1.runDigitalHumanReview)(appDir, {
|
|
462
|
+
goalText: text,
|
|
463
|
+
intentSignals: intent.signals,
|
|
464
|
+
intentDomain: intent.domain,
|
|
465
|
+
intentFlow: intent.flow
|
|
466
|
+
});
|
|
467
|
+
let stories = (0, digital_reviewers_1.convertFindingsToUserStories)(review.findings);
|
|
468
|
+
const reviewPath = (0, digital_reviewers_1.writeDigitalReviewReport)(appDir, review);
|
|
469
|
+
const storiesPath = (0, digital_reviewers_1.writeUserStoriesBacklog)(appDir, stories);
|
|
470
|
+
(0, digital_reviewers_1.appendDigitalReviewRound)(appDir, round, review, stories);
|
|
471
|
+
if (reviewPath) {
|
|
472
|
+
printWhy(`Digital-review report: ${reviewPath}`);
|
|
473
|
+
}
|
|
474
|
+
if (storiesPath) {
|
|
475
|
+
printWhy(`Digital-review user stories: ${storiesPath} (${stories.length} stories)`);
|
|
476
|
+
}
|
|
477
|
+
if (review.passed) {
|
|
478
|
+
printWhy(`Iteration ${round}: digital reviewers approved (${review.summary}).`);
|
|
479
|
+
deliveryApproved = true;
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
printWhy(`Iteration ${round}: reviewers requested improvements (${review.summary}).`);
|
|
466
483
|
review.diagnostics.forEach((issue) => printWhy(`Reviewer issue: ${issue}`));
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const repair = (0, ai_autopilot_1.improveGeneratedApp)(appDir, text, provider, review.diagnostics, intent.domain);
|
|
484
|
+
const storyDiagnostics = (0, digital_reviewers_1.storiesToDiagnostics)(stories);
|
|
485
|
+
const repair = (0, ai_autopilot_1.improveGeneratedApp)(appDir, text, provider, [...review.diagnostics, ...storyDiagnostics, "Implement all prioritized user stories before next review."], intent.domain);
|
|
470
486
|
if (!repair.attempted || !repair.applied) {
|
|
471
|
-
printWhy(`
|
|
487
|
+
printWhy(`Iteration ${round}: repair skipped (${repair.reason || "unknown reason"}).`);
|
|
472
488
|
break;
|
|
473
489
|
}
|
|
474
|
-
printWhy(`
|
|
490
|
+
printWhy(`Iteration ${round}: repair applied (${repair.fileCount} files). Re-validating lifecycle.`);
|
|
475
491
|
lifecycle = (0, app_lifecycle_1.runAppLifecycle)(projectRoot, activeProject, {
|
|
476
492
|
goalText: text,
|
|
477
493
|
intentSignals: intent.signals,
|
|
478
494
|
intentDomain: intent.domain,
|
|
479
495
|
intentFlow: intent.flow
|
|
480
496
|
});
|
|
481
|
-
lifecycle.summary.forEach((line) => printWhy(`Lifecycle (
|
|
497
|
+
lifecycle.summary.forEach((line) => printWhy(`Lifecycle (iteration ${round}): ${line}`));
|
|
482
498
|
if (!lifecycle.qualityPassed) {
|
|
499
|
+
printWhy("Quality gates failed after story implementation. Applying one quality-repair pass.");
|
|
483
500
|
const qualityRepair = (0, ai_autopilot_1.improveGeneratedApp)(appDir, text, provider, lifecycle.qualityDiagnostics, intent.domain);
|
|
484
501
|
if (qualityRepair.attempted && qualityRepair.applied) {
|
|
485
|
-
printWhy(`Quality regression repaired after digital review (${qualityRepair.fileCount} files). Re-validating delivery.`);
|
|
486
502
|
lifecycle = (0, app_lifecycle_1.runAppLifecycle)(projectRoot, activeProject, {
|
|
487
503
|
goalText: text,
|
|
488
504
|
intentSignals: intent.signals,
|
|
@@ -492,7 +508,7 @@ async function runHello(input, runQuestions) {
|
|
|
492
508
|
}
|
|
493
509
|
}
|
|
494
510
|
if (!lifecycle.qualityPassed) {
|
|
495
|
-
printWhy(
|
|
511
|
+
printWhy(`Iteration ${round}: lifecycle quality still failing.`);
|
|
496
512
|
continue;
|
|
497
513
|
}
|
|
498
514
|
review = (0, digital_reviewers_1.runDigitalHumanReview)(appDir, {
|
|
@@ -501,17 +517,23 @@ async function runHello(input, runQuestions) {
|
|
|
501
517
|
intentDomain: intent.domain,
|
|
502
518
|
intentFlow: intent.flow
|
|
503
519
|
});
|
|
520
|
+
stories = (0, digital_reviewers_1.convertFindingsToUserStories)(review.findings);
|
|
504
521
|
(0, digital_reviewers_1.writeDigitalReviewReport)(appDir, review);
|
|
505
|
-
|
|
506
|
-
|
|
522
|
+
(0, digital_reviewers_1.writeUserStoriesBacklog)(appDir, stories);
|
|
523
|
+
(0, digital_reviewers_1.appendDigitalReviewRound)(appDir, round, review, stories);
|
|
524
|
+
if (review.passed) {
|
|
525
|
+
printWhy(`Iteration ${round}: delivery improved and approved (${review.summary}).`);
|
|
526
|
+
deliveryApproved = true;
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
printWhy(`Iteration ${round}: additional improvements still required (${review.summary}).`);
|
|
507
530
|
}
|
|
508
531
|
}
|
|
509
|
-
if (!
|
|
510
|
-
printWhy("Digital-review quality bar not met after
|
|
532
|
+
if (!deliveryApproved) {
|
|
533
|
+
printWhy("Digital-review quality bar not met after configured iterations.");
|
|
511
534
|
printRecoveryNext(activeProject, "finish", text);
|
|
512
535
|
return;
|
|
513
536
|
}
|
|
514
|
-
printWhy(`Digital reviewers approved delivery quality (${review.summary}).`);
|
|
515
537
|
}
|
|
516
538
|
(0, local_metrics_1.recordActivationMetric)("completed", {
|
|
517
539
|
project: activeProject,
|
package/dist/context/flags.d.ts
CHANGED
package/dist/context/flags.js
CHANGED
|
@@ -15,7 +15,8 @@ const flags = {
|
|
|
15
15
|
scope: undefined,
|
|
16
16
|
metricsLocal: false,
|
|
17
17
|
provider: process.env.SDD_AI_PROVIDER_DEFAULT ?? "gemini",
|
|
18
|
-
model: process.env.SDD_AI_MODEL_DEFAULT
|
|
18
|
+
model: process.env.SDD_AI_MODEL_DEFAULT,
|
|
19
|
+
iterations: 1
|
|
19
20
|
};
|
|
20
21
|
function setFlags(next) {
|
|
21
22
|
if ("approve" in next) {
|
|
@@ -57,6 +58,10 @@ function setFlags(next) {
|
|
|
57
58
|
if ("model" in next) {
|
|
58
59
|
flags.model = typeof next.model === "string" ? next.model : flags.model;
|
|
59
60
|
}
|
|
61
|
+
if ("iterations" in next) {
|
|
62
|
+
const raw = Number(next.iterations);
|
|
63
|
+
flags.iterations = Number.isFinite(raw) ? Math.trunc(raw) : flags.iterations;
|
|
64
|
+
}
|
|
60
65
|
}
|
|
61
66
|
function getFlags() {
|
|
62
67
|
return { ...flags };
|