create-quiver 0.12.1 → 0.14.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 (110) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +24 -9
  3. package/README_FOR_AI.md +15 -6
  4. package/ROADMAP.md +15 -2
  5. package/docs/COMMANDS.md.template +12 -3
  6. package/docs/TROUBLESHOOTING.md.template +29 -0
  7. package/docs/WORKFLOW.md.template +13 -12
  8. package/package.json +2 -1
  9. package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +2 -2
  10. package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +5 -5
  11. package/specs/quiver-v27-reliability-ai-workflow-hardening/AUDIT_V24_V25_V26.md +67 -0
  12. package/specs/quiver-v27-reliability-ai-workflow-hardening/COMMAND_CONTRACTS.md +125 -0
  13. package/specs/quiver-v27-reliability-ai-workflow-hardening/COVERAGE_MATRIX.md +74 -0
  14. package/specs/quiver-v27-reliability-ai-workflow-hardening/EVIDENCE_REPORT.md +179 -0
  15. package/specs/quiver-v27-reliability-ai-workflow-hardening/EXECUTION_PLAN.md +71 -0
  16. package/specs/quiver-v27-reliability-ai-workflow-hardening/SPEC.md +176 -0
  17. package/specs/quiver-v27-reliability-ai-workflow-hardening/STATUS.md +37 -0
  18. package/specs/quiver-v27-reliability-ai-workflow-hardening/pr.md +132 -0
  19. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/CLOSURE_BRIEF.md +36 -0
  20. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/EXECUTION_BRIEF.md +56 -0
  21. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/slice.json +75 -0
  22. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/CLOSURE_BRIEF.md +37 -0
  23. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/EXECUTION_BRIEF.md +54 -0
  24. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/slice.json +79 -0
  25. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/CLOSURE_BRIEF.md +34 -0
  26. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/EXECUTION_BRIEF.md +54 -0
  27. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/slice.json +75 -0
  28. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/CLOSURE_BRIEF.md +36 -0
  29. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/EXECUTION_BRIEF.md +55 -0
  30. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/slice.json +78 -0
  31. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/CLOSURE_BRIEF.md +31 -0
  32. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/EXECUTION_BRIEF.md +55 -0
  33. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/slice.json +77 -0
  34. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/CLOSURE_BRIEF.md +31 -0
  35. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/EXECUTION_BRIEF.md +55 -0
  36. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/slice.json +84 -0
  37. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/CLOSURE_BRIEF.md +32 -0
  38. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/EXECUTION_BRIEF.md +57 -0
  39. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/slice.json +99 -0
  40. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/CLOSURE_BRIEF.md +31 -0
  41. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/EXECUTION_BRIEF.md +57 -0
  42. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/slice.json +88 -0
  43. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/CLOSURE_BRIEF.md +31 -0
  44. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/EXECUTION_BRIEF.md +56 -0
  45. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/slice.json +85 -0
  46. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/CLOSURE_BRIEF.md +32 -0
  47. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/EXECUTION_BRIEF.md +56 -0
  48. package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/slice.json +91 -0
  49. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/COVERAGE_MATRIX.md +117 -0
  50. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/EVIDENCE_REPORT.md +200 -0
  51. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/EXECUTION_PLAN.md +60 -0
  52. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/SPEC.md +132 -0
  53. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/STATUS.md +36 -0
  54. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/pr.md +128 -0
  55. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-00-reconciliation-and-evidence-freeze/CLOSURE_BRIEF.md +44 -0
  56. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-00-reconciliation-and-evidence-freeze/EXECUTION_BRIEF.md +56 -0
  57. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-00-reconciliation-and-evidence-freeze/slice.json +71 -0
  58. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-01-ai-run-state-approvals-and-clean-output/CLOSURE_BRIEF.md +38 -0
  59. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-01-ai-run-state-approvals-and-clean-output/EXECUTION_BRIEF.md +53 -0
  60. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-01-ai-run-state-approvals-and-clean-output/slice.json +83 -0
  61. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-02-structured-technical-plan-contract-and-repair-flow/CLOSURE_BRIEF.md +33 -0
  62. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-02-structured-technical-plan-contract-and-repair-flow/EXECUTION_BRIEF.md +53 -0
  63. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-02-structured-technical-plan-contract-and-repair-flow/slice.json +85 -0
  64. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-03-active-slice-reconciliation-and-ai-inspect/CLOSURE_BRIEF.md +34 -0
  65. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-03-active-slice-reconciliation-and-ai-inspect/EXECUTION_BRIEF.md +52 -0
  66. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-03-active-slice-reconciliation-and-ai-inspect/slice.json +82 -0
  67. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-04-spec-validation-scope-and-worktree-reliability/CLOSURE_BRIEF.md +32 -0
  68. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-04-spec-validation-scope-and-worktree-reliability/EXECUTION_BRIEF.md +55 -0
  69. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-04-spec-validation-scope-and-worktree-reliability/slice.json +85 -0
  70. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-05-review-plan-closure-and-agent-dx/CLOSURE_BRIEF.md +35 -0
  71. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-05-review-plan-closure-and-agent-dx/EXECUTION_BRIEF.md +59 -0
  72. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-05-review-plan-closure-and-agent-dx/slice.json +94 -0
  73. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-06-backward-compatibility-docs-and-release-readiness/CLOSURE_BRIEF.md +40 -0
  74. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-06-backward-compatibility-docs-and-release-readiness/EXECUTION_BRIEF.md +56 -0
  75. package/specs/quiver-v28-pixel-quiver-feedback-reconciliation/slices/slice-06-backward-compatibility-docs-and-release-readiness/slice.json +98 -0
  76. package/src/create-quiver/commands/ai.js +563 -21
  77. package/src/create-quiver/commands/flow.js +52 -4
  78. package/src/create-quiver/commands/graph.js +7 -7
  79. package/src/create-quiver/commands/plan.js +6 -15
  80. package/src/create-quiver/commands/spec.js +292 -0
  81. package/src/create-quiver/index.js +125 -25
  82. package/src/create-quiver/lib/agent-profiles.js +15 -3
  83. package/src/create-quiver/lib/ai/artifacts.js +318 -0
  84. package/src/create-quiver/lib/ai/context-packs.js +2 -2
  85. package/src/create-quiver/lib/ai/execution-plan.js +9 -0
  86. package/src/create-quiver/lib/ai/executor.js +3 -2
  87. package/src/create-quiver/lib/ai/export-state.js +287 -95
  88. package/src/create-quiver/lib/ai/github.js +93 -4
  89. package/src/create-quiver/lib/ai/plan-review.js +161 -0
  90. package/src/create-quiver/lib/ai/run-state.js +17 -2
  91. package/src/create-quiver/lib/ai/spec-generator.js +87 -13
  92. package/src/create-quiver/lib/ai/spec-templates.js +72 -12
  93. package/src/create-quiver/lib/analyze.js +2 -2
  94. package/src/create-quiver/lib/approvals.js +14 -2
  95. package/src/create-quiver/lib/doctor.js +79 -0
  96. package/src/create-quiver/lib/git.js +40 -1
  97. package/src/create-quiver/lib/handoff.js +43 -1
  98. package/src/create-quiver/lib/init-docs.js +11 -7
  99. package/src/create-quiver/lib/init-layout.js +1 -0
  100. package/src/create-quiver/lib/lifecycle.js +52 -3
  101. package/src/create-quiver/lib/locks.js +134 -0
  102. package/src/create-quiver/lib/package-safety.js +7 -0
  103. package/src/create-quiver/lib/paths.js +74 -0
  104. package/src/create-quiver/lib/project-scan.js +74 -0
  105. package/src/create-quiver/lib/project-state-resolver.js +430 -0
  106. package/src/create-quiver/lib/readiness.js +48 -7
  107. package/src/create-quiver/lib/scope.js +2 -1
  108. package/src/create-quiver/lib/slice.js +8 -4
  109. package/src/create-quiver/lib/spec-worktrees.js +169 -38
  110. package/src/create-quiver/lib/statuses.js +115 -0
@@ -3,6 +3,12 @@ const path = require('node:path');
3
3
 
4
4
  const { redactSecrets } = require('../lib/evidence');
5
5
  const { formatActionableError } = require('../lib/actionable-error');
6
+ const {
7
+ assertProviderPromptWithinLimit,
8
+ compactRevisionInput,
9
+ extractCleanProviderOutput,
10
+ writeRawProviderArtifact,
11
+ } = require('../lib/ai/artifacts');
6
12
  const { buildContextPackMetadata, normalizeRole } = require('../lib/ai/context-packs');
7
13
  const { runExecuteSlice, runPromptSlice } = require('../lib/ai/executor');
8
14
  const { runExecutePlan } = require('../lib/ai/execution-plan');
@@ -20,24 +26,32 @@ const {
20
26
  PLAN_REVIEW_PROMPT_SOURCE,
21
27
  buildPlanReviewPrompt,
22
28
  readPlanReview,
29
+ reviewBlocksApproval,
23
30
  resolveReviewedTechnicalPlanInput,
24
31
  resolveTechnicalPlanReviewInput,
25
32
  savePlanReview,
26
33
  summarizePlanReview,
27
34
  } = require('../lib/ai/plan-review');
28
- const { buildSpecGenerationManifest, describeSpecGeneration, generateSpecArtifacts } = require('../lib/ai/spec-generator');
35
+ const {
36
+ buildSpecGenerationManifest,
37
+ describeSpecGeneration,
38
+ generateSpecArtifacts,
39
+ validateTechnicalPlanSpecContract,
40
+ } = require('../lib/ai/spec-generator');
29
41
  const { buildProviderInvocation, runProvider } = require('../lib/ai/providers');
30
42
  const {
31
43
  createAiRun,
32
44
  ensureAiRun,
33
45
  formatAiRunResume,
34
46
  formatAiRunStatus,
47
+ listAiRuns,
35
48
  recordAiRunApproval,
36
49
  resolveAiRun,
37
50
  updateAiRunPhase,
38
51
  } = require('../lib/ai/run-state');
39
52
  const {
40
53
  agentProfilesPath,
54
+ buildAgentProfileState,
41
55
  getAgentProfile,
42
56
  listAgentProfiles,
43
57
  resolveProfileProvider,
@@ -46,12 +60,15 @@ const {
46
60
  const {
47
61
  PLANNER_APPROVAL_PHASES,
48
62
  approvePlannerPhase,
63
+ findDraftVersion,
64
+ latestDraftVersion,
49
65
  readPhaseApproval,
50
66
  resolveApprovedPlannerInput,
51
67
  savePlannerDraft,
52
68
  summarizePlannerApproval,
53
69
  } = require('../lib/approvals');
54
70
  const { assertPlannerPhaseReady, getPlannerPhaseDetails, normalizePlannerPhase, PlannerPhaseError } = require('../lib/ai/phase-gates');
71
+ const { collectActiveSliceState, resolveProjectState } = require('../lib/project-state-resolver');
55
72
 
56
73
  const DEFAULT_ONBOARD_PROVIDER = 'codex';
57
74
  const DEFAULT_ONBOARD_ROLE = 'planner';
@@ -126,6 +143,13 @@ function buildPlanContext({ role, context, phase, inputText, inputPath, repoRoot
126
143
  : 'Task: produce a technical plan only. Do not create files or modify product code.',
127
144
  ];
128
145
 
146
+ if (phaseDetails.phase === 'technical-plan') {
147
+ sections.push(
148
+ 'Required output contract: include a fenced json block with `{ "spec": { "slices": [...] } }` so Quiver can create specs after review and approval.',
149
+ 'Each `spec.slices[]` item must include at least `slice_id`, `title`, `objective`, and `files`.',
150
+ );
151
+ }
152
+
129
153
  if (relativeInputPath) {
130
154
  sections.push(`Input file: ${relativeInputPath}`);
131
155
  }
@@ -298,15 +322,19 @@ function writeProviderOutput(result) {
298
322
  }
299
323
  }
300
324
 
301
- function getRedactedProviderText(result) {
302
- return redactSecrets([result.stdout, result.stderr].filter(Boolean).join(''));
325
+ function writeCleanProviderOutput(clean) {
326
+ const output = String(clean?.cleanOutput || '');
327
+ if (!output) {
328
+ return;
329
+ }
330
+ process.stdout.write(output.endsWith('\n') ? output : `${output}\n`);
303
331
  }
304
332
 
305
333
  function normalizeText(value) {
306
334
  return String(value || '').replace(/\r\n/g, '\n');
307
335
  }
308
336
 
309
- function buildRevisionInput({ phase, feedbackPath, feedbackText, repoRoot }) {
337
+ function buildRevisionInput({ phase, feedbackPath, feedbackText, repoRoot, compactionOptions = {} }) {
310
338
  const current = readPhaseApproval(repoRoot, phase);
311
339
  if (!current.draft) {
312
340
  throw new Error(formatError(`ai revise --phase ${phase} requires an existing draft; current status is ${current.status}. Run \`npx create-quiver ai plan --phase ${phase} --input <file>\` first.`));
@@ -327,7 +355,7 @@ function buildRevisionInput({ phase, feedbackPath, feedbackText, repoRoot }) {
327
355
  feedbackText.trimEnd(),
328
356
  );
329
357
 
330
- return sections.join('\n\n');
358
+ return compactRevisionInput(sections.join('\n\n'), compactionOptions);
331
359
  }
332
360
 
333
361
  function buildManagedContextBlock(content) {
@@ -545,10 +573,262 @@ function formatApprovalDryRunResult({ phase, input, version }) {
545
573
  return `${lines.join('\n')}\n`;
546
574
  }
547
575
 
576
+ function stripCreateQuiverPrefix(message) {
577
+ return String(message || '').replace(/^create-quiver:\s*/, '');
578
+ }
579
+
580
+ function readCurrentDraftForApproval(repoRoot, phase, version) {
581
+ const approval = readPhaseApproval(repoRoot, phase);
582
+ const selectedDraft = findDraftVersion(approval.meta, version);
583
+ if (!selectedDraft) {
584
+ throw new Error(formatError(`missing ${phase} draft version ${version}`));
585
+ }
586
+ const latestVersion = latestDraftVersion(approval.meta);
587
+ if (latestVersion && Number(selectedDraft.version) !== latestVersion) {
588
+ throw new Error(formatError(`${phase} draft version ${version} is not current; latest draft version is ${latestVersion}. Approve the latest version or revise again.`));
589
+ }
590
+ const draftPath = path.resolve(repoRoot, selectedDraft.path);
591
+ if (!fs.existsSync(draftPath)) {
592
+ throw new Error(formatError(`missing ${phase} draft artifact: ${selectedDraft.path}`));
593
+ }
594
+ return {
595
+ approval,
596
+ contents: fs.readFileSync(draftPath, 'utf8'),
597
+ draft: selectedDraft,
598
+ path: selectedDraft.path,
599
+ };
600
+ }
601
+
602
+ function assertTechnicalPlanDraftHasSpecContract(repoRoot, version) {
603
+ const draft = readCurrentDraftForApproval(repoRoot, 'technical-plan', version);
604
+ try {
605
+ validateTechnicalPlanSpecContract(repoRoot, {
606
+ inputPath: draft.path,
607
+ inputText: draft.contents,
608
+ });
609
+ } catch (error) {
610
+ throw new Error(formatError([
611
+ `technical-plan draft v${version} cannot be approved because it cannot create specs.`,
612
+ stripCreateQuiverPrefix(error.message || error),
613
+ 'Required contract: include a structured JSON block with `spec.slices[]` before approval.',
614
+ 'Next safe command: npx create-quiver ai revise --phase technical-plan --input <feedback.md> --dry-run',
615
+ ].join('\n')));
616
+ }
617
+ return draft;
618
+ }
619
+
620
+ function resolveApprovedTechnicalPlanForRepair(repoRoot, explicitInput = '') {
621
+ const approval = readPhaseApproval(repoRoot, 'technical-plan');
622
+ if (!approval.approved?.path) {
623
+ throw new Error(formatError('ai repair-plan requires an approved technical-plan artifact. Run `npx create-quiver ai approvals` to inspect planner state.'));
624
+ }
625
+
626
+ const approvedPath = approval.approved.path;
627
+ if (explicitInput) {
628
+ const explicit = path.resolve(repoRoot, explicitInput);
629
+ const approved = path.resolve(repoRoot, approvedPath);
630
+ if (explicit !== approved) {
631
+ throw new Error(formatError(`ai repair-plan input must match the approved technical-plan artifact: ${approvedPath}`));
632
+ }
633
+ }
634
+
635
+ const contents = readTextFile(approvedPath, repoRoot);
636
+ try {
637
+ validateTechnicalPlanSpecContract(repoRoot, {
638
+ inputPath: approvedPath,
639
+ inputText: contents,
640
+ });
641
+ } catch (error) {
642
+ return {
643
+ approval,
644
+ contents,
645
+ path: approvedPath,
646
+ validationError: stripCreateQuiverPrefix(error.message || error),
647
+ };
648
+ }
649
+
650
+ throw new Error(formatError('approved technical-plan already includes a valid structured `spec.slices[]` contract. No repair draft is needed.'));
651
+ }
652
+
653
+ function buildRepairPlanContext({ context, inputText, inputPath, repoRoot, role, validationError }) {
654
+ const pack = buildContextPackMetadata({
655
+ role,
656
+ packName: context,
657
+ repoRoot,
658
+ });
659
+ const prompt = [
660
+ pack.prompt,
661
+ 'Phase: technical-plan',
662
+ 'Task: repair the approved technical plan into a new draft only. Do not approve it, create specs, modify product code, or expand scope.',
663
+ 'Preserve the approved intent, scope, risks, and decisions.',
664
+ 'Add the required Quiver structured JSON contract in a fenced json block.',
665
+ 'The JSON must include `{ "spec": { "slug": "...", "title": "...", "objective": "...", "slices": [...] } }`.',
666
+ 'Each item in `spec.slices[]` must include at least `slice_id`, `title`, `objective`, and `files`.',
667
+ `Validation failure to repair: ${validationError}`,
668
+ `Approved technical-plan artifact: ${inputPath}`,
669
+ 'Approved technical-plan contents:',
670
+ inputText.trimEnd(),
671
+ ].join('\n\n');
672
+
673
+ return {
674
+ pack,
675
+ prompt,
676
+ };
677
+ }
678
+
679
+ function formatRepairPlanResult(result, repoRoot) {
680
+ const relativePath = path.relative(repoRoot, result.filePath).split(path.sep).join('/');
681
+ return [
682
+ 'AI technical-plan repair draft saved',
683
+ `Draft: ${relativePath}`,
684
+ `Version: v${result.version}`,
685
+ `Source approved artifact: ${result.sourcePath}`,
686
+ 'Original approved artifact: preserved',
687
+ 'Next safe commands:',
688
+ '- npx create-quiver ai review-plan --dry-run',
689
+ '- npx create-quiver ai review-plan',
690
+ `- npx create-quiver ai approve --phase technical-plan --version ${result.version}`,
691
+ ].join('\n').concat('\n');
692
+ }
693
+
694
+ function formatActiveSliceReconciliationReport(report, options = {}) {
695
+ const lines = [
696
+ 'AI active-slice reconciliation',
697
+ `Mode: ${options.dryRun ? 'dry-run' : 'read-only'}`,
698
+ `Decision: ${report.reconciliation.decision}`,
699
+ `Reason: ${report.reconciliation.reason}`,
700
+ '',
701
+ 'Supported sources:',
702
+ ];
703
+
704
+ for (const source of report.supported_sources) {
705
+ lines.push(`- ${source.path}: ${source.exists ? 'exists' : 'missing'}`);
706
+ }
707
+
708
+ lines.push('', 'Detected sources:');
709
+ if (report.sources.length === 0) {
710
+ lines.push('- none');
711
+ } else {
712
+ for (const source of report.sources) {
713
+ const ref = source.ref || '(unresolved)';
714
+ const status = source.status ? ` status=${source.status}` : '';
715
+ const issue = source.issue ? ` issue=${source.issue}` : '';
716
+ lines.push(`- ${source.source_id}: ${ref}${status}${issue}`);
717
+ }
718
+ }
719
+
720
+ lines.push('', 'Planned changes:');
721
+ if (report.reconciliation.planned_changes.length === 0) {
722
+ lines.push('- none');
723
+ } else {
724
+ for (const change of report.reconciliation.planned_changes) {
725
+ lines.push(`- ${change}`);
726
+ }
727
+ }
728
+
729
+ lines.push('', 'Risks:');
730
+ if (report.reconciliation.risks.length === 0) {
731
+ lines.push('- none');
732
+ } else {
733
+ for (const risk of report.reconciliation.risks) {
734
+ lines.push(`- ${risk}`);
735
+ }
736
+ }
737
+
738
+ lines.push('', options.dryRun ? 'No files were changed.' : 'This command is read-only; use start-slice or cleanup-slice for intentional writes.');
739
+ return `${lines.join('\n')}\n`;
740
+ }
741
+
742
+ function readRunApprovals(repoRoot, run) {
743
+ if (!run?.approvals_path) {
744
+ return [];
745
+ }
746
+ const filePath = path.resolve(repoRoot, run.approvals_path);
747
+ if (!fs.existsSync(filePath)) {
748
+ return [];
749
+ }
750
+ try {
751
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
752
+ return Array.isArray(parsed.approvals) ? parsed.approvals : [];
753
+ } catch {
754
+ return [];
755
+ }
756
+ }
757
+
758
+ function collectRunApprovalRows(repoRoot) {
759
+ const activeRun = resolveAiRun(repoRoot, '');
760
+ return listAiRuns(repoRoot)
761
+ .flatMap((run) => readRunApprovals(repoRoot, run).map((approval) => ({
762
+ run,
763
+ approval,
764
+ relation: activeRun && run.run_id === activeRun.run_id
765
+ ? 'active'
766
+ : run.status === 'closed'
767
+ ? 'historical'
768
+ : 'other-open',
769
+ })));
770
+ }
771
+
772
+ function approvalArtifactForRelation(report) {
773
+ return report?.approved?.path || report?.draft?.path || '';
774
+ }
775
+
776
+ function classifyGlobalApprovalRelation(report, runApprovalRows) {
777
+ const artifact = approvalArtifactForRelation(report);
778
+ if (!artifact || report.status === 'missing') {
779
+ return 'none';
780
+ }
781
+ const matches = runApprovalRows.filter((row) => row.approval?.artifact === artifact);
782
+ if (matches.some((row) => row.relation === 'active')) {
783
+ return 'active';
784
+ }
785
+ if (matches.length > 0) {
786
+ return 'historical';
787
+ }
788
+ return 'orphaned';
789
+ }
790
+
791
+ function formatRunScopedApprovals(repoRoot, runApprovalRows) {
792
+ const runs = listAiRuns(repoRoot);
793
+ const activeRun = resolveAiRun(repoRoot, '');
794
+ const lines = [
795
+ 'Run-scoped approvals',
796
+ `Active run: ${activeRun ? activeRun.run_id : '(none)'}`,
797
+ ];
798
+
799
+ if (runs.length === 0) {
800
+ lines.push('- no AI runs found');
801
+ return `${lines.join('\n')}\n`;
802
+ }
803
+
804
+ for (const run of runs.slice().reverse()) {
805
+ const relation = activeRun && run.run_id === activeRun.run_id
806
+ ? 'active'
807
+ : run.status === 'closed'
808
+ ? 'historical'
809
+ : 'other-open';
810
+ const approvals = runApprovalRows.filter((row) => row.run.run_id === run.run_id);
811
+ lines.push(`Run: ${run.run_id} (${relation}, phase: ${run.phase}, status: ${run.status})`);
812
+ if (approvals.length === 0) {
813
+ lines.push('- no run-scoped approvals');
814
+ continue;
815
+ }
816
+ for (const row of approvals) {
817
+ const version = row.approval.version ? ` v${row.approval.version}` : '';
818
+ lines.push(`- ${row.approval.phase || 'unknown'}${version}: ${row.approval.artifact || '(missing artifact)'}`);
819
+ }
820
+ }
821
+
822
+ return `${lines.join('\n')}\n`;
823
+ }
824
+
548
825
  function formatApprovalStatusReport(repoRoot) {
549
- const sections = ['AI approvals status'];
826
+ const runApprovalRows = collectRunApprovalRows(repoRoot);
827
+ const sections = ['AI approvals status', formatRunScopedApprovals(repoRoot, runApprovalRows).trimEnd(), 'Global planner approvals'];
550
828
  for (const phase of PLANNER_APPROVAL_PHASES) {
551
- sections.push(summarizePlannerApproval(repoRoot, phase).trimEnd());
829
+ const summary = summarizePlannerApproval(repoRoot, phase).trimEnd();
830
+ const relation = classifyGlobalApprovalRelation(readPhaseApproval(repoRoot, phase), runApprovalRows);
831
+ sections.push(`${summary}\nRun relation: ${relation}`);
552
832
  }
553
833
  sections.push(summarizePlanReview(repoRoot).trimEnd());
554
834
  return `${sections.join('\n\n')}\n`;
@@ -637,12 +917,14 @@ async function runOnboard(repoRoot, options = {}) {
637
917
  throw annotateProviderError(error, 'onboard');
638
918
  }
639
919
 
640
- writeProviderOutput(result);
641
-
642
920
  if (!result.ok) {
921
+ writeProviderOutput(result);
643
922
  throw annotateProviderError(result.error || new Error('provider run failed'), 'onboard');
644
923
  }
645
924
 
925
+ const clean = extractCleanProviderOutput(result, { prompt, projectRoot: repoRoot });
926
+ writeCleanProviderOutput(clean);
927
+
646
928
  return {
647
929
  task: 'onboard',
648
930
  provider,
@@ -723,6 +1005,7 @@ async function runPlan(repoRoot, options = {}) {
723
1005
  const context = options.context || DEFAULT_PLAN_CONTEXT;
724
1006
  const timeoutMs = normalizeTimeout(options.timeout);
725
1007
  let inputPath = options.input || '';
1008
+ let inputCompaction = null;
726
1009
 
727
1010
  if (phase === 'spec') {
728
1011
  const resolved = resolveReviewedTechnicalPlanInput(repoRoot, inputPath || undefined);
@@ -781,12 +1064,15 @@ async function runPlan(repoRoot, options = {}) {
781
1064
  throw new Error(formatError(`missing feedback input file for ai revise phase '${phase}'`));
782
1065
  }
783
1066
  const feedbackText = readTextFile(inputPath, repoRoot);
784
- inputText = buildRevisionInput({
1067
+ const revisionInput = buildRevisionInput({
785
1068
  phase,
786
1069
  feedbackPath: inputPath,
787
1070
  feedbackText,
788
1071
  repoRoot,
1072
+ compactionOptions: options,
789
1073
  });
1074
+ inputText = revisionInput.text;
1075
+ inputCompaction = revisionInput.compaction;
790
1076
  } else if (phase === 'technical-plan') {
791
1077
  const resolved = resolveApprovedPlannerInput(repoRoot, phase, inputPath || undefined);
792
1078
  inputPath = resolved.inputPath;
@@ -809,6 +1095,7 @@ async function runPlan(repoRoot, options = {}) {
809
1095
  revise: options.revise === true,
810
1096
  });
811
1097
  const prompt = contextInfo.prompt;
1098
+ assertProviderPromptWithinLimit(prompt, options);
812
1099
  let invocation;
813
1100
 
814
1101
  try {
@@ -865,18 +1152,33 @@ async function runPlan(repoRoot, options = {}) {
865
1152
  throw annotateProviderError(error, 'plan', phase);
866
1153
  }
867
1154
 
868
- writeProviderOutput(result);
869
-
870
1155
  if (!result.ok) {
1156
+ writeProviderOutput(result);
871
1157
  throw annotateProviderError(result.error || new Error('provider run failed'), 'plan', phase);
872
1158
  }
873
1159
 
874
- const draft = savePlannerDraft(repoRoot, phase, inputPath, getRedactedProviderText(result));
875
1160
  const lifecycleRun = ensureAiRun(repoRoot, {
876
1161
  command: `ai plan --phase ${phase}`,
877
1162
  input: inputPath,
878
1163
  runId: options.runId,
879
1164
  });
1165
+ const clean = extractCleanProviderOutput(result, { prompt, projectRoot: repoRoot });
1166
+ writeCleanProviderOutput(clean);
1167
+ const rawArtifact = writeRawProviderArtifact(repoRoot, lifecycleRun.run_id, `ai-plan-${phase}`, result, {
1168
+ metadata: {
1169
+ phase,
1170
+ input_path: inputPath,
1171
+ prompt_bytes: invocation.promptLength,
1172
+ clean_output_source: clean.source,
1173
+ stripped_prompt_echo: clean.strippedPromptEcho,
1174
+ input_compaction: inputCompaction,
1175
+ },
1176
+ });
1177
+ const draft = savePlannerDraft(repoRoot, phase, inputPath, clean.cleanOutput, {
1178
+ rawArtifactPath: rawArtifact.path,
1179
+ outputSource: clean.source,
1180
+ inputCompaction,
1181
+ });
880
1182
  updateAiRunPhase(repoRoot, lifecycleRun.run_id, phase === 'acceptance' ? 'acceptance-draft' : 'technical-plan-draft', {
881
1183
  artifact: path.relative(repoRoot, draft.filePath).split(path.sep).join('/'),
882
1184
  command: `ai plan --phase ${phase}`,
@@ -911,6 +1213,7 @@ async function runReviewPlan(repoRoot, options = {}) {
911
1213
  inputText,
912
1214
  inputPath,
913
1215
  });
1216
+ assertProviderPromptWithinLimit(built.prompt, options);
914
1217
  let invocation;
915
1218
 
916
1219
  try {
@@ -987,20 +1290,41 @@ async function runReviewPlan(repoRoot, options = {}) {
987
1290
  throw annotateProviderError(error, 'review-plan');
988
1291
  }
989
1292
 
990
- writeProviderOutput(result);
991
-
992
1293
  if (!result.ok) {
1294
+ writeProviderOutput(result);
993
1295
  throw annotateProviderError(result.error || new Error('provider run failed'), 'review-plan');
994
1296
  }
995
1297
 
1298
+ const lifecycleRun = ensureAiRun(repoRoot, {
1299
+ command: 'ai review-plan',
1300
+ input: inputPath,
1301
+ runId: options.runId,
1302
+ phase: 'technical-plan-reviewed',
1303
+ });
1304
+ const clean = extractCleanProviderOutput(result, { prompt: built.prompt, projectRoot: repoRoot });
1305
+ const rawArtifact = writeRawProviderArtifact(repoRoot, lifecycleRun.run_id, 'ai-review-plan', result, {
1306
+ metadata: {
1307
+ phase: 'plan-review',
1308
+ input_path: inputPath,
1309
+ input_kind: resolved.kind,
1310
+ input_version: resolved.version || null,
1311
+ prompt_bytes: invocation.promptLength,
1312
+ clean_output_source: clean.source,
1313
+ stripped_prompt_echo: clean.strippedPromptEcho,
1314
+ },
1315
+ });
1316
+ writeCleanProviderOutput(clean);
996
1317
  const saved = savePlanReview(repoRoot, {
997
- contents: getRedactedProviderText(result),
1318
+ contents: clean.cleanOutput,
998
1319
  inputPath,
999
1320
  inputKind: resolved.kind,
1000
1321
  inputVersion: resolved.version,
1322
+ outputSource: clean.source,
1323
+ rawArtifactPath: rawArtifact.path,
1001
1324
  });
1002
1325
  const relativePath = path.relative(repoRoot, saved.filePath).split(path.sep).join('/');
1003
- process.stdout.write(`AI plan review saved\nArtifact: ${relativePath}\nPrompt source: ${PLAN_REVIEW_PROMPT_SOURCE}\n`);
1326
+ const summary = summarizePlanReview(repoRoot).trimEnd();
1327
+ process.stdout.write(`AI plan review saved\nArtifact: ${relativePath}\nPrompt source: ${PLAN_REVIEW_PROMPT_SOURCE}\n${summary}\n`);
1004
1328
 
1005
1329
  return {
1006
1330
  task: 'review-plan',
@@ -1016,6 +1340,146 @@ async function runReviewPlan(repoRoot, options = {}) {
1016
1340
  };
1017
1341
  }
1018
1342
 
1343
+ async function runRepairPlan(repoRoot, options = {}) {
1344
+ const role = normalizeRole(options.role || DEFAULT_PLAN_ROLE);
1345
+ const provider = resolveProviderForProfile(repoRoot, role, options.provider, options.providerExplicit, DEFAULT_PLAN_PROVIDER);
1346
+ const context = options.context || DEFAULT_PLAN_CONTEXT;
1347
+ const timeoutMs = normalizeTimeout(options.timeout);
1348
+ const source = resolveApprovedTechnicalPlanForRepair(repoRoot, options.input || '');
1349
+ const built = buildRepairPlanContext({
1350
+ context,
1351
+ inputText: source.contents,
1352
+ inputPath: source.path,
1353
+ repoRoot,
1354
+ role,
1355
+ validationError: source.validationError,
1356
+ });
1357
+ assertProviderPromptWithinLimit(built.prompt, options);
1358
+ let invocation;
1359
+
1360
+ try {
1361
+ invocation = buildProviderInvocation(provider, {
1362
+ prompt: built.prompt,
1363
+ cwd: repoRoot,
1364
+ timeoutMs,
1365
+ });
1366
+ } catch (error) {
1367
+ throw annotateProviderError(error, 'repair-plan');
1368
+ }
1369
+
1370
+ if (options.dryRun) {
1371
+ const report = {
1372
+ task: 'repair-plan',
1373
+ provider,
1374
+ role,
1375
+ contextPack: built.pack.packName,
1376
+ phase: 'technical-plan',
1377
+ invocation,
1378
+ };
1379
+ process.stdout.write(formatDryRunReport(report));
1380
+ process.stdout.write(`Source approved artifact: ${source.path}\n`);
1381
+ process.stdout.write(`Validation failure: ${source.validationError}\n`);
1382
+ return report;
1383
+ }
1384
+
1385
+ if (options.printPrompt) {
1386
+ const report = {
1387
+ task: 'repair-plan',
1388
+ provider,
1389
+ role,
1390
+ contextPack: built.pack.packName,
1391
+ phase: 'technical-plan',
1392
+ invocation,
1393
+ prompt: built.prompt,
1394
+ inputPath: source.path,
1395
+ inputKind: 'approved',
1396
+ inputVersion: source.approval.meta?.approved?.version || null,
1397
+ };
1398
+ process.stdout.write(formatPromptOnlyReport(report));
1399
+ return report;
1400
+ }
1401
+
1402
+ let providerResult;
1403
+ try {
1404
+ providerResult = await (options.runProviderFn || runProvider)(provider, {
1405
+ prompt: built.prompt,
1406
+ cwd: repoRoot,
1407
+ timeoutMs,
1408
+ dryRun: false,
1409
+ probe: options.probe,
1410
+ spawn: options.spawn,
1411
+ tempRoot: options.tempRoot,
1412
+ tempFileName: options.tempFileName,
1413
+ tempFilePrefix: options.tempFilePrefix,
1414
+ });
1415
+ } catch (error) {
1416
+ throw annotateProviderError(error, 'repair-plan');
1417
+ }
1418
+
1419
+ if (!providerResult.ok) {
1420
+ writeProviderOutput(providerResult);
1421
+ throw annotateProviderError(providerResult.error || new Error('provider run failed'), 'repair-plan');
1422
+ }
1423
+
1424
+ const lifecycleRun = ensureAiRun(repoRoot, {
1425
+ command: 'ai repair-plan',
1426
+ input: source.path,
1427
+ runId: options.runId,
1428
+ });
1429
+ const clean = extractCleanProviderOutput(providerResult, { prompt: built.prompt, projectRoot: repoRoot });
1430
+ const rawArtifact = writeRawProviderArtifact(repoRoot, lifecycleRun.run_id, 'ai-repair-plan', providerResult, {
1431
+ metadata: {
1432
+ phase: 'technical-plan-repair',
1433
+ input_path: source.path,
1434
+ prompt_bytes: invocation.promptLength,
1435
+ clean_output_source: clean.source,
1436
+ stripped_prompt_echo: clean.strippedPromptEcho,
1437
+ validation_failure: source.validationError,
1438
+ },
1439
+ });
1440
+
1441
+ try {
1442
+ validateTechnicalPlanSpecContract(repoRoot, {
1443
+ inputPath: source.path,
1444
+ inputText: clean.cleanOutput,
1445
+ });
1446
+ } catch (error) {
1447
+ throw new Error(formatError([
1448
+ 'ai repair-plan provider output is still missing the required structured `spec.slices[]` contract.',
1449
+ stripCreateQuiverPrefix(error.message || error),
1450
+ `Raw provider artifact: ${rawArtifact.path}`,
1451
+ 'No technical-plan draft was written.',
1452
+ ].join('\n')));
1453
+ }
1454
+
1455
+ writeCleanProviderOutput(clean);
1456
+ const draft = savePlannerDraft(repoRoot, 'technical-plan', source.path, clean.cleanOutput, {
1457
+ rawArtifactPath: rawArtifact.path,
1458
+ outputSource: clean.source,
1459
+ });
1460
+ updateAiRunPhase(repoRoot, lifecycleRun.run_id, 'technical-plan-draft', {
1461
+ artifact: path.relative(repoRoot, draft.filePath).split(path.sep).join('/'),
1462
+ command: 'ai repair-plan',
1463
+ });
1464
+ process.stdout.write(formatRepairPlanResult({
1465
+ ...draft,
1466
+ sourcePath: source.path,
1467
+ }, repoRoot));
1468
+
1469
+ return {
1470
+ task: 'repair-plan',
1471
+ provider,
1472
+ role,
1473
+ contextPack: built.pack.packName,
1474
+ phase: 'technical-plan',
1475
+ inputPath: source.path,
1476
+ filePath: path.relative(repoRoot, draft.filePath).split(path.sep).join('/'),
1477
+ version: draft.version || null,
1478
+ invocation,
1479
+ result: providerResult,
1480
+ };
1481
+ }
1482
+
1019
1483
  async function runRevise(repoRoot, options = {}) {
1020
1484
  const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
1021
1485
  if (phase === 'spec') {
@@ -1051,8 +1515,14 @@ async function runApprove(repoRoot, options = {}) {
1051
1515
  if (phase === 'technical-plan') {
1052
1516
  const review = readPlanReview(repoRoot);
1053
1517
  if (review.status !== 'unapproved' && review.status !== 'reviewed') {
1054
- throw new Error(formatError(`ai approve --phase technical-plan requires a production review for the current draft; current review status is ${review.status}. Run \`npx create-quiver ai review-plan\`.`));
1518
+ throw new Error(formatError(`ai approve --phase technical-plan requires a production review for the current draft; current review status is ${review.status}. Run \`npx create-quiver ai review-plan --dry-run\`, then \`npx create-quiver ai review-plan\`.`));
1055
1519
  }
1520
+ if (reviewBlocksApproval(review)) {
1521
+ const result = review.meta.review_result;
1522
+ const requiredFixes = Array.isArray(result.required_fixes) ? result.required_fixes.length : 0;
1523
+ throw new Error(formatError(`ai approve --phase technical-plan is blocked by plan review; approval recommendation is ${result.approval_recommendation}. Required fixes: ${requiredFixes}. Next command: ${result.next_command}`));
1524
+ }
1525
+ assertTechnicalPlanDraftHasSpecContract(repoRoot, options.version);
1056
1526
  }
1057
1527
 
1058
1528
  const inputText = '';
@@ -1211,14 +1681,53 @@ function runTraceReport(repoRoot, options = {}) {
1211
1681
  };
1212
1682
  }
1213
1683
 
1684
+ function runActiveSlice(repoRoot, options = {}) {
1685
+ const command = String(options.command || 'status').trim().toLowerCase();
1686
+ if (command !== 'status' && command !== 'reconcile') {
1687
+ throw new Error(formatError(`unsupported ai active-slice subcommand: ${command}. Supported tasks: status, reconcile`));
1688
+ }
1689
+ if (command === 'reconcile' && options.dryRun !== true) {
1690
+ throw new Error(formatError('ai active-slice reconcile is dry-run first. Run `npx create-quiver ai active-slice reconcile --dry-run`.'));
1691
+ }
1692
+
1693
+ const state = resolveProjectState(repoRoot, { allowGraphErrors: true });
1694
+ const report = collectActiveSliceState(repoRoot, { slices: state.graph.nodes });
1695
+ process.stdout.write(formatActiveSliceReconciliationReport(report, {
1696
+ dryRun: options.dryRun === true,
1697
+ }));
1698
+ return {
1699
+ task: 'active-slice',
1700
+ command,
1701
+ dryRun: options.dryRun === true,
1702
+ report,
1703
+ };
1704
+ }
1705
+
1214
1706
  function runLifecycleRun(repoRoot, options = {}) {
1215
1707
  const command = String(options.command || '').trim().toLowerCase();
1216
- if (command !== 'create') {
1217
- throw new Error(formatError(`unsupported ai run subcommand: ${command}. Supported tasks: create`));
1708
+ if (command !== 'create' && command !== 'close') {
1709
+ throw new Error(formatError(`unsupported ai run subcommand: ${command}. Supported tasks: create, close`));
1218
1710
  }
1219
- if (!options.input) {
1711
+ if (command === 'create' && !options.input) {
1220
1712
  throw new Error(formatError('ai run create requires --input <requirements.md>'));
1221
1713
  }
1714
+ if (command === 'close') {
1715
+ const current = resolveAiRun(repoRoot, options.runId || '');
1716
+ if (!current) {
1717
+ throw new Error(formatError('ai run close requires an active run or --run <id>'));
1718
+ }
1719
+ const run = updateAiRunPhase(repoRoot, current.run_id, 'closed', {
1720
+ command: 'ai run close',
1721
+ });
1722
+ const report = `AI run closed\n${formatAiRunStatus(repoRoot, run)}`;
1723
+ process.stdout.write(report);
1724
+ return {
1725
+ task: 'run',
1726
+ command,
1727
+ run,
1728
+ report,
1729
+ };
1730
+ }
1222
1731
  const run = createAiRun(repoRoot, {
1223
1732
  command: 'ai run create',
1224
1733
  input: options.input,
@@ -1261,6 +1770,21 @@ function formatAgentProfileList(profiles) {
1261
1770
  return `${lines.join('\n')}\n`;
1262
1771
  }
1263
1772
 
1773
+ function formatAgentProfileDryRun(repoRoot, result) {
1774
+ const relativePath = path.relative(repoRoot, result.filePath).split(path.sep).join('/');
1775
+ const verb = result.action === 'update' ? 'update' : 'create';
1776
+ return [
1777
+ 'AI agent profile dry-run',
1778
+ '- Writes: none',
1779
+ `- Would ${verb}: ${relativePath}`,
1780
+ '',
1781
+ formatAgentProfile(result.profile).trimEnd(),
1782
+ '',
1783
+ 'No secrets or provider credentials are stored in agent profiles.',
1784
+ '',
1785
+ ].join('\n');
1786
+ }
1787
+
1264
1788
  function runAgent(repoRoot, options = {}) {
1265
1789
  const command = String(options.command || '').trim().toLowerCase();
1266
1790
 
@@ -1271,6 +1795,22 @@ function runAgent(repoRoot, options = {}) {
1271
1795
  if (!options.provider) {
1272
1796
  throw new Error(formatError('ai agent set requires --provider. Supported providers: codex, claude, gemini.'));
1273
1797
  }
1798
+ if (options.dryRun) {
1799
+ const preview = buildAgentProfileState(repoRoot, options.role, {
1800
+ context: options.context,
1801
+ label: options.label,
1802
+ model: options.model,
1803
+ provider: options.provider,
1804
+ });
1805
+ process.stdout.write(formatAgentProfileDryRun(repoRoot, preview));
1806
+ return {
1807
+ task: 'agent',
1808
+ command,
1809
+ dryRun: true,
1810
+ profile: preview.profile,
1811
+ filePath: path.relative(repoRoot, preview.filePath).split(path.sep).join('/'),
1812
+ };
1813
+ }
1274
1814
  const result = setAgentProfile(repoRoot, options.role, {
1275
1815
  context: options.context,
1276
1816
  label: options.label,
@@ -1441,6 +1981,7 @@ module.exports = {
1441
1981
  normalizeTimeout,
1442
1982
  readTextFile,
1443
1983
  runAgent,
1984
+ runActiveSlice,
1444
1985
  runDoctor,
1445
1986
  runExecutePlan,
1446
1987
  runExecuteSlice,
@@ -1453,6 +1994,7 @@ module.exports = {
1453
1994
  runApprove,
1454
1995
  runApprovalStatus,
1455
1996
  runPrepareContext,
1997
+ runRepairPlan,
1456
1998
  runReviewPlan,
1457
1999
  runRevise,
1458
2000
  runPr,