create-quiver 0.12.0 → 0.12.1

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 (109) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +49 -17
  3. package/README_FOR_AI.md +31 -29
  4. package/ROADMAP.md +15 -3
  5. package/docs/AI_ONBOARDING_PROMPT.md.template +7 -1
  6. package/docs/COMMANDS.md.template +44 -18
  7. package/docs/STATUS.md.template +5 -1
  8. package/docs/WORKFLOW.md.template +13 -11
  9. package/package.json +9 -3
  10. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EVIDENCE_REPORT.md +293 -0
  11. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EXECUTION_PLAN.md +58 -0
  12. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/SPEC.md +242 -0
  13. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/STATUS.md +35 -0
  14. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/pr.md +77 -0
  15. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +34 -0
  16. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
  17. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/slice.json +52 -0
  18. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/CLOSURE_BRIEF.md +36 -0
  19. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/EXECUTION_BRIEF.md +52 -0
  20. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/slice.json +56 -0
  21. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/CLOSURE_BRIEF.md +43 -0
  22. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/EXECUTION_BRIEF.md +54 -0
  23. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/slice.json +52 -0
  24. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/CLOSURE_BRIEF.md +35 -0
  25. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/EXECUTION_BRIEF.md +53 -0
  26. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/slice.json +54 -0
  27. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/CLOSURE_BRIEF.md +34 -0
  28. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/EXECUTION_BRIEF.md +54 -0
  29. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/slice.json +52 -0
  30. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/CLOSURE_BRIEF.md +34 -0
  31. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/EXECUTION_BRIEF.md +54 -0
  32. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/slice.json +53 -0
  33. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/CLOSURE_BRIEF.md +33 -0
  34. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/EXECUTION_BRIEF.md +56 -0
  35. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/slice.json +55 -0
  36. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/CLOSURE_BRIEF.md +33 -0
  37. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/EXECUTION_BRIEF.md +54 -0
  38. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/slice.json +52 -0
  39. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/CLOSURE_BRIEF.md +39 -0
  40. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/EXECUTION_BRIEF.md +56 -0
  41. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/slice.json +53 -0
  42. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/CLOSURE_BRIEF.md +38 -0
  43. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/EXECUTION_BRIEF.md +57 -0
  44. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/slice.json +52 -0
  45. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/CLOSURE_BRIEF.md +39 -0
  46. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/EXECUTION_BRIEF.md +55 -0
  47. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/slice.json +56 -0
  48. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/CLOSURE_BRIEF.md +36 -0
  49. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/EXECUTION_BRIEF.md +54 -0
  50. package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/slice.json +53 -0
  51. package/specs/quiver-v26-0121-smoke-hardening/EVIDENCE_REPORT.md +208 -0
  52. package/specs/quiver-v26-0121-smoke-hardening/EXECUTION_PLAN.md +57 -0
  53. package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +137 -0
  54. package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +32 -0
  55. package/specs/quiver-v26-0121-smoke-hardening/pr.md +96 -0
  56. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/CLOSURE_BRIEF.md +35 -0
  57. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/EXECUTION_BRIEF.md +55 -0
  58. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/slice.json +73 -0
  59. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/CLOSURE_BRIEF.md +38 -0
  60. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/EXECUTION_BRIEF.md +51 -0
  61. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/slice.json +76 -0
  62. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/CLOSURE_BRIEF.md +37 -0
  63. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/EXECUTION_BRIEF.md +52 -0
  64. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/slice.json +75 -0
  65. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/CLOSURE_BRIEF.md +37 -0
  66. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/EXECUTION_BRIEF.md +53 -0
  67. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/slice.json +77 -0
  68. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/CLOSURE_BRIEF.md +35 -0
  69. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/EXECUTION_BRIEF.md +52 -0
  70. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/slice.json +77 -0
  71. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/CLOSURE_BRIEF.md +34 -0
  72. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/EXECUTION_BRIEF.md +54 -0
  73. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/slice.json +84 -0
  74. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/CLOSURE_BRIEF.md +35 -0
  75. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/EXECUTION_BRIEF.md +53 -0
  76. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/slice.json +82 -0
  77. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/CLOSURE_BRIEF.md +35 -0
  78. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/EXECUTION_BRIEF.md +55 -0
  79. package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/slice.json +92 -0
  80. package/src/create-quiver/commands/ai.js +577 -27
  81. package/src/create-quiver/commands/flow.js +6 -5
  82. package/src/create-quiver/commands/graph.js +6 -4
  83. package/src/create-quiver/commands/plan.js +3 -3
  84. package/src/create-quiver/index.js +328 -12
  85. package/src/create-quiver/lib/actionable-error.js +27 -0
  86. package/src/create-quiver/lib/agent-profiles.js +1 -1
  87. package/src/create-quiver/lib/ai/context-packs.js +4 -0
  88. package/src/create-quiver/lib/ai/execution-plan.js +7 -1
  89. package/src/create-quiver/lib/ai/executor.js +270 -20
  90. package/src/create-quiver/lib/ai/export-state.js +534 -0
  91. package/src/create-quiver/lib/ai/github.js +83 -0
  92. package/src/create-quiver/lib/ai/onboarding-template.js +215 -2
  93. package/src/create-quiver/lib/ai/plan-review.js +5 -2
  94. package/src/create-quiver/lib/ai/providers.js +4 -3
  95. package/src/create-quiver/lib/ai/run-state.js +414 -0
  96. package/src/create-quiver/lib/ai/spec-generator.js +12 -0
  97. package/src/create-quiver/lib/ai/spec-templates.js +78 -9
  98. package/src/create-quiver/lib/approvals.js +22 -3
  99. package/src/create-quiver/lib/demo.js +189 -14
  100. package/src/create-quiver/lib/doctor.js +75 -0
  101. package/src/create-quiver/lib/handoff.js +81 -12
  102. package/src/create-quiver/lib/init-docs.js +24 -6
  103. package/src/create-quiver/lib/init-layout.js +8 -0
  104. package/src/create-quiver/lib/json.js +53 -3
  105. package/src/create-quiver/lib/readiness.js +18 -3
  106. package/src/create-quiver/lib/scope.js +50 -7
  107. package/src/create-quiver/lib/slice-graph.js +138 -38
  108. package/src/create-quiver/lib/slice.js +6 -1
  109. package/src/create-quiver/lib/spec-worktrees.js +16 -2
@@ -1,14 +1,25 @@
1
1
  const fs = require('node:fs');
2
2
  const path = require('node:path');
3
3
 
4
+ const { redactSecrets } = require('../lib/evidence');
5
+ const { formatActionableError } = require('../lib/actionable-error');
4
6
  const { buildContextPackMetadata, normalizeRole } = require('../lib/ai/context-packs');
5
7
  const { runExecuteSlice, runPromptSlice } = require('../lib/ai/executor');
6
8
  const { runExecutePlan } = require('../lib/ai/execution-plan');
7
9
  const { buildPrCreatePlan, formatPreflightReport, formatPrCreateReport, preflightGitHubPr, runGhPrCreate } = require('../lib/ai/github');
8
10
  const { buildContextPreparationDrafts, buildPlannerOnboardingPrompt } = require('../lib/ai/onboarding-template');
11
+ const {
12
+ collectLifecycleExport,
13
+ formatLifecycleExportMarkdown,
14
+ formatLifecycleInspect,
15
+ formatSlicesList,
16
+ formatSpecsList,
17
+ formatTraceReport,
18
+ } = require('../lib/ai/export-state');
9
19
  const {
10
20
  PLAN_REVIEW_PROMPT_SOURCE,
11
21
  buildPlanReviewPrompt,
22
+ readPlanReview,
12
23
  resolveReviewedTechnicalPlanInput,
13
24
  resolveTechnicalPlanReviewInput,
14
25
  savePlanReview,
@@ -16,6 +27,15 @@ const {
16
27
  } = require('../lib/ai/plan-review');
17
28
  const { buildSpecGenerationManifest, describeSpecGeneration, generateSpecArtifacts } = require('../lib/ai/spec-generator');
18
29
  const { buildProviderInvocation, runProvider } = require('../lib/ai/providers');
30
+ const {
31
+ createAiRun,
32
+ ensureAiRun,
33
+ formatAiRunResume,
34
+ formatAiRunStatus,
35
+ recordAiRunApproval,
36
+ resolveAiRun,
37
+ updateAiRunPhase,
38
+ } = require('../lib/ai/run-state');
19
39
  const {
20
40
  agentProfilesPath,
21
41
  getAgentProfile,
@@ -26,6 +46,7 @@ const {
26
46
  const {
27
47
  PLANNER_APPROVAL_PHASES,
28
48
  approvePlannerPhase,
49
+ readPhaseApproval,
29
50
  resolveApprovedPlannerInput,
30
51
  savePlannerDraft,
31
52
  summarizePlannerApproval,
@@ -39,6 +60,8 @@ const DEFAULT_PLAN_PROVIDER = 'codex';
39
60
  const DEFAULT_PLAN_ROLE = 'planner';
40
61
  const DEFAULT_PLAN_CONTEXT = 'planning';
41
62
  const DEFAULT_PLAN_PHASE = 'acceptance';
63
+ const CONTEXT_PREP_START = '<!-- quiver:context-prep:start -->';
64
+ const CONTEXT_PREP_END = '<!-- quiver:context-prep:end -->';
42
65
 
43
66
  function formatError(message) {
44
67
  return `create-quiver: ${message}`;
@@ -85,7 +108,7 @@ function resolveProviderForProfile(repoRoot, role, provider, providerExplicit, f
85
108
  return resolveProfileProvider(repoRoot, role, fallbackProvider);
86
109
  }
87
110
 
88
- function buildPlanContext({ role, context, phase, inputText, inputPath, repoRoot }) {
111
+ function buildPlanContext({ role, context, phase, inputText, inputPath, repoRoot, revise = false }) {
89
112
  const phaseDetails = getPlannerPhaseDetails(phase);
90
113
  const pack = buildContextPackMetadata({
91
114
  role,
@@ -96,7 +119,9 @@ function buildPlanContext({ role, context, phase, inputText, inputPath, repoRoot
96
119
  const sections = [
97
120
  pack.prompt,
98
121
  `Phase: ${phaseDetails.phase}`,
99
- phaseDetails.phase === 'acceptance'
122
+ revise
123
+ ? 'Task: revise the current draft and produce a new version only. Do not advance phase, approve, create specs, or modify product code.'
124
+ : phaseDetails.phase === 'acceptance'
100
125
  ? 'Task: produce acceptance criteria only. Do not create files or modify product code.'
101
126
  : 'Task: produce a technical plan only. Do not create files or modify product code.',
102
127
  ];
@@ -167,6 +192,52 @@ function formatDryRunReport({ task, provider, role, contextPack, phase, invocati
167
192
  return `${lines.join('\n')}\n`;
168
193
  }
169
194
 
195
+ function formatPromptOnlyReport({ task, provider, role, contextPack, phase, invocation, prompt, onboardingPlan, promptSource, inputPath, inputKind, inputVersion }) {
196
+ const lines = [
197
+ `AI ${task} prompt-only`,
198
+ `Provider: ${provider}`,
199
+ `Role: ${role}`,
200
+ `Context pack: ${contextPack}`,
201
+ ];
202
+
203
+ if (phase) {
204
+ lines.push(`Phase: ${phase}`);
205
+ }
206
+
207
+ lines.push(`Command: ${invocation.command} ${invocation.args.join(' ')}`);
208
+ lines.push(`Timeout: ${invocation.timeoutMs}ms`);
209
+ lines.push(`Prompt transport: ${invocation.promptTransport.mode}`);
210
+ lines.push(`Prompt length: ${invocation.promptLength} bytes`);
211
+
212
+ if (onboardingPlan) {
213
+ lines.push(`Prompt source: ${onboardingPlan.promptSource}`);
214
+ lines.push(`Selected docs: ${onboardingPlan.selectedDocs.length}`);
215
+ lines.push(`Documentation debt: ${onboardingPlan.missingDocs.length}`);
216
+ }
217
+
218
+ if (promptSource) {
219
+ lines.push(`Prompt source: ${promptSource}`);
220
+ }
221
+
222
+ if (inputPath) {
223
+ lines.push(`Input file: ${inputPath}`);
224
+ }
225
+
226
+ if (inputKind) {
227
+ lines.push(`Input kind: ${inputKind}`);
228
+ }
229
+
230
+ if (inputVersion) {
231
+ lines.push(`Input version: v${inputVersion}`);
232
+ }
233
+
234
+ lines.push('--- PROMPT START ---');
235
+ lines.push(String(prompt || '').trimEnd());
236
+ lines.push('--- PROMPT END ---');
237
+
238
+ return `${lines.join('\n')}\n`;
239
+ }
240
+
170
241
  function formatPathList(items, emptyLabel = 'none') {
171
242
  if (!Array.isArray(items) || items.length === 0) {
172
243
  return [`- ${emptyLabel}`];
@@ -175,28 +246,41 @@ function formatPathList(items, emptyLabel = 'none') {
175
246
  return items.map((item) => `- ${item}`);
176
247
  }
177
248
 
178
- function formatContextPreparationReport({ dryRun, plan, docs, writtenDocs }) {
249
+ function formatContextPreparationReport({ dryRun, plan, writePlan, writtenDocs, snapshot, completed = false }) {
179
250
  const lines = [
180
- dryRun ? 'AI prepare-context dry-run' : 'AI prepare-context completed',
251
+ dryRun ? 'AI prepare-context dry-run' : completed ? 'AI prepare-context completed' : 'AI prepare-context write plan',
181
252
  `Mode: ${dryRun ? 'dry-run' : 'live'}`,
182
253
  `Project: ${plan.projectName}`,
183
254
  `Project slug: ${plan.projectSlug}`,
184
255
  'Writes: docs-only',
185
256
  'Product code: untouched',
186
- `Proposed docs: ${docs.length > 0 ? docs.map((doc) => doc.path).join(', ') : 'none'}`,
257
+ `Proposed docs: ${writePlan.length > 0 ? writePlan.map((item) => item.path).join(', ') : 'none'}`,
187
258
  ];
188
259
 
189
260
  if (!dryRun) {
190
- lines.push(`Written docs: ${writtenDocs.length > 0 ? writtenDocs.join(', ') : 'none'}`);
261
+ lines.push(`${completed ? 'Written docs' : 'Planned writes'}: ${writtenDocs.length > 0 ? writtenDocs.join(', ') : 'none'}`);
262
+ if (snapshot) {
263
+ lines.push(`Snapshot: ${snapshot.root}`);
264
+ }
265
+ }
266
+
267
+ if (completed) {
268
+ return `${lines.join('\n')}\n`;
191
269
  }
192
270
 
193
271
  lines.push(
272
+ 'Proposed changes:',
273
+ ...writePlan.map((item) => `- ${item.path}: ${item.action}${item.reason ? ` (${item.reason})` : ''}`),
274
+ 'Diff preview:',
275
+ ...formatDiffPreview(writePlan),
194
276
  'Files considered:',
195
277
  ...plan.filesConsidered.map((item) => `- ${item.path}: ${item.present ? 'present' : 'absent'}${item.reason ? ` (${item.reason})` : ''}`),
196
278
  'Assumptions:',
197
279
  ...formatPathList(plan.assumptions),
198
280
  'Risks:',
199
281
  ...formatPathList(plan.risks),
282
+ 'Contradictions:',
283
+ ...formatPathList(plan.contradictions),
200
284
  'Omitted paths:',
201
285
  ...formatPathList(plan.omittedPaths),
202
286
  'Uncertainty markers: TODO | Assumption | Pending confirmation',
@@ -207,20 +291,192 @@ function formatContextPreparationReport({ dryRun, plan, docs, writtenDocs }) {
207
291
 
208
292
  function writeProviderOutput(result) {
209
293
  if (result.stdout) {
210
- process.stdout.write(result.stdout);
294
+ process.stdout.write(redactSecrets(result.stdout));
211
295
  }
212
296
  if (result.stderr) {
213
- process.stderr.write(result.stderr);
297
+ process.stderr.write(redactSecrets(result.stderr));
214
298
  }
215
299
  }
216
300
 
217
- function writeDraftDocs(repoRoot, drafts) {
218
- const writtenDocs = [];
219
- for (const draft of drafts) {
301
+ function getRedactedProviderText(result) {
302
+ return redactSecrets([result.stdout, result.stderr].filter(Boolean).join(''));
303
+ }
304
+
305
+ function normalizeText(value) {
306
+ return String(value || '').replace(/\r\n/g, '\n');
307
+ }
308
+
309
+ function buildRevisionInput({ phase, feedbackPath, feedbackText, repoRoot }) {
310
+ const current = readPhaseApproval(repoRoot, phase);
311
+ if (!current.draft) {
312
+ throw new Error(formatError(`ai revise --phase ${phase} requires an existing draft; current status is ${current.status}. Run \`npx create-quiver ai plan --phase ${phase} --input <file>\` first.`));
313
+ }
314
+
315
+ const sections = [];
316
+
317
+ if (phase === 'technical-plan') {
318
+ const acceptance = resolveApprovedPlannerInput(repoRoot, phase, undefined);
319
+ const acceptanceText = readTextFile(acceptance.inputPath, repoRoot);
320
+ sections.push(`Approved acceptance input (${acceptance.inputPath}):`, acceptanceText.trimEnd());
321
+ }
322
+
323
+ sections.push(
324
+ `Current ${phase} draft (${current.draft.path}):`,
325
+ current.draft.contents.trimEnd(),
326
+ `Human feedback (${feedbackPath}):`,
327
+ feedbackText.trimEnd(),
328
+ );
329
+
330
+ return sections.join('\n\n');
331
+ }
332
+
333
+ function buildManagedContextBlock(content) {
334
+ return `${CONTEXT_PREP_START}\n${String(content || '').trimEnd()}\n${CONTEXT_PREP_END}\n`;
335
+ }
336
+
337
+ function mergeContextDraft(existingContent, draftContent) {
338
+ const existing = normalizeText(existingContent);
339
+ const block = buildManagedContextBlock(draftContent);
340
+ const startIndex = existing.indexOf(CONTEXT_PREP_START);
341
+ const endIndex = existing.indexOf(CONTEXT_PREP_END);
342
+
343
+ if (startIndex >= 0 && endIndex > startIndex) {
344
+ const before = existing.slice(0, startIndex).trimEnd();
345
+ const after = existing.slice(endIndex + CONTEXT_PREP_END.length).trimStart();
346
+ return `${before}\n\n${block}${after ? `\n${after}` : ''}`;
347
+ }
348
+
349
+ return `${existing.trimEnd()}\n\n${block}`;
350
+ }
351
+
352
+ function firstChangedLineIndex(beforeLines, afterLines) {
353
+ const max = Math.max(beforeLines.length, afterLines.length);
354
+ for (let index = 0; index < max; index += 1) {
355
+ if (beforeLines[index] !== afterLines[index]) {
356
+ return index;
357
+ }
358
+ }
359
+ return -1;
360
+ }
361
+
362
+ function buildDiffSnippet(pathLabel, beforeContent, afterContent, maxLines = 10) {
363
+ const beforeLines = normalizeText(beforeContent).split('\n');
364
+ const afterLines = normalizeText(afterContent).split('\n');
365
+ const changedAt = firstChangedLineIndex(beforeLines, afterLines);
366
+
367
+ if (changedAt === -1) {
368
+ return [`diff -- ${pathLabel}`, ' no changes'];
369
+ }
370
+
371
+ const start = Math.max(0, changedAt - 2);
372
+ const beforeSnippet = beforeLines.slice(start, start + maxLines);
373
+ const afterSnippet = afterLines.slice(start, start + maxLines);
374
+ const lines = [
375
+ `--- ${pathLabel} (current)`,
376
+ `+++ ${pathLabel} (proposed)`,
377
+ ];
378
+
379
+ for (const line of beforeSnippet) {
380
+ if (line) {
381
+ lines.push(`- ${line}`);
382
+ }
383
+ }
384
+
385
+ for (const line of afterSnippet) {
386
+ if (line) {
387
+ lines.push(`+ ${line}`);
388
+ }
389
+ }
390
+
391
+ return lines;
392
+ }
393
+
394
+ function buildContextWritePlan(repoRoot, drafts) {
395
+ return drafts.map((draft) => {
220
396
  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);
397
+ const exists = fs.existsSync(destinationPath);
398
+ const currentContent = exists ? fs.readFileSync(destinationPath, 'utf8') : '';
399
+ const proposedContent = exists
400
+ ? mergeContextDraft(currentContent, draft.content)
401
+ : `${String(draft.content || '').replace(/\s+$/g, '')}\n`;
402
+ const changed = normalizeText(currentContent) !== normalizeText(proposedContent);
403
+
404
+ return {
405
+ path: draft.path,
406
+ destinationPath,
407
+ action: changed ? (exists ? 'update' : 'create') : 'skip',
408
+ reason: changed ? (exists ? 'human content preserved; Quiver block appended or refreshed' : 'missing approved context doc') : 'already up to date',
409
+ exists,
410
+ currentContent,
411
+ proposedContent,
412
+ diff: buildDiffSnippet(draft.path, currentContent, proposedContent),
413
+ };
414
+ });
415
+ }
416
+
417
+ function formatDiffPreview(writePlan) {
418
+ const lines = [];
419
+ for (const item of writePlan) {
420
+ if (item.action === 'skip') {
421
+ continue;
422
+ }
423
+ lines.push(...item.diff);
424
+ }
425
+ return lines.length > 0 ? lines : ['- no changes'];
426
+ }
427
+
428
+ function createContextSnapshots(repoRoot, run, writePlan, now = new Date()) {
429
+ const stamp = now.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
430
+ const snapshotRoot = path.join(repoRoot, '.quiver', 'runs', run.run_id, 'snapshots', stamp);
431
+ const manifest = {
432
+ schema_version: 1,
433
+ run_id: run.run_id,
434
+ created_at: now.toISOString(),
435
+ entries: [],
436
+ };
437
+
438
+ fs.mkdirSync(snapshotRoot, { recursive: true });
439
+
440
+ for (const item of writePlan) {
441
+ if (item.action === 'skip') {
442
+ continue;
443
+ }
444
+ const entry = {
445
+ path: item.path,
446
+ action: item.action,
447
+ existed: item.exists,
448
+ snapshot_path: null,
449
+ };
450
+
451
+ if (item.exists) {
452
+ const snapshotPath = path.join(snapshotRoot, item.path);
453
+ fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });
454
+ fs.copyFileSync(item.destinationPath, snapshotPath);
455
+ entry.snapshot_path = path.relative(repoRoot, snapshotPath).split(path.sep).join('/');
456
+ }
457
+
458
+ manifest.entries.push(entry);
459
+ }
460
+
461
+ const manifestPath = path.join(snapshotRoot, 'manifest.json');
462
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
463
+
464
+ return {
465
+ root: path.relative(repoRoot, snapshotRoot).split(path.sep).join('/'),
466
+ manifestPath: path.relative(repoRoot, manifestPath).split(path.sep).join('/'),
467
+ entries: manifest.entries,
468
+ };
469
+ }
470
+
471
+ function writeDraftDocs(writePlan) {
472
+ const writtenDocs = [];
473
+ for (const item of writePlan) {
474
+ if (item.action === 'skip') {
475
+ continue;
476
+ }
477
+ fs.mkdirSync(path.dirname(item.destinationPath), { recursive: true });
478
+ fs.writeFileSync(item.destinationPath, item.proposedContent);
479
+ writtenDocs.push(item.path);
224
480
  }
225
481
  return writtenDocs;
226
482
  }
@@ -350,6 +606,20 @@ async function runOnboard(repoRoot, options = {}) {
350
606
  return report;
351
607
  }
352
608
 
609
+ if (options.printPrompt) {
610
+ const report = {
611
+ task: 'onboard',
612
+ provider,
613
+ role,
614
+ contextPack: context,
615
+ invocation,
616
+ onboardingPlan: contextInfo.plan,
617
+ prompt,
618
+ };
619
+ process.stdout.write(formatPromptOnlyReport(report));
620
+ return report;
621
+ }
622
+
353
623
  let result;
354
624
  try {
355
625
  result = await (options.runProviderFn || runProvider)(provider, {
@@ -386,33 +656,62 @@ async function runOnboard(repoRoot, options = {}) {
386
656
 
387
657
  async function runPrepareContext(repoRoot, options = {}) {
388
658
  const draftPack = buildContextPreparationDrafts(repoRoot);
659
+ const writePlan = buildContextWritePlan(repoRoot, draftPack.docs);
389
660
  const report = {
390
661
  task: 'prepare-context',
391
662
  dryRun: options.dryRun === true,
392
663
  docs: draftPack.docs.map((doc) => doc.path),
393
664
  plan: draftPack.plan,
665
+ writePlan: writePlan.map((item) => ({
666
+ path: item.path,
667
+ action: item.action,
668
+ reason: item.reason,
669
+ })),
394
670
  };
395
671
 
396
672
  if (options.dryRun) {
397
673
  process.stdout.write(formatContextPreparationReport({
398
674
  dryRun: true,
399
- docs: draftPack.docs,
400
675
  plan: draftPack.plan,
676
+ writePlan,
401
677
  writtenDocs: [],
402
678
  }));
403
679
  return report;
404
680
  }
405
681
 
406
- const writtenDocs = writeDraftDocs(repoRoot, draftPack.docs);
682
+ const lifecycleRun = ensureAiRun(repoRoot, {
683
+ command: 'ai prepare-context',
684
+ input: options.input || '',
685
+ runId: options.runId,
686
+ phase: 'created',
687
+ });
688
+ const snapshot = createContextSnapshots(repoRoot, lifecycleRun, writePlan, options.now || new Date());
689
+ const plannedDocs = writePlan.filter((item) => item.action !== 'skip').map((item) => item.path);
407
690
  process.stdout.write(formatContextPreparationReport({
408
691
  dryRun: false,
409
- docs: draftPack.docs,
410
692
  plan: draftPack.plan,
693
+ writePlan,
694
+ writtenDocs: plannedDocs,
695
+ snapshot,
696
+ }));
697
+ const writtenDocs = writeDraftDocs(writePlan);
698
+ updateAiRunPhase(repoRoot, lifecycleRun.run_id, 'onboarding-ready', {
699
+ artifact: snapshot.manifestPath,
700
+ command: 'ai prepare-context',
701
+ });
702
+ process.stdout.write(formatContextPreparationReport({
703
+ dryRun: false,
704
+ plan: draftPack.plan,
705
+ writePlan,
411
706
  writtenDocs,
707
+ snapshot,
708
+ completed: true,
412
709
  }));
413
710
 
414
711
  return {
415
712
  ...report,
713
+ runId: lifecycleRun.run_id,
714
+ snapshot,
416
715
  writtenDocs,
417
716
  };
418
717
  }
@@ -436,6 +735,17 @@ async function runPlan(repoRoot, options = {}) {
436
735
  specSlug: options.specSlug,
437
736
  });
438
737
 
738
+ if (options.printPrompt) {
739
+ const report = {
740
+ task: 'plan',
741
+ phase,
742
+ manifest,
743
+ };
744
+ process.stdout.write('AI plan prompt-only\nPhase: spec\nNo provider prompt is used for spec generation; showing the local generation plan instead.\n');
745
+ process.stdout.write(formatSpecDryRunReport({ manifest, repoRoot }));
746
+ return report;
747
+ }
748
+
439
749
  if (options.dryRun) {
440
750
  const report = {
441
751
  task: 'plan',
@@ -464,7 +774,20 @@ async function runPlan(repoRoot, options = {}) {
464
774
 
465
775
  assertPlannerPhaseReady(phase);
466
776
 
467
- if (phase === 'technical-plan') {
777
+ let inputText = '';
778
+
779
+ if (options.revise === true) {
780
+ if (!inputPath) {
781
+ throw new Error(formatError(`missing feedback input file for ai revise phase '${phase}'`));
782
+ }
783
+ const feedbackText = readTextFile(inputPath, repoRoot);
784
+ inputText = buildRevisionInput({
785
+ phase,
786
+ feedbackPath: inputPath,
787
+ feedbackText,
788
+ repoRoot,
789
+ });
790
+ } else if (phase === 'technical-plan') {
468
791
  const resolved = resolveApprovedPlannerInput(repoRoot, phase, inputPath || undefined);
469
792
  inputPath = resolved.inputPath;
470
793
  }
@@ -473,7 +796,9 @@ async function runPlan(repoRoot, options = {}) {
473
796
  throw new Error(formatError(`missing input file for ai plan phase '${phase}'`));
474
797
  }
475
798
 
476
- const inputText = readTextFile(inputPath, repoRoot);
799
+ if (!inputText) {
800
+ inputText = readTextFile(inputPath, repoRoot);
801
+ }
477
802
  const contextInfo = buildPlanContext({
478
803
  role,
479
804
  context,
@@ -481,6 +806,7 @@ async function runPlan(repoRoot, options = {}) {
481
806
  inputText,
482
807
  inputPath,
483
808
  repoRoot,
809
+ revise: options.revise === true,
484
810
  });
485
811
  const prompt = contextInfo.prompt;
486
812
  let invocation;
@@ -508,6 +834,20 @@ async function runPlan(repoRoot, options = {}) {
508
834
  return report;
509
835
  }
510
836
 
837
+ if (options.printPrompt) {
838
+ const report = {
839
+ task: 'plan',
840
+ provider,
841
+ role,
842
+ contextPack: contextInfo.pack.packName,
843
+ phase,
844
+ invocation,
845
+ prompt,
846
+ };
847
+ process.stdout.write(formatPromptOnlyReport(report));
848
+ return report;
849
+ }
850
+
511
851
  let result;
512
852
  try {
513
853
  result = await (options.runProviderFn || runProvider)(provider, {
@@ -531,7 +871,16 @@ async function runPlan(repoRoot, options = {}) {
531
871
  throw annotateProviderError(result.error || new Error('provider run failed'), 'plan', phase);
532
872
  }
533
873
 
534
- savePlannerDraft(repoRoot, phase, inputPath, [result.stdout, result.stderr].filter(Boolean).join(''));
874
+ const draft = savePlannerDraft(repoRoot, phase, inputPath, getRedactedProviderText(result));
875
+ const lifecycleRun = ensureAiRun(repoRoot, {
876
+ command: `ai plan --phase ${phase}`,
877
+ input: inputPath,
878
+ runId: options.runId,
879
+ });
880
+ updateAiRunPhase(repoRoot, lifecycleRun.run_id, phase === 'acceptance' ? 'acceptance-draft' : 'technical-plan-draft', {
881
+ artifact: path.relative(repoRoot, draft.filePath).split(path.sep).join('/'),
882
+ command: `ai plan --phase ${phase}`,
883
+ });
535
884
 
536
885
  return {
537
886
  task: 'plan',
@@ -603,6 +952,24 @@ async function runReviewPlan(repoRoot, options = {}) {
603
952
  return report;
604
953
  }
605
954
 
955
+ if (options.printPrompt) {
956
+ const report = {
957
+ task: 'review-plan',
958
+ provider,
959
+ role: 'reviewer',
960
+ contextPack: pack.packName,
961
+ phase: 'plan-review',
962
+ invocation,
963
+ prompt: built.prompt,
964
+ promptSource: built.promptSource,
965
+ inputPath,
966
+ inputKind: resolved.kind,
967
+ inputVersion: resolved.version,
968
+ };
969
+ process.stdout.write(formatPromptOnlyReport(report));
970
+ return report;
971
+ }
972
+
606
973
  let result;
607
974
  try {
608
975
  result = await (options.runProviderFn || runProvider)(provider, {
@@ -627,7 +994,7 @@ async function runReviewPlan(repoRoot, options = {}) {
627
994
  }
628
995
 
629
996
  const saved = savePlanReview(repoRoot, {
630
- contents: [result.stdout, result.stderr].filter(Boolean).join(''),
997
+ contents: getRedactedProviderText(result),
631
998
  inputPath,
632
999
  inputKind: resolved.kind,
633
1000
  inputVersion: resolved.version,
@@ -649,17 +1016,46 @@ async function runReviewPlan(repoRoot, options = {}) {
649
1016
  };
650
1017
  }
651
1018
 
1019
+ async function runRevise(repoRoot, options = {}) {
1020
+ const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
1021
+ if (phase === 'spec') {
1022
+ throw new Error(formatError(`ai revise does not support phase '${phase}'`));
1023
+ }
1024
+
1025
+ const approval = readPhaseApproval(repoRoot, phase);
1026
+ if (approval.status !== 'draft' && approval.status !== 'stale') {
1027
+ throw new Error(formatError(`ai revise --phase ${phase} requires an existing draft; current status is ${approval.status}. Run \`npx create-quiver ai plan --phase ${phase} --input <file>\` first.`));
1028
+ }
1029
+
1030
+ return runPlan(repoRoot, {
1031
+ ...options,
1032
+ phase,
1033
+ revise: true,
1034
+ });
1035
+ }
1036
+
652
1037
  async function runApprove(repoRoot, options = {}) {
653
1038
  const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
654
1039
  if (phase === 'spec') {
655
1040
  throw new Error(formatError(`ai approve does not support phase '${phase}'`));
656
1041
  }
657
1042
 
658
- if (!options.input && !options.version) {
659
- throw new Error(formatError(`missing input file for ai approve phase '${phase}'`));
1043
+ if (!options.version) {
1044
+ throw new Error(formatError(`ai approve --phase ${phase} requires --version <n>. Review drafts with \`npx create-quiver ai approvals\`.`));
660
1045
  }
661
1046
 
662
- const inputText = options.version ? '' : readTextFile(options.input, repoRoot);
1047
+ if (options.input) {
1048
+ throw new Error(formatError(`ai approve --phase ${phase} approves saved draft versions only. Use \`npx create-quiver ai revise --phase ${phase} --input ${options.input}\` to create a new draft first.`));
1049
+ }
1050
+
1051
+ if (phase === 'technical-plan') {
1052
+ const review = readPlanReview(repoRoot);
1053
+ if (review.status !== 'unapproved' && review.status !== 'reviewed') {
1054
+ throw new Error(formatError(`ai approve --phase technical-plan requires a production review for the current draft; current review status is ${review.status}. Run \`npx create-quiver ai review-plan\`.`));
1055
+ }
1056
+ }
1057
+
1058
+ const inputText = '';
663
1059
 
664
1060
  if (options.dryRun) {
665
1061
  process.stdout.write(formatApprovalDryRunResult({ phase, input: options.input, version: options.version }));
@@ -675,6 +1071,21 @@ async function runApprove(repoRoot, options = {}) {
675
1071
  const result = approvePlannerPhase(repoRoot, phase, options.input || '', inputText, {
676
1072
  version: options.version || undefined,
677
1073
  });
1074
+ const lifecycleRun = ensureAiRun(repoRoot, {
1075
+ command: `ai approve --phase ${phase}`,
1076
+ input: options.input || result.filePath,
1077
+ runId: options.runId,
1078
+ });
1079
+ recordAiRunApproval(repoRoot, lifecycleRun.run_id, {
1080
+ artifact: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
1081
+ phase,
1082
+ source_file: options.input || `draft version ${options.version}`,
1083
+ version: result.version || null,
1084
+ });
1085
+ updateAiRunPhase(repoRoot, lifecycleRun.run_id, phase === 'acceptance' ? 'acceptance-approved' : 'technical-plan-approved', {
1086
+ artifact: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
1087
+ command: `ai approve --phase ${phase}`,
1088
+ });
678
1089
  process.stdout.write(formatApprovalResult({
679
1090
  ...result,
680
1091
  sourceFile: options.input || `draft version ${options.version}`,
@@ -699,6 +1110,131 @@ async function runApprovalStatus(repoRoot) {
699
1110
  };
700
1111
  }
701
1112
 
1113
+ function runLifecycleStatus(repoRoot, options = {}) {
1114
+ const run = resolveAiRun(repoRoot, options.runId || '');
1115
+ const report = formatAiRunStatus(repoRoot, run);
1116
+ process.stdout.write(report);
1117
+ return {
1118
+ task: 'status',
1119
+ run,
1120
+ report,
1121
+ };
1122
+ }
1123
+
1124
+ function runLifecycleResume(repoRoot, options = {}) {
1125
+ const run = resolveAiRun(repoRoot, options.runId || '');
1126
+ const report = formatAiRunResume(repoRoot, run);
1127
+ process.stdout.write(report);
1128
+ return {
1129
+ task: 'resume',
1130
+ run,
1131
+ report,
1132
+ };
1133
+ }
1134
+
1135
+ function runInspect(repoRoot, options = {}) {
1136
+ const report = collectLifecycleExport(repoRoot, {
1137
+ includeCompleted: options.includeCompleted === true,
1138
+ });
1139
+ process.stdout.write(formatLifecycleInspect(report));
1140
+ return {
1141
+ task: 'inspect',
1142
+ report,
1143
+ };
1144
+ }
1145
+
1146
+ function runExport(repoRoot, options = {}) {
1147
+ const report = collectLifecycleExport(repoRoot, {
1148
+ includeCompleted: options.includeCompleted === true,
1149
+ });
1150
+ const format = String(options.format || 'json').trim().toLowerCase();
1151
+
1152
+ if (format === 'json') {
1153
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
1154
+ return {
1155
+ task: 'export',
1156
+ format,
1157
+ report,
1158
+ };
1159
+ }
1160
+
1161
+ if (format === 'markdown' || format === 'md') {
1162
+ process.stdout.write(formatLifecycleExportMarkdown(report));
1163
+ return {
1164
+ task: 'export',
1165
+ format: 'markdown',
1166
+ report,
1167
+ };
1168
+ }
1169
+
1170
+ throw new Error(formatError(`unsupported ai export format: ${format}. Supported formats: json, markdown`));
1171
+ }
1172
+
1173
+ function runSpecsList(repoRoot, options = {}) {
1174
+ const report = collectLifecycleExport(repoRoot, {
1175
+ includeCompleted: options.includeCompleted === true,
1176
+ });
1177
+ if (options.json === true) {
1178
+ process.stdout.write(`${JSON.stringify({ specs: report.specs }, null, 2)}\n`);
1179
+ } else {
1180
+ process.stdout.write(formatSpecsList(report));
1181
+ }
1182
+ return {
1183
+ task: 'specs',
1184
+ specs: report.specs,
1185
+ };
1186
+ }
1187
+
1188
+ function runSlicesList(repoRoot, options = {}) {
1189
+ const report = collectLifecycleExport(repoRoot, {
1190
+ includeCompleted: options.includeCompleted === true,
1191
+ });
1192
+ if (options.json === true) {
1193
+ process.stdout.write(`${JSON.stringify({ slices: report.slices }, null, 2)}\n`);
1194
+ } else {
1195
+ process.stdout.write(formatSlicesList(report));
1196
+ }
1197
+ return {
1198
+ task: 'slices',
1199
+ slices: report.slices,
1200
+ };
1201
+ }
1202
+
1203
+ function runTraceReport(repoRoot, options = {}) {
1204
+ const report = collectLifecycleExport(repoRoot, {
1205
+ includeCompleted: options.includeCompleted === true,
1206
+ });
1207
+ process.stdout.write(formatTraceReport(report));
1208
+ return {
1209
+ task: 'trace',
1210
+ report,
1211
+ };
1212
+ }
1213
+
1214
+ function runLifecycleRun(repoRoot, options = {}) {
1215
+ const command = String(options.command || '').trim().toLowerCase();
1216
+ if (command !== 'create') {
1217
+ throw new Error(formatError(`unsupported ai run subcommand: ${command}. Supported tasks: create`));
1218
+ }
1219
+ if (!options.input) {
1220
+ throw new Error(formatError('ai run create requires --input <requirements.md>'));
1221
+ }
1222
+ const run = createAiRun(repoRoot, {
1223
+ command: 'ai run create',
1224
+ input: options.input,
1225
+ runId: options.runId,
1226
+ specSlug: options.specSlug,
1227
+ });
1228
+ const report = formatAiRunStatus(repoRoot, run);
1229
+ process.stdout.write(report);
1230
+ return {
1231
+ task: 'run',
1232
+ command,
1233
+ run,
1234
+ report,
1235
+ };
1236
+ }
1237
+
702
1238
  function formatAgentProfile(profile) {
703
1239
  const lines = [
704
1240
  `Role: ${profile.role}`,
@@ -730,7 +1266,7 @@ function runAgent(repoRoot, options = {}) {
730
1266
 
731
1267
  if (command === 'set') {
732
1268
  if (!options.role) {
733
- throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent set <planner|executor|reviewer|researcher> --provider <provider>'));
1269
+ throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent set <planner|executor|reviewer|doctor> --provider <provider>'));
734
1270
  }
735
1271
  if (!options.provider) {
736
1272
  throw new Error(formatError('ai agent set requires --provider. Supported providers: codex, claude, gemini.'));
@@ -754,11 +1290,16 @@ function runAgent(repoRoot, options = {}) {
754
1290
 
755
1291
  if (command === 'show') {
756
1292
  if (!options.role) {
757
- throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent show <planner|executor|reviewer|researcher>'));
1293
+ throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent show <planner|executor|reviewer|doctor>'));
758
1294
  }
759
1295
  const profile = getAgentProfile(repoRoot, options.role);
760
1296
  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>`));
1297
+ throw new Error(formatActionableError({
1298
+ failure: `agent profile '${options.role}' is not configured.`,
1299
+ impact: 'Quiver will fall back to default provider behavior and may use the wrong model/cost profile.',
1300
+ fix: `Configure the ${options.role} profile with a supported provider and optional model label.`,
1301
+ nextCommand: `npx create-quiver ai agent set ${options.role} --provider <provider> --model <label>`,
1302
+ }));
762
1303
  }
763
1304
  process.stdout.write(formatAgentProfile(profile));
764
1305
  return {
@@ -903,12 +1444,21 @@ module.exports = {
903
1444
  runDoctor,
904
1445
  runExecutePlan,
905
1446
  runExecuteSlice,
1447
+ runLifecycleResume,
1448
+ runLifecycleRun,
1449
+ runLifecycleStatus,
1450
+ runExport,
1451
+ runInspect,
906
1452
  runPromptSlice,
907
1453
  runApprove,
908
1454
  runApprovalStatus,
909
1455
  runPrepareContext,
910
1456
  runReviewPlan,
1457
+ runRevise,
911
1458
  runPr,
1459
+ runSlicesList,
1460
+ runSpecsList,
1461
+ runTraceReport,
912
1462
  runOnboard,
913
1463
  runPlan,
914
1464
  writeProviderOutput,