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
@@ -1,8 +1,11 @@
1
1
  const fs = require('node:fs');
2
2
  const path = require('node:path');
3
+ const cp = require('node:child_process');
3
4
 
4
5
  const { buildContextPackMetadata, normalizeRole } = require('./context-packs');
5
6
  const { buildProviderInvocation, runProvider } = require('./providers');
7
+ const { resolveProfileProvider } = require('../agent-profiles');
8
+ const { runGit } = require('../git');
6
9
  const { captureWorktreeSnapshot, validateScopeSnapshot } = require('../scope');
7
10
  const { resolveSliceContext } = require('../slice');
8
11
 
@@ -74,6 +77,199 @@ function formatList(items) {
74
77
  return items.map((item) => `- ${item}`);
75
78
  }
76
79
 
80
+ function extractMarkdownHeading(text) {
81
+ const match = String(text || '').match(/^#\s+(.+)$/m);
82
+ return match ? match[1].trim() : '';
83
+ }
84
+
85
+ function extractMarkdownSection(text, headings) {
86
+ const lines = String(text || '').split(/\r?\n/);
87
+ const normalized = new Set(headings.map((heading) => String(heading).trim().toLowerCase()));
88
+ const section = [];
89
+ let capture = false;
90
+
91
+ for (const line of lines) {
92
+ const heading = line.match(/^##\s+(.+)$/);
93
+ if (heading) {
94
+ const key = heading[1].trim().toLowerCase();
95
+ if (normalized.has(key)) {
96
+ capture = true;
97
+ continue;
98
+ }
99
+ if (capture) {
100
+ break;
101
+ }
102
+ }
103
+
104
+ if (capture) {
105
+ section.push(line);
106
+ }
107
+ }
108
+
109
+ return section.join('\n').trim();
110
+ }
111
+
112
+ function buildSpecExcerpt(repoRoot, slice) {
113
+ const specPath = path.join(slice.specDirAbs, 'SPEC.md');
114
+ if (!fs.existsSync(specPath)) {
115
+ return {
116
+ path: toRelativePath(repoRoot, specPath),
117
+ lines: ['- n/a'],
118
+ };
119
+ }
120
+
121
+ const text = fs.readFileSync(specPath, 'utf8');
122
+ const title = extractMarkdownHeading(text);
123
+ const objective = extractMarkdownSection(text, ['Objective', 'Objetivo']);
124
+ const lines = [];
125
+
126
+ if (title) {
127
+ lines.push(`- Title: ${title}`);
128
+ }
129
+ if (objective) {
130
+ lines.push(`- Objective: ${objective.replace(/\s+/g, ' ').slice(0, 500)}`);
131
+ }
132
+ if (lines.length === 0) {
133
+ lines.push('- SPEC.md exists, but no short title/objective excerpt was found.');
134
+ }
135
+
136
+ return {
137
+ path: toRelativePath(repoRoot, specPath),
138
+ lines,
139
+ };
140
+ }
141
+
142
+ function buildManualExecutorPrompt({ repoRoot, slicePath, role, context, tokenLimit } = {}) {
143
+ const executorContext = buildExecuteSliceContext({
144
+ repoRoot,
145
+ slicePath,
146
+ role: role || DEFAULT_EXECUTE_ROLE,
147
+ context: context || DEFAULT_EXECUTE_CONTEXT,
148
+ });
149
+ const canonicalRepoRoot = canonicalizeRepoRoot(repoRoot);
150
+ const closurePath = path.join(path.dirname(executorContext.slice.sliceAbs), 'CLOSURE_BRIEF.md');
151
+ const closureText = readTextFile(closurePath, canonicalRepoRoot);
152
+ const relativeClosurePath = toRelativePath(canonicalRepoRoot, closurePath);
153
+ const specExcerpt = buildSpecExcerpt(canonicalRepoRoot, executorContext.slice);
154
+ const slice = executorContext.slice;
155
+ const objective = String(slice.json.objective || slice.json.description || slice.sliceId).trim();
156
+ const restrictions = [
157
+ 'Do not read the whole repo.',
158
+ 'Do not modify files outside the allowed files.',
159
+ 'Before editing, list the files you will read and the files you expect to modify.',
160
+ 'Do not add unrequested features.',
161
+ 'Do not refactor architecture unless the slice explicitly requires it.',
162
+ 'If another file is needed, justify why before reading it.',
163
+ 'If blocked, stop and report the blocker before improvising.',
164
+ ];
165
+ const outputFormat = [
166
+ '## Cambios realizados',
167
+ '## Archivos modificados',
168
+ '## Comandos ejecutados',
169
+ '## Validaciones',
170
+ '## Riesgos pendientes',
171
+ '## Proximo paso recomendado',
172
+ ];
173
+ const promptLines = [
174
+ 'Act as a WDD + SDD executor agent.',
175
+ '',
176
+ 'MODE: controlled SLICE execution.',
177
+ '',
178
+ 'Slice objective:',
179
+ objective || 'n/a',
180
+ '',
181
+ 'Minimal context:',
182
+ `- Spec: ${slice.specSlug}`,
183
+ `- Slice: ${slice.sliceId}`,
184
+ `- Slice file: ${slice.sliceRel}`,
185
+ `- Execution brief: ${executorContext.briefPath}`,
186
+ `- Closure brief: ${relativeClosurePath}`,
187
+ '',
188
+ 'Relevant SPEC excerpts:',
189
+ `- Source: ${specExcerpt.path}`,
190
+ ...specExcerpt.lines,
191
+ '',
192
+ 'Allowed files:',
193
+ ...formatList(executorContext.allowedFiles),
194
+ '',
195
+ 'Restrictions:',
196
+ ...formatList(restrictions),
197
+ '',
198
+ 'Acceptance criteria:',
199
+ ...formatList(slice.acceptance),
200
+ '',
201
+ 'Validation commands:',
202
+ ...formatList(executorContext.validationCommands),
203
+ '',
204
+ 'Exact deliverable expected:',
205
+ '- Implement only this slice.',
206
+ '- Keep the change inside the allowed files.',
207
+ '- Leave evidence in the final report.',
208
+ '',
209
+ 'Required final report format:',
210
+ ...outputFormat.map((line) => `- ${line}`),
211
+ '',
212
+ `Suggested token limit: ${Number(tokenLimit) > 0 ? Number(tokenLimit) : 3000}`,
213
+ '',
214
+ 'Execution brief content:',
215
+ executorContext.briefText.trimEnd(),
216
+ '',
217
+ 'Closure brief content:',
218
+ closureText.trimEnd(),
219
+ ];
220
+
221
+ return {
222
+ allowedFiles: executorContext.allowedFiles,
223
+ closurePath: relativeClosurePath,
224
+ prompt: `${promptLines.join('\n')}\n`,
225
+ slice,
226
+ specExcerpt,
227
+ validationCommands: executorContext.validationCommands,
228
+ };
229
+ }
230
+
231
+ function runPromptSlice(repoRoot, options = {}) {
232
+ if (!options.slice) {
233
+ throw new Error(formatError('missing required --slice path for ai prompt-slice'));
234
+ }
235
+
236
+ const built = buildManualExecutorPrompt({
237
+ repoRoot,
238
+ slicePath: options.slice,
239
+ tokenLimit: options.tokenLimit,
240
+ });
241
+ process.stdout.write(built.prompt);
242
+
243
+ return {
244
+ task: 'prompt-slice',
245
+ slice: built.slice.sliceId,
246
+ specSlug: built.slice.specSlug,
247
+ prompt: built.prompt,
248
+ };
249
+ }
250
+
251
+ function buildRecoveryGuidance(slice) {
252
+ const sliceRef = slice && slice.sliceRel ? slice.sliceRel : '<slice.json>';
253
+ return [
254
+ 'Recovery:',
255
+ `- Retry: npx create-quiver ai execute-slice --slice ${sliceRef}`,
256
+ '- Abort: inspect the local changes, then manually revert or stash anything you do not want to keep.',
257
+ '- Commit: rerun with --commit only after provider, scope, and validation pass.',
258
+ ].join('\n');
259
+ }
260
+
261
+ function appendRecovery(error, slice) {
262
+ if (!error || !error.message || error.message.includes('Recovery:')) {
263
+ return error;
264
+ }
265
+
266
+ const wrapped = new Error(`${error.message}\n\n${buildRecoveryGuidance(slice)}`);
267
+ wrapped.cause = error;
268
+ wrapped.code = error.code;
269
+ wrapped.details = error.details;
270
+ return wrapped;
271
+ }
272
+
77
273
  function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
78
274
  const canonicalRepoRoot = canonicalizeRepoRoot(repoRoot);
79
275
  const resolvedRole = normalizeRole(role || DEFAULT_EXECUTE_ROLE);
@@ -123,7 +319,7 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
123
319
 
124
320
  sections.push(
125
321
  'Constraints:',
126
- '- Do not commit automatically.',
322
+ '- Do not commit manually. Quiver can create the slice commit after scope and validation pass when the user enables --commit.',
127
323
  '- Do not fix scope violations automatically.',
128
324
  '- Do not run multiple executors concurrently.',
129
325
  '- Stay inside the allowed files declared by slice.json.',
@@ -142,7 +338,7 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
142
338
  };
143
339
  }
144
340
 
145
- function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, briefPath, invocation, validationCommands, allowedFiles }) {
341
+ function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, briefPath, invocation, validationCommands, allowedFiles, commitEnabled }) {
146
342
  const lines = [
147
343
  'AI execute-slice dry-run',
148
344
  `Provider: ${provider}`,
@@ -155,6 +351,7 @@ function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, br
155
351
  `Timeout: ${invocation.timeoutMs}ms`,
156
352
  `Prompt transport: ${invocation.promptTransport.mode}`,
157
353
  `Prompt length: ${invocation.promptLength} bytes`,
354
+ `Commit after validation: ${commitEnabled ? 'enabled' : 'disabled'}`,
158
355
  'Allowed files:',
159
356
  ...formatList(allowedFiles),
160
357
  'Validation commands:',
@@ -164,7 +361,7 @@ function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, br
164
361
  return `${lines.join('\n')}\n`;
165
362
  }
166
363
 
167
- function formatExecuteSliceResult({ slice, changedFiles, scopeResult }) {
364
+ function formatExecuteSliceResult({ slice, changedFiles, scopeResult, validationResults, commitResult, commitEnabled }) {
168
365
  const lines = [
169
366
  'AI execute-slice completed',
170
367
  `Slice: ${slice.sliceId}`,
@@ -177,6 +374,17 @@ function formatExecuteSliceResult({ slice, changedFiles, scopeResult }) {
177
374
  }
178
375
 
179
376
  lines.push(`Scope validation: ${scopeResult.ok ? 'passed' : 'failed'}`);
377
+ if (!Array.isArray(validationResults) || validationResults.length === 0) {
378
+ lines.push('Validation commands: skipped (none declared)');
379
+ } else {
380
+ lines.push(`Validation commands: passed (${validationResults.length})`);
381
+ }
382
+ if (commitResult) {
383
+ lines.push(`Commit: created ${commitResult.hash}`);
384
+ lines.push(`Commit message: ${commitResult.message}`);
385
+ } else {
386
+ lines.push(`Commit: ${commitEnabled ? 'not created' : 'skipped'}`);
387
+ }
180
388
 
181
389
  return `${lines.join('\n')}\n`;
182
390
  }
@@ -190,9 +398,105 @@ function annotateProviderError(error, scope) {
190
398
  return wrapped;
191
399
  }
192
400
 
401
+ function runValidationCommand(command, repoRoot) {
402
+ try {
403
+ const stdout = cp.execSync(command, {
404
+ cwd: repoRoot,
405
+ encoding: 'utf8',
406
+ shell: true,
407
+ stdio: ['ignore', 'pipe', 'pipe'],
408
+ });
409
+ return {
410
+ command,
411
+ ok: true,
412
+ stdout,
413
+ stderr: '',
414
+ exitCode: 0,
415
+ };
416
+ } catch (error) {
417
+ return {
418
+ command,
419
+ ok: false,
420
+ stdout: error.stdout ? String(error.stdout) : '',
421
+ stderr: error.stderr ? String(error.stderr) : '',
422
+ exitCode: Number.isInteger(error.status) ? error.status : 1,
423
+ error,
424
+ };
425
+ }
426
+ }
427
+
428
+ function runValidationCommands(repoRoot, commands, runner = runValidationCommand) {
429
+ const results = [];
430
+ for (const command of commands) {
431
+ const result = runner(command, repoRoot);
432
+ results.push(result);
433
+ if (!result.ok) {
434
+ const details = [
435
+ formatError(`validation command failed: ${command}`),
436
+ `Exit code: ${result.exitCode}`,
437
+ ];
438
+ if (result.stderr) {
439
+ details.push(`stderr:\n${result.stderr.trimEnd()}`);
440
+ }
441
+ if (result.stdout) {
442
+ details.push(`stdout:\n${result.stdout.trimEnd()}`);
443
+ }
444
+ const error = new Error(details.join('\n'));
445
+ error.code = 'VALIDATION_FAILED';
446
+ error.details = { command, result, results };
447
+ throw error;
448
+ }
449
+ }
450
+ return results;
451
+ }
452
+
453
+ function commitTypeForSlice(slice) {
454
+ const type = String(slice.json.type || slice.json.git?.branch_type || '').trim().toLowerCase();
455
+ if (type === 'bugfix' || type === 'hotfix' || type === 'fix') {
456
+ return 'fix';
457
+ }
458
+ if (type === 'docs' || type === 'documentation') {
459
+ return 'docs';
460
+ }
461
+ if (type === 'test' || type === 'tests') {
462
+ return 'test';
463
+ }
464
+ if (type === 'chore') {
465
+ return 'chore';
466
+ }
467
+ return 'feat';
468
+ }
469
+
470
+ function buildSliceCommitMessage(slice) {
471
+ const title = String(slice.json.title || slice.sliceId || 'slice').trim();
472
+ const ticket = String(slice.ticket || '').trim();
473
+ const subject = ticket ? `${ticket} ${title}` : title;
474
+ return `${commitTypeForSlice(slice)}: ${subject}`;
475
+ }
476
+
477
+ function commitSliceChanges(repoRoot, slice, changedFiles, options = {}) {
478
+ if (!Array.isArray(changedFiles) || changedFiles.length === 0) {
479
+ const error = new Error(formatError('commit requested but provider produced no changed files.'));
480
+ error.code = 'NO_CHANGES_TO_COMMIT';
481
+ throw error;
482
+ }
483
+
484
+ const message = options.message || buildSliceCommitMessage(slice);
485
+ runGit(['add', '--', ...changedFiles], repoRoot);
486
+ runGit(['commit', '-m', message], repoRoot);
487
+
488
+ return {
489
+ files: changedFiles,
490
+ hash: runGit(['rev-parse', '--short', 'HEAD'], repoRoot),
491
+ message,
492
+ };
493
+ }
494
+
193
495
  async function runExecuteSlice(repoRoot, options = {}) {
194
- const provider = String(options.provider || DEFAULT_EXECUTE_PROVIDER).trim().toLowerCase();
195
496
  const role = normalizeRole(options.role || DEFAULT_EXECUTE_ROLE);
497
+ const provider = options.providerExplicit === true || (options.provider && options.providerExplicit !== false)
498
+ ? String(options.provider || DEFAULT_EXECUTE_PROVIDER).trim().toLowerCase()
499
+ : resolveProfileProvider(repoRoot, role, DEFAULT_EXECUTE_PROVIDER);
196
500
  const context = options.context || DEFAULT_EXECUTE_CONTEXT;
197
501
  const timeoutMs = normalizeTimeout(options.timeout);
198
502
 
@@ -231,6 +535,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
231
535
  briefPath: executorContext.briefPath,
232
536
  allowedFiles: executorContext.allowedFiles,
233
537
  validationCommands: executorContext.validationCommands,
538
+ commitEnabled: options.commit === true,
234
539
  };
235
540
  process.stdout.write(formatExecuteSliceDryRunReport({
236
541
  provider,
@@ -241,13 +546,14 @@ async function runExecuteSlice(repoRoot, options = {}) {
241
546
  invocation,
242
547
  validationCommands: executorContext.validationCommands,
243
548
  allowedFiles: executorContext.allowedFiles,
549
+ commitEnabled: options.commit === true,
244
550
  }));
245
551
  return report;
246
552
  }
247
553
 
248
554
  const beforeSnapshot = captureWorktreeSnapshot(repoRoot);
249
555
  if (beforeSnapshot.files.length > 0 && options.allowDirty !== true) {
250
- throw new Error(formatError(`ai execute-slice requires a clean worktree before running. Commit or stash first: ${beforeSnapshot.files.join(', ')}`));
556
+ throw appendRecovery(new Error(formatError(`ai execute-slice requires a clean worktree before running. Commit or stash first: ${beforeSnapshot.files.join(', ')}`)), executorContext.slice);
251
557
  }
252
558
 
253
559
  let result;
@@ -264,7 +570,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
264
570
  tempFilePrefix: options.tempFilePrefix,
265
571
  });
266
572
  } catch (error) {
267
- throw annotateProviderError(error, 'execute-slice');
573
+ throw appendRecovery(annotateProviderError(error, 'execute-slice'), executorContext.slice);
268
574
  }
269
575
 
270
576
  if (result.stdout) {
@@ -275,21 +581,64 @@ async function runExecuteSlice(repoRoot, options = {}) {
275
581
  }
276
582
 
277
583
  if (!result.ok) {
278
- throw annotateProviderError(result.error || new Error('provider run failed'), 'execute-slice');
584
+ throw appendRecovery(annotateProviderError(result.error || new Error('provider run failed'), 'execute-slice'), executorContext.slice);
279
585
  }
280
586
 
281
587
  const afterSnapshot = captureWorktreeSnapshot(repoRoot);
282
- const scopeResult = validateScopeSnapshot({
283
- allowedFiles: executorContext.allowedFiles,
284
- beforeSnapshot,
285
- afterSnapshot,
286
- strict: true,
287
- });
588
+ let scopeResult;
589
+ try {
590
+ scopeResult = validateScopeSnapshot({
591
+ allowedFiles: executorContext.allowedFiles,
592
+ beforeSnapshot,
593
+ afterSnapshot,
594
+ strict: true,
595
+ });
596
+ } catch (error) {
597
+ throw appendRecovery(error, executorContext.slice);
598
+ }
599
+
600
+ let validationResults = [];
601
+ try {
602
+ validationResults = runValidationCommands(
603
+ repoRoot,
604
+ executorContext.validationCommands,
605
+ options.runValidationCommandFn,
606
+ );
607
+ } catch (error) {
608
+ throw appendRecovery(error, executorContext.slice);
609
+ }
610
+
611
+ const finalSnapshot = captureWorktreeSnapshot(repoRoot);
612
+ let finalScopeResult;
613
+ try {
614
+ finalScopeResult = validateScopeSnapshot({
615
+ allowedFiles: executorContext.allowedFiles,
616
+ beforeSnapshot,
617
+ afterSnapshot: finalSnapshot,
618
+ strict: true,
619
+ });
620
+ } catch (error) {
621
+ throw appendRecovery(error, executorContext.slice);
622
+ }
623
+
624
+ let commitResult = null;
625
+ if (options.commit === true) {
626
+ try {
627
+ commitResult = commitSliceChanges(repoRoot, executorContext.slice, finalScopeResult.changedFiles, {
628
+ message: options.commitMessage,
629
+ });
630
+ } catch (error) {
631
+ throw appendRecovery(error, executorContext.slice);
632
+ }
633
+ }
288
634
 
289
635
  process.stdout.write(formatExecuteSliceResult({
290
636
  slice: executorContext.slice,
291
- changedFiles: scopeResult.changedFiles,
292
- scopeResult,
637
+ changedFiles: finalScopeResult.changedFiles,
638
+ scopeResult: finalScopeResult,
639
+ validationResults,
640
+ commitResult,
641
+ commitEnabled: options.commit === true,
293
642
  }));
294
643
 
295
644
  return {
@@ -302,8 +651,10 @@ async function runExecuteSlice(repoRoot, options = {}) {
302
651
  invocation,
303
652
  result,
304
653
  beforeSnapshot,
305
- afterSnapshot,
306
- scopeResult,
654
+ afterSnapshot: finalSnapshot,
655
+ scopeResult: finalScopeResult,
656
+ validationResults,
657
+ commitResult,
307
658
  };
308
659
  }
309
660
 
@@ -312,12 +663,20 @@ module.exports = {
312
663
  DEFAULT_EXECUTE_PROVIDER,
313
664
  DEFAULT_EXECUTE_ROLE,
314
665
  annotateProviderError,
666
+ appendRecovery,
315
667
  buildExecuteSliceContext,
668
+ buildManualExecutorPrompt,
669
+ buildRecoveryGuidance,
670
+ buildSliceCommitMessage,
316
671
  canonicalizeRepoRoot,
672
+ commitSliceChanges,
317
673
  formatExecuteSliceDryRunReport,
318
674
  formatExecuteSliceResult,
675
+ runValidationCommand,
676
+ runValidationCommands,
319
677
  normalizeTimeout,
320
678
  readTextFile,
321
679
  resolveSliceJsonPath,
322
680
  runExecuteSlice,
681
+ runPromptSlice,
323
682
  };