create-quiver 0.7.0 → 0.9.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 (89) hide show
  1. package/.claude/settings.local.json +52 -0
  2. package/.github/workflows/ci.yml +2 -2
  3. package/BACKLOG.md +139 -0
  4. package/CHANGELOG.md +20 -1
  5. package/README.md +31 -1
  6. package/README_FOR_AI.md +25 -13
  7. package/ROADMAP.md +28 -6
  8. package/docs/AI_ONBOARDING_PROMPT.md.template +4 -0
  9. package/docs/COMMANDS.md.template +25 -0
  10. package/docs/INDEX.md.template +2 -0
  11. package/docs/SUPPORT_MATRIX.md.template +9 -0
  12. package/docs/WORKFLOW.md.template +4 -0
  13. package/docs/examples/graph.md.template +62 -0
  14. package/docs/examples/next.md.template +27 -0
  15. package/docs/examples/plan.md.template +28 -0
  16. package/package.json +5 -2
  17. package/package.template.json +8 -3
  18. package/scripts/check-slice-readiness.sh +6 -4
  19. package/scripts/init-docs.sh +57 -9
  20. package/specs/[project-name]/HANDOFF.md.template +37 -0
  21. package/specs/[project-name]/slices/slice-template/slice.json +5 -0
  22. package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-01-project-scan-command/slice.json +1 -1
  23. package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-02-ai-onboarding-prompt/slice.json +1 -1
  24. package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-03-doctor-readme-adoption-flow/slice.json +1 -1
  25. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +1 -1
  26. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +1 -1
  27. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +1 -1
  28. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +1 -1
  29. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +1 -1
  30. package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +1 -1
  31. package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +1 -1
  32. package/specs/quiver-v15-init-required-before-migrate/EVIDENCE_REPORT.md +26 -0
  33. package/specs/quiver-v15-init-required-before-migrate/SPEC.md +66 -0
  34. package/specs/quiver-v15-init-required-before-migrate/STATUS.md +26 -0
  35. package/specs/quiver-v15-init-required-before-migrate/slices/slice-01-migrate-initialization-precondition/slice.json +65 -0
  36. package/specs/quiver-v15-init-required-before-migrate/slices/slice-02-doctor-not-initialized-guidance/slice.json +61 -0
  37. package/specs/quiver-v15-init-required-before-migrate/slices/slice-03-docs-smokes-init-before-migrate/slice.json +64 -0
  38. package/specs/quiver-v16-handoff-contract/EVIDENCE_REPORT.md +26 -0
  39. package/specs/quiver-v16-handoff-contract/SPEC.md +68 -0
  40. package/specs/quiver-v16-handoff-contract/STATUS.md +26 -0
  41. package/specs/quiver-v16-handoff-contract/slices/slice-01-handoff-template-and-contract/slice.json +66 -0
  42. package/specs/quiver-v16-handoff-contract/slices/slice-02-check-handoff-command/slice.json +70 -0
  43. package/specs/quiver-v16-handoff-contract/slices/slice-03-handoff-scaffold-optional/slice.json +67 -0
  44. package/specs/quiver-v17-orchestration-foundation/EVIDENCE_REPORT.md +32 -0
  45. package/specs/quiver-v17-orchestration-foundation/SPEC.md +79 -0
  46. package/specs/quiver-v17-orchestration-foundation/STATUS.md +31 -0
  47. package/specs/quiver-v17-orchestration-foundation/slices/slice-01-ci-matrix-verified/slice.json +68 -0
  48. package/specs/quiver-v17-orchestration-foundation/slices/slice-02-slice-graph-library/slice.json +65 -0
  49. package/specs/quiver-v17-orchestration-foundation/slices/slice-03-depends-on-validation/slice.json +72 -0
  50. package/specs/quiver-v18-slice-orchestration/EVIDENCE_REPORT.md +38 -0
  51. package/specs/quiver-v18-slice-orchestration/SPEC.md +91 -0
  52. package/specs/quiver-v18-slice-orchestration/STATUS.md +33 -0
  53. package/specs/quiver-v18-slice-orchestration/slices/slice-01-plan-command/slice.json +79 -0
  54. package/specs/quiver-v18-slice-orchestration/slices/slice-02-graph-mvp-tree/slice.json +75 -0
  55. package/specs/quiver-v18-slice-orchestration/slices/slice-03-graph-extended-formats/slice.json +70 -0
  56. package/specs/quiver-v18-slice-orchestration/slices/slice-04-next-command/slice.json +73 -0
  57. package/specs/quiver-v18-stabilization/EVIDENCE_REPORT.md +26 -0
  58. package/specs/quiver-v18-stabilization/SPEC.md +62 -0
  59. package/specs/quiver-v18-stabilization/STATUS.md +30 -0
  60. package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/CLOSURE_BRIEF.md +29 -0
  61. package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/EXECUTION_BRIEF.md +134 -0
  62. package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/slice.json +56 -0
  63. package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/CLOSURE_BRIEF.md +29 -0
  64. package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/EXECUTION_BRIEF.md +118 -0
  65. package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/slice.json +57 -0
  66. package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/CLOSURE_BRIEF.md +23 -0
  67. package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/EXECUTION_BRIEF.md +73 -0
  68. package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/slice.json +49 -0
  69. package/specs/quiver-v19-self-install-dev-dep/EVIDENCE_REPORT.md +19 -0
  70. package/specs/quiver-v19-self-install-dev-dep/SPEC.md +51 -0
  71. package/specs/quiver-v19-self-install-dev-dep/STATUS.md +20 -0
  72. package/specs/quiver-v19-self-install-dev-dep/slices/slice-01-auto-install-dev-dep/CLOSURE_BRIEF.md +29 -0
  73. package/specs/quiver-v19-self-install-dev-dep/slices/slice-01-auto-install-dev-dep/EXECUTION_BRIEF.md +287 -0
  74. package/specs/quiver-v19-self-install-dev-dep/slices/slice-01-auto-install-dev-dep/slice.json +56 -0
  75. package/src/create-quiver/commands/graph.js +97 -0
  76. package/src/create-quiver/commands/next.js +134 -0
  77. package/src/create-quiver/commands/plan.js +205 -0
  78. package/src/create-quiver/index.js +203 -3
  79. package/src/create-quiver/lib/handoff.js +104 -0
  80. package/src/create-quiver/lib/init-docs.js +108 -13
  81. package/src/create-quiver/lib/json.js +14 -0
  82. package/src/create-quiver/lib/lifecycle.js +3 -2
  83. package/src/create-quiver/lib/readiness.js +55 -1
  84. package/src/create-quiver/lib/renderers/dot.js +129 -0
  85. package/src/create-quiver/lib/renderers/mermaid.js +119 -0
  86. package/src/create-quiver/lib/renderers/tree.js +116 -0
  87. package/src/create-quiver/lib/slice-graph.js +453 -0
  88. package/src/create-quiver/lib/slice.js +2 -1
  89. package/src/create-quiver/lib/state.js +50 -0
@@ -2,12 +2,17 @@ const fs = require('fs');
2
2
  const os = require('os');
3
3
  const path = require('path');
4
4
  const { execFileSync } = require('child_process');
5
+ const { checkHandoff, scaffoldHandoff } = require('./lib/handoff');
5
6
  const { collectDoctorWarnings } = require('./lib/doctor');
6
- const { initializeProjectDocs } = require('./lib/init-docs');
7
+ const { runGraph } = require('./commands/graph');
8
+ const { runNext } = require('./commands/next');
9
+ const { runPlan } = require('./commands/plan');
10
+ const { initializeProjectDocs, installSelfAsDevDep } = require('./lib/init-docs');
7
11
  const { checkPrReadiness, checkScope, checkSliceReadiness } = require('./lib/readiness');
8
12
  const { cleanupSlice, refreshActiveSlicesBoard, startSlice } = require('./lib/lifecycle');
9
13
  const { relativePosixPath, resolveTargetRoot } = require('./lib/paths');
10
14
  const {
15
+ hasQuiverInitializationEvidence,
11
16
  readState,
12
17
  updateStateForAnalyze,
13
18
  updateStateForMigrate,
@@ -23,11 +28,16 @@ function printUsage() {
23
28
  console.log(`Usage:
24
29
  npx create-quiver [options]
25
30
  npx create-quiver analyze [options]
31
+ npx create-quiver plan [options]
32
+ npx create-quiver graph [options]
33
+ npx create-quiver next [options]
26
34
  npx create-quiver migrate [options]
27
35
  npx create-quiver doctor [options]
28
36
  npx create-quiver start-slice [options] <slice.json>
29
37
  npx create-quiver check-slice [options] <slice.json>
30
38
  npx create-quiver check-pr <slice.json>
39
+ npx create-quiver check-handoff <handoff.md>
40
+ npx create-quiver new-handoff <spec-slug>
31
41
  npx create-quiver cleanup-slice [options] <slice.json>
32
42
  npx create-quiver check-scope [options] <slice.json>
33
43
  npx create-quiver refresh-active-slices
@@ -35,6 +45,15 @@ function printUsage() {
35
45
  Options:
36
46
  -n, --name <project-name> Project name to generate
37
47
  -d, --dir <target-dir> Target directory to scaffold into or inspect
48
+ --spec <slug> Restrict plan output to one spec
49
+ --format <name> Graph output format (tree, mermaid, dot)
50
+ --show-conflicts Show shared file paths in graph output
51
+ --level <n> Restrict graph output to one level
52
+ --json Emit machine-readable JSON
53
+ --only-ready Show only slices with no pending dependencies
54
+ --all-ready List every ready slice returned by next
55
+ --auto-start Prompt for confirmation and run start-slice on next
56
+ --unicode Prefer Unicode output when supported
38
57
  -y, --yes Skip prompts and use the provided inputs
39
58
  -h, --help Show this help message
40
59
 
@@ -42,11 +61,20 @@ Examples:
42
61
  npx create-quiver --name "My Project"
43
62
  npx create-quiver --name "My Project" --dir ./my-project
44
63
  cd ./my-project && npx create-quiver analyze
64
+ cd ./my-project && npx create-quiver plan --json
65
+ cd ./my-project && npx create-quiver graph --show-conflicts
66
+ cd ./my-project && npx create-quiver graph --format mermaid
67
+ cd ./my-project && npx create-quiver graph --format dot
68
+ cd ./my-project && npx create-quiver next
69
+ cd ./my-project && npx create-quiver next --all-ready
70
+ cd ./my-project && npx create-quiver next --auto-start
45
71
  cd ./my-project && npx create-quiver migrate
46
72
  cd ./my-project && npx create-quiver doctor
47
73
  cd ./my-project && npx create-quiver start-slice specs/my-project/slices/slice-01/slice.json
48
74
  cd ./my-project && npx create-quiver check-slice specs/my-project/slices/slice-01/slice.json
49
75
  cd ./my-project && npx create-quiver check-pr specs/my-project/slices/slice-01/slice.json
76
+ cd ./my-project && npx create-quiver check-handoff specs/my-project/HANDOFF.md
77
+ cd ./my-project && npx create-quiver new-handoff my-spec
50
78
  cd ./my-project && npx create-quiver cleanup-slice specs/my-project/slices/slice-01/slice.json
51
79
  cd ./my-project && npx create-quiver check-scope specs/my-project/slices/slice-01/slice.json
52
80
  cd ./my-project && npx create-quiver refresh-active-slices
@@ -68,10 +96,19 @@ function parseArgs(argv) {
68
96
  targetDir: '.',
69
97
  strict: false,
70
98
  strictOverlap: false,
99
+ json: false,
100
+ onlyReady: false,
101
+ allReady: false,
102
+ autoStart: false,
103
+ specSlug: '',
104
+ format: 'tree',
105
+ showConflicts: false,
106
+ level: null,
107
+ unicode: false,
71
108
  };
72
109
 
73
110
  const args = [...argv];
74
- const commandModes = new Set(['doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'cleanup-slice', 'check-scope', 'refresh-active-slices']);
111
+ const commandModes = new Set(['plan', 'graph', 'next', 'doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'check-handoff', 'new-handoff', 'cleanup-slice', 'check-scope', 'refresh-active-slices']);
75
112
  if (commandModes.has(args[0])) {
76
113
  result.mode = args[0];
77
114
  args.shift();
@@ -84,6 +121,12 @@ function parseArgs(argv) {
84
121
  } else if (args[0] === '--doctor') {
85
122
  result.mode = 'doctor';
86
123
  args.shift();
124
+ } else if (args[0] === '--check-handoff') {
125
+ result.mode = 'check-handoff';
126
+ args.shift();
127
+ } else if (args[0] === '--new-handoff') {
128
+ result.mode = 'new-handoff';
129
+ args.shift();
87
130
  }
88
131
 
89
132
  const positional = [];
@@ -101,6 +144,11 @@ function parseArgs(argv) {
101
144
  continue;
102
145
  }
103
146
 
147
+ if (arg === '--skip-install') {
148
+ result.skipInstall = true;
149
+ continue;
150
+ }
151
+
104
152
  if (arg === '--doctor') {
105
153
  result.mode = 'doctor';
106
154
  continue;
@@ -111,6 +159,16 @@ function parseArgs(argv) {
111
159
  continue;
112
160
  }
113
161
 
162
+ if (arg === '--check-handoff') {
163
+ result.mode = 'check-handoff';
164
+ continue;
165
+ }
166
+
167
+ if (arg === '--new-handoff') {
168
+ result.mode = 'new-handoff';
169
+ continue;
170
+ }
171
+
114
172
  if (arg === '--allow-draft') {
115
173
  result.allowDraft = true;
116
174
  continue;
@@ -141,6 +199,67 @@ function parseArgs(argv) {
141
199
  continue;
142
200
  }
143
201
 
202
+ if (arg === '--json') {
203
+ result.json = true;
204
+ continue;
205
+ }
206
+
207
+ if (arg === '--show-conflicts') {
208
+ result.showConflicts = true;
209
+ continue;
210
+ }
211
+
212
+ if (arg === '--format') {
213
+ const value = args[++index];
214
+ if (!value) {
215
+ throw new Error(formatError('missing value for --format'));
216
+ }
217
+ result.format = value;
218
+ continue;
219
+ }
220
+
221
+ if (arg === '--level') {
222
+ const value = args[++index];
223
+ if (typeof value === 'undefined') {
224
+ throw new Error(formatError('missing value for --level'));
225
+ }
226
+ const parsed = Number.parseInt(value, 10);
227
+ if (!Number.isInteger(parsed) || parsed < 0) {
228
+ throw new Error(formatError('invalid value for --level'));
229
+ }
230
+ result.level = parsed;
231
+ continue;
232
+ }
233
+
234
+ if (arg === '--only-ready') {
235
+ result.onlyReady = true;
236
+ continue;
237
+ }
238
+
239
+ if (arg === '--all-ready') {
240
+ result.allReady = true;
241
+ continue;
242
+ }
243
+
244
+ if (arg === '--auto-start') {
245
+ result.autoStart = true;
246
+ continue;
247
+ }
248
+
249
+ if (arg === '--unicode') {
250
+ result.unicode = true;
251
+ continue;
252
+ }
253
+
254
+ if (arg === '--spec') {
255
+ const value = args[++index];
256
+ if (!value) {
257
+ throw new Error(formatError('missing value for --spec'));
258
+ }
259
+ result.specSlug = value;
260
+ continue;
261
+ }
262
+
144
263
  if (arg === '--gate') {
145
264
  const value = args[++index];
146
265
  if (!value) {
@@ -183,6 +302,10 @@ function parseArgs(argv) {
183
302
  if (positional.length > 0) {
184
303
  result.targetDir = positional.shift();
185
304
  }
305
+ } else if (result.mode === 'plan') {
306
+ if (positional.length > 0) {
307
+ throw new Error(formatError('plan does not accept positional arguments; use --spec <slug>'));
308
+ }
186
309
  } else if (result.mode === 'refresh-active-slices') {
187
310
  if (positional.length > 0) {
188
311
  throw new Error(formatError('refresh-active-slices does not accept positional arguments'));
@@ -1022,6 +1145,10 @@ function runMigrate(targetDir) {
1022
1145
  throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
1023
1146
  }
1024
1147
 
1148
+ if (!hasQuiverInitializationEvidence(projectRoot)) {
1149
+ throw new Error(formatError('migrate requires a project previously initialized by Quiver.\nRun: npx create-quiver --name "Project Name"'));
1150
+ }
1151
+
1025
1152
  const packageJson = loadPackageJson(projectRoot);
1026
1153
  const projectName = packageJson.name || path.basename(projectRoot) || 'Quiver Project';
1027
1154
  const packageRoot = path.resolve(__dirname, '../..');
@@ -1038,6 +1165,15 @@ function runMigrate(targetDir) {
1038
1165
  });
1039
1166
  updateStateForMigrate(projectRoot, projectName, CLI_VERSION);
1040
1167
 
1168
+ if (!args.skipInstall) {
1169
+ const installResult = installSelfAsDevDep(projectRoot, CLI_VERSION);
1170
+ if (installResult === 'installed') {
1171
+ console.log(`Added create-quiver@${CLI_VERSION} as dev dependency`);
1172
+ } else if (installResult === 'failed') {
1173
+ console.warn(`Warning: could not install create-quiver automatically. Run: npm install -D create-quiver@${CLI_VERSION}`);
1174
+ }
1175
+ }
1176
+
1041
1177
  console.log(`Quiver migration completed for ${projectRoot}`);
1042
1178
  console.log('Missing workflow files were restored without overwriting existing project files.');
1043
1179
  } finally {
@@ -1052,6 +1188,10 @@ function runDoctor(targetDir) {
1052
1188
  throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
1053
1189
  }
1054
1190
 
1191
+ if (!hasQuiverInitializationEvidence(projectRoot)) {
1192
+ throw new Error(formatError('doctor requires a project previously initialized by Quiver.\nRun init first: npx create-quiver --name "Project Name"'));
1193
+ }
1194
+
1055
1195
  const generatedSpecs = listGeneratedSpecDirs(projectRoot);
1056
1196
  if (generatedSpecs.length !== 1) {
1057
1197
  throw new Error(formatError(`expected exactly one generated spec directory, found ${generatedSpecs.length || 0}`));
@@ -1144,10 +1284,11 @@ function runDoctor(targetDir) {
1144
1284
  if (!hasQuiverState) {
1145
1285
  console.log('- Run migration first: npx create-quiver migrate');
1146
1286
  } else if (!hasScanArtifacts) {
1147
- console.log('- Analyze the project first: npx create-quiver analyze');
1287
+ console.log('- Analyze the project first: npx create-quiver analyze');
1148
1288
  } else {
1149
1289
  console.log('- Ask your AI agent: Read AGENTS.md, then docs/AI_ONBOARDING_PROMPT.md and execute it.');
1150
1290
  }
1291
+ console.log('- Check the next ready slice: npx create-quiver next');
1151
1292
  console.log(`- Start a slice: npx create-quiver start-slice specs/${projectSlug}/slices/slice-template/slice.json`);
1152
1293
  console.log(`- Validate a slice: npx create-quiver check-slice specs/${projectSlug}/slices/slice-template/slice.json`);
1153
1294
  console.log(`- Validate the PR gate: npx create-quiver check-pr specs/${projectSlug}/slices/slice-template/slice.json`);
@@ -1177,6 +1318,37 @@ async function run(argv) {
1177
1318
  return;
1178
1319
  }
1179
1320
 
1321
+ if (args.mode === 'plan') {
1322
+ runPlan(process.cwd(), {
1323
+ json: args.json,
1324
+ onlyReady: args.onlyReady,
1325
+ specSlug: args.specSlug,
1326
+ unicode: args.unicode,
1327
+ });
1328
+ return;
1329
+ }
1330
+
1331
+ if (args.mode === 'graph') {
1332
+ runGraph(process.cwd(), {
1333
+ format: args.format,
1334
+ json: args.json,
1335
+ level: args.level,
1336
+ showConflicts: args.showConflicts,
1337
+ unicode: args.unicode,
1338
+ });
1339
+ return;
1340
+ }
1341
+
1342
+ if (args.mode === 'next') {
1343
+ await runNext(process.cwd(), {
1344
+ allReady: args.allReady,
1345
+ autoStart: args.autoStart,
1346
+ json: args.json,
1347
+ specSlug: args.specSlug,
1348
+ });
1349
+ return;
1350
+ }
1351
+
1180
1352
  if (args.mode === 'migrate') {
1181
1353
  runMigrate(args.targetDir);
1182
1354
  return;
@@ -1205,6 +1377,25 @@ async function run(argv) {
1205
1377
  return;
1206
1378
  }
1207
1379
 
1380
+ if (args.mode === 'check-handoff') {
1381
+ const repoRoot = process.cwd();
1382
+ const handoffInput = args.targetDir;
1383
+ if (!handoffInput || handoffInput === '.') {
1384
+ throw new Error(formatError('missing handoff path. Use: npx create-quiver check-handoff specs/<spec-slug>/HANDOFF.md'));
1385
+ }
1386
+ const resolved = checkHandoff(handoffInput, repoRoot);
1387
+ console.log(`PASS: Handoff validated at ${resolved.relativePath}`);
1388
+ return;
1389
+ }
1390
+
1391
+ if (args.mode === 'new-handoff') {
1392
+ const repoRoot = process.cwd();
1393
+ const handoffSlug = args.targetDir;
1394
+ const resolved = scaffoldHandoff(handoffSlug, repoRoot);
1395
+ console.log(`PASS: Handoff scaffolded at ${resolved.relativePath}`);
1396
+ return;
1397
+ }
1398
+
1208
1399
  if (args.mode === 'cleanup-slice') {
1209
1400
  cleanupSlice(path.resolve(process.cwd(), args.targetDir), {
1210
1401
  closeBaseline: args.closeBaseline,
@@ -1238,6 +1429,15 @@ async function run(argv) {
1238
1429
  copyTemplate(templateRoot, targetDir);
1239
1430
  runInitDocs(targetDir, projectName);
1240
1431
 
1432
+ if (!args.skipInstall) {
1433
+ const installResult = installSelfAsDevDep(targetDir, CLI_VERSION);
1434
+ if (installResult === 'installed') {
1435
+ console.log(`Added create-quiver@${CLI_VERSION} as dev dependency`);
1436
+ } else if (installResult === 'failed') {
1437
+ console.warn(`Warning: could not install create-quiver automatically. Run: npm install -D create-quiver@${CLI_VERSION}`);
1438
+ }
1439
+ }
1440
+
1241
1441
  console.log(`Installed Quiver into ${targetDir}`);
1242
1442
  printInitNextSteps(targetDir, projectName);
1243
1443
  } finally {
@@ -0,0 +1,104 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const REQUIRED_HEADINGS = [
5
+ '## Background',
6
+ '## What you will change',
7
+ '## Validation checklist',
8
+ '## Out of scope',
9
+ '## Expected deliverable',
10
+ '## Constraints',
11
+ ];
12
+
13
+ const HANDOFF_TEMPLATE_PATH = path.resolve(__dirname, '..', '..', '..', 'specs', '[project-name]', 'HANDOFF.md.template');
14
+
15
+ function normalizePosixPath(filePath, pathLib = path) {
16
+ return filePath.split(pathLib.sep).join('/');
17
+ }
18
+
19
+ function resolveHandoffPath(repoRoot, handoffInput, pathLib = path) {
20
+ const absolutePath = pathLib.resolve(repoRoot, handoffInput);
21
+ const relativePath = normalizePosixPath(pathLib.relative(repoRoot, absolutePath), pathLib);
22
+
23
+ if (relativePath.startsWith('..') || pathLib.isAbsolute(relativePath)) {
24
+ throw new Error(`create-quiver: handoff must live at specs/<spec-slug>/HANDOFF.md (got ${normalizePosixPath(handoffInput, pathLib)})`);
25
+ }
26
+
27
+ const match = relativePath.match(/^specs\/([^/]+)\/HANDOFF\.md$/);
28
+ if (!match) {
29
+ throw new Error(`create-quiver: handoff must live at specs/<spec-slug>/HANDOFF.md (got ${relativePath})`);
30
+ }
31
+
32
+ return {
33
+ absolutePath,
34
+ relativePath,
35
+ specSlug: match[1],
36
+ };
37
+ }
38
+
39
+ function readHandoffSections(text) {
40
+ return String(text || '')
41
+ .split(/\r?\n/)
42
+ .map((line) => line.trim())
43
+ .filter((line) => line.startsWith('## '))
44
+ .map((line) => line.replace(/\s+$/, ''));
45
+ }
46
+
47
+ function validateHandoffSections(text) {
48
+ const sections = new Set(readHandoffSections(text));
49
+ return REQUIRED_HEADINGS.filter((heading) => !sections.has(heading));
50
+ }
51
+
52
+ function checkHandoff(handoffInput, repoRoot = process.cwd()) {
53
+ const resolved = resolveHandoffPath(repoRoot, handoffInput);
54
+
55
+ if (!fs.existsSync(resolved.absolutePath)) {
56
+ throw new Error(`create-quiver: missing handoff file: ${resolved.relativePath}`);
57
+ }
58
+
59
+ const text = fs.readFileSync(resolved.absolutePath, 'utf8');
60
+ const missingSections = validateHandoffSections(text);
61
+ if (missingSections.length > 0) {
62
+ throw new Error(`create-quiver: handoff is missing required sections: ${missingSections.join(', ')}`);
63
+ }
64
+
65
+ return resolved;
66
+ }
67
+
68
+ function scaffoldHandoff(specSlug, repoRoot = process.cwd()) {
69
+ const trimmedSlug = String(specSlug || '').trim();
70
+ if (!trimmedSlug) {
71
+ throw new Error('create-quiver: missing handoff slug. Use: npx create-quiver new-handoff <spec-slug>');
72
+ }
73
+
74
+ if (!fs.existsSync(HANDOFF_TEMPLATE_PATH)) {
75
+ throw new Error('create-quiver: missing handoff template at specs/[project-name]/HANDOFF.md.template');
76
+ }
77
+
78
+ const resolved = resolveHandoffPath(repoRoot, path.join('specs', trimmedSlug, 'HANDOFF.md'));
79
+ if (fs.existsSync(resolved.absolutePath)) {
80
+ throw new Error(`create-quiver: handoff already exists at ${resolved.relativePath}`);
81
+ }
82
+
83
+ const templateText = fs.readFileSync(HANDOFF_TEMPLATE_PATH, 'utf8');
84
+ const projectName = trimmedSlug.replace(/-/g, ' ');
85
+ const currentDate = new Date().toISOString().slice(0, 10);
86
+ const renderedText = templateText
87
+ .replace(/{{PROJECT_NAME}}/g, projectName)
88
+ .replace(/{{PROJECT_SLUG}}/g, trimmedSlug)
89
+ .replace(/{{FECHA}}/g, currentDate);
90
+
91
+ fs.mkdirSync(path.dirname(resolved.absolutePath), { recursive: true });
92
+ fs.writeFileSync(resolved.absolutePath, renderedText);
93
+
94
+ return checkHandoff(resolved.relativePath, repoRoot);
95
+ }
96
+
97
+ module.exports = {
98
+ REQUIRED_HEADINGS,
99
+ checkHandoff,
100
+ readHandoffSections,
101
+ scaffoldHandoff,
102
+ resolveHandoffPath,
103
+ validateHandoffSections,
104
+ };
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { execSync } = require('child_process');
3
4
  const { writeState } = require('./state');
4
5
 
5
6
  function ensureDir(dirPath) {
@@ -61,6 +62,9 @@ function renderTemplate(text, replacements) {
61
62
  .replace(/{{PRIMARY_DEV}}/g, replacements.primaryDev || 'not defined')
62
63
  .replace(/{{PRIMARY_TEST}}/g, replacements.primaryTest || 'not defined')
63
64
  .replace(/{{ANALYZE_COMMAND}}/g, replacements.analyzeCommand || 'npx create-quiver analyze')
65
+ .replace(/{{PLAN_COMMAND}}/g, replacements.planCommand || 'npx create-quiver plan')
66
+ .replace(/{{GRAPH_COMMAND}}/g, replacements.graphCommand || 'npx create-quiver graph')
67
+ .replace(/{{NEXT_COMMAND}}/g, replacements.nextCommand || 'npx create-quiver next')
64
68
  .replace(/{{DOCTOR_COMMAND}}/g, replacements.doctorCommand || 'npx create-quiver doctor')
65
69
  .replace(/{{START_SLICE_COMMAND}}/g, replacements.startSliceCommand || 'npx create-quiver start-slice <slice.json>')
66
70
  .replace(/{{CHECK_SLICE_COMMAND}}/g, replacements.checkSliceCommand || 'npx create-quiver check-slice <slice.json>')
@@ -227,12 +231,22 @@ function buildReadme(projectName, projectSlug) {
227
231
 
228
232
  ## Quick Start
229
233
 
230
- Run Quiver from this project root. Do not install it globally.
234
+ Run Quiver from this project root. Do not install it globally.
231
235
 
232
236
  \`\`\`bash
233
237
  npm install
234
- npx create-quiver analyze
235
- npx create-quiver doctor
238
+ {{ANALYZE_COMMAND}}
239
+ {{PLAN_COMMAND}}
240
+ {{GRAPH_COMMAND}}
241
+ {{DOCTOR_COMMAND}}
242
+ {{NEXT_COMMAND}}
243
+ \`\`\`
244
+
245
+ Exportable graph formats are available when you need a PR-ready Mermaid block or Graphviz source:
246
+
247
+ \`\`\`bash
248
+ {{GRAPH_COMMAND}} --format mermaid
249
+ {{GRAPH_COMMAND}} --format dot
236
250
  \`\`\`
237
251
 
238
252
  If this project needs a pinned Quiver version, install it as a devDependency:
@@ -251,17 +265,28 @@ The generated project includes \`quiver:*\` npm scripts that call the Node CLI a
251
265
 
252
266
  \`\`\`bash
253
267
  npm run quiver:analyze
268
+ npm run quiver:plan
269
+ npm run quiver:graph
270
+ npm run quiver:next
254
271
  npm run quiver:doctor
255
272
  npm run quiver:migrate
256
273
  npm run quiver:start-slice -- specs/${projectSlug}/slices/slice-01/slice.json
257
274
  npm run quiver:check-slice -- specs/${projectSlug}/slices/slice-01/slice.json
258
275
  npm run quiver:check-pr -- specs/${projectSlug}/slices/slice-01/slice.json
276
+ npm run quiver:check-handoff -- specs/${projectSlug}/HANDOFF.md
259
277
  npm run quiver:cleanup-slice -- specs/${projectSlug}/slices/slice-01/slice.json
260
278
  npm run quiver:check-scope -- specs/${projectSlug}/slices/slice-01/slice.json
261
279
  npm run quiver:refresh-active-slices
262
280
  \`\`\`
263
281
 
282
+ The \`quiver:graph\` script prints the tree view by default; use \`npx create-quiver graph --format mermaid\` for PR-ready Markdown and \`--format dot\` when you want Graphviz source.
283
+ The \`quiver:next\` script points to the next ready slice and can auto-start it behind a confirmation prompt.
284
+ Use \`npx create-quiver next --all-ready\` when you want the full ready level instead of a single suggestion.
264
285
  The legacy Bash wrappers remain in \`tools/scripts/\` for compatibility, but new project-level automation should prefer the \`quiver:*\` scripts and the direct \`npx create-quiver ...\` commands below.
286
+ \`npm run quiver:migrate\` is only for projects that were already initialized by Quiver.
287
+ \`npm run check-handoff -- specs/${projectSlug}/HANDOFF.md\` is available as a legacy-friendly alias for the handoff validator.
288
+ If a new bounded transfer is needed, scaffold \`specs/${projectSlug}/HANDOFF.md\` with \`npx create-quiver new-handoff ${projectSlug}\` and validate it with \`npx create-quiver check-handoff specs/${projectSlug}/HANDOFF.md\`.
289
+ For exceptional context transfers between agents or phases, a dedicated \`HANDOFF.md\` can live alongside the usual spec and docs files.
265
290
 
266
291
  ## Cross-Platform Support
267
292
 
@@ -274,8 +299,19 @@ If the project already existed before this Quiver version, upgrade it from the p
274
299
  \`\`\`bash
275
300
  cd /path/to/your-project
276
301
  npx create-quiver migrate
277
- npx create-quiver analyze
278
- npx create-quiver doctor
302
+ {{ANALYZE_COMMAND}}
303
+ {{PLAN_COMMAND}}
304
+ {{GRAPH_COMMAND}}
305
+ {{NEXT_COMMAND}}
306
+ {{DOCTOR_COMMAND}}
307
+ \`\`\`
308
+
309
+ Use \`{{GRAPH_COMMAND}} --format mermaid\` for GitHub-friendly graph embeds or \`{{GRAPH_COMMAND}} --format dot\` for Graphviz pipelines.
310
+
311
+ If the project never ran Quiver initialization before, do not use \`migrate\` as bootstrap. Run:
312
+
313
+ \`\`\`bash
314
+ npx create-quiver --name "Project Name"
279
315
  \`\`\`
280
316
 
281
317
  If your team prefers a pinned local dependency, update the package first and then run the same flow:
@@ -283,10 +319,15 @@ If your team prefers a pinned local dependency, update the package first and the
283
319
  \`\`\`bash
284
320
  npm install --save-dev create-quiver@latest
285
321
  npx create-quiver migrate
286
- npx create-quiver analyze
287
- npx create-quiver doctor
322
+ {{ANALYZE_COMMAND}}
323
+ {{PLAN_COMMAND}}
324
+ {{GRAPH_COMMAND}}
325
+ {{NEXT_COMMAND}}
326
+ {{DOCTOR_COMMAND}}
288
327
  \`\`\`
289
328
 
329
+ The tree output remains the default, but Mermaid and DOT are available on demand for exported docs and slide decks.
330
+
290
331
  ## AI Context Onboarding
291
332
 
292
333
  Read \`AGENTS.md\` first, then open \`docs/AI_ONBOARDING_PROMPT.md\` after analysis.
@@ -300,6 +341,7 @@ Prepare the project context docs and report assumptions, risks, and files change
300
341
  \`\`\`
301
342
 
302
343
  Review the AI changes to docs/AI_CONTEXT.md, docs/CONTEXTO.md, docs/STATUS.md, and specs/${projectSlug}/SPEC.md before starting implementation work. Use \`docs/PROJECT_MAP.md\` for stack and command details.
344
+ If the work was explicitly transferred through a handoff artifact, read \`specs/${projectSlug}/HANDOFF.md\` before implementation.
303
345
 
304
346
  ## Decision Log
305
347
 
@@ -309,15 +351,21 @@ Record durable decisions in \`docs/DECISIONS.md\` so future AI agents do not re-
309
351
 
310
352
  1. Review or refine specs/${projectSlug}/SPEC.md.
311
353
  2. Create the first slice from specs/${projectSlug}/slices/slice-template/slice.json.
312
- 3. Start work with \`npx create-quiver start-slice <slice.json>\` or \`npm run quiver:start-slice -- <slice.json>\`.
313
- 4. Make one commit per slice.
314
- 5. Open one PR per spec.
354
+ 3. Review the plan with \`{{PLAN_COMMAND}}\` or \`npm run quiver:plan\`.
355
+ 4. Inspect parallel lots with \`{{GRAPH_COMMAND}}\` or \`npm run quiver:graph\`.
356
+ 5. Check the next ready slice with \`{{NEXT_COMMAND}}\` or \`npm run quiver:next\`.
357
+ 6. Start work with \`{{START_SLICE_COMMAND}}\` or \`npm run quiver:start-slice -- <slice.json>\`.
358
+ 7. Make one commit per slice.
359
+ 8. Open one PR per spec.
315
360
 
316
361
  ## Verification Checklist
317
362
 
318
363
  - [ ] npm install completes
319
- - [ ] npx create-quiver analyze completes
320
- - [ ] npx create-quiver doctor completes
364
+ - [ ] {{ANALYZE_COMMAND}} completes
365
+ - [ ] {{PLAN_COMMAND}} completes
366
+ - [ ] {{GRAPH_COMMAND}} completes
367
+ - [ ] {{NEXT_COMMAND}} completes
368
+ - [ ] {{DOCTOR_COMMAND}} completes
321
369
  - [ ] AI agent executed docs/AI_ONBOARDING_PROMPT.md
322
370
  - [ ] Context docs were reviewed before the first slice
323
371
 
@@ -326,6 +374,9 @@ Record durable decisions in \`docs/DECISIONS.md\` so future AI agents do not re-
326
374
  - [AI Context](./docs/AI_CONTEXT.md) - Contexto resumido para IA
327
375
  - [Decision Log](./docs/DECISIONS.md) - Decisiones durables del proyecto
328
376
  - [AI Onboarding Prompt](./docs/AI_ONBOARDING_PROMPT.md) - Handoff exacto para agentes después del análisis
377
+ - [Handoff](./specs/${projectSlug}/HANDOFF.md) - Transferencia excepcional entre agentes o fases
378
+ - [Check Handoff](./docs/WORKFLOW.md) - Valida el handoff con \`npx create-quiver check-handoff\`
379
+ - [Commands](./docs/COMMANDS.md) - Tabla canónica de comandos de orquestación
329
380
  - [Contexto](./docs/CONTEXTO.md) - Qué es ${projectName}
330
381
  - [Workflow](./docs/WORKFLOW.md) - Cómo implementar
331
382
  - [Support Matrix](./docs/SUPPORT_MATRIX.md) - Qué entornos están soportados
@@ -382,6 +433,7 @@ function initializeProjectDocs(options) {
382
433
  });
383
434
  const templateCopies = [
384
435
  ['docs/INDEX.md.template', 'docs/INDEX.md'],
436
+ ['docs/COMMANDS.md.template', 'docs/COMMANDS.md'],
385
437
  ['docs/DECISIONS.md.template', 'docs/DECISIONS.md'],
386
438
  ['docs/AI_CONTEXT.md.template', 'docs/AI_CONTEXT.md', frontMatterFor('Agent-facing project context pack', 'onboarding, implementation, review')],
387
439
  ['docs/AI_ONBOARDING_PROMPT.md.template', 'docs/AI_ONBOARDING_PROMPT.md', frontMatterFor('AI onboarding handoff prompt', 'onboarding after analysis')],
@@ -398,6 +450,7 @@ function initializeProjectDocs(options) {
398
450
  ['docs/TESTING_GUIDE_FOR_AI.md.template', 'docs/TESTING_GUIDE_FOR_AI.md'],
399
451
  ['docs/ai/LESSONS.md.template', 'docs/ai/LESSONS.md', frontMatterFor('Slice learnings log', 'after slice completion')],
400
452
  ['specs/[project-name]/SPEC.md.template', `specs/${replacements.projectSlug}/SPEC.md`],
453
+ ['specs/[project-name]/HANDOFF.md.template', `specs/${replacements.projectSlug}/HANDOFF.md`],
401
454
  ['specs/[project-name]/STATUS.md.template', `specs/${replacements.projectSlug}/STATUS.md`],
402
455
  ['specs/[project-name]/EVIDENCE_REPORT.md.template', `specs/${replacements.projectSlug}/EVIDENCE_REPORT.md`],
403
456
  ['specs/[project-name]/slices/slice-template/slice.json', `specs/${replacements.projectSlug}/slices/slice-template/slice.json`],
@@ -479,6 +532,9 @@ function initializeProjectDocs(options) {
479
532
  primaryDev: packageScripts.dev || packageScripts.start || 'not defined',
480
533
  primaryTest: packageScripts.test || 'not defined',
481
534
  analyzeCommand: 'npx create-quiver analyze',
535
+ planCommand: 'npx create-quiver plan',
536
+ graphCommand: 'npx create-quiver graph',
537
+ nextCommand: 'npx create-quiver next',
482
538
  doctorCommand: 'npx create-quiver doctor',
483
539
  startSliceCommand: 'npx create-quiver start-slice <slice.json>',
484
540
  checkSliceCommand: 'npx create-quiver check-slice <slice.json>',
@@ -492,6 +548,9 @@ function initializeProjectDocs(options) {
492
548
  ['docs/QUICK.md.template', 'docs/ai/QUICK.md'],
493
549
  ['docs/STANDARD.md.template', 'docs/ai/STANDARD.md'],
494
550
  ['docs/DEEP.md.template', 'docs/ai/DEEP.md'],
551
+ ['docs/examples/plan.md.template', 'docs/examples/plan.md'],
552
+ ['docs/examples/graph.md.template', 'docs/examples/graph.md'],
553
+ ['docs/examples/next.md.template', 'docs/examples/next.md'],
495
554
  ];
496
555
 
497
556
  for (const [source, destination] of tierCopies) {
@@ -597,7 +656,7 @@ function initializeProjectDocs(options) {
597
656
 
598
657
  const readmePath = path.join(projectRoot, 'README.md');
599
658
  if (!fs.existsSync(readmePath)) {
600
- fs.writeFileSync(readmePath, `${buildReadme(projectName, replacements.projectSlug)}\n`);
659
+ fs.writeFileSync(readmePath, `${renderTemplate(buildReadme(projectName, replacements.projectSlug), replacements)}\n`);
601
660
  operations.push({ source: 'README.md template', destination: 'README.md', result: 'created' });
602
661
  } else {
603
662
  operations.push({ source: 'README.md template', destination: 'README.md', result: 'skipped' });
@@ -609,8 +668,44 @@ function initializeProjectDocs(options) {
609
668
  };
610
669
  }
611
670
 
671
+ function detectPackageManager(projectRoot) {
672
+ if (fs.existsSync(path.join(projectRoot, 'bun.lockb'))) return 'bun';
673
+ if (fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))) return 'pnpm';
674
+ if (fs.existsSync(path.join(projectRoot, 'yarn.lock'))) return 'yarn';
675
+ return 'npm';
676
+ }
677
+
678
+ function installSelfAsDevDep(projectRoot, version) {
679
+ const packageJsonPath = path.join(projectRoot, 'package.json');
680
+ if (!fs.existsSync(packageJsonPath)) {
681
+ return 'skipped-no-package-json';
682
+ }
683
+
684
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
685
+ if (pkg.devDependencies && pkg.devDependencies['create-quiver']) {
686
+ return 'skipped-already-present';
687
+ }
688
+
689
+ const pm = detectPackageManager(projectRoot);
690
+ const commands = {
691
+ npm: `npm install -D create-quiver@${version}`,
692
+ yarn: `yarn add -D create-quiver@${version}`,
693
+ pnpm: `pnpm add -D create-quiver@${version}`,
694
+ bun: `bun add -d create-quiver@${version}`,
695
+ };
696
+
697
+ try {
698
+ execSync(commands[pm], { cwd: projectRoot, stdio: 'inherit' });
699
+ return 'installed';
700
+ } catch {
701
+ return 'failed';
702
+ }
703
+ }
704
+
612
705
  module.exports = {
613
706
  initializeProjectDocs,
614
707
  writeFrontMatter,
615
708
  toProjectSlug,
709
+ detectPackageManager,
710
+ installSelfAsDevDep,
616
711
  };