create-quiver 0.10.0 → 0.12.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 (165) hide show
  1. package/BACKLOG.md +16 -17
  2. package/CHANGELOG.md +34 -0
  3. package/README.md +174 -39
  4. package/README_FOR_AI.md +48 -24
  5. package/ROADMAP.md +22 -11
  6. package/docs/AI_CONTEXT.md.template +2 -0
  7. package/docs/AI_ONBOARDING_PROMPT.md.template +25 -18
  8. package/docs/COMMANDS.md.template +59 -11
  9. package/docs/CONTEXTO.md.template +2 -0
  10. package/docs/DECISIONS.md.template +1 -0
  11. package/docs/INDEX.md.template +20 -18
  12. package/docs/STATUS.md.template +1 -0
  13. package/docs/SUPPORT_MATRIX.md.template +2 -2
  14. package/docs/TROUBLESHOOTING.md.template +50 -0
  15. package/docs/WORKFLOW.md.template +25 -17
  16. package/package.json +19 -2
  17. package/package.template.json +13 -1
  18. package/scripts/init-docs.sh +11 -4
  19. package/scripts/package-quiver.sh +18 -2
  20. package/specs/quiver-v22-guided-ai-workflow/EVIDENCE_REPORT.md +58 -0
  21. package/specs/quiver-v22-guided-ai-workflow/EXECUTION_PLAN.md +88 -0
  22. package/specs/quiver-v22-guided-ai-workflow/SPEC.md +228 -0
  23. package/specs/quiver-v22-guided-ai-workflow/STATUS.md +42 -0
  24. package/specs/quiver-v22-guided-ai-workflow/pr.md +104 -0
  25. package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +35 -0
  26. package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
  27. package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/slice.json +51 -0
  28. package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/CLOSURE_BRIEF.md +31 -0
  29. package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/EXECUTION_BRIEF.md +58 -0
  30. package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/slice.json +55 -0
  31. package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/CLOSURE_BRIEF.md +30 -0
  32. package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/EXECUTION_BRIEF.md +57 -0
  33. package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/slice.json +57 -0
  34. package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/CLOSURE_BRIEF.md +32 -0
  35. package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/EXECUTION_BRIEF.md +56 -0
  36. package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/slice.json +56 -0
  37. package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/CLOSURE_BRIEF.md +33 -0
  38. package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/EXECUTION_BRIEF.md +56 -0
  39. package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/slice.json +58 -0
  40. package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/CLOSURE_BRIEF.md +32 -0
  41. package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/EXECUTION_BRIEF.md +56 -0
  42. package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/slice.json +54 -0
  43. package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/CLOSURE_BRIEF.md +32 -0
  44. package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/EXECUTION_BRIEF.md +58 -0
  45. package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/slice.json +57 -0
  46. package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/CLOSURE_BRIEF.md +32 -0
  47. package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/EXECUTION_BRIEF.md +58 -0
  48. package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/slice.json +55 -0
  49. package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/CLOSURE_BRIEF.md +32 -0
  50. package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/EXECUTION_BRIEF.md +58 -0
  51. package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/slice.json +53 -0
  52. package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/CLOSURE_BRIEF.md +33 -0
  53. package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/EXECUTION_BRIEF.md +59 -0
  54. package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/slice.json +59 -0
  55. package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +34 -0
  56. package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +58 -0
  57. package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/slice.json +60 -0
  58. package/specs/quiver-v23-guided-flow-productization/EVIDENCE_REPORT.md +80 -0
  59. package/specs/quiver-v23-guided-flow-productization/EXECUTION_PLAN.md +80 -0
  60. package/specs/quiver-v23-guided-flow-productization/SPEC.md +203 -0
  61. package/specs/quiver-v23-guided-flow-productization/STATUS.md +39 -0
  62. package/specs/quiver-v23-guided-flow-productization/pr.md +119 -0
  63. package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
  64. package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
  65. package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/slice.json +51 -0
  66. package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/CLOSURE_BRIEF.md +33 -0
  67. package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/EXECUTION_BRIEF.md +35 -0
  68. package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/slice.json +56 -0
  69. package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/CLOSURE_BRIEF.md +31 -0
  70. package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/EXECUTION_BRIEF.md +29 -0
  71. package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/slice.json +55 -0
  72. package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/CLOSURE_BRIEF.md +33 -0
  73. package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/EXECUTION_BRIEF.md +29 -0
  74. package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/slice.json +54 -0
  75. package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/CLOSURE_BRIEF.md +32 -0
  76. package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/EXECUTION_BRIEF.md +30 -0
  77. package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/slice.json +59 -0
  78. package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/CLOSURE_BRIEF.md +31 -0
  79. package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/EXECUTION_BRIEF.md +29 -0
  80. package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/slice.json +53 -0
  81. package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/CLOSURE_BRIEF.md +33 -0
  82. package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/EXECUTION_BRIEF.md +30 -0
  83. package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/slice.json +54 -0
  84. package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/CLOSURE_BRIEF.md +33 -0
  85. package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/EXECUTION_BRIEF.md +30 -0
  86. package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/slice.json +55 -0
  87. package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/CLOSURE_BRIEF.md +32 -0
  88. package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/EXECUTION_BRIEF.md +30 -0
  89. package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/slice.json +55 -0
  90. package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/CLOSURE_BRIEF.md +33 -0
  91. package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/EXECUTION_BRIEF.md +34 -0
  92. package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/slice.json +57 -0
  93. package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
  94. package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +32 -0
  95. package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/slice.json +63 -0
  96. package/specs/quiver-v24-dx-onboarding-hardening/EVIDENCE_REPORT.md +55 -0
  97. package/specs/quiver-v24-dx-onboarding-hardening/EXECUTION_PLAN.md +43 -0
  98. package/specs/quiver-v24-dx-onboarding-hardening/SPEC.md +149 -0
  99. package/specs/quiver-v24-dx-onboarding-hardening/STATUS.md +31 -0
  100. package/specs/quiver-v24-dx-onboarding-hardening/pr.md +76 -0
  101. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +31 -0
  102. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
  103. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/slice.json +51 -0
  104. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/CLOSURE_BRIEF.md +38 -0
  105. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/EXECUTION_BRIEF.md +53 -0
  106. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/slice.json +55 -0
  107. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/CLOSURE_BRIEF.md +33 -0
  108. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/EXECUTION_BRIEF.md +50 -0
  109. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/slice.json +52 -0
  110. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/CLOSURE_BRIEF.md +33 -0
  111. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/EXECUTION_BRIEF.md +50 -0
  112. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/slice.json +53 -0
  113. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/CLOSURE_BRIEF.md +33 -0
  114. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/EXECUTION_BRIEF.md +50 -0
  115. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/slice.json +70 -0
  116. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/CLOSURE_BRIEF.md +36 -0
  117. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/EXECUTION_BRIEF.md +49 -0
  118. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/slice.json +52 -0
  119. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/CLOSURE_BRIEF.md +43 -0
  120. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/EXECUTION_BRIEF.md +53 -0
  121. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/slice.json +60 -0
  122. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/CLOSURE_BRIEF.md +32 -0
  123. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/EXECUTION_BRIEF.md +50 -0
  124. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/slice.json +51 -0
  125. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/CLOSURE_BRIEF.md +34 -0
  126. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/EXECUTION_BRIEF.md +52 -0
  127. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/slice.json +54 -0
  128. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/CLOSURE_BRIEF.md +34 -0
  129. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/EXECUTION_BRIEF.md +51 -0
  130. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/slice.json +59 -0
  131. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
  132. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +54 -0
  133. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/slice.json +76 -0
  134. package/src/create-quiver/commands/ai.js +508 -35
  135. package/src/create-quiver/commands/demo.js +22 -0
  136. package/src/create-quiver/commands/evidence.js +37 -0
  137. package/src/create-quiver/commands/flow.js +561 -0
  138. package/src/create-quiver/commands/graph.js +14 -1
  139. package/src/create-quiver/commands/next.js +28 -0
  140. package/src/create-quiver/commands/plan.js +6 -3
  141. package/src/create-quiver/commands/prepare.js +236 -0
  142. package/src/create-quiver/commands/spec.js +133 -0
  143. package/src/create-quiver/index.js +688 -25
  144. package/src/create-quiver/lib/agent-profiles.js +148 -0
  145. package/src/create-quiver/lib/ai/context-packs.js +12 -0
  146. package/src/create-quiver/lib/ai/execution-plan.js +370 -10
  147. package/src/create-quiver/lib/ai/executor.js +376 -17
  148. package/src/create-quiver/lib/ai/github.js +196 -0
  149. package/src/create-quiver/lib/ai/onboarding-template.js +365 -0
  150. package/src/create-quiver/lib/ai/plan-review.js +283 -0
  151. package/src/create-quiver/lib/ai/providers.js +1 -0
  152. package/src/create-quiver/lib/ai/safety.js +5 -0
  153. package/src/create-quiver/lib/ai/spec-templates.js +2 -2
  154. package/src/create-quiver/lib/approvals.js +350 -0
  155. package/src/create-quiver/lib/demo.js +657 -0
  156. package/src/create-quiver/lib/doctor.js +234 -0
  157. package/src/create-quiver/lib/evidence.js +115 -0
  158. package/src/create-quiver/lib/init-docs.js +284 -17
  159. package/src/create-quiver/lib/init-layout.js +26 -1
  160. package/src/create-quiver/lib/lifecycle.js +6 -0
  161. package/src/create-quiver/lib/package-safety.js +117 -0
  162. package/src/create-quiver/lib/readiness.js +85 -18
  163. package/src/create-quiver/lib/slice-graph.js +1 -0
  164. package/src/create-quiver/lib/slice.js +8 -8
  165. package/src/create-quiver/lib/spec-worktrees.js +349 -0
@@ -3,15 +3,27 @@ const os = require('os');
3
3
  const path = require('path');
4
4
  const { execFileSync } = require('child_process');
5
5
  const { checkHandoff, scaffoldHandoff } = require('./lib/handoff');
6
- const { collectDoctorReport } = require('./lib/doctor');
7
- const { runDoctor: runAiDoctor, runExecuteSlice: runAiExecuteSlice, runOnboard, runPlan: runAiPlan, runPr: runAiPr } = require('./commands/ai');
6
+ const {
7
+ applyDoctorFixPlan,
8
+ buildDoctorFixPlan,
9
+ collectDoctorReport,
10
+ formatDoctorFixPlan,
11
+ } = require('./lib/doctor');
12
+ const { runAgent: runAiAgent, runApprovalStatus: runAiApprovalStatus, runApprove: runAiApprove, runDoctor: runAiDoctor, runExecutePlan: runAiExecutePlan, runExecuteSlice: runAiExecuteSlice, runOnboard, runPlan: runAiPlan, runPrepareContext: runAiPrepareContext, runPr: runAiPr, runPromptSlice: runAiPromptSlice, runReviewPlan: runAiReviewPlan } = require('./commands/ai');
13
+ const { runDemo } = require('./commands/demo');
14
+ const { runPrepare } = require('./commands/prepare');
15
+ const { runEvidence } = require('./commands/evidence');
16
+ const { runFlow } = require('./commands/flow');
8
17
  const { runGraph } = require('./commands/graph');
9
18
  const { runNext } = require('./commands/next');
10
19
  const { runPlan } = require('./commands/plan');
20
+ const { runCreateSpec } = require('./commands/spec');
11
21
  const { buildInitLayout, formatInitLayoutPlan } = require('./lib/init-layout');
12
- const { initializeProjectDocs, installSelfAsDevDep } = require('./lib/init-docs');
22
+ const { initializeProjectDocs, installSelfAsDevDep, refreshAiContextDoc } = require('./lib/init-docs');
13
23
  const { checkPrReadiness, checkScope, checkSliceReadiness } = require('./lib/readiness');
14
24
  const { cleanupSlice, refreshActiveSlicesBoard, startSlice } = require('./lib/lifecycle');
25
+ const { buildSpecStatus, closeSpecWorktree, formatSpecCloseResult, formatSpecStartResult, formatSpecStatus, startSpecWorktree } = require('./lib/spec-worktrees');
26
+ const { getContextPathExclusionReason } = require('./lib/ai/safety');
15
27
  const { relativePosixPath, resolveTargetRoot } = require('./lib/paths');
16
28
  const {
17
29
  CURRENT_SCAN_RELATIVE_PATH,
@@ -35,17 +47,74 @@ function formatError(message) {
35
47
  return `create-quiver: ${message}`;
36
48
  }
37
49
 
50
+ const SUPPORTED_COMMAND_MODES = new Set([
51
+ 'init',
52
+ 'flow',
53
+ 'plan',
54
+ 'graph',
55
+ 'next',
56
+ 'doctor',
57
+ 'prepare',
58
+ 'analyze',
59
+ 'migrate',
60
+ 'start-slice',
61
+ 'check-slice',
62
+ 'check-pr',
63
+ 'check-handoff',
64
+ 'new-handoff',
65
+ 'cleanup-slice',
66
+ 'check-scope',
67
+ 'refresh-active-slices',
68
+ 'spec',
69
+ 'evidence',
70
+ 'demo',
71
+ 'ai',
72
+ ]);
73
+
74
+ const SUPPORTED_AI_COMMANDS = new Set([
75
+ 'agent',
76
+ 'approve',
77
+ 'approval-status',
78
+ 'approvals',
79
+ 'doctor',
80
+ 'execute-plan',
81
+ 'execute-slice',
82
+ 'executor-prompt',
83
+ 'onboard',
84
+ 'plan',
85
+ 'prepare-context',
86
+ 'pr',
87
+ 'prompt-slice',
88
+ 'review-plan',
89
+ ]);
90
+
91
+ const SUPPORTED_SPEC_COMMANDS = new Set(['close', 'create', 'start', 'status']);
92
+ const SUPPORTED_DEMO_COMMANDS = new Set(['create']);
93
+
94
+ function unsupportedCommandMessage(commandName) {
95
+ return [
96
+ `unsupported command: ${commandName}`,
97
+ 'Run: npx create-quiver --help',
98
+ `If you meant to initialize a project, use: npx create-quiver init --name "${commandName}"`,
99
+ 'If this command exists in newer docs, update create-quiver and rerun the command.',
100
+ ].join('\n');
101
+ }
102
+
38
103
  function printUsage() {
39
104
  console.log(`Usage:
40
105
  npx create-quiver [options]
41
106
  npx create-quiver init [options]
42
107
  npx create-quiver analyze [options]
108
+ npx create-quiver flow [options]
43
109
  npx create-quiver plan [options]
44
110
  npx create-quiver ai <task> [options]
111
+ npx create-quiver ai agent <set|list|show> [role] [options]
112
+ npx create-quiver ai prepare-context [options]
45
113
  npx create-quiver graph [options]
46
114
  npx create-quiver next [options]
47
115
  npx create-quiver migrate [options]
48
116
  npx create-quiver doctor [options]
117
+ npx create-quiver prepare [options]
49
118
  npx create-quiver start-slice [options] <slice.json>
50
119
  npx create-quiver check-slice [options] <slice.json>
51
120
  npx create-quiver check-pr <slice.json>
@@ -54,6 +123,12 @@ function printUsage() {
54
123
  npx create-quiver cleanup-slice [options] <slice.json>
55
124
  npx create-quiver check-scope [options] <slice.json>
56
125
  npx create-quiver refresh-active-slices
126
+ npx create-quiver spec create [options]
127
+ npx create-quiver spec start <spec-dir>
128
+ npx create-quiver spec status <spec-dir>
129
+ npx create-quiver spec close <spec-dir>
130
+ npx create-quiver evidence run [options] -- <command>
131
+ npx create-quiver demo create spec-viewer [options]
57
132
 
58
133
  Options:
59
134
  -n, --name <project-name> Project name to generate
@@ -63,15 +138,33 @@ Options:
63
138
  --show-conflicts Show shared file paths in graph output
64
139
  --level <n> Restrict graph output to one level
65
140
  --json Emit machine-readable JSON
141
+ --include-completed Include completed slices in plan, graph, or next history output
66
142
  --only-ready Show only slices with no pending dependencies
67
143
  --all-ready List every ready slice returned by next
68
144
  --auto-start Prompt for confirmation and run start-slice on next
145
+ --local For check-slice, run structural validation without remote/base checks
69
146
  --unicode Prefer Unicode output when supported
70
147
  --minimal Plan or run the minimal init profile
71
148
  --full Plan or run the full compatibility init profile
72
149
  --legacy-scripts Include legacy Bash wrappers in init profile
73
150
  --include-templates Export packaged templates in init profile
74
- --dry-run Preview init or AI work without executing writes/providers
151
+ --dry-run Preview init, prepare, spec create, demo, or AI work without executing writes/providers
152
+ --fix For doctor, apply safe non-destructive repairs
153
+ --execute For ai execute-plan, run the planned slices instead of printing commands
154
+ --create For ai pr, create the PR after preflight instead of printing the plan only
155
+ --commit For ai execute-slice, commit validated slice changes after provider, scope, and tests pass
156
+ --allow-dirty For ai execute-slice, allow pre-existing dirty files and ignore them for scope diff
157
+ --mode <name> Execution mode for ai execute-plan (auto, manual, delegated)
158
+ --provider <name> Provider CLI to preflight for prepare or AI commands
159
+ --model <label> Free-form model label for AI agent profiles
160
+ --version <n> Draft version to approve for AI planner phases
161
+ --ssh-host-alias <name> SSH host alias to validate for prepare or AI commands
162
+ --identity-file <path> SSH identity file to validate for prepare or AI commands
163
+ --remote <name> Git remote name for check-slice or AI PR checks
164
+ --base <branch> Base branch for check-slice, ai pr, or spec close (default: main)
165
+ --output <file> Output file for evidence run
166
+ --max-output <n> Maximum stdout/stderr chars per evidence section
167
+ --title <text> Override PR title for ai pr create
75
168
  -y, --yes Skip prompts and use the provided inputs
76
169
  -h, --help Show this help message
77
170
 
@@ -80,13 +173,28 @@ Examples:
80
173
  npx create-quiver init --name "My Project" --dry-run
81
174
  npx create-quiver --name "My Project"
82
175
  npx create-quiver --name "My Project" --dir ./my-project
176
+ cd ./my-project && npx create-quiver flow
83
177
  cd ./my-project && npx create-quiver analyze
84
178
  cd ./my-project && npx create-quiver plan --json
85
179
  cd ./my-project && npx create-quiver ai onboard --dry-run
180
+ cd ./my-project && npx create-quiver ai prepare-context --dry-run
181
+ cd ./my-project && npx create-quiver ai agent set planner --provider codex --model gpt-5.5
182
+ cd ./my-project && npx create-quiver ai agent list
86
183
  cd ./my-project && npx create-quiver ai plan --phase acceptance --input requirements.md --dry-run
184
+ cd ./my-project && npx create-quiver ai approve --phase acceptance --input acceptance.md
185
+ cd ./my-project && npx create-quiver ai plan --phase technical-plan --dry-run
186
+ cd ./my-project && npx create-quiver ai review-plan --dry-run
187
+ cd ./my-project && npx create-quiver ai approve --phase technical-plan --version 1
188
+ cd ./my-project && npx create-quiver spec create --dry-run
189
+ cd ./my-project && npx create-quiver ai approvals
190
+ cd ./my-project && npx create-quiver ai prompt-slice --slice specs/my-project/slices/slice-01/slice.json --dry-run
87
191
  cd ./my-project && npx create-quiver ai execute-slice --slice specs/my-project/slices/slice-01/slice.json --dry-run
192
+ cd ./my-project && npx create-quiver ai execute-slice --slice specs/my-project/slices/slice-01/slice.json --commit
193
+ cd ./my-project && npx create-quiver ai execute-plan --dry-run --commit
88
194
  cd ./my-project && npx create-quiver ai doctor --dry-run --ssh-host-alias github-work --identity-file ~/.ssh/github-work
89
195
  cd ./my-project && npx create-quiver ai pr --dry-run --ssh-host-alias github-work --identity-file ~/.ssh/github-work
196
+ cd ./my-project && npx create-quiver ai pr --create --input specs/my-project/pr.md --ssh-host-alias github-work --identity-file ~/.ssh/github-work
197
+ cd ./my-project && npx create-quiver prepare --dry-run --provider codex --ssh-host-alias github-work --identity-file ~/.ssh/github-work
90
198
  cd ./my-project && npx create-quiver graph --show-conflicts
91
199
  cd ./my-project && npx create-quiver graph --format mermaid
92
200
  cd ./my-project && npx create-quiver graph --format dot
@@ -103,6 +211,11 @@ Examples:
103
211
  cd ./my-project && npx create-quiver cleanup-slice specs/my-project/slices/slice-01/slice.json
104
212
  cd ./my-project && npx create-quiver check-scope specs/my-project/slices/slice-01/slice.json
105
213
  cd ./my-project && npx create-quiver refresh-active-slices
214
+ cd ./my-project && npx create-quiver spec start specs/my-project
215
+ cd ./my-project && npx create-quiver spec status specs/my-project
216
+ cd ./my-project && npx create-quiver spec close specs/my-project --dry-run
217
+ cd ./my-project && npx create-quiver evidence run -- npm test
218
+ cd ./my-project && npx create-quiver demo create spec-viewer --dry-run
106
219
  node bin/create-quiver.js doctor --dir ./my-project
107
220
  `);
108
221
  }
@@ -114,15 +227,19 @@ function parseArgs(argv) {
114
227
  explicitInit: false,
115
228
  mode: 'init',
116
229
  allowDraft: false,
230
+ checkSliceLocal: false,
117
231
  closeBaseline: false,
118
232
  discard: false,
233
+ doctorFix: false,
119
234
  dryRun: false,
120
235
  gate: 'execution',
121
236
  projectName: '',
122
237
  targetDir: '.',
238
+ targetDirExplicit: false,
123
239
  strict: false,
124
240
  strictOverlap: false,
125
241
  json: false,
242
+ includeCompleted: false,
126
243
  onlyReady: false,
127
244
  allReady: false,
128
245
  autoStart: false,
@@ -132,13 +249,28 @@ function parseArgs(argv) {
132
249
  level: null,
133
250
  unicode: false,
134
251
  aiCommand: '',
252
+ aiAgentCommand: '',
253
+ aiAgentRole: '',
135
254
  aiPhase: 'acceptance',
136
255
  aiProvider: 'codex',
256
+ aiProviderExplicit: false,
257
+ aiModel: '',
258
+ aiLabel: '',
259
+ aiVersion: '',
260
+ prepareProvider: '',
137
261
  aiRole: '',
138
262
  aiContext: '',
139
263
  aiInput: '',
140
264
  aiSlice: '',
141
265
  aiTimeout: null,
266
+ aiCommit: false,
267
+ aiAllowDirty: false,
268
+ aiExecute: false,
269
+ aiExecutionMode: 'auto',
270
+ aiCreate: false,
271
+ aiBaseBranch: 'main',
272
+ baseBranchExplicit: false,
273
+ aiTitle: '',
142
274
  aiSshHostAlias: '',
143
275
  aiIdentityFile: '',
144
276
  aiRemote: 'origin',
@@ -146,14 +278,30 @@ function parseArgs(argv) {
146
278
  initIncludeTemplates: false,
147
279
  initLegacyScripts: false,
148
280
  initMinimal: false,
281
+ specCommand: '',
282
+ demoCommand: '',
283
+ demoName: '',
284
+ evidenceCommand: '',
285
+ evidenceArgs: [],
286
+ evidenceOutput: '',
287
+ evidenceMaxOutput: null,
149
288
  };
150
289
 
151
290
  const args = [...argv];
152
- const commandModes = new Set(['init', 'plan', 'graph', 'next', 'doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'check-handoff', 'new-handoff', 'cleanup-slice', 'check-scope', 'refresh-active-slices', 'ai']);
153
- if (commandModes.has(args[0])) {
291
+ if (SUPPORTED_COMMAND_MODES.has(args[0])) {
154
292
  result.mode = args[0];
155
293
  result.explicitInit = args[0] === 'init';
156
294
  args.shift();
295
+ if (result.mode === 'spec') {
296
+ result.specCommand = args.shift() || '';
297
+ }
298
+ if (result.mode === 'evidence') {
299
+ result.evidenceCommand = args.shift() || '';
300
+ }
301
+ if (result.mode === 'demo') {
302
+ result.demoCommand = args.shift() || '';
303
+ result.demoName = args.shift() || '';
304
+ }
157
305
  } else if (args[0] === '--analyze') {
158
306
  result.mode = 'analyze';
159
307
  args.shift();
@@ -169,6 +317,8 @@ function parseArgs(argv) {
169
317
  } else if (args[0] === '--new-handoff') {
170
318
  result.mode = 'new-handoff';
171
319
  args.shift();
320
+ } else if (args[0] && !args[0].startsWith('-')) {
321
+ throw new Error(formatError(unsupportedCommandMessage(args[0])));
172
322
  }
173
323
 
174
324
  const positional = [];
@@ -176,6 +326,11 @@ function parseArgs(argv) {
176
326
  for (let index = 0; index < args.length; index += 1) {
177
327
  const arg = args[index];
178
328
 
329
+ if (arg === '--') {
330
+ result.evidenceArgs = args.slice(index + 1);
331
+ break;
332
+ }
333
+
179
334
  if (arg === '-h' || arg === '--help') {
180
335
  result.help = true;
181
336
  continue;
@@ -216,6 +371,11 @@ function parseArgs(argv) {
216
371
  continue;
217
372
  }
218
373
 
374
+ if (arg === '--local') {
375
+ result.checkSliceLocal = true;
376
+ continue;
377
+ }
378
+
219
379
  if (arg === '--close-baseline') {
220
380
  result.closeBaseline = true;
221
381
  continue;
@@ -226,11 +386,45 @@ function parseArgs(argv) {
226
386
  continue;
227
387
  }
228
388
 
389
+ if (arg === '--fix') {
390
+ result.doctorFix = true;
391
+ continue;
392
+ }
393
+
229
394
  if (arg === '--dry-run') {
230
395
  result.dryRun = true;
231
396
  continue;
232
397
  }
233
398
 
399
+ if (arg === '--commit') {
400
+ result.aiCommit = true;
401
+ continue;
402
+ }
403
+
404
+ if (arg === '--execute') {
405
+ result.aiExecute = true;
406
+ continue;
407
+ }
408
+
409
+ if (arg === '--create') {
410
+ result.aiCreate = true;
411
+ continue;
412
+ }
413
+
414
+ if (arg === '--mode') {
415
+ const value = args[++index];
416
+ if (!value) {
417
+ throw new Error(formatError('missing value for --mode'));
418
+ }
419
+ result.aiExecutionMode = value;
420
+ continue;
421
+ }
422
+
423
+ if (arg === '--allow-dirty') {
424
+ result.aiAllowDirty = true;
425
+ continue;
426
+ }
427
+
234
428
  if (arg === '--minimal') {
235
429
  result.initMinimal = true;
236
430
  continue;
@@ -266,6 +460,11 @@ function parseArgs(argv) {
266
460
  continue;
267
461
  }
268
462
 
463
+ if (arg === '--include-completed') {
464
+ result.includeCompleted = true;
465
+ continue;
466
+ }
467
+
269
468
  if (arg === '--show-conflicts') {
270
469
  result.showConflicts = true;
271
470
  continue;
@@ -319,6 +518,35 @@ function parseArgs(argv) {
319
518
  throw new Error(formatError('missing value for --provider'));
320
519
  }
321
520
  result.aiProvider = value;
521
+ result.prepareProvider = value;
522
+ result.aiProviderExplicit = true;
523
+ continue;
524
+ }
525
+
526
+ if (arg === '--model') {
527
+ const value = args[++index];
528
+ if (!value) {
529
+ throw new Error(formatError('missing value for --model'));
530
+ }
531
+ result.aiModel = value;
532
+ continue;
533
+ }
534
+
535
+ if (arg === '--label') {
536
+ const value = args[++index];
537
+ if (!value) {
538
+ throw new Error(formatError('missing value for --label'));
539
+ }
540
+ result.aiLabel = value;
541
+ continue;
542
+ }
543
+
544
+ if (arg === '--version') {
545
+ const value = args[++index];
546
+ if (!value) {
547
+ throw new Error(formatError('missing value for --version'));
548
+ }
549
+ result.aiVersion = value;
322
550
  continue;
323
551
  }
324
552
 
@@ -398,6 +626,47 @@ function parseArgs(argv) {
398
626
  continue;
399
627
  }
400
628
 
629
+ if (arg === '--base') {
630
+ const value = args[++index];
631
+ if (!value) {
632
+ throw new Error(formatError('missing value for --base'));
633
+ }
634
+ result.aiBaseBranch = value;
635
+ result.baseBranchExplicit = true;
636
+ continue;
637
+ }
638
+
639
+ if (arg === '--output') {
640
+ const value = args[++index];
641
+ if (!value) {
642
+ throw new Error(formatError('missing value for --output'));
643
+ }
644
+ result.evidenceOutput = value;
645
+ continue;
646
+ }
647
+
648
+ if (arg === '--max-output') {
649
+ const value = args[++index];
650
+ if (typeof value === 'undefined') {
651
+ throw new Error(formatError('missing value for --max-output'));
652
+ }
653
+ const parsed = Number.parseInt(value, 10);
654
+ if (!Number.isInteger(parsed) || parsed <= 0) {
655
+ throw new Error(formatError('invalid value for --max-output'));
656
+ }
657
+ result.evidenceMaxOutput = parsed;
658
+ continue;
659
+ }
660
+
661
+ if (arg === '--title') {
662
+ const value = args[++index];
663
+ if (!value) {
664
+ throw new Error(formatError('missing value for --title'));
665
+ }
666
+ result.aiTitle = value;
667
+ continue;
668
+ }
669
+
401
670
  if (arg === '--phase') {
402
671
  const value = args[++index];
403
672
  if (!value) {
@@ -440,6 +709,7 @@ function parseArgs(argv) {
440
709
  throw new Error(formatError('missing value for --dir'));
441
710
  }
442
711
  result.targetDir = value;
712
+ result.targetDirExplicit = true;
443
713
  continue;
444
714
  }
445
715
 
@@ -462,17 +732,78 @@ function parseArgs(argv) {
462
732
  if (positional.length > 0) {
463
733
  throw new Error(formatError('plan does not accept positional arguments; use --spec <slug>'));
464
734
  }
735
+ } else if (result.mode === 'flow') {
736
+ if (positional.length > 0) {
737
+ throw new Error(formatError('flow does not accept positional arguments'));
738
+ }
465
739
  } else if (result.mode === 'ai') {
466
740
  if (!result.aiCommand && positional.length > 0) {
467
741
  result.aiCommand = positional.shift();
468
742
  }
743
+ if (result.aiCommand === 'agent') {
744
+ if (!result.aiAgentCommand && positional.length > 0) {
745
+ result.aiAgentCommand = positional.shift();
746
+ }
747
+ if (!result.aiAgentRole && positional.length > 0) {
748
+ result.aiAgentRole = positional.shift();
749
+ }
750
+ }
469
751
  if (positional.length > 0) {
470
752
  throw new Error(formatError('ai does not accept extra positional arguments'));
471
753
  }
754
+ } else if (result.mode === 'prepare') {
755
+ if (positional.length > 0) {
756
+ throw new Error(formatError('prepare does not accept positional arguments'));
757
+ }
472
758
  } else if (result.mode === 'refresh-active-slices') {
473
759
  if (positional.length > 0) {
474
760
  throw new Error(formatError('refresh-active-slices does not accept positional arguments'));
475
761
  }
762
+ } else if (result.mode === 'spec') {
763
+ if (!result.specCommand && positional.length > 0) {
764
+ result.specCommand = positional.shift();
765
+ }
766
+ if (!result.specCommand) {
767
+ throw new Error(formatError('missing spec subcommand. Use: npx create-quiver spec <create|start|status|close>'));
768
+ }
769
+ if (result.specCommand !== 'create' && positional.length > 0) {
770
+ result.targetDir = positional.shift();
771
+ }
772
+ if (result.specCommand === 'create' && positional.length > 0) {
773
+ throw new Error(formatError('spec create does not accept positional arguments; use --input <file> or --spec <slug>'));
774
+ }
775
+ } else if (result.mode === 'evidence') {
776
+ if (!result.evidenceCommand && positional.length > 0) {
777
+ result.evidenceCommand = positional.shift();
778
+ }
779
+ if (!result.evidenceCommand) {
780
+ throw new Error(formatError('missing evidence subcommand. Use: npx create-quiver evidence run -- <command>'));
781
+ }
782
+ if (result.evidenceCommand !== 'run') {
783
+ throw new Error(formatError(`unsupported evidence subcommand: ${result.evidenceCommand}. Supported tasks: run`));
784
+ }
785
+ if (positional.length > 0) {
786
+ throw new Error(formatError('evidence run does not accept positional arguments before --'));
787
+ }
788
+ } else if (result.mode === 'demo') {
789
+ if (!result.demoCommand && positional.length > 0) {
790
+ result.demoCommand = positional.shift();
791
+ }
792
+ if (!result.demoName && positional.length > 0) {
793
+ result.demoName = positional.shift();
794
+ }
795
+ if (!result.demoCommand) {
796
+ throw new Error(formatError('missing demo subcommand. Use: npx create-quiver demo create spec-viewer'));
797
+ }
798
+ if (!SUPPORTED_DEMO_COMMANDS.has(result.demoCommand)) {
799
+ throw new Error(formatError(`unsupported demo subcommand: ${result.demoCommand}. Supported tasks: create`));
800
+ }
801
+ if (result.demoName !== 'spec-viewer') {
802
+ throw new Error(formatError(`unsupported demo: ${result.demoName || '(missing)'}. Supported demos: spec-viewer`));
803
+ }
804
+ if (positional.length > 0) {
805
+ throw new Error(formatError('demo create spec-viewer does not accept positional target paths; use --dir <target-dir>'));
806
+ }
476
807
  } else {
477
808
  if (positional.length > 0) {
478
809
  result.targetDir = positional.shift();
@@ -698,6 +1029,43 @@ function escapeMarkdownCell(value) {
698
1029
  return String(value).replace(/\|/g, '\\|');
699
1030
  }
700
1031
 
1032
+ function summarizeSkippedPaths(scan) {
1033
+ const details = Array.isArray(scan.skipped_path_details) && scan.skipped_path_details.length > 0
1034
+ ? scan.skipped_path_details
1035
+ : (Array.isArray(scan.skipped_paths) ? scan.skipped_paths.map((item) => ({ reason: 'excluded path', path: item })) : []);
1036
+ const counts = new Map();
1037
+ const dependencySegments = new Set(['node_modules', '.pnpm-store', '.npm', '.yarn']);
1038
+ const outputSegments = new Set(['dist', 'build', 'coverage', 'out', 'tmp', 'temp', 'cache', '.cache', '.turbo', '.next', '.nuxt', '.parcel-cache', 'generated', 'gen', 'artifacts', 'reports', 'vendor', 'target']);
1039
+
1040
+ for (const item of details) {
1041
+ const reason = item.reason || 'excluded path';
1042
+ let label = reason;
1043
+ if (reason === 'env-file') {
1044
+ label = 'env files';
1045
+ } else if (reason === 'git-metadata') {
1046
+ label = 'git metadata';
1047
+ } else if (reason === 'hidden-directory') {
1048
+ label = 'hidden directories';
1049
+ } else if (reason.startsWith('secret-file:')) {
1050
+ label = 'secret files';
1051
+ } else if (reason.startsWith('unsafe-segment:')) {
1052
+ const segment = reason.slice('unsafe-segment:'.length);
1053
+ if (segment === '.quiver') {
1054
+ label = 'local AI state';
1055
+ } else if (dependencySegments.has(segment)) {
1056
+ label = 'dependency folders';
1057
+ } else if (outputSegments.has(segment)) {
1058
+ label = 'generated/output/cache folders';
1059
+ } else {
1060
+ label = segment;
1061
+ }
1062
+ }
1063
+ counts.set(label, (counts.get(label) || 0) + 1);
1064
+ }
1065
+
1066
+ return Array.from(counts.entries()).map(([reason, count]) => ({ reason, count }));
1067
+ }
1068
+
701
1069
  function collectPackageManagers(projectRoot) {
702
1070
  const packageManagerField = readJsonIfExists(path.join(projectRoot, 'package.json'))?.packageManager;
703
1071
 
@@ -725,6 +1093,7 @@ function collectPackageManagers(projectRoot) {
725
1093
  function collectProjectFiles(projectRoot, maxDepth = 2) {
726
1094
  const files = [];
727
1095
  const skippedPaths = [];
1096
+ const skippedPathDetails = [];
728
1097
  const ignoredDirs = new Set([
729
1098
  '.git',
730
1099
  'node_modules',
@@ -741,6 +1110,12 @@ function collectProjectFiles(projectRoot, maxDepth = 2) {
741
1110
  ]);
742
1111
  const allowedHiddenDirs = new Set(['.github', '.vscode', '.devcontainer']);
743
1112
 
1113
+ function skipPath(relativePath, reason) {
1114
+ const normalized = relativePath.split(path.sep).join('/');
1115
+ skippedPaths.push(normalized);
1116
+ skippedPathDetails.push({ path: normalized, reason });
1117
+ }
1118
+
744
1119
  function walk(currentDir, depth, relativeDir = '') {
745
1120
  const entries = fs.readdirSync(currentDir, { withFileTypes: true });
746
1121
 
@@ -750,12 +1125,18 @@ function collectProjectFiles(projectRoot, maxDepth = 2) {
750
1125
 
751
1126
  if (entry.isDirectory()) {
752
1127
  if (ignoredDirs.has(entry.name)) {
753
- skippedPaths.push(entryRelativePath);
1128
+ skipPath(entryRelativePath, `unsafe-segment:${entry.name}`);
1129
+ continue;
1130
+ }
1131
+
1132
+ const directoryReason = getContextPathExclusionReason(entryRelativePath);
1133
+ if (directoryReason) {
1134
+ skipPath(entryRelativePath, directoryReason);
754
1135
  continue;
755
1136
  }
756
1137
 
757
1138
  if (entry.name.startsWith('.') && !allowedHiddenDirs.has(entry.name)) {
758
- skippedPaths.push(entryRelativePath);
1139
+ skipPath(entryRelativePath, 'hidden-directory');
759
1140
  continue;
760
1141
  }
761
1142
 
@@ -766,13 +1147,19 @@ function collectProjectFiles(projectRoot, maxDepth = 2) {
766
1147
  continue;
767
1148
  }
768
1149
 
1150
+ const fileReason = getContextPathExclusionReason(entryRelativePath);
1151
+ if (fileReason) {
1152
+ skipPath(entryRelativePath, fileReason);
1153
+ continue;
1154
+ }
1155
+
769
1156
  files.push(entryRelativePath);
770
1157
  }
771
1158
  }
772
1159
 
773
1160
  walk(projectRoot, 0);
774
1161
 
775
- return { files, skippedPaths };
1162
+ return { files, skipped_path_details: skippedPathDetails, skippedPaths };
776
1163
  }
777
1164
 
778
1165
  function collectRootEntries(projectRoot) {
@@ -820,6 +1207,7 @@ function collectLanguageSignals(files) {
820
1207
  }
821
1208
 
822
1209
  const languages = [];
1210
+ const seenLanguages = new Set();
823
1211
  const extToLanguage = new Map([
824
1212
  ['.ts', 'typescript'],
825
1213
  ['.tsx', 'typescript'],
@@ -845,14 +1233,90 @@ function collectLanguageSignals(files) {
845
1233
  ]);
846
1234
 
847
1235
  for (const [ext, language] of extToLanguage.entries()) {
848
- if (extensions.has(ext)) {
1236
+ if (extensions.has(ext) && !seenLanguages.has(language)) {
849
1237
  languages.push(language);
1238
+ seenLanguages.add(language);
850
1239
  }
851
1240
  }
852
1241
 
853
1242
  return languages;
854
1243
  }
855
1244
 
1245
+ function parseCreateQuiverScriptCommand(command) {
1246
+ const normalized = String(command || '').trim();
1247
+ const match = normalized.match(/^npx\s+create-quiver(?:@[^\s]+)?\s+(.+)$/);
1248
+ if (!match) {
1249
+ return null;
1250
+ }
1251
+
1252
+ const tokens = match[1].split(/\s+/).filter(Boolean);
1253
+ if (tokens.length === 0) {
1254
+ return null;
1255
+ }
1256
+
1257
+ return {
1258
+ commandName: tokens[0],
1259
+ subcommand: tokens[1] || '',
1260
+ };
1261
+ }
1262
+
1263
+ function findUnsupportedCreateQuiverScripts(scripts = {}) {
1264
+ const unsupported = [];
1265
+
1266
+ for (const [scriptName, command] of Object.entries(scripts)) {
1267
+ const parsed = parseCreateQuiverScriptCommand(command);
1268
+ if (!parsed) {
1269
+ continue;
1270
+ }
1271
+
1272
+ if (!SUPPORTED_COMMAND_MODES.has(parsed.commandName)) {
1273
+ unsupported.push({
1274
+ command,
1275
+ reason: `unsupported command "${parsed.commandName}"`,
1276
+ scriptName,
1277
+ });
1278
+ continue;
1279
+ }
1280
+
1281
+ if (parsed.commandName === 'ai' && !SUPPORTED_AI_COMMANDS.has(parsed.subcommand)) {
1282
+ unsupported.push({
1283
+ command,
1284
+ reason: `unsupported ai subcommand "${parsed.subcommand || '(missing)'}"`,
1285
+ scriptName,
1286
+ });
1287
+ continue;
1288
+ }
1289
+
1290
+ if (parsed.commandName === 'spec' && !SUPPORTED_SPEC_COMMANDS.has(parsed.subcommand)) {
1291
+ unsupported.push({
1292
+ command,
1293
+ reason: `unsupported spec subcommand "${parsed.subcommand || '(missing)'}"`,
1294
+ scriptName,
1295
+ });
1296
+ continue;
1297
+ }
1298
+
1299
+ if (parsed.commandName === 'demo' && !SUPPORTED_DEMO_COMMANDS.has(parsed.subcommand)) {
1300
+ unsupported.push({
1301
+ command,
1302
+ reason: `unsupported demo subcommand "${parsed.subcommand || '(missing)'}"`,
1303
+ scriptName,
1304
+ });
1305
+ }
1306
+ }
1307
+
1308
+ return unsupported;
1309
+ }
1310
+
1311
+ function detectNodeProject(files, rootEntries, packageJson, languages) {
1312
+ const hasPackageJson = Boolean(packageJson);
1313
+ const hasJavaScriptSignals = languages.some((language) => language === 'javascript' || language === 'typescript');
1314
+ const hasSourceDirectories = detectSourceDirectories(rootEntries).length > 0;
1315
+ const hasSourceFiles = files.some((file) => /\.(?:c|m)?jsx?$/i.test(file) || /\.(?:c|m)?tsx?$/i.test(file));
1316
+
1317
+ return hasJavaScriptSignals && (hasPackageJson || hasSourceDirectories || hasSourceFiles);
1318
+ }
1319
+
856
1320
  function collectWorkspaces(packageJson) {
857
1321
  if (!packageJson) {
858
1322
  return [];
@@ -982,6 +1446,11 @@ function detectFrameworks(projectRoot, files, rootEntries, packageJson) {
982
1446
  evidence.push({ framework: 'react', signals: ['react', 'typescript files'] });
983
1447
  }
984
1448
 
1449
+ if (frameworks.length === 0 && detectNodeProject(files, rootEntries, packageJson, languages)) {
1450
+ frameworks.push('node');
1451
+ evidence.push({ framework: 'node', signals: ['package.json', 'javascript or typescript source files'] });
1452
+ }
1453
+
985
1454
  const primary = frameworks[0] || 'unknown';
986
1455
 
987
1456
  return {
@@ -1076,7 +1545,7 @@ function detectRisks(projectRoot, scan) {
1076
1545
  function buildProjectScan(projectRoot) {
1077
1546
  const packageJson = readJsonIfExists(path.join(projectRoot, 'package.json'));
1078
1547
  const rootEntries = collectRootEntries(projectRoot);
1079
- const { files, skippedPaths } = collectProjectFiles(projectRoot);
1548
+ const { files, skippedPaths, skipped_path_details } = collectProjectFiles(projectRoot);
1080
1549
  const topLevelDirectories = rootEntries.filter((entry) => entry.type === 'directory' && !entry.name.startsWith('.')).map((entry) => entry.name);
1081
1550
  const sourceDirectories = detectSourceDirectories(rootEntries);
1082
1551
  const configFiles = detectConfigFiles(rootEntries);
@@ -1134,6 +1603,7 @@ function buildProjectScan(projectRoot) {
1134
1603
  },
1135
1604
  risks: [],
1136
1605
  skipped_paths: skippedPaths,
1606
+ skipped_path_details,
1137
1607
  };
1138
1608
 
1139
1609
  scan.risks = detectRisks(projectRoot, scan);
@@ -1250,7 +1720,7 @@ function renderProjectMap(scan) {
1250
1720
  }
1251
1721
 
1252
1722
  const relevantScripts = Object.entries(scan.commands.scripts)
1253
- .filter(([name]) => /(^|:)(analyze|doctor|migrate|test|build|lint|dev|start|check)(:|$)|analyze|doctor|migrate|test|build|lint|dev|start|check/i.test(name))
1723
+ .filter(([name]) => /(^|:)(analyze|doctor|migrate|validate|test|build|lint|dev|start|check)(:|$)|analyze|doctor|migrate|validate|test|build|lint|dev|start|check/i.test(name))
1254
1724
  .slice(0, 12);
1255
1725
 
1256
1726
  if (relevantScripts.length > 0) {
@@ -1310,9 +1780,10 @@ function renderProjectMap(scan) {
1310
1780
 
1311
1781
  lines.push('');
1312
1782
  lines.push('## Skipped Paths');
1313
- if (scan.skipped_paths.length > 0) {
1314
- for (const skippedPath of scan.skipped_paths) {
1315
- lines.push(`- ${skippedPath}`);
1783
+ const skippedSummaries = summarizeSkippedPaths(scan);
1784
+ if (skippedSummaries.length > 0) {
1785
+ for (const skippedPath of skippedSummaries) {
1786
+ lines.push(`- ${skippedPath.reason}: ${skippedPath.count}`);
1316
1787
  }
1317
1788
  } else {
1318
1789
  lines.push('- None');
@@ -1320,10 +1791,8 @@ function renderProjectMap(scan) {
1320
1791
 
1321
1792
  lines.push('');
1322
1793
  lines.push('## Do Not Read First');
1323
- if (scan.skipped_paths.length > 0) {
1324
- for (const skippedPath of scan.skipped_paths) {
1325
- lines.push(`- ${skippedPath}`);
1326
- }
1794
+ if (skippedSummaries.length > 0) {
1795
+ lines.push('- Hidden, generated, secret, and cache paths are excluded from the analysis scan.');
1327
1796
  } else {
1328
1797
  lines.push('- None detected, but still prioritize docs and config files before source trees.');
1329
1798
  }
@@ -1351,11 +1820,13 @@ function runAnalyze(targetDir) {
1351
1820
 
1352
1821
  const scan = buildProjectScan(projectRoot);
1353
1822
  const artifacts = writeProjectScanArtifacts(projectRoot, scan);
1823
+ const aiContextPath = refreshAiContextDoc(projectRoot, scan);
1354
1824
  updateStateForAnalyze(projectRoot, CLI_VERSION);
1355
1825
 
1356
1826
  console.log(`Project analysis completed for ${projectRoot}`);
1357
1827
  console.log(`Wrote ${relativePosixPath(projectRoot, artifacts.jsonPath)}`);
1358
1828
  console.log(`Wrote ${relativePosixPath(projectRoot, artifacts.mdPath)}`);
1829
+ console.log(`Wrote ${relativePosixPath(projectRoot, aiContextPath)}`);
1359
1830
  console.log(`Detected primary stack: ${scan.stack.primary}`);
1360
1831
  console.log(`Detected package manager: ${scan.project.package_manager}`);
1361
1832
  }
@@ -1410,7 +1881,7 @@ function runMigrate(targetDir, options = {}) {
1410
1881
  }
1411
1882
  }
1412
1883
 
1413
- function runDoctor(targetDir) {
1884
+ function runDoctor(targetDir, options = {}) {
1414
1885
  const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
1415
1886
 
1416
1887
  if (!fs.existsSync(projectRoot)) {
@@ -1421,6 +1892,17 @@ function runDoctor(targetDir) {
1421
1892
  throw new Error(formatError('doctor requires a project previously initialized by Quiver.\nRun init first: npx create-quiver --name "Project Name"'));
1422
1893
  }
1423
1894
 
1895
+ const fixPlan = buildDoctorFixPlan(projectRoot);
1896
+ if (options.fix) {
1897
+ if (options.dryRun) {
1898
+ console.log(formatDoctorFixPlan(fixPlan, { dryRun: true }));
1899
+ return;
1900
+ }
1901
+
1902
+ applyDoctorFixPlan(projectRoot, fixPlan);
1903
+ console.log(formatDoctorFixPlan(fixPlan));
1904
+ }
1905
+
1424
1906
  const doctorReport = collectDoctorReport(projectRoot);
1425
1907
  const specSlugs = doctorReport.specSlugs;
1426
1908
  const specRequiredFiles = specSlugs.flatMap((projectSlug) => [
@@ -1475,12 +1957,18 @@ function runDoctor(targetDir) {
1475
1957
  const missingNodeNativeScripts = ['quiver:migrate', 'quiver:analyze', 'quiver:doctor']
1476
1958
  .filter((name) => typeof pkg.scripts?.[name] !== 'string');
1477
1959
  const missingAiScripts = [
1960
+ 'quiver:ai:agent',
1478
1961
  'quiver:ai:onboard',
1479
1962
  'quiver:ai:plan',
1963
+ 'quiver:ai:review-plan',
1964
+ 'quiver:ai:approve',
1965
+ 'quiver:ai:prompt-slice',
1480
1966
  'quiver:ai:execute-slice',
1967
+ 'quiver:ai:execute-plan',
1481
1968
  'quiver:ai:pr',
1482
1969
  'quiver:ai:doctor',
1483
1970
  ].filter((name) => typeof pkg.scripts?.[name] !== 'string');
1971
+ const unsupportedCreateQuiverScripts = findUnsupportedCreateQuiverScripts(pkg.scripts || {});
1484
1972
  const hasScanArtifacts = hasProjectScanArtifact(projectRoot)
1485
1973
  && fs.existsSync(path.join(projectRoot, PROJECT_MAP_RELATIVE_PATH));
1486
1974
  const quiverState = readState(projectRoot);
@@ -1523,6 +2011,9 @@ function runDoctor(targetDir) {
1523
2011
  if (legacyOnlyScripts.length > 0) {
1524
2012
  console.log(`- Warning: legacy Bash workflow scripts detected for ${legacyOnlyScripts.join(', ')}. Run npx create-quiver migrate to add quiver:* npm scripts.`);
1525
2013
  }
2014
+ for (const script of unsupportedCreateQuiverScripts) {
2015
+ console.log(`- Warning: package.json script ${script.scriptName} targets ${script.reason}: \`${script.command}\`. Update create-quiver or regenerate scripts with npx create-quiver migrate.`);
2016
+ }
1526
2017
  for (const warning of softWarnings) {
1527
2018
  console.log(`- Warning: ${warning}`);
1528
2019
  }
@@ -1540,7 +2031,7 @@ function runDoctor(targetDir) {
1540
2031
  console.log(`- Validate a slice: npx create-quiver check-slice specs/${projectSlug}/slices/<slice-id>/slice.json`);
1541
2032
  console.log(`- Validate the PR gate: npx create-quiver check-pr specs/${projectSlug}/slices/<slice-id>/slice.json`);
1542
2033
  } else {
1543
- console.log('- Create real specs and slices only after acceptance criteria and the technical plan are approved.');
2034
+ console.log('- Create real specs and slices only after acceptance criteria are approved and the technical plan is reviewed and approved.');
1544
2035
  }
1545
2036
  }
1546
2037
 
@@ -1550,7 +2041,7 @@ function printInitNextSteps(targetDir, projectName) {
1550
2041
  console.log(`- Review AGENTS.md, then ${path.join(targetDir, 'docs', 'AI_ONBOARDING_PROMPT.md')}`);
1551
2042
  console.log(`- Review ${path.join(targetDir, 'docs', 'WORKFLOW.md')}`);
1552
2043
  console.log('- Analyze the project with npx create-quiver analyze');
1553
- console.log('- Create real specs and slices after acceptance criteria and the technical plan are approved.');
2044
+ console.log('- Create real specs and slices after acceptance criteria are approved and the technical plan is reviewed and approved.');
1554
2045
  }
1555
2046
 
1556
2047
  async function run(argv) {
@@ -1566,8 +2057,16 @@ async function run(argv) {
1566
2057
  return;
1567
2058
  }
1568
2059
 
2060
+ if (args.mode === 'flow') {
2061
+ await runFlow(process.cwd(), {
2062
+ json: args.json,
2063
+ });
2064
+ return;
2065
+ }
2066
+
1569
2067
  if (args.mode === 'plan') {
1570
2068
  runPlan(process.cwd(), {
2069
+ includeCompleted: args.includeCompleted,
1571
2070
  json: args.json,
1572
2071
  onlyReady: args.onlyReady,
1573
2072
  specSlug: args.specSlug,
@@ -1576,9 +2075,31 @@ async function run(argv) {
1576
2075
  return;
1577
2076
  }
1578
2077
 
2078
+ if (args.mode === 'prepare') {
2079
+ await runPrepare(process.cwd(), {
2080
+ dryRun: args.dryRun,
2081
+ identityFile: args.aiIdentityFile || undefined,
2082
+ provider: args.prepareProvider || undefined,
2083
+ sshHostAlias: args.aiSshHostAlias || undefined,
2084
+ });
2085
+ return;
2086
+ }
2087
+
1579
2088
  if (args.mode === 'ai') {
1580
2089
  if (!args.aiCommand) {
1581
- throw new Error(formatError('missing ai subcommand. Use: npx create-quiver ai onboard | plan | execute-slice | doctor | pr'));
2090
+ throw new Error(formatError('missing ai subcommand. Use: npx create-quiver ai onboard | prepare-context | plan | review-plan | approve | approvals | agent | prompt-slice | execute-slice | execute-plan | doctor | pr'));
2091
+ }
2092
+
2093
+ if (args.aiCommand === 'agent') {
2094
+ runAiAgent(process.cwd(), {
2095
+ command: args.aiAgentCommand,
2096
+ context: args.aiContext || undefined,
2097
+ label: args.aiLabel || undefined,
2098
+ model: args.aiModel || undefined,
2099
+ provider: args.aiProviderExplicit ? args.aiProvider : undefined,
2100
+ role: args.aiAgentRole || undefined,
2101
+ });
2102
+ return;
1582
2103
  }
1583
2104
 
1584
2105
  if (args.aiCommand === 'onboard') {
@@ -1587,12 +2108,20 @@ async function run(argv) {
1587
2108
  dryRun: args.dryRun,
1588
2109
  input: args.aiInput || undefined,
1589
2110
  provider: args.aiProvider,
2111
+ providerExplicit: args.aiProviderExplicit,
1590
2112
  role: args.aiRole,
1591
2113
  timeout: args.aiTimeout,
1592
2114
  });
1593
2115
  return;
1594
2116
  }
1595
2117
 
2118
+ if (args.aiCommand === 'prepare-context') {
2119
+ await runAiPrepareContext(process.cwd(), {
2120
+ dryRun: args.dryRun,
2121
+ });
2122
+ return;
2123
+ }
2124
+
1596
2125
  if (args.aiCommand === 'plan') {
1597
2126
  await runAiPlan(process.cwd(), {
1598
2127
  context: args.aiContext || undefined,
@@ -1600,6 +2129,7 @@ async function run(argv) {
1600
2129
  input: args.aiInput || undefined,
1601
2130
  phase: args.aiPhase,
1602
2131
  provider: args.aiProvider,
2132
+ providerExplicit: args.aiProviderExplicit,
1603
2133
  role: args.aiRole,
1604
2134
  specSlug: args.specSlug || undefined,
1605
2135
  timeout: args.aiTimeout,
@@ -1607,11 +2137,41 @@ async function run(argv) {
1607
2137
  return;
1608
2138
  }
1609
2139
 
2140
+ if (args.aiCommand === 'review-plan') {
2141
+ await runAiReviewPlan(process.cwd(), {
2142
+ context: args.aiContext || undefined,
2143
+ dryRun: args.dryRun,
2144
+ input: args.aiInput || undefined,
2145
+ provider: args.aiProvider,
2146
+ providerExplicit: args.aiProviderExplicit,
2147
+ timeout: args.aiTimeout,
2148
+ });
2149
+ return;
2150
+ }
2151
+
2152
+ if (args.aiCommand === 'approve') {
2153
+ await runAiApprove(process.cwd(), {
2154
+ dryRun: args.dryRun,
2155
+ input: args.aiInput || undefined,
2156
+ phase: args.aiPhase,
2157
+ version: args.aiVersion || undefined,
2158
+ });
2159
+ return;
2160
+ }
2161
+
2162
+ if (args.aiCommand === 'approvals' || args.aiCommand === 'approval-status') {
2163
+ await runAiApprovalStatus(process.cwd());
2164
+ return;
2165
+ }
2166
+
1610
2167
  if (args.aiCommand === 'execute-slice') {
1611
2168
  await runAiExecuteSlice(process.cwd(), {
2169
+ allowDirty: args.aiAllowDirty,
2170
+ commit: args.aiCommit,
1612
2171
  context: args.aiContext || undefined,
1613
2172
  dryRun: args.dryRun,
1614
2173
  provider: args.aiProvider,
2174
+ providerExplicit: args.aiProviderExplicit,
1615
2175
  role: args.aiRole,
1616
2176
  slice: args.aiSlice || undefined,
1617
2177
  timeout: args.aiTimeout,
@@ -1619,6 +2179,31 @@ async function run(argv) {
1619
2179
  return;
1620
2180
  }
1621
2181
 
2182
+ if (args.aiCommand === 'prompt-slice' || args.aiCommand === 'executor-prompt') {
2183
+ runAiPromptSlice(process.cwd(), {
2184
+ slice: args.aiSlice || undefined,
2185
+ });
2186
+ return;
2187
+ }
2188
+
2189
+ if (args.aiCommand === 'execute-plan') {
2190
+ await runAiExecutePlan(process.cwd(), {
2191
+ allowDirty: args.aiAllowDirty,
2192
+ commit: args.aiCommit,
2193
+ context: args.aiContext || undefined,
2194
+ dryRun: args.dryRun,
2195
+ execute: args.aiExecute,
2196
+ json: args.json,
2197
+ mode: args.aiExecutionMode,
2198
+ provider: args.aiProvider,
2199
+ providerExplicit: args.aiProviderExplicit,
2200
+ role: args.aiRole,
2201
+ specSlug: args.specSlug || undefined,
2202
+ timeout: args.aiTimeout,
2203
+ });
2204
+ return;
2205
+ }
2206
+
1622
2207
  if (args.aiCommand === 'doctor') {
1623
2208
  await runAiDoctor(process.cwd(), {
1624
2209
  dryRun: args.dryRun,
@@ -1631,23 +2216,29 @@ async function run(argv) {
1631
2216
 
1632
2217
  if (args.aiCommand === 'pr') {
1633
2218
  await runAiPr(process.cwd(), {
2219
+ baseBranch: args.aiBaseBranch,
2220
+ create: args.aiCreate,
1634
2221
  dryRun: args.dryRun,
2222
+ input: args.aiInput || undefined,
1635
2223
  remote: args.aiRemote || undefined,
1636
2224
  sshHostAlias: args.aiSshHostAlias || undefined,
1637
2225
  identityFile: args.aiIdentityFile || undefined,
2226
+ title: args.aiTitle || undefined,
1638
2227
  });
1639
2228
  return;
1640
2229
  }
1641
2230
 
1642
- throw new Error(formatError(`unsupported ai subcommand: ${args.aiCommand}. Supported tasks: onboard, plan, execute-slice, doctor, pr`));
2231
+ throw new Error(formatError(`unsupported ai subcommand: ${args.aiCommand}. Supported tasks: onboard, plan, review-plan, approve, approvals, agent, prompt-slice, execute-slice, execute-plan, doctor, pr`));
1643
2232
  }
1644
2233
 
1645
2234
  if (args.mode === 'graph') {
1646
2235
  runGraph(process.cwd(), {
1647
2236
  format: args.format,
2237
+ includeCompleted: args.includeCompleted,
1648
2238
  json: args.json,
1649
2239
  level: args.level,
1650
2240
  showConflicts: args.showConflicts,
2241
+ specSlug: args.specSlug,
1651
2242
  unicode: args.unicode,
1652
2243
  });
1653
2244
  return;
@@ -1657,19 +2248,45 @@ async function run(argv) {
1657
2248
  await runNext(process.cwd(), {
1658
2249
  allReady: args.allReady,
1659
2250
  autoStart: args.autoStart,
2251
+ includeCompleted: args.includeCompleted,
1660
2252
  json: args.json,
1661
2253
  specSlug: args.specSlug,
1662
2254
  });
1663
2255
  return;
1664
2256
  }
1665
2257
 
2258
+ if (args.mode === 'evidence') {
2259
+ const result = runEvidence(process.cwd(), {
2260
+ command: args.evidenceArgs,
2261
+ maxOutput: args.evidenceMaxOutput || undefined,
2262
+ output: args.evidenceOutput || undefined,
2263
+ subcommand: args.evidenceCommand,
2264
+ });
2265
+ process.exitCode = result.exitCode;
2266
+ return;
2267
+ }
2268
+
2269
+ if (args.mode === 'demo') {
2270
+ const demoTarget = resolveTargetRoot(process.cwd(), args.targetDirExplicit ? args.targetDir : 'quiver-spec-viewer');
2271
+ runDemo({
2272
+ command: args.demoCommand,
2273
+ demo: args.demoName,
2274
+ dryRun: args.dryRun,
2275
+ targetRoot: demoTarget,
2276
+ });
2277
+ return;
2278
+ }
2279
+
1666
2280
  if (args.mode === 'migrate') {
1667
2281
  runMigrate(args.targetDir, { skipInstall: args.skipInstall });
1668
2282
  return;
1669
2283
  }
1670
2284
 
1671
2285
  if (args.mode === 'doctor') {
1672
- runDoctor(args.targetDir);
2286
+ runDoctor(args.targetDir, {
2287
+ dryRun: args.dryRun,
2288
+ fix: args.doctorFix,
2289
+ });
1673
2290
  return;
1674
2291
  }
1675
2292
 
@@ -1680,7 +2297,10 @@ async function run(argv) {
1680
2297
 
1681
2298
  if (args.mode === 'check-slice') {
1682
2299
  checkSliceReadiness(args.targetDir, {
2300
+ baseBranch: args.baseBranchExplicit ? args.aiBaseBranch : '',
1683
2301
  gate: args.gate,
2302
+ local: args.checkSliceLocal,
2303
+ remote: args.aiRemote,
1684
2304
  strictOverlap: args.strictOverlap,
1685
2305
  });
1686
2306
  return;
@@ -1731,6 +2351,47 @@ async function run(argv) {
1731
2351
  return;
1732
2352
  }
1733
2353
 
2354
+ if (args.mode === 'spec') {
2355
+ if (args.specCommand === 'create') {
2356
+ runCreateSpec(process.cwd(), {
2357
+ dryRun: args.dryRun,
2358
+ input: args.aiInput || undefined,
2359
+ specSlug: args.specSlug || undefined,
2360
+ });
2361
+ return;
2362
+ }
2363
+
2364
+ if (!args.targetDir || args.targetDir === '.') {
2365
+ throw new Error(formatError('missing spec directory. Use: npx create-quiver spec <start|status|close> <spec-dir>'));
2366
+ }
2367
+
2368
+ if (args.specCommand === 'start') {
2369
+ const report = startSpecWorktree(process.cwd(), args.targetDir);
2370
+ process.stdout.write(formatSpecStartResult(report));
2371
+ return;
2372
+ }
2373
+
2374
+ if (args.specCommand === 'status') {
2375
+ const report = buildSpecStatus(process.cwd(), args.targetDir);
2376
+ process.stdout.write(formatSpecStatus(report));
2377
+ return;
2378
+ }
2379
+
2380
+ if (args.specCommand === 'close') {
2381
+ const report = closeSpecWorktree(process.cwd(), args.targetDir, {
2382
+ baseBranch: args.aiBaseBranch,
2383
+ discard: args.discard,
2384
+ dryRun: args.dryRun,
2385
+ force: args.force,
2386
+ remote: args.aiRemote,
2387
+ });
2388
+ process.stdout.write(formatSpecCloseResult(report));
2389
+ return;
2390
+ }
2391
+
2392
+ throw new Error(formatError(`unsupported spec subcommand: ${args.specCommand}. Supported tasks: create, start, status, close`));
2393
+ }
2394
+
1734
2395
  const packageRoot = path.resolve(__dirname, '../..');
1735
2396
  const targetDir = resolveTargetRoot(process.cwd(), args.targetDir);
1736
2397
  const projectName = args.projectName || path.basename(targetDir) || 'Quiver Project';
@@ -1785,6 +2446,8 @@ async function run(argv) {
1785
2446
  module.exports = {
1786
2447
  runAnalyze,
1787
2448
  runDoctor,
2449
+ runFlow,
1788
2450
  runMigrate,
2451
+ runPrepare,
1789
2452
  run,
1790
2453
  };