create-quiver 0.15.1 → 0.15.3

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 (136) hide show
  1. package/.github/workflows/ci.yml +3 -0
  2. package/README.md +3 -0
  3. package/README_FOR_AI.md +11 -7
  4. package/docs/CLI_UX_GUIDE.md +16 -2
  5. package/docs/COMMANDS.md.template +16 -4
  6. package/docs/INDEX.md +87 -0
  7. package/docs/WORKFLOW.md.template +4 -4
  8. package/docs/reference/commands.md +23 -5
  9. package/package.json +3 -1
  10. package/specs/quiver-v33-approval-ux-and-planner-progress/EVIDENCE_REPORT.md +93 -0
  11. package/specs/quiver-v33-approval-ux-and-planner-progress/EXECUTION_PLAN.md +83 -0
  12. package/specs/quiver-v33-approval-ux-and-planner-progress/SPEC.md +158 -0
  13. package/specs/quiver-v33-approval-ux-and-planner-progress/STATUS.md +31 -0
  14. package/specs/quiver-v33-approval-ux-and-planner-progress/pr.md +109 -0
  15. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-00-approval-ux-foundation/CLOSURE_BRIEF.md +30 -0
  16. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-00-approval-ux-foundation/EXECUTION_BRIEF.md +56 -0
  17. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-00-approval-ux-foundation/slice.json +65 -0
  18. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-01-approval-candidates-model/CLOSURE_BRIEF.md +19 -0
  19. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-01-approval-candidates-model/EXECUTION_BRIEF.md +51 -0
  20. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-01-approval-candidates-model/slice.json +74 -0
  21. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-02-approve-interactive-selection/CLOSURE_BRIEF.md +20 -0
  22. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-02-approve-interactive-selection/EXECUTION_BRIEF.md +53 -0
  23. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-02-approve-interactive-selection/slice.json +79 -0
  24. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-03-technical-plan-review-decision-data/CLOSURE_BRIEF.md +19 -0
  25. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-03-technical-plan-review-decision-data/EXECUTION_BRIEF.md +50 -0
  26. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-03-technical-plan-review-decision-data/slice.json +75 -0
  27. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-04-revise-input-guardrails/CLOSURE_BRIEF.md +19 -0
  28. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-04-revise-input-guardrails/EXECUTION_BRIEF.md +51 -0
  29. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-04-revise-input-guardrails/slice.json +73 -0
  30. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-05-provider-progress-alignment/CLOSURE_BRIEF.md +20 -0
  31. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-05-provider-progress-alignment/EXECUTION_BRIEF.md +51 -0
  32. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-05-provider-progress-alignment/slice.json +91 -0
  33. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-06-workflow-surface-integration/CLOSURE_BRIEF.md +20 -0
  34. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-06-workflow-surface-integration/EXECUTION_BRIEF.md +53 -0
  35. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-06-workflow-surface-integration/slice.json +87 -0
  36. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-07-docs-tests-release-readiness/CLOSURE_BRIEF.md +21 -0
  37. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-07-docs-tests-release-readiness/EXECUTION_BRIEF.md +55 -0
  38. package/specs/quiver-v33-approval-ux-and-planner-progress/slices/slice-07-docs-tests-release-readiness/slice.json +97 -0
  39. package/specs/quiver-v34-cli-dashboard-status/EVIDENCE_REPORT.md +119 -0
  40. package/specs/quiver-v34-cli-dashboard-status/EXECUTION_PLAN.md +77 -0
  41. package/specs/quiver-v34-cli-dashboard-status/SPEC.md +152 -0
  42. package/specs/quiver-v34-cli-dashboard-status/STATUS.md +29 -0
  43. package/specs/quiver-v34-cli-dashboard-status/pr.md +99 -0
  44. package/specs/quiver-v34-cli-dashboard-status/slices/slice-00-dashboard-foundation/CLOSURE_BRIEF.md +19 -0
  45. package/specs/quiver-v34-cli-dashboard-status/slices/slice-00-dashboard-foundation/EXECUTION_BRIEF.md +49 -0
  46. package/specs/quiver-v34-cli-dashboard-status/slices/slice-00-dashboard-foundation/slice.json +68 -0
  47. package/specs/quiver-v34-cli-dashboard-status/slices/slice-01-dashboard-report-contract/CLOSURE_BRIEF.md +19 -0
  48. package/specs/quiver-v34-cli-dashboard-status/slices/slice-01-dashboard-report-contract/EXECUTION_BRIEF.md +53 -0
  49. package/specs/quiver-v34-cli-dashboard-status/slices/slice-01-dashboard-report-contract/slice.json +78 -0
  50. package/specs/quiver-v34-cli-dashboard-status/slices/slice-02-dashboard-command-rendering/CLOSURE_BRIEF.md +19 -0
  51. package/specs/quiver-v34-cli-dashboard-status/slices/slice-02-dashboard-command-rendering/EXECUTION_BRIEF.md +53 -0
  52. package/specs/quiver-v34-cli-dashboard-status/slices/slice-02-dashboard-command-rendering/slice.json +82 -0
  53. package/specs/quiver-v34-cli-dashboard-status/slices/slice-03-dashboard-edge-cases-and-guardrails/CLOSURE_BRIEF.md +19 -0
  54. package/specs/quiver-v34-cli-dashboard-status/slices/slice-03-dashboard-edge-cases-and-guardrails/EXECUTION_BRIEF.md +54 -0
  55. package/specs/quiver-v34-cli-dashboard-status/slices/slice-03-dashboard-edge-cases-and-guardrails/slice.json +77 -0
  56. package/specs/quiver-v34-cli-dashboard-status/slices/slice-04-docs-templates-and-scripts/CLOSURE_BRIEF.md +19 -0
  57. package/specs/quiver-v34-cli-dashboard-status/slices/slice-04-docs-templates-and-scripts/EXECUTION_BRIEF.md +54 -0
  58. package/specs/quiver-v34-cli-dashboard-status/slices/slice-04-docs-templates-and-scripts/slice.json +88 -0
  59. package/specs/quiver-v34-cli-dashboard-status/slices/slice-05-tests-smokes-release-readiness/CLOSURE_BRIEF.md +26 -0
  60. package/specs/quiver-v34-cli-dashboard-status/slices/slice-05-tests-smokes-release-readiness/EXECUTION_BRIEF.md +57 -0
  61. package/specs/quiver-v34-cli-dashboard-status/slices/slice-05-tests-smokes-release-readiness/slice.json +85 -0
  62. package/specs/quiver-v35-compact-dashboard-version-ux/EVIDENCE_REPORT.md +43 -0
  63. package/specs/quiver-v35-compact-dashboard-version-ux/EXECUTION_PLAN.md +85 -0
  64. package/specs/quiver-v35-compact-dashboard-version-ux/SPEC.md +147 -0
  65. package/specs/quiver-v35-compact-dashboard-version-ux/STATUS.md +33 -0
  66. package/specs/quiver-v35-compact-dashboard-version-ux/pr.md +106 -0
  67. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-00-foundation-and-doc-router/CLOSURE_BRIEF.md +19 -0
  68. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-00-foundation-and-doc-router/EXECUTION_BRIEF.md +38 -0
  69. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-00-foundation-and-doc-router/slice.json +64 -0
  70. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-01-dashboard-cli-contract/CLOSURE_BRIEF.md +19 -0
  71. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-01-dashboard-cli-contract/EXECUTION_BRIEF.md +49 -0
  72. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-01-dashboard-cli-contract/slice.json +80 -0
  73. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-02-dashboard-compact-renderer/CLOSURE_BRIEF.md +19 -0
  74. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-02-dashboard-compact-renderer/EXECUTION_BRIEF.md +48 -0
  75. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-02-dashboard-compact-renderer/slice.json +73 -0
  76. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-03-dashboard-details-sections/CLOSURE_BRIEF.md +19 -0
  77. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-03-dashboard-details-sections/EXECUTION_BRIEF.md +48 -0
  78. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-03-dashboard-details-sections/slice.json +72 -0
  79. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-04-version-command/CLOSURE_BRIEF.md +19 -0
  80. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-04-version-command/EXECUTION_BRIEF.md +49 -0
  81. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-04-version-command/slice.json +82 -0
  82. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-05-docs-help-generated-guidance/CLOSURE_BRIEF.md +19 -0
  83. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-05-docs-help-generated-guidance/EXECUTION_BRIEF.md +50 -0
  84. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-05-docs-help-generated-guidance/slice.json +90 -0
  85. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-06-tests-smokes-release-readiness/CLOSURE_BRIEF.md +21 -0
  86. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-06-tests-smokes-release-readiness/EXECUTION_BRIEF.md +51 -0
  87. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-06-tests-smokes-release-readiness/slice.json +85 -0
  88. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-07-package-and-cross-platform-smoke/CLOSURE_BRIEF.md +20 -0
  89. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-07-package-and-cross-platform-smoke/EXECUTION_BRIEF.md +49 -0
  90. package/specs/quiver-v35-compact-dashboard-version-ux/slices/slice-07-package-and-cross-platform-smoke/slice.json +79 -0
  91. package/specs/quiver-v36-ai-run-watch-portable/EVIDENCE_REPORT.md +33 -0
  92. package/specs/quiver-v36-ai-run-watch-portable/EXECUTION_PLAN.md +39 -0
  93. package/specs/quiver-v36-ai-run-watch-portable/SPEC.md +166 -0
  94. package/specs/quiver-v36-ai-run-watch-portable/STATUS.md +31 -0
  95. package/specs/quiver-v36-ai-run-watch-portable/pr.md +78 -0
  96. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-00-foundation-and-handoffs/CLOSURE_BRIEF.md +19 -0
  97. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-00-foundation-and-handoffs/EXECUTION_BRIEF.md +43 -0
  98. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-00-foundation-and-handoffs/slice.json +61 -0
  99. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-01-run-schema-path-safety/CLOSURE_BRIEF.md +18 -0
  100. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-01-run-schema-path-safety/EXECUTION_BRIEF.md +43 -0
  101. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-01-run-schema-path-safety/slice.json +65 -0
  102. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-02-event-writer-redacted-logs/CLOSURE_BRIEF.md +18 -0
  103. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-02-event-writer-redacted-logs/EXECUTION_BRIEF.md +43 -0
  104. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-02-event-writer-redacted-logs/slice.json +66 -0
  105. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-03-provider-streaming-integration/CLOSURE_BRIEF.md +18 -0
  106. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-03-provider-streaming-integration/EXECUTION_BRIEF.md +41 -0
  107. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-03-provider-streaming-integration/slice.json +71 -0
  108. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-04-ai-run-watch-command/CLOSURE_BRIEF.md +18 -0
  109. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-04-ai-run-watch-command/EXECUTION_BRIEF.md +44 -0
  110. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-04-ai-run-watch-command/slice.json +73 -0
  111. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-05-main-progress-user-guidance/CLOSURE_BRIEF.md +18 -0
  112. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-05-main-progress-user-guidance/EXECUTION_BRIEF.md +44 -0
  113. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-05-main-progress-user-guidance/slice.json +71 -0
  114. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-06-docs-generated-guidance/CLOSURE_BRIEF.md +19 -0
  115. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-06-docs-generated-guidance/EXECUTION_BRIEF.md +45 -0
  116. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-06-docs-generated-guidance/slice.json +77 -0
  117. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-07-tests-cross-platform-readiness/CLOSURE_BRIEF.md +22 -0
  118. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-07-tests-cross-platform-readiness/EXECUTION_BRIEF.md +46 -0
  119. package/specs/quiver-v36-ai-run-watch-portable/slices/slice-07-tests-cross-platform-readiness/slice.json +87 -0
  120. package/src/create-quiver/commands/ai.js +112 -12
  121. package/src/create-quiver/commands/dashboard.js +31 -0
  122. package/src/create-quiver/commands/flow.js +74 -12
  123. package/src/create-quiver/commands/spec.js +8 -1
  124. package/src/create-quiver/index.js +107 -4
  125. package/src/create-quiver/lib/ai/approval-candidates.js +86 -0
  126. package/src/create-quiver/lib/ai/plan-review.js +77 -1
  127. package/src/create-quiver/lib/ai/run-state.js +21 -4
  128. package/src/create-quiver/lib/approvals.js +103 -0
  129. package/src/create-quiver/lib/cli/ux-flags.js +6 -0
  130. package/src/create-quiver/lib/dashboard.js +896 -0
  131. package/src/create-quiver/lib/git.js +13 -4
  132. package/src/create-quiver/lib/init-docs.js +6 -0
  133. package/src/create-quiver/lib/init-layout.js +2 -0
  134. package/src/create-quiver/lib/paths.js +45 -3
  135. package/src/create-quiver/lib/project-state-resolver.js +22 -2
  136. package/src/create-quiver/lib/version.js +157 -0
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
 
4
4
  const { readPhaseApproval } = require('../lib/approvals');
5
+ const { approvalCandidateCommand, buildApprovalCandidateReport, formatCandidateSummary } = require('../lib/ai/approval-candidates');
5
6
  const { readPlanReview } = require('../lib/ai/plan-review');
6
7
  const { listAgentProfiles } = require('../lib/agent-profiles');
7
8
  const { readProjectScanStatus } = require('../lib/project-scan');
@@ -89,6 +90,25 @@ function safeReadPlanReview(projectRoot) {
89
90
  }
90
91
  }
91
92
 
93
+ function safeBuildApprovalCandidateReport(projectRoot, phase) {
94
+ try {
95
+ return buildApprovalCandidateReport(projectRoot, phase);
96
+ } catch (error) {
97
+ return {
98
+ phase,
99
+ approval_status: 'invalid',
100
+ latest_version: null,
101
+ current: null,
102
+ recommended: null,
103
+ candidates: [],
104
+ history: [],
105
+ approved: null,
106
+ next_command: 'npx create-quiver ai approvals',
107
+ error: error.message,
108
+ };
109
+ }
110
+ }
111
+
92
112
  function summarizeDocs(projectRoot) {
93
113
  const docs = {
94
114
  hasProjectMap: exists(projectRoot, 'docs/PROJECT_MAP.md'),
@@ -250,11 +270,23 @@ function detectFlowState(projectRoot) {
250
270
  acceptance: safeReadApproval(projectRoot, 'acceptance'),
251
271
  technicalPlan: safeReadApproval(projectRoot, 'technical-plan'),
252
272
  };
273
+ const approvalCandidates = {
274
+ acceptance: safeBuildApprovalCandidateReport(projectRoot, 'acceptance'),
275
+ technicalPlan: safeBuildApprovalCandidateReport(projectRoot, 'technical-plan'),
276
+ };
253
277
  const planReview = safeReadPlanReview(projectRoot);
254
278
  const agents = summarizeAgentProfiles(projectRoot);
255
279
  const packageManager = detectPackageManager(projectRoot);
256
280
  const specSlugs = listSpecSlugs(projectRoot);
257
281
  const facts = buildFacts({ initialized, docs, approvals, planReview, agents, packageManager, specSlugs, state });
282
+ const acceptanceApprovalCommand = approvalCandidateCommand(
283
+ approvalCandidates.acceptance,
284
+ 'npx create-quiver ai approve --phase acceptance --version <n>',
285
+ );
286
+ const technicalPlanApprovalCommand = approvalCandidateCommand(
287
+ approvalCandidates.technicalPlan,
288
+ 'npx create-quiver ai approve --phase technical-plan --version <n>',
289
+ );
258
290
 
259
291
  if (!initialized) {
260
292
  return baseReport({
@@ -334,12 +366,12 @@ function detectFlowState(projectRoot) {
334
366
  return baseReport({
335
367
  stage: 'criteria-draft',
336
368
  label: 'acceptance criteria need approval',
337
- blockers: ['Acceptance criteria draft exists but is not approved.'],
338
- nextCommand: 'npx create-quiver ai approve --phase acceptance --version <n>',
369
+ blockers: ['Acceptance criteria draft exists but is not approved.', formatCandidateSummary(approvalCandidates.acceptance.current)].filter(Boolean),
370
+ nextCommand: acceptanceApprovalCommand,
339
371
  suggestedCommands: [
340
372
  'npx create-quiver ai approvals',
341
373
  'npx create-quiver ai revise --phase acceptance --input feedback.md --dry-run',
342
- 'npx create-quiver ai approve --phase acceptance --version <n>',
374
+ acceptanceApprovalCommand,
343
375
  ],
344
376
  facts,
345
377
  });
@@ -349,12 +381,12 @@ function detectFlowState(projectRoot) {
349
381
  return baseReport({
350
382
  stage: 'criteria-stale',
351
383
  label: 'acceptance criteria approval is stale',
352
- blockers: ['Acceptance criteria changed after approval.'],
353
- nextCommand: 'npx create-quiver ai approve --phase acceptance --version <n>',
384
+ blockers: ['Acceptance criteria changed after approval.', formatCandidateSummary(approvalCandidates.acceptance.current)].filter(Boolean),
385
+ nextCommand: acceptanceApprovalCommand,
354
386
  suggestedCommands: [
355
387
  'npx create-quiver ai approvals',
356
388
  'npx create-quiver ai revise --phase acceptance --input feedback.md --dry-run',
357
- 'npx create-quiver ai approve --phase acceptance --version <n>',
389
+ acceptanceApprovalCommand,
358
390
  ],
359
391
  facts,
360
392
  });
@@ -390,14 +422,29 @@ function detectFlowState(projectRoot) {
390
422
  });
391
423
  }
392
424
 
425
+ if (!approvalCandidates.technicalPlan.current?.approvable) {
426
+ return baseReport({
427
+ stage: 'technical-plan-review-blocked',
428
+ label: 'technical plan review requires revision',
429
+ blockers: ['Technical plan review blocks approval.', formatCandidateSummary(approvalCandidates.technicalPlan.current)].filter(Boolean),
430
+ nextCommand: approvalCandidates.technicalPlan.current?.next_command || 'npx create-quiver ai revise --phase technical-plan --input <feedback.md> --dry-run',
431
+ suggestedCommands: [
432
+ 'npx create-quiver ai approvals',
433
+ approvalCandidates.technicalPlan.current?.next_command || 'npx create-quiver ai revise --phase technical-plan --input <feedback.md> --dry-run',
434
+ 'npx create-quiver ai review-plan --dry-run',
435
+ ],
436
+ facts,
437
+ });
438
+ }
439
+
393
440
  return baseReport({
394
441
  stage: 'technical-plan-draft',
395
442
  label: 'technical plan needs approval',
396
- blockers: ['Technical plan draft was reviewed but is not approved.'],
397
- nextCommand: 'npx create-quiver ai approve --phase technical-plan --version <n>',
443
+ blockers: ['Technical plan draft was reviewed but is not approved.', formatCandidateSummary(approvalCandidates.technicalPlan.current)].filter(Boolean),
444
+ nextCommand: technicalPlanApprovalCommand,
398
445
  suggestedCommands: [
399
446
  'npx create-quiver ai approvals',
400
- 'npx create-quiver ai approve --phase technical-plan --version <n>',
447
+ technicalPlanApprovalCommand,
401
448
  ],
402
449
  facts,
403
450
  });
@@ -419,14 +466,29 @@ function detectFlowState(projectRoot) {
419
466
  });
420
467
  }
421
468
 
469
+ if (!approvalCandidates.technicalPlan.current?.approvable) {
470
+ return baseReport({
471
+ stage: 'technical-plan-review-blocked',
472
+ label: 'technical plan review requires revision',
473
+ blockers: ['Technical plan changed after approval and latest review blocks approval.', formatCandidateSummary(approvalCandidates.technicalPlan.current)].filter(Boolean),
474
+ nextCommand: approvalCandidates.technicalPlan.current?.next_command || 'npx create-quiver ai revise --phase technical-plan --input <feedback.md> --dry-run',
475
+ suggestedCommands: [
476
+ 'npx create-quiver ai approvals',
477
+ approvalCandidates.technicalPlan.current?.next_command || 'npx create-quiver ai revise --phase technical-plan --input <feedback.md> --dry-run',
478
+ 'npx create-quiver ai review-plan --dry-run',
479
+ ],
480
+ facts,
481
+ });
482
+ }
483
+
422
484
  return baseReport({
423
485
  stage: 'technical-plan-stale',
424
486
  label: 'technical plan approval is stale',
425
- blockers: ['Technical plan changed after approval and the latest draft was reviewed.'],
426
- nextCommand: 'npx create-quiver ai approve --phase technical-plan --version <n>',
487
+ blockers: ['Technical plan changed after approval and the latest draft was reviewed.', formatCandidateSummary(approvalCandidates.technicalPlan.current)].filter(Boolean),
488
+ nextCommand: technicalPlanApprovalCommand,
427
489
  suggestedCommands: [
428
490
  'npx create-quiver ai approvals',
429
- 'npx create-quiver ai approve --phase technical-plan --version <n>',
491
+ technicalPlanApprovalCommand,
430
492
  ],
431
493
  facts,
432
494
  });
@@ -7,6 +7,7 @@ const { selectOption } = require('../lib/cli/selectors');
7
7
  const { createUx } = require('../lib/cli/ux');
8
8
  const { parseJsonWithComments } = require('../lib/json');
9
9
  const { assertPathInsideRoot, validateProjectRelativePaths } = require('../lib/paths');
10
+ const { buildApprovalCandidateReport, formatReviewSummary } = require('../lib/ai/approval-candidates');
10
11
  const { resolveReviewedTechnicalPlanInput } = require('../lib/ai/plan-review');
11
12
  const {
12
13
  buildSpecGenerationManifest,
@@ -439,6 +440,11 @@ async function resolveInteractiveSpecCreateOptions(repoRoot, preview, options =
439
440
  stdinIsTTY: options.stdinIsTTY,
440
441
  stdoutIsTTY: options.stdoutIsTTY,
441
442
  };
443
+ const technicalPlanCandidates = buildApprovalCandidateReport(repoRoot, 'technical-plan');
444
+ const approvedVersion = technicalPlanCandidates.approved?.version
445
+ ? `v${technicalPlanCandidates.approved.version}`
446
+ : 'approved';
447
+ const reviewSummary = formatReviewSummary(technicalPlanCandidates.review);
442
448
  const selectedMethodology = await selectOption('¿Qué metodología aplica esta spec?', SPEC_METHODOLOGY_OPTIONS, {
443
449
  ...selectorOptions,
444
450
  defaultValue: 'wdd-sdd',
@@ -450,7 +456,7 @@ async function resolveInteractiveSpecCreateOptions(repoRoot, preview, options =
450
456
  {
451
457
  label: preview.inputPath,
452
458
  value: preview.inputPath,
453
- hint: 'Plan técnico revisado y aprobado',
459
+ hint: ['Plan técnico revisado y aprobado', approvedVersion, reviewSummary].filter(Boolean).join(', '),
454
460
  default: true,
455
461
  },
456
462
  ], {
@@ -484,6 +490,7 @@ async function resolveInteractiveSpecCreateOptions(repoRoot, preview, options =
484
490
  { label: 'Spec', value: preview.manifest.slug },
485
491
  { label: 'Metodologia', value: selectedMethodology.label },
486
492
  { label: 'Input', value: selectedInput.value },
493
+ { label: 'Plan tecnico', value: ['aprobado', approvedVersion, reviewSummary].filter(Boolean).join(', ') },
487
494
  { label: 'Target', value: preview.relativeSpecDir },
488
495
  { label: 'Archivos planificados', value: preview.preview.files.length },
489
496
  { label: 'Revision', value: selectedReview.value === 'review' ? 'abrir preview' : 'confirmacion directa' },
@@ -35,6 +35,7 @@ const {
35
35
  runSpecsList: runAiSpecsList,
36
36
  runTraceReport: runAiTraceReport,
37
37
  } = require('./commands/ai');
38
+ const { runDashboard } = require('./commands/dashboard');
38
39
  const { runDemo } = require('./commands/demo');
39
40
  const { runPrepare } = require('./commands/prepare');
40
41
  const { runEvidence } = require('./commands/evidence');
@@ -43,6 +44,7 @@ const { runGraph } = require('./commands/graph');
43
44
  const { runNext } = require('./commands/next');
44
45
  const { runPlan } = require('./commands/plan');
45
46
  const { runCreateSpec, runValidateSpec } = require('./commands/spec');
47
+ const { collectVersionReport, formatHumanVersionReport } = require('./lib/version');
46
48
  const { buildInitLayout, formatInitLayoutPlan } = require('./lib/init-layout');
47
49
  const {
48
50
  formatInstallSelfCommand,
@@ -82,7 +84,9 @@ function formatError(message) {
82
84
 
83
85
  const SUPPORTED_COMMAND_MODES = new Set([
84
86
  'init',
87
+ 'version',
85
88
  'flow',
89
+ 'dashboard',
86
90
  'plan',
87
91
  'graph',
88
92
  'next',
@@ -152,6 +156,8 @@ const COMMAND_HELP_GROUPS = [
152
156
  ['analyze', 'Scan the project and write docs/PROJECT_MAP.md plus .quiver scan data.'],
153
157
  ['doctor', 'Validate the Quiver layout, generated docs, environment, and next safe steps.'],
154
158
  ['flow', 'Show the read-only guided workflow stage, blockers, and next safe command.'],
159
+ ['dashboard', 'Show compact read-only project, spec, slice, run, approval, and agent status.'],
160
+ ['version', 'Show a Quiver-branded version report; use --json for metadata.'],
155
161
  ['prepare', 'Run setup diagnostics for providers, GitHub, SSH, and project readiness.'],
156
162
  ['migrate', 'Upgrade an already initialized Quiver project to the current contract.'],
157
163
  ],
@@ -251,6 +257,8 @@ function printUsage() {
251
257
  npx create-quiver init [options]
252
258
  npx create-quiver analyze [options]
253
259
  npx create-quiver flow [options]
260
+ npx create-quiver dashboard [options]
261
+ npx create-quiver version [--json]
254
262
  npx create-quiver plan [options]
255
263
  npx create-quiver ai <task> [options]
256
264
  npx create-quiver ai run create --input <requirements.md>
@@ -294,12 +302,15 @@ ${formatCommandHelpGroups()}
294
302
  Options:
295
303
  -n, --name <project-name> Project name to generate
296
304
  -d, --dir <target-dir> Target directory to scaffold into or inspect
297
- --spec <slug> Restrict plan output to one spec
305
+ --spec <slug> Restrict plan, graph, next, or dashboard output to one spec
298
306
  --format <name> Graph or AI export output format (tree, mermaid, dot, json, markdown)
299
307
  --show-conflicts Show shared file paths in graph output
300
308
  --level <n> Restrict graph output to one level
301
309
  --json Emit machine-readable JSON
302
- --include-completed Include completed slices in plan, graph, or next history output
310
+ --include-completed Include completed slices in dashboard, plan, graph, or next history output
311
+ --details Show the full human dashboard report
312
+ --section <name> Show one human dashboard section
313
+ --limit <n> Limit dashboard human lists (1-100)
303
314
  --only-ready Show only slices with no pending dependencies
304
315
  --all-ready List every ready slice returned by next
305
316
  --auto-start Prompt for confirmation and run start-slice on next
@@ -345,6 +356,11 @@ Examples:
345
356
  npx create-quiver --name "My Project"
346
357
  npx create-quiver --name "My Project" --dir ./my-project
347
358
  cd ./my-project && npx create-quiver flow
359
+ cd ./my-project && npx create-quiver dashboard
360
+ cd ./my-project && npx create-quiver dashboard --section slices --limit 10
361
+ cd ./my-project && npx create-quiver dashboard --json
362
+ cd ./my-project && npx create-quiver version
363
+ cd ./my-project && npx create-quiver version --json
348
364
  cd ./my-project && npx create-quiver analyze
349
365
  cd ./my-project && npx create-quiver plan --json
350
366
  cd ./my-project && npx create-quiver ai onboard --dry-run
@@ -437,6 +453,10 @@ function parseArgs(argv) {
437
453
  interactive: false,
438
454
  review: false,
439
455
  includeCompleted: false,
456
+ dashboardDetails: false,
457
+ dashboardSection: '',
458
+ dashboardLimit: null,
459
+ dashboardOptionErrors: [],
440
460
  onlyReady: false,
441
461
  allReady: false,
442
462
  autoStart: false,
@@ -525,6 +545,14 @@ function parseArgs(argv) {
525
545
 
526
546
  const positional = [];
527
547
 
548
+ function missingInputValueMessage() {
549
+ const aiCommandHint = result.mode === 'ai' ? positional[0] : '';
550
+ if (aiCommandHint === 'revise') {
551
+ return `missing feedback input file for ai revise --phase ${result.aiPhase}. Use: npx create-quiver ai revise --phase ${result.aiPhase} --input <feedback.md> --dry-run`;
552
+ }
553
+ return 'missing value for --input';
554
+ }
555
+
528
556
  for (let index = 0; index < args.length; index += 1) {
529
557
  const arg = args[index];
530
558
 
@@ -692,6 +720,33 @@ function parseArgs(argv) {
692
720
  continue;
693
721
  }
694
722
 
723
+ if (arg === '--details') {
724
+ result.dashboardDetails = true;
725
+ continue;
726
+ }
727
+
728
+ if (arg === '--section') {
729
+ const value = args[index + 1];
730
+ if (!value || String(value).startsWith('--')) {
731
+ result.dashboardOptionErrors.push('missing value for --section');
732
+ continue;
733
+ }
734
+ result.dashboardSection = value;
735
+ index += 1;
736
+ continue;
737
+ }
738
+
739
+ if (arg === '--limit') {
740
+ const value = args[index + 1];
741
+ if (!value || String(value).startsWith('--')) {
742
+ result.dashboardOptionErrors.push('missing value for --limit');
743
+ continue;
744
+ }
745
+ result.dashboardLimit = value;
746
+ index += 1;
747
+ continue;
748
+ }
749
+
695
750
  if (arg === '--show-conflicts') {
696
751
  result.showConflicts = true;
697
752
  continue;
@@ -875,8 +930,8 @@ function parseArgs(argv) {
875
930
 
876
931
  if (arg === '--input') {
877
932
  const value = args[++index];
878
- if (!value) {
879
- throw new Error(formatError('missing value for --input'));
933
+ if (!value || String(value).startsWith('--')) {
934
+ throw new Error(formatError(missingInputValueMessage()));
880
935
  }
881
936
  result.aiInput = value;
882
937
  continue;
@@ -1041,6 +1096,14 @@ function parseArgs(argv) {
1041
1096
  if (positional.length > 0) {
1042
1097
  throw new Error(formatError('flow does not accept positional arguments'));
1043
1098
  }
1099
+ } else if (result.mode === 'version') {
1100
+ if (positional.length > 0) {
1101
+ throw new Error(formatError('version does not accept positional arguments'));
1102
+ }
1103
+ } else if (result.mode === 'dashboard') {
1104
+ if (positional.length > 0) {
1105
+ throw new Error(formatError('dashboard does not accept positional arguments; use --spec <slug>'));
1106
+ }
1044
1107
  } else if (result.mode === 'ai') {
1045
1108
  if (!result.aiCommand && positional.length > 0) {
1046
1109
  result.aiCommand = positional.shift();
@@ -1134,6 +1197,14 @@ function parseArgs(argv) {
1134
1197
  throw new Error(formatError('too many positional arguments'));
1135
1198
  }
1136
1199
 
1200
+ const requestedDashboardFlags = [];
1201
+ if (result.dashboardDetails) requestedDashboardFlags.push('--details');
1202
+ if (result.dashboardSection || result.dashboardOptionErrors.some((error) => error.includes('--section'))) requestedDashboardFlags.push('--section');
1203
+ if (result.dashboardLimit !== null || result.dashboardOptionErrors.some((error) => error.includes('--limit'))) requestedDashboardFlags.push('--limit');
1204
+ if (result.mode !== 'dashboard' && requestedDashboardFlags.length > 0) {
1205
+ throw new Error(formatError(`${requestedDashboardFlags.join(', ')} ${requestedDashboardFlags.length === 1 ? 'is' : 'are'} only supported by dashboard. Use: npx create-quiver dashboard ${requestedDashboardFlags[0]}${requestedDashboardFlags[0] === '--section' ? ' <name>' : requestedDashboardFlags[0] === '--limit' ? ' <n>' : ''}`));
1206
+ }
1207
+
1137
1208
  return result;
1138
1209
  }
1139
1210
 
@@ -2819,6 +2890,37 @@ async function run(argv) {
2819
2890
  return;
2820
2891
  }
2821
2892
 
2893
+ if (args.mode === 'dashboard') {
2894
+ runDashboard(process.cwd(), {
2895
+ details: args.dashboardDetails,
2896
+ includeCompleted: args.includeCompleted,
2897
+ json: args.json,
2898
+ limit: args.dashboardLimit,
2899
+ noColor: args.noColor,
2900
+ optionErrors: args.dashboardOptionErrors,
2901
+ section: args.dashboardSection,
2902
+ specSlug: args.specSlug,
2903
+ });
2904
+ return;
2905
+ }
2906
+
2907
+ if (args.mode === 'version') {
2908
+ const report = collectVersionReport(process.cwd(), {
2909
+ cliVersion: CLI_VERSION,
2910
+ env: process.env,
2911
+ });
2912
+ if (args.json) {
2913
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
2914
+ } else {
2915
+ process.stdout.write(formatHumanVersionReport(report, {
2916
+ env: process.env,
2917
+ noColor: args.noColor,
2918
+ stdoutIsTTY: Boolean(process.stdout.isTTY),
2919
+ }));
2920
+ }
2921
+ return;
2922
+ }
2923
+
2822
2924
  if (args.mode === 'plan') {
2823
2925
  runPlan(process.cwd(), {
2824
2926
  includeCompleted: args.includeCompleted,
@@ -3388,6 +3490,7 @@ async function run(argv) {
3388
3490
  module.exports = {
3389
3491
  resolveInteractiveInitOptions,
3390
3492
  runAnalyze,
3493
+ runDashboard,
3391
3494
  runDoctor,
3392
3495
  runFlow,
3393
3496
  runMigrate,
@@ -0,0 +1,86 @@
1
+ const { buildPlannerApprovalCandidates } = require('../approvals');
2
+ const { buildTechnicalPlanApprovalCandidates } = require('./plan-review');
3
+
4
+ function buildApprovalCandidateReport(projectRoot, phase) {
5
+ return phase === 'technical-plan'
6
+ ? buildTechnicalPlanApprovalCandidates(projectRoot)
7
+ : buildPlannerApprovalCandidates(projectRoot, phase);
8
+ }
9
+
10
+ function formatReviewSummary(review) {
11
+ if (!review || !review.recommendation) {
12
+ return '';
13
+ }
14
+
15
+ const parts = [`review=${review.recommendation}`];
16
+ if (review.blocking) {
17
+ parts.push('blocking');
18
+ }
19
+ if (review.required_fixes_count) {
20
+ parts.push(`required fixes=${review.required_fixes_count}`);
21
+ }
22
+ if (review.optional_hardening_count) {
23
+ parts.push(`optional=${review.optional_hardening_count}`);
24
+ }
25
+ if (review.risks_count) {
26
+ parts.push(`risks=${review.risks_count}`);
27
+ }
28
+ return parts.join(', ');
29
+ }
30
+
31
+ function formatCandidateSummary(candidate) {
32
+ if (!candidate) {
33
+ return '';
34
+ }
35
+
36
+ const parts = [
37
+ candidate.label,
38
+ candidate.current ? 'current' : 'history',
39
+ candidate.approvable ? 'approvable' : 'blocked',
40
+ candidate.reason,
41
+ formatReviewSummary(candidate.review),
42
+ ].filter(Boolean);
43
+ return parts.join(', ');
44
+ }
45
+
46
+ function approvalCandidateCommand(report, fallback = 'npx create-quiver ai approvals') {
47
+ if (report?.recommended?.next_command) {
48
+ return report.recommended.next_command;
49
+ }
50
+ if (report?.current?.next_command) {
51
+ return report.current.next_command;
52
+ }
53
+ return report?.next_command || fallback;
54
+ }
55
+
56
+ function formatApprovalDecisionLines(report) {
57
+ const lines = [];
58
+ if (!report || !Array.isArray(report.candidates)) {
59
+ return lines;
60
+ }
61
+
62
+ lines.push(`Candidates: ${report.candidates.length}`);
63
+ if (report.latest_version) {
64
+ lines.push(`Latest draft: v${report.latest_version}`);
65
+ }
66
+ if (report.current) {
67
+ lines.push(`Current candidate: ${formatCandidateSummary(report.current)}`);
68
+ }
69
+ if (report.recommended) {
70
+ lines.push(`Recommended approval: ${report.recommended.next_command}`);
71
+ } else if (report.next_command) {
72
+ lines.push(`Recommended next command: ${report.next_command}`);
73
+ }
74
+ if (report.review?.status) {
75
+ lines.push(`Review status: ${report.review.status}`);
76
+ }
77
+ return lines;
78
+ }
79
+
80
+ module.exports = {
81
+ approvalCandidateCommand,
82
+ buildApprovalCandidateReport,
83
+ formatApprovalDecisionLines,
84
+ formatCandidateSummary,
85
+ formatReviewSummary,
86
+ };
@@ -1,7 +1,7 @@
1
1
  const fs = require('node:fs');
2
2
  const path = require('node:path');
3
3
 
4
- const { readPhaseApproval, resolveApprovedPlannerInput } = require('../approvals');
4
+ const { buildPlannerApprovalCandidates, readPhaseApproval, resolveApprovedPlannerInput } = require('../approvals');
5
5
  const { quiverInternalPaths } = require('../init-layout');
6
6
 
7
7
  const PLAN_REVIEW_PROMPT_SOURCE = 'packaged production-readiness plan review template';
@@ -186,6 +186,81 @@ function reviewBlocksApproval(review) {
186
186
  return result.blocking === true || result.approval_recommendation === 'revise';
187
187
  }
188
188
 
189
+ function normalizeReviewDecision(review) {
190
+ const result = review?.meta?.review_result || null;
191
+ const requiredFixes = normalizeList(result?.required_fixes);
192
+ const optionalHardening = normalizeList(result?.optional_hardening);
193
+ const risks = normalizeList(result?.risks);
194
+ const blocking = reviewBlocksApproval(review);
195
+ const recommendation = result?.approval_recommendation || (review.status === 'missing' || review.status === 'stale' ? 'review-required' : 'approve-with-risk');
196
+ const nextCommand = result?.next_command || (review.status === 'unapproved' || review.status === 'reviewed'
197
+ ? 'npx create-quiver ai approve --phase technical-plan --version <n>'
198
+ : 'npx create-quiver ai review-plan --dry-run');
199
+
200
+ return {
201
+ status: review.status,
202
+ recommendation,
203
+ blocking: blocking || review.status === 'missing' || review.status === 'stale',
204
+ required_fixes: requiredFixes,
205
+ optional_hardening: optionalHardening,
206
+ risks,
207
+ required_fixes_count: requiredFixes.length,
208
+ optional_hardening_count: optionalHardening.length,
209
+ risks_count: risks.length,
210
+ source_file: review.meta?.source_file || '',
211
+ source_kind: review.meta?.source_kind || null,
212
+ source_version: review.meta?.source_version || null,
213
+ reviewed_at: review.meta?.reviewed_at || '',
214
+ next_command: nextCommand,
215
+ };
216
+ }
217
+
218
+ function buildTechnicalPlanApprovalCandidates(projectRoot) {
219
+ const base = buildPlannerApprovalCandidates(projectRoot, 'technical-plan');
220
+ const review = readPlanReview(projectRoot);
221
+ const reviewDecision = normalizeReviewDecision(review);
222
+ const reviewReady = (review.status === 'unapproved' || review.status === 'reviewed') && reviewDecision.blocking !== true;
223
+ const candidates = base.candidates.map((candidate) => {
224
+ const matchesReviewVersion = !reviewDecision.source_version
225
+ || !candidate.version
226
+ || Number(reviewDecision.source_version) === Number(candidate.version);
227
+ const approvable = candidate.approvable === true && reviewReady && matchesReviewVersion;
228
+ const reason = approvable
229
+ ? `latest draft has plan-review recommendation ${reviewDecision.recommendation}`
230
+ : review.status === 'missing'
231
+ ? 'technical-plan requires production review before approval'
232
+ : review.status === 'stale'
233
+ ? 'technical-plan review is stale'
234
+ : reviewDecision.blocking
235
+ ? `plan-review blocks approval with recommendation ${reviewDecision.recommendation}`
236
+ : matchesReviewVersion
237
+ ? candidate.reason
238
+ : `plan-review targets v${reviewDecision.source_version}; candidate is v${candidate.version}`;
239
+
240
+ return {
241
+ ...candidate,
242
+ approvable,
243
+ recommended: candidate.current === true && approvable,
244
+ blocked: !approvable,
245
+ status: approvable ? 'approvable' : candidate.current ? 'blocked' : 'history',
246
+ reason,
247
+ recommended_action: approvable ? 'approve' : reviewDecision.blocking || review.status === 'missing' || review.status === 'stale' ? 'review-or-revise' : 'inspect',
248
+ next_command: approvable ? candidate.next_command : reviewDecision.next_command,
249
+ review: reviewDecision,
250
+ };
251
+ });
252
+
253
+ return {
254
+ ...base,
255
+ candidates,
256
+ current: candidates.find((candidate) => candidate.current) || null,
257
+ recommended: candidates.find((candidate) => candidate.recommended) || null,
258
+ history: candidates.filter((candidate) => !candidate.current),
259
+ review: reviewDecision,
260
+ next_command: candidates.find((candidate) => candidate.recommended)?.next_command || reviewDecision.next_command,
261
+ };
262
+ }
263
+
189
264
  function latestTechnicalPlanDraft(approval) {
190
265
  const version = Number(approval.meta?.draft?.version || 0);
191
266
  if (!version) {
@@ -435,6 +510,7 @@ module.exports = {
435
510
  PLAN_REVIEW_RECOMMENDATIONS,
436
511
  assertPlanReviewed,
437
512
  buildPlanReviewPrompt,
513
+ buildTechnicalPlanApprovalCandidates,
438
514
  derivePlanReviewResult,
439
515
  planReviewMetaPath,
440
516
  planReviewPath,
@@ -2,6 +2,7 @@ const fs = require('node:fs');
2
2
  const os = require('node:os');
3
3
  const path = require('node:path');
4
4
 
5
+ const { buildApprovalCandidateReport } = require('./approval-candidates');
5
6
  const { quiverInternalPaths } = require('../init-layout');
6
7
 
7
8
  const AI_RUN_PHASES = Object.freeze([
@@ -104,8 +105,24 @@ function phaseRank(phase) {
104
105
  return AI_RUN_PHASES.indexOf(phase);
105
106
  }
106
107
 
107
- function nextCommandForPhase(phase) {
108
+ function nextCommandForPhase(phase, projectRoot = '') {
108
109
  assertKnownPhase(phase);
110
+ if (projectRoot && phase === 'acceptance-draft') {
111
+ try {
112
+ const report = buildApprovalCandidateReport(projectRoot, 'acceptance');
113
+ return report.recommended?.next_command || report.current?.next_command || PHASE_NEXT_COMMAND[phase];
114
+ } catch {
115
+ return PHASE_NEXT_COMMAND[phase];
116
+ }
117
+ }
118
+ if (projectRoot && phase === 'technical-plan-reviewed') {
119
+ try {
120
+ const report = buildApprovalCandidateReport(projectRoot, 'technical-plan');
121
+ return report.recommended?.next_command || report.current?.next_command || PHASE_NEXT_COMMAND[phase];
122
+ } catch {
123
+ return PHASE_NEXT_COMMAND[phase];
124
+ }
125
+ }
109
126
  return PHASE_NEXT_COMMAND[phase];
110
127
  }
111
128
 
@@ -374,12 +391,12 @@ function formatAiRunStatus(projectRoot, run) {
374
391
  if (otherOpenRuns.length > 0) {
375
392
  lines.push('Other open runs:');
376
393
  for (const item of otherOpenRuns) {
377
- lines.push(`- ${item.run_id}: ${item.phase} (${item.status}) -> ${nextCommandForPhase(item.phase)}`);
394
+ lines.push(`- ${item.run_id}: ${item.phase} (${item.status}) -> ${nextCommandForPhase(item.phase, projectRoot)}`);
378
395
  }
379
396
  }
380
397
 
381
398
  lines.push(
382
- `Next safe command: ${nextCommandForPhase(run.phase)}`,
399
+ `Next safe command: ${nextCommandForPhase(run.phase, projectRoot)}`,
383
400
  '',
384
401
  );
385
402
 
@@ -400,7 +417,7 @@ function formatAiRunResume(projectRoot, run) {
400
417
  'AI run resume',
401
418
  `Run: ${run.run_id}`,
402
419
  `Current phase: ${run.phase}`,
403
- `Next safe command: ${nextCommandForPhase(run.phase)}`,
420
+ `Next safe command: ${nextCommandForPhase(run.phase, projectRoot)}`,
404
421
  `State: ${toRelativePosix(projectRoot, runStatePath(projectRoot, run.run_id))}`,
405
422
  '',
406
423
  ].join('\n');