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
@@ -2,10 +2,34 @@ const fs = require('node:fs');
2
2
  const path = require('node:path');
3
3
 
4
4
  const { buildContextPackMetadata, normalizeRole } = require('../lib/ai/context-packs');
5
- const { runExecuteSlice } = require('../lib/ai/executor');
6
- const { formatPreflightReport, preflightGitHubPr } = require('../lib/ai/github');
5
+ const { runExecuteSlice, runPromptSlice } = require('../lib/ai/executor');
6
+ const { runExecutePlan } = require('../lib/ai/execution-plan');
7
+ const { buildPrCreatePlan, formatPreflightReport, formatPrCreateReport, preflightGitHubPr, runGhPrCreate } = require('../lib/ai/github');
8
+ const { buildContextPreparationDrafts, buildPlannerOnboardingPrompt } = require('../lib/ai/onboarding-template');
9
+ const {
10
+ PLAN_REVIEW_PROMPT_SOURCE,
11
+ buildPlanReviewPrompt,
12
+ resolveReviewedTechnicalPlanInput,
13
+ resolveTechnicalPlanReviewInput,
14
+ savePlanReview,
15
+ summarizePlanReview,
16
+ } = require('../lib/ai/plan-review');
7
17
  const { buildSpecGenerationManifest, describeSpecGeneration, generateSpecArtifacts } = require('../lib/ai/spec-generator');
8
18
  const { buildProviderInvocation, runProvider } = require('../lib/ai/providers');
19
+ const {
20
+ agentProfilesPath,
21
+ getAgentProfile,
22
+ listAgentProfiles,
23
+ resolveProfileProvider,
24
+ setAgentProfile,
25
+ } = require('../lib/agent-profiles');
26
+ const {
27
+ PLANNER_APPROVAL_PHASES,
28
+ approvePlannerPhase,
29
+ resolveApprovedPlannerInput,
30
+ savePlannerDraft,
31
+ summarizePlannerApproval,
32
+ } = require('../lib/approvals');
9
33
  const { assertPlannerPhaseReady, getPlannerPhaseDetails, normalizePlannerPhase, PlannerPhaseError } = require('../lib/ai/phase-gates');
10
34
 
11
35
  const DEFAULT_ONBOARD_PROVIDER = 'codex';
@@ -33,6 +57,14 @@ function readTextFile(filePath, repoRoot) {
33
57
  return fs.readFileSync(resolved, 'utf8');
34
58
  }
35
59
 
60
+ function readTextFileOrEmpty(filePath, repoRoot) {
61
+ if (!filePath) {
62
+ return '';
63
+ }
64
+
65
+ return readTextFile(filePath, repoRoot);
66
+ }
67
+
36
68
  function normalizeTimeout(timeoutMs) {
37
69
  if (timeoutMs === undefined || timeoutMs === null || timeoutMs === '') {
38
70
  return undefined;
@@ -46,6 +78,13 @@ function normalizeTimeout(timeoutMs) {
46
78
  return parsed;
47
79
  }
48
80
 
81
+ function resolveProviderForProfile(repoRoot, role, provider, providerExplicit, fallbackProvider) {
82
+ if (providerExplicit === true || (provider && providerExplicit !== false)) {
83
+ return String(provider || fallbackProvider).trim().toLowerCase();
84
+ }
85
+ return resolveProfileProvider(repoRoot, role, fallbackProvider);
86
+ }
87
+
49
88
  function buildPlanContext({ role, context, phase, inputText, inputPath, repoRoot }) {
50
89
  const phaseDetails = getPlannerPhaseDetails(phase);
51
90
  const pack = buildContextPackMetadata({
@@ -88,32 +127,21 @@ function buildOnboardContext({ role, context, inputText, inputPath, repoRoot })
88
127
  repoRoot,
89
128
  });
90
129
  const relativeInputPath = inputPath ? path.relative(repoRoot, path.resolve(repoRoot, inputPath)).split(path.sep).join('/') : '';
91
- const sections = [
92
- pack.prompt,
93
- 'Task: onboard the project context for planning.',
94
- 'Read Quiver context, the WDD/SDD workflow, the project scan/map, assumptions, risks, and relevant docs.',
95
- 'Do not modify product code.',
96
- ];
97
-
98
- if (relativeInputPath) {
99
- sections.push(`Input file: ${relativeInputPath}`);
100
- }
101
-
102
- if (pack.scanArtifact) {
103
- sections.push(`Project scan artifact: ${pack.scanArtifact.path} (${pack.scanArtifact.source})`);
104
- }
105
-
106
- if (inputText) {
107
- sections.push('Input:', inputText.trimEnd());
108
- }
130
+ const built = buildPlannerOnboardingPrompt({
131
+ pack,
132
+ inputText,
133
+ inputPath: relativeInputPath,
134
+ repoRoot,
135
+ });
109
136
 
110
137
  return {
111
138
  pack,
112
- prompt: sections.join('\n\n'),
139
+ plan: built.plan,
140
+ prompt: built.prompt,
113
141
  };
114
142
  }
115
143
 
116
- function formatDryRunReport({ task, provider, role, contextPack, phase, invocation }) {
144
+ function formatDryRunReport({ task, provider, role, contextPack, phase, invocation, onboardingPlan }) {
117
145
  const lines = [
118
146
  `AI ${task} dry-run`,
119
147
  `Provider: ${provider}`,
@@ -130,6 +158,50 @@ function formatDryRunReport({ task, provider, role, contextPack, phase, invocati
130
158
  lines.push(`Prompt transport: ${invocation.promptTransport.mode}`);
131
159
  lines.push(`Prompt length: ${invocation.promptLength} bytes`);
132
160
 
161
+ if (onboardingPlan) {
162
+ lines.push(`Prompt source: ${onboardingPlan.promptSource}`);
163
+ lines.push(`Selected docs: ${onboardingPlan.selectedDocs.length}`);
164
+ lines.push(`Documentation debt: ${onboardingPlan.missingDocs.length}`);
165
+ }
166
+
167
+ return `${lines.join('\n')}\n`;
168
+ }
169
+
170
+ function formatPathList(items, emptyLabel = 'none') {
171
+ if (!Array.isArray(items) || items.length === 0) {
172
+ return [`- ${emptyLabel}`];
173
+ }
174
+
175
+ return items.map((item) => `- ${item}`);
176
+ }
177
+
178
+ function formatContextPreparationReport({ dryRun, plan, docs, writtenDocs }) {
179
+ const lines = [
180
+ dryRun ? 'AI prepare-context dry-run' : 'AI prepare-context completed',
181
+ `Mode: ${dryRun ? 'dry-run' : 'live'}`,
182
+ `Project: ${plan.projectName}`,
183
+ `Project slug: ${plan.projectSlug}`,
184
+ 'Writes: docs-only',
185
+ 'Product code: untouched',
186
+ `Proposed docs: ${docs.length > 0 ? docs.map((doc) => doc.path).join(', ') : 'none'}`,
187
+ ];
188
+
189
+ if (!dryRun) {
190
+ lines.push(`Written docs: ${writtenDocs.length > 0 ? writtenDocs.join(', ') : 'none'}`);
191
+ }
192
+
193
+ lines.push(
194
+ 'Files considered:',
195
+ ...plan.filesConsidered.map((item) => `- ${item.path}: ${item.present ? 'present' : 'absent'}${item.reason ? ` (${item.reason})` : ''}`),
196
+ 'Assumptions:',
197
+ ...formatPathList(plan.assumptions),
198
+ 'Risks:',
199
+ ...formatPathList(plan.risks),
200
+ 'Omitted paths:',
201
+ ...formatPathList(plan.omittedPaths),
202
+ 'Uncertainty markers: TODO | Assumption | Pending confirmation',
203
+ );
204
+
133
205
  return `${lines.join('\n')}\n`;
134
206
  }
135
207
 
@@ -142,6 +214,17 @@ function writeProviderOutput(result) {
142
214
  }
143
215
  }
144
216
 
217
+ function writeDraftDocs(repoRoot, drafts) {
218
+ const writtenDocs = [];
219
+ for (const draft of drafts) {
220
+ const destinationPath = path.join(repoRoot, draft.path);
221
+ fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
222
+ fs.writeFileSync(destinationPath, `${draft.content.replace(/\s+$/g, '')}\n`);
223
+ writtenDocs.push(draft.path);
224
+ }
225
+ return writtenDocs;
226
+ }
227
+
145
228
  function formatSpecDryRunReport({ manifest, repoRoot }) {
146
229
  const preview = describeSpecGeneration(manifest, repoRoot);
147
230
  const relativeSpecDir = path.relative(repoRoot, preview.specDir).split(path.sep).join('/');
@@ -178,6 +261,43 @@ function formatSpecGenerationResult(result, repoRoot) {
178
261
  return `${lines.join('\n')}\n`;
179
262
  }
180
263
 
264
+ function formatApprovalResult(result, repoRoot) {
265
+ const relativePath = path.relative(repoRoot, result.filePath).split(path.sep).join('/');
266
+ const lines = [
267
+ 'AI approval saved',
268
+ `Phase: ${result.phase}`,
269
+ `Status: approved`,
270
+ `Artifact: ${relativePath}`,
271
+ `Source file: ${result.sourceFile}`,
272
+ `Timestamp: ${result.createdAt}`,
273
+ ];
274
+ if (result.version) {
275
+ lines.push(`Version: v${result.version}`);
276
+ }
277
+
278
+ return `${lines.join('\n')}\n`;
279
+ }
280
+
281
+ function formatApprovalDryRunResult({ phase, input, version }) {
282
+ const lines = ['AI approval dry-run', `Phase: ${phase}`];
283
+ if (version) {
284
+ lines.push(`Version: v${version}`);
285
+ }
286
+ if (input) {
287
+ lines.push(`Input file: ${input}`);
288
+ }
289
+ return `${lines.join('\n')}\n`;
290
+ }
291
+
292
+ function formatApprovalStatusReport(repoRoot) {
293
+ const sections = ['AI approvals status'];
294
+ for (const phase of PLANNER_APPROVAL_PHASES) {
295
+ sections.push(summarizePlannerApproval(repoRoot, phase).trimEnd());
296
+ }
297
+ sections.push(summarizePlanReview(repoRoot).trimEnd());
298
+ return `${sections.join('\n\n')}\n`;
299
+ }
300
+
181
301
  function annotateProviderError(error, scope, phase) {
182
302
  const phaseLabel = phase ? ` phase '${phase}'` : '';
183
303
  const message = error && error.message ? error.message : String(error);
@@ -198,12 +318,13 @@ function annotateGitHubError(error, scope) {
198
318
  }
199
319
 
200
320
  async function runOnboard(repoRoot, options = {}) {
201
- const provider = String(options.provider || DEFAULT_ONBOARD_PROVIDER).trim().toLowerCase();
202
321
  const role = normalizeRole(options.role || DEFAULT_ONBOARD_ROLE);
322
+ const provider = resolveProviderForProfile(repoRoot, role, options.provider, options.providerExplicit, DEFAULT_ONBOARD_PROVIDER);
203
323
  const context = options.context || DEFAULT_ONBOARD_CONTEXT;
204
324
  const timeoutMs = normalizeTimeout(options.timeout);
205
325
  const inputText = readTextFile(options.input, repoRoot);
206
- const prompt = buildOnboardContext({ role, context, inputText, inputPath: options.input, repoRoot }).prompt;
326
+ const contextInfo = buildOnboardContext({ role, context, inputText, inputPath: options.input, repoRoot });
327
+ const prompt = contextInfo.prompt;
207
328
  let invocation;
208
329
 
209
330
  try {
@@ -223,6 +344,7 @@ async function runOnboard(repoRoot, options = {}) {
223
344
  role,
224
345
  contextPack: context,
225
346
  invocation,
347
+ onboardingPlan: contextInfo.plan,
226
348
  };
227
349
  process.stdout.write(formatDryRunReport(report));
228
350
  return report;
@@ -257,25 +379,58 @@ async function runOnboard(repoRoot, options = {}) {
257
379
  role,
258
380
  contextPack: context,
259
381
  invocation,
382
+ onboardingPlan: contextInfo.plan,
260
383
  result,
261
384
  };
262
385
  }
263
386
 
387
+ async function runPrepareContext(repoRoot, options = {}) {
388
+ const draftPack = buildContextPreparationDrafts(repoRoot);
389
+ const report = {
390
+ task: 'prepare-context',
391
+ dryRun: options.dryRun === true,
392
+ docs: draftPack.docs.map((doc) => doc.path),
393
+ plan: draftPack.plan,
394
+ };
395
+
396
+ if (options.dryRun) {
397
+ process.stdout.write(formatContextPreparationReport({
398
+ dryRun: true,
399
+ docs: draftPack.docs,
400
+ plan: draftPack.plan,
401
+ writtenDocs: [],
402
+ }));
403
+ return report;
404
+ }
405
+
406
+ const writtenDocs = writeDraftDocs(repoRoot, draftPack.docs);
407
+ process.stdout.write(formatContextPreparationReport({
408
+ dryRun: false,
409
+ docs: draftPack.docs,
410
+ plan: draftPack.plan,
411
+ writtenDocs,
412
+ }));
413
+
414
+ return {
415
+ ...report,
416
+ writtenDocs,
417
+ };
418
+ }
419
+
264
420
  async function runPlan(repoRoot, options = {}) {
265
421
  const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
266
422
  const role = normalizeRole(options.role || DEFAULT_PLAN_ROLE);
267
- const provider = String(options.provider || DEFAULT_PLAN_PROVIDER).trim().toLowerCase();
423
+ const provider = resolveProviderForProfile(repoRoot, role, options.provider, options.providerExplicit, DEFAULT_PLAN_PROVIDER);
268
424
  const context = options.context || DEFAULT_PLAN_CONTEXT;
269
425
  const timeoutMs = normalizeTimeout(options.timeout);
270
-
271
- if (!options.input) {
272
- throw new Error(formatError(`missing input file for ai plan phase '${phase}'`));
273
- }
426
+ let inputPath = options.input || '';
274
427
 
275
428
  if (phase === 'spec') {
276
- const inputText = readTextFile(options.input, repoRoot);
429
+ const resolved = resolveReviewedTechnicalPlanInput(repoRoot, inputPath || undefined);
430
+ inputPath = resolved.inputPath;
431
+ const inputText = readTextFileOrEmpty(inputPath, repoRoot);
277
432
  const manifest = buildSpecGenerationManifest({
278
- inputPath: options.input,
433
+ inputPath,
279
434
  inputText,
280
435
  repoRoot,
281
436
  specSlug: options.specSlug,
@@ -292,7 +447,7 @@ async function runPlan(repoRoot, options = {}) {
292
447
  }
293
448
 
294
449
  const result = generateSpecArtifacts(repoRoot, {
295
- input: options.input,
450
+ input: inputPath,
296
451
  specSlug: options.specSlug,
297
452
  });
298
453
  process.stdout.write(formatSpecGenerationResult(result, repoRoot));
@@ -309,13 +464,22 @@ async function runPlan(repoRoot, options = {}) {
309
464
 
310
465
  assertPlannerPhaseReady(phase);
311
466
 
312
- const inputText = readTextFile(options.input, repoRoot);
467
+ if (phase === 'technical-plan') {
468
+ const resolved = resolveApprovedPlannerInput(repoRoot, phase, inputPath || undefined);
469
+ inputPath = resolved.inputPath;
470
+ }
471
+
472
+ if (!inputPath) {
473
+ throw new Error(formatError(`missing input file for ai plan phase '${phase}'`));
474
+ }
475
+
476
+ const inputText = readTextFile(inputPath, repoRoot);
313
477
  const contextInfo = buildPlanContext({
314
478
  role,
315
479
  context,
316
480
  phase,
317
481
  inputText,
318
- inputPath: options.input,
482
+ inputPath,
319
483
  repoRoot,
320
484
  });
321
485
  const prompt = contextInfo.prompt;
@@ -367,6 +531,8 @@ async function runPlan(repoRoot, options = {}) {
367
531
  throw annotateProviderError(result.error || new Error('provider run failed'), 'plan', phase);
368
532
  }
369
533
 
534
+ savePlannerDraft(repoRoot, phase, inputPath, [result.stdout, result.stderr].filter(Boolean).join(''));
535
+
370
536
  return {
371
537
  task: 'plan',
372
538
  provider,
@@ -378,6 +544,244 @@ async function runPlan(repoRoot, options = {}) {
378
544
  };
379
545
  }
380
546
 
547
+ async function runReviewPlan(repoRoot, options = {}) {
548
+ const role = 'planner';
549
+ const provider = resolveProviderForProfile(repoRoot, 'reviewer', options.provider, options.providerExplicit, DEFAULT_PLAN_PROVIDER);
550
+ const context = options.context || DEFAULT_PLAN_CONTEXT;
551
+ const timeoutMs = normalizeTimeout(options.timeout);
552
+ const resolved = resolveTechnicalPlanReviewInput(repoRoot, options.input || undefined);
553
+ const inputPath = resolved.inputPath;
554
+ const inputText = readTextFile(inputPath, repoRoot);
555
+ const pack = buildContextPackMetadata({
556
+ role,
557
+ packName: context,
558
+ repoRoot,
559
+ });
560
+ const built = buildPlanReviewPrompt({
561
+ pack,
562
+ inputText,
563
+ inputPath,
564
+ });
565
+ let invocation;
566
+
567
+ try {
568
+ invocation = buildProviderInvocation(provider, {
569
+ prompt: built.prompt,
570
+ cwd: repoRoot,
571
+ timeoutMs,
572
+ });
573
+ } catch (error) {
574
+ throw annotateProviderError(error, 'review-plan');
575
+ }
576
+
577
+ if (options.dryRun) {
578
+ const report = {
579
+ task: 'review-plan',
580
+ provider,
581
+ role: 'reviewer',
582
+ contextPack: pack.packName,
583
+ invocation,
584
+ promptSource: built.promptSource,
585
+ inputPath,
586
+ inputKind: resolved.kind,
587
+ inputVersion: resolved.version,
588
+ };
589
+ process.stdout.write(formatDryRunReport({
590
+ task: 'review-plan',
591
+ provider,
592
+ role: 'reviewer',
593
+ contextPack: pack.packName,
594
+ phase: 'plan-review',
595
+ invocation,
596
+ }));
597
+ process.stdout.write(`Prompt source: ${built.promptSource}\n`);
598
+ process.stdout.write(`Input file: ${inputPath}\n`);
599
+ process.stdout.write(`Input kind: ${resolved.kind}\n`);
600
+ if (resolved.version) {
601
+ process.stdout.write(`Input version: v${resolved.version}\n`);
602
+ }
603
+ return report;
604
+ }
605
+
606
+ let result;
607
+ try {
608
+ result = await (options.runProviderFn || runProvider)(provider, {
609
+ prompt: built.prompt,
610
+ cwd: repoRoot,
611
+ timeoutMs,
612
+ dryRun: false,
613
+ probe: options.probe,
614
+ spawn: options.spawn,
615
+ tempRoot: options.tempRoot,
616
+ tempFileName: options.tempFileName,
617
+ tempFilePrefix: options.tempFilePrefix,
618
+ });
619
+ } catch (error) {
620
+ throw annotateProviderError(error, 'review-plan');
621
+ }
622
+
623
+ writeProviderOutput(result);
624
+
625
+ if (!result.ok) {
626
+ throw annotateProviderError(result.error || new Error('provider run failed'), 'review-plan');
627
+ }
628
+
629
+ const saved = savePlanReview(repoRoot, {
630
+ contents: [result.stdout, result.stderr].filter(Boolean).join(''),
631
+ inputPath,
632
+ inputKind: resolved.kind,
633
+ inputVersion: resolved.version,
634
+ });
635
+ const relativePath = path.relative(repoRoot, saved.filePath).split(path.sep).join('/');
636
+ process.stdout.write(`AI plan review saved\nArtifact: ${relativePath}\nPrompt source: ${PLAN_REVIEW_PROMPT_SOURCE}\n`);
637
+
638
+ return {
639
+ task: 'review-plan',
640
+ provider,
641
+ role: 'reviewer',
642
+ contextPack: pack.packName,
643
+ inputPath,
644
+ inputKind: resolved.kind,
645
+ inputVersion: resolved.version,
646
+ filePath: relativePath,
647
+ invocation,
648
+ result,
649
+ };
650
+ }
651
+
652
+ async function runApprove(repoRoot, options = {}) {
653
+ const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
654
+ if (phase === 'spec') {
655
+ throw new Error(formatError(`ai approve does not support phase '${phase}'`));
656
+ }
657
+
658
+ if (!options.input && !options.version) {
659
+ throw new Error(formatError(`missing input file for ai approve phase '${phase}'`));
660
+ }
661
+
662
+ const inputText = options.version ? '' : readTextFile(options.input, repoRoot);
663
+
664
+ if (options.dryRun) {
665
+ process.stdout.write(formatApprovalDryRunResult({ phase, input: options.input, version: options.version }));
666
+ return {
667
+ task: 'approve',
668
+ phase,
669
+ input: options.input,
670
+ version: options.version || null,
671
+ dryRun: true,
672
+ };
673
+ }
674
+
675
+ const result = approvePlannerPhase(repoRoot, phase, options.input || '', inputText, {
676
+ version: options.version || undefined,
677
+ });
678
+ process.stdout.write(formatApprovalResult({
679
+ ...result,
680
+ sourceFile: options.input || `draft version ${options.version}`,
681
+ }, repoRoot));
682
+
683
+ return {
684
+ task: 'approve',
685
+ phase,
686
+ input: options.input,
687
+ filePath: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
688
+ createdAt: result.createdAt,
689
+ version: result.version || null,
690
+ };
691
+ }
692
+
693
+ async function runApprovalStatus(repoRoot) {
694
+ const report = formatApprovalStatusReport(repoRoot);
695
+ process.stdout.write(report);
696
+ return {
697
+ task: 'approval-status',
698
+ report,
699
+ };
700
+ }
701
+
702
+ function formatAgentProfile(profile) {
703
+ const lines = [
704
+ `Role: ${profile.role}`,
705
+ `Provider: ${profile.provider}`,
706
+ `Model: ${profile.model || '(not set)'}`,
707
+ `Label: ${profile.label || '(not set)'}`,
708
+ `Context: ${profile.context || '(not set)'}`,
709
+ `Updated: ${profile.updated_at}`,
710
+ ];
711
+ return `${lines.join('\n')}\n`;
712
+ }
713
+
714
+ function formatAgentProfileList(profiles) {
715
+ const lines = ['AI agent profiles'];
716
+ for (const item of profiles) {
717
+ if (!item.configured) {
718
+ lines.push(`- ${item.role}: not configured`);
719
+ continue;
720
+ }
721
+ const model = item.profile.model ? ` model=${item.profile.model}` : '';
722
+ const label = item.profile.label ? ` label=${item.profile.label}` : '';
723
+ lines.push(`- ${item.role}: provider=${item.profile.provider}${model}${label}`);
724
+ }
725
+ return `${lines.join('\n')}\n`;
726
+ }
727
+
728
+ function runAgent(repoRoot, options = {}) {
729
+ const command = String(options.command || '').trim().toLowerCase();
730
+
731
+ if (command === 'set') {
732
+ if (!options.role) {
733
+ throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent set <planner|executor|reviewer|researcher> --provider <provider>'));
734
+ }
735
+ if (!options.provider) {
736
+ throw new Error(formatError('ai agent set requires --provider. Supported providers: codex, claude, gemini.'));
737
+ }
738
+ const result = setAgentProfile(repoRoot, options.role, {
739
+ context: options.context,
740
+ label: options.label,
741
+ model: options.model,
742
+ provider: options.provider,
743
+ });
744
+ process.stdout.write('AI agent profile saved\n');
745
+ process.stdout.write(formatAgentProfile(result.profile));
746
+ process.stdout.write(`State: ${path.relative(repoRoot, result.filePath).split(path.sep).join('/')}\n`);
747
+ return {
748
+ task: 'agent',
749
+ command,
750
+ profile: result.profile,
751
+ filePath: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
752
+ };
753
+ }
754
+
755
+ if (command === 'show') {
756
+ if (!options.role) {
757
+ throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent show <planner|executor|reviewer|researcher>'));
758
+ }
759
+ const profile = getAgentProfile(repoRoot, options.role);
760
+ if (!profile) {
761
+ throw new Error(formatError(`agent profile '${options.role}' is not configured. Run: npx create-quiver ai agent set ${options.role} --provider <provider> --model <label>`));
762
+ }
763
+ process.stdout.write(formatAgentProfile(profile));
764
+ return {
765
+ task: 'agent',
766
+ command,
767
+ profile,
768
+ };
769
+ }
770
+
771
+ if (command === 'list' || command === 'ls' || command === '') {
772
+ const profiles = listAgentProfiles(repoRoot);
773
+ process.stdout.write(formatAgentProfileList(profiles));
774
+ process.stdout.write(`State: ${path.relative(repoRoot, agentProfilesPath(repoRoot)).split(path.sep).join('/')}\n`);
775
+ return {
776
+ task: 'agent',
777
+ command: 'list',
778
+ profiles,
779
+ };
780
+ }
781
+
782
+ throw new Error(formatError(`unsupported ai agent subcommand: ${command}. Supported tasks: set, list, show`));
783
+ }
784
+
381
785
  async function runGitHubTask(repoRoot, options = {}, mode = 'pr') {
382
786
  const dryRun = options.dryRun === true;
383
787
  let report;
@@ -409,7 +813,69 @@ async function runGitHubTask(repoRoot, options = {}, mode = 'pr') {
409
813
  }
410
814
 
411
815
  async function runPr(repoRoot, options = {}) {
412
- return runGitHubTask(repoRoot, options, 'pr');
816
+ const dryRun = options.dryRun === true;
817
+ const create = options.create === true;
818
+ let preflight;
819
+
820
+ try {
821
+ preflight = await (options.preflightFn || preflightGitHubPr)(repoRoot, {
822
+ remote: options.remote,
823
+ sshHostAlias: options.sshHostAlias,
824
+ identityFile: options.identityFile,
825
+ gitFlowGuidePath: options.gitFlowGuidePath,
826
+ ghCommand: options.ghCommand,
827
+ ghProbe: options.ghProbe,
828
+ ghAuthProbe: options.ghAuthProbe,
829
+ ghProbeArgs: options.ghProbeArgs,
830
+ ghAuthArgs: options.ghAuthArgs,
831
+ blockedBranches: options.blockedBranches,
832
+ });
833
+ } catch (error) {
834
+ throw annotateGitHubError(error, 'pr');
835
+ }
836
+
837
+ let plan;
838
+ try {
839
+ plan = buildPrCreatePlan(repoRoot, preflight, {
840
+ baseBranch: options.baseBranch,
841
+ ghCommand: options.ghCommand,
842
+ input: options.input,
843
+ prBodyPath: options.prBodyPath,
844
+ title: options.title,
845
+ });
846
+ } catch (error) {
847
+ throw annotateGitHubError(error, 'pr');
848
+ }
849
+
850
+ if (dryRun || !create) {
851
+ process.stdout.write(formatPrCreateReport({ preflight, plan }, { dryRun, create }));
852
+ return {
853
+ task: 'pr',
854
+ dryRun,
855
+ create,
856
+ preflight,
857
+ plan,
858
+ };
859
+ }
860
+
861
+ let result;
862
+ try {
863
+ result = runGhPrCreate(plan, {
864
+ ghCreateRunner: options.ghCreateRunner,
865
+ });
866
+ } catch (error) {
867
+ throw annotateGitHubError(error, 'pr');
868
+ }
869
+
870
+ process.stdout.write(formatPrCreateReport({ preflight, plan, result }, { dryRun: false, create: true }));
871
+ return {
872
+ task: 'pr',
873
+ dryRun: false,
874
+ create: true,
875
+ preflight,
876
+ plan,
877
+ result,
878
+ };
413
879
  }
414
880
 
415
881
  async function runDoctor(repoRoot, options = {}) {
@@ -433,8 +899,15 @@ module.exports = {
433
899
  formatSpecDryRunReport,
434
900
  normalizeTimeout,
435
901
  readTextFile,
902
+ runAgent,
436
903
  runDoctor,
904
+ runExecutePlan,
437
905
  runExecuteSlice,
906
+ runPromptSlice,
907
+ runApprove,
908
+ runApprovalStatus,
909
+ runPrepareContext,
910
+ runReviewPlan,
438
911
  runPr,
439
912
  runOnboard,
440
913
  runPlan,
@@ -0,0 +1,22 @@
1
+ const { buildDemoPlan, formatDemoPlan, writeDemoPlan } = require('../lib/demo');
2
+
3
+ function runDemo(options = {}) {
4
+ if (options.command !== 'create') {
5
+ throw new Error(`create-quiver: unsupported demo subcommand: ${options.command || '(missing)'}. Supported tasks: create`);
6
+ }
7
+
8
+ const plan = buildDemoPlan(options.targetRoot, { demo: options.demo });
9
+
10
+ if (options.dryRun) {
11
+ console.log(formatDemoPlan(plan, { dryRun: true }));
12
+ return plan;
13
+ }
14
+
15
+ writeDemoPlan(plan);
16
+ console.log(formatDemoPlan(plan));
17
+ return plan;
18
+ }
19
+
20
+ module.exports = {
21
+ runDemo,
22
+ };