create-quiver 0.5.0 → 0.7.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 (69) hide show
  1. package/.github/workflows/ci.yml +7 -30
  2. package/AGENTS.md.template +41 -0
  3. package/CHANGELOG.md +5 -0
  4. package/README.md +53 -9
  5. package/README_FOR_AI.md +36 -14
  6. package/ROADMAP.md +78 -0
  7. package/docs/AI_CONTEXT.md.template +19 -25
  8. package/docs/AI_ONBOARDING_PROMPT.md.template +12 -0
  9. package/docs/CONTEXTO.md.template +4 -17
  10. package/docs/DECISIONS.md.template +18 -0
  11. package/docs/DEEP.md.template +34 -0
  12. package/docs/DOCUMENTATION_GUIDE.md.template +9 -7
  13. package/docs/GITFLOW_PR_GUIDE.md.template +7 -0
  14. package/docs/INDEX.md.template +9 -0
  15. package/docs/QUICK.md.template +27 -0
  16. package/docs/STANDARD.md.template +49 -0
  17. package/docs/STATUS.md.template +2 -2
  18. package/docs/SUPPORT_MATRIX.md.template +7 -4
  19. package/docs/TESTING_GUIDE_FOR_AI.md.template +4 -3
  20. package/docs/TROUBLESHOOTING.md.template +14 -0
  21. package/docs/WORKFLOW.md.template +19 -5
  22. package/package.json +3 -1
  23. package/package.template.json +13 -1
  24. package/scripts/cleanup-slice.sh +2 -172
  25. package/scripts/init-docs.sh +246 -44
  26. package/scripts/package-quiver.sh +5 -0
  27. package/scripts/start-slice.sh +3 -425
  28. package/specs/[project-name]/EVIDENCE_REPORT.md.template +3 -1
  29. package/specs/[project-name]/slices/slice-template/slice.json +2 -2
  30. package/specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md +38 -0
  31. package/specs/quiver-v11-existing-project-migration/SPEC.md +59 -0
  32. package/specs/quiver-v11-existing-project-migration/STATUS.md +26 -0
  33. package/specs/quiver-v11-existing-project-migration/slices/slice-01-non-destructive-migrate-command/slice.json +73 -0
  34. package/specs/quiver-v11-existing-project-migration/slices/slice-02-version-metadata-doctor-upgrade-checks/slice.json +71 -0
  35. package/specs/quiver-v11-existing-project-migration/slices/slice-03-upgrade-docs-legacy-project-smokes/slice.json +78 -0
  36. package/specs/quiver-v12-cross-platform-native-runtime/EVIDENCE_REPORT.md +30 -0
  37. package/specs/quiver-v12-cross-platform-native-runtime/SPEC.md +86 -0
  38. package/specs/quiver-v12-cross-platform-native-runtime/STATUS.md +29 -0
  39. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +69 -0
  40. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +76 -0
  41. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +74 -0
  42. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +81 -0
  43. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +78 -0
  44. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-06-cross-platform-ci-release-readiness/slice.json +74 -0
  45. package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +28 -0
  46. package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +68 -0
  47. package/specs/quiver-v13-token-efficient-ai-context/STATUS.md +26 -0
  48. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-01-token-efficient-ai-modes-guidance/slice.json +65 -0
  49. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-02-decision-log-context-checkpoint/slice.json +64 -0
  50. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-03-project-map-reading-order/slice.json +66 -0
  51. package/specs/quiver-v14-tiered-context-pack/EVIDENCE_REPORT.md +42 -0
  52. package/specs/quiver-v14-tiered-context-pack/SPEC.md +116 -0
  53. package/specs/quiver-v14-tiered-context-pack/STATUS.md +35 -0
  54. package/specs/quiver-v14-tiered-context-pack/slices/slice-01-tiered-context-pack/slice.json +77 -0
  55. package/specs/quiver-v14-tiered-context-pack/slices/slice-02-agents-md-router/slice.json +74 -0
  56. package/specs/quiver-v14-tiered-context-pack/slices/slice-03-active-slice-lifecycle/slice.json +74 -0
  57. package/specs/quiver-v14-tiered-context-pack/slices/slice-04-dedup-frontmatter/slice.json +83 -0
  58. package/specs/quiver-v14-tiered-context-pack/slices/slice-05-doctor-smokes-tiered-pack/slice.json +84 -0
  59. package/src/create-quiver/index.js +360 -40
  60. package/src/create-quiver/lib/analyze.js +9 -0
  61. package/src/create-quiver/lib/doctor.js +212 -0
  62. package/src/create-quiver/lib/git.js +154 -0
  63. package/src/create-quiver/lib/init-docs.js +616 -0
  64. package/src/create-quiver/lib/lifecycle.js +478 -0
  65. package/src/create-quiver/lib/paths.js +19 -0
  66. package/src/create-quiver/lib/readiness.js +300 -0
  67. package/src/create-quiver/lib/scope.js +5 -0
  68. package/src/create-quiver/lib/slice.js +194 -0
  69. package/src/create-quiver/lib/state.js +89 -0
@@ -2,6 +2,18 @@ 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 { collectDoctorWarnings } = require('./lib/doctor');
6
+ const { initializeProjectDocs } = require('./lib/init-docs');
7
+ const { checkPrReadiness, checkScope, checkSliceReadiness } = require('./lib/readiness');
8
+ const { cleanupSlice, refreshActiveSlicesBoard, startSlice } = require('./lib/lifecycle');
9
+ const { relativePosixPath, resolveTargetRoot } = require('./lib/paths');
10
+ const {
11
+ readState,
12
+ updateStateForAnalyze,
13
+ updateStateForMigrate,
14
+ } = require('./lib/state');
15
+ const cliPackageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../..', 'package.json'), 'utf8'));
16
+ const CLI_VERSION = cliPackageJson.version || '0.0.0';
5
17
 
6
18
  function formatError(message) {
7
19
  return `create-quiver: ${message}`;
@@ -11,7 +23,14 @@ function printUsage() {
11
23
  console.log(`Usage:
12
24
  npx create-quiver [options]
13
25
  npx create-quiver analyze [options]
26
+ npx create-quiver migrate [options]
14
27
  npx create-quiver doctor [options]
28
+ npx create-quiver start-slice [options] <slice.json>
29
+ npx create-quiver check-slice [options] <slice.json>
30
+ npx create-quiver check-pr <slice.json>
31
+ npx create-quiver cleanup-slice [options] <slice.json>
32
+ npx create-quiver check-scope [options] <slice.json>
33
+ npx create-quiver refresh-active-slices
15
34
 
16
35
  Options:
17
36
  -n, --name <project-name> Project name to generate
@@ -22,8 +41,15 @@ Options:
22
41
  Examples:
23
42
  npx create-quiver --name "My Project"
24
43
  npx create-quiver --name "My Project" --dir ./my-project
25
- npx create-quiver analyze --dir ./my-project
26
- npx create-quiver doctor --dir ./my-project
44
+ cd ./my-project && npx create-quiver analyze
45
+ cd ./my-project && npx create-quiver migrate
46
+ cd ./my-project && npx create-quiver doctor
47
+ cd ./my-project && npx create-quiver start-slice specs/my-project/slices/slice-01/slice.json
48
+ cd ./my-project && npx create-quiver check-slice specs/my-project/slices/slice-01/slice.json
49
+ cd ./my-project && npx create-quiver check-pr specs/my-project/slices/slice-01/slice.json
50
+ cd ./my-project && npx create-quiver cleanup-slice specs/my-project/slices/slice-01/slice.json
51
+ cd ./my-project && npx create-quiver check-scope specs/my-project/slices/slice-01/slice.json
52
+ cd ./my-project && npx create-quiver refresh-active-slices
27
53
  node bin/create-quiver.js doctor --dir ./my-project
28
54
  `);
29
55
  }
@@ -33,17 +59,28 @@ function parseArgs(argv) {
33
59
  help: false,
34
60
  force: false,
35
61
  mode: 'init',
62
+ allowDraft: false,
63
+ closeBaseline: false,
64
+ discard: false,
65
+ dryRun: false,
66
+ gate: 'execution',
36
67
  projectName: '',
37
68
  targetDir: '.',
69
+ strict: false,
70
+ strictOverlap: false,
38
71
  };
39
72
 
40
73
  const args = [...argv];
41
- if (args[0] === 'doctor' || args[0] === 'analyze') {
74
+ const commandModes = new Set(['doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'cleanup-slice', 'check-scope', 'refresh-active-slices']);
75
+ if (commandModes.has(args[0])) {
42
76
  result.mode = args[0];
43
77
  args.shift();
44
78
  } else if (args[0] === '--analyze') {
45
79
  result.mode = 'analyze';
46
80
  args.shift();
81
+ } else if (args[0] === '--migrate') {
82
+ result.mode = 'migrate';
83
+ args.shift();
47
84
  } else if (args[0] === '--doctor') {
48
85
  result.mode = 'doctor';
49
86
  args.shift();
@@ -69,6 +106,50 @@ function parseArgs(argv) {
69
106
  continue;
70
107
  }
71
108
 
109
+ if (arg === '--migrate') {
110
+ result.mode = 'migrate';
111
+ continue;
112
+ }
113
+
114
+ if (arg === '--allow-draft') {
115
+ result.allowDraft = true;
116
+ continue;
117
+ }
118
+
119
+ if (arg === '--close-baseline') {
120
+ result.closeBaseline = true;
121
+ continue;
122
+ }
123
+
124
+ if (arg === '--discard') {
125
+ result.discard = true;
126
+ continue;
127
+ }
128
+
129
+ if (arg === '--dry-run') {
130
+ result.dryRun = true;
131
+ continue;
132
+ }
133
+
134
+ if (arg === '--strict') {
135
+ result.strict = true;
136
+ continue;
137
+ }
138
+
139
+ if (arg === '--strict-overlap') {
140
+ result.strictOverlap = true;
141
+ continue;
142
+ }
143
+
144
+ if (arg === '--gate') {
145
+ const value = args[++index];
146
+ if (!value) {
147
+ throw new Error(formatError('missing value for --gate'));
148
+ }
149
+ result.gate = value;
150
+ continue;
151
+ }
152
+
72
153
  if (arg === '-n' || arg === '--name' || arg === '--project-name') {
73
154
  const value = args[++index];
74
155
  if (!value) {
@@ -102,6 +183,10 @@ function parseArgs(argv) {
102
183
  if (positional.length > 0) {
103
184
  result.targetDir = positional.shift();
104
185
  }
186
+ } else if (result.mode === 'refresh-active-slices') {
187
+ if (positional.length > 0) {
188
+ throw new Error(formatError('refresh-active-slices does not accept positional arguments'));
189
+ }
105
190
  } else {
106
191
  if (positional.length > 0) {
107
192
  result.targetDir = positional.shift();
@@ -177,9 +262,26 @@ function copyTemplate(templateRoot, targetDir) {
177
262
  return docsTemplateDir;
178
263
  }
179
264
 
265
+ function mergeDirectoryTree(sourceDir, targetDir) {
266
+ if (!fs.existsSync(sourceDir)) {
267
+ return;
268
+ }
269
+
270
+ fs.mkdirSync(targetDir, { recursive: true });
271
+ fs.cpSync(sourceDir, targetDir, {
272
+ recursive: true,
273
+ force: false,
274
+ errorOnExist: false,
275
+ preserveTimestamps: true,
276
+ });
277
+ }
278
+
180
279
  function runInitDocs(repoRoot, projectName) {
181
- runCommand('bash', ['docs-template/scripts/init-docs.sh', projectName], {
182
- cwd: repoRoot,
280
+ initializeProjectDocs({
281
+ projectRoot: repoRoot,
282
+ projectName,
283
+ cliVersion: CLI_VERSION,
284
+ migrateMode: false,
183
285
  });
184
286
  }
185
287
 
@@ -692,6 +794,49 @@ function buildProjectScan(projectRoot) {
692
794
 
693
795
  function renderProjectMap(scan) {
694
796
  const lines = [];
797
+ const projectSlug = scan.project.name
798
+ .toLowerCase()
799
+ .replace(/[^a-z0-9]+/g, '-')
800
+ .replace(/^-+|-+$/g, '') || 'project';
801
+ const docsFiles = new Set(scan.docs.files);
802
+ const hasDecisionLog = docsFiles.has('docs/DECISIONS.md');
803
+ const hasAiPrompt = docsFiles.has('docs/AI_ONBOARDING_PROMPT.md');
804
+ const sourceDirs = scan.structure.source_directories.length > 0 ? scan.structure.source_directories : [];
805
+ const configFiles = scan.structure.config_files.length > 0 ? scan.structure.config_files : [];
806
+ const highSignalFiles = [
807
+ 'README.md',
808
+ 'docs/INDEX.md',
809
+ 'docs/AI_CONTEXT.md',
810
+ 'docs/DECISIONS.md',
811
+ 'docs/PROJECT_SCAN.json',
812
+ 'docs/PROJECT_MAP.md',
813
+ 'docs/AI_ONBOARDING_PROMPT.md',
814
+ 'docs/CONTEXTO.md',
815
+ 'docs/WORKFLOW.md',
816
+ 'docs/SUPPORT_MATRIX.md',
817
+ 'docs/TROUBLESHOOTING.md',
818
+ 'package.json',
819
+ ...configFiles,
820
+ ].filter((value, index, array) => array.indexOf(value) === index);
821
+ const likelyTestCommands = [
822
+ ['Install', scan.commands.install || 'npm install'],
823
+ ['dev', scan.commands.common.dev || 'not defined'],
824
+ ['build', scan.commands.common.build || 'not defined'],
825
+ ['test', scan.commands.common.test || 'not defined'],
826
+ ['lint', scan.commands.common.lint || 'not defined'],
827
+ ];
828
+ const readingOrder = [
829
+ 'README.md',
830
+ 'docs/INDEX.md',
831
+ 'docs/AI_CONTEXT.md',
832
+ 'docs/PROJECT_SCAN.json',
833
+ 'docs/PROJECT_MAP.md',
834
+ hasDecisionLog ? 'docs/DECISIONS.md' : 'docs/DECISIONS.md (create with migrate if missing)',
835
+ 'docs/CONTEXTO.md',
836
+ 'docs/WORKFLOW.md',
837
+ 'docs/SUPPORT_MATRIX.md',
838
+ 'docs/TROUBLESHOOTING.md',
839
+ ];
695
840
 
696
841
  lines.push('# Project Map');
697
842
  lines.push('');
@@ -705,37 +850,83 @@ function renderProjectMap(scan) {
705
850
  }
706
851
 
707
852
  lines.push('');
708
- lines.push('## Stack');
709
- lines.push(`- Primary: ${scan.stack.primary}`);
710
- lines.push(`- Frameworks: ${scan.stack.frameworks.length > 0 ? scan.stack.frameworks.join(', ') : 'none detected'}`);
711
- lines.push(`- Languages: ${scan.stack.languages.length > 0 ? scan.stack.languages.join(', ') : 'none detected'}`);
853
+ lines.push('## Suggested Reading Order');
854
+ for (const item of readingOrder) {
855
+ lines.push(`- ${item}`);
856
+ }
712
857
 
713
- if (scan.stack.evidence.length > 0) {
714
- lines.push('');
715
- lines.push('### Evidence');
716
- for (const item of scan.stack.evidence) {
717
- lines.push(`- ${item.framework}: ${item.signals.join(', ')}`);
858
+ if (hasAiPrompt) {
859
+ lines.push('- docs/AI_ONBOARDING_PROMPT.md');
860
+ }
861
+
862
+ lines.push(`- specs/${projectSlug}/SPEC.md`);
863
+ if (sourceDirs.length > 0) {
864
+ for (const sourceDir of sourceDirs) {
865
+ lines.push(`- ${sourceDir}/...`);
866
+ }
867
+ }
868
+
869
+ lines.push('');
870
+ lines.push('## Entry Points');
871
+ lines.push(`- Project overview: ${scan.docs.has_readme ? 'README.md' : 'docs/CONTEXTO.md'}`);
872
+ lines.push(`- AI context: ${hasDecisionLog ? 'docs/AI_CONTEXT.md + docs/DECISIONS.md' : 'docs/AI_CONTEXT.md'}`);
873
+ lines.push('- Analysis outputs: docs/PROJECT_SCAN.json, docs/PROJECT_MAP.md');
874
+ lines.push(`- Workflow contract: docs/WORKFLOW.md`);
875
+ lines.push(`- Spec contract: specs/${projectSlug}/SPEC.md`);
876
+ if (sourceDirs.length > 0) {
877
+ lines.push(`- Source roots: ${sourceDirs.join(', ')}`);
878
+ } else {
879
+ lines.push('- Source roots: none detected');
880
+ }
881
+
882
+ lines.push('');
883
+ lines.push('## Primary Config Files');
884
+ if (configFiles.length > 0) {
885
+ for (const configFile of configFiles) {
886
+ lines.push(`- ${configFile}`);
718
887
  }
888
+ } else {
889
+ lines.push('- none detected');
719
890
  }
720
891
 
721
892
  lines.push('');
722
893
  lines.push('## Commands');
894
+ lines.push('See **Likely Test Commands** for the current read-friendly command summary.');
895
+
896
+ lines.push('');
897
+ lines.push('## Likely Test Commands');
723
898
  lines.push('| Command | Value |');
724
899
  lines.push('|---------|-------|');
725
- lines.push(`| Install | ${escapeMarkdownCell(scan.commands.install || 'npm install')} |`);
726
- lines.push(`| dev | ${escapeMarkdownCell(scan.commands.common.dev || 'not defined')} |`);
727
- lines.push(`| build | ${escapeMarkdownCell(scan.commands.common.build || 'not defined')} |`);
728
- lines.push(`| test | ${escapeMarkdownCell(scan.commands.common.test || 'not defined')} |`);
729
- lines.push(`| lint | ${escapeMarkdownCell(scan.commands.common.lint || 'not defined')} |`);
900
+ for (const [name, value] of likelyTestCommands) {
901
+ lines.push(`| ${name} | ${escapeMarkdownCell(value)} |`);
902
+ }
903
+
904
+ const relevantScripts = Object.entries(scan.commands.scripts)
905
+ .filter(([name]) => /(^|:)(analyze|doctor|migrate|test|build|lint|dev|start|check)(:|$)|analyze|doctor|migrate|test|build|lint|dev|start|check/i.test(name))
906
+ .slice(0, 12);
730
907
 
731
- if (Object.keys(scan.commands.scripts).length > 0) {
908
+ if (relevantScripts.length > 0) {
732
909
  lines.push('');
733
910
  lines.push('### package.json scripts');
734
- for (const [name, command] of Object.entries(scan.commands.scripts)) {
911
+ for (const [name, command] of relevantScripts) {
735
912
  lines.push(`- ${name}: \`${command}\``);
736
913
  }
737
914
  }
738
915
 
916
+ lines.push('');
917
+ lines.push('## Stack');
918
+ lines.push(`- Primary: ${scan.stack.primary}`);
919
+ lines.push(`- Frameworks: ${scan.stack.frameworks.length > 0 ? scan.stack.frameworks.join(', ') : 'none detected'}`);
920
+ lines.push(`- Languages: ${scan.stack.languages.length > 0 ? scan.stack.languages.join(', ') : 'none detected'}`);
921
+
922
+ if (scan.stack.evidence.length > 0) {
923
+ lines.push('');
924
+ lines.push('### Evidence');
925
+ for (const item of scan.stack.evidence) {
926
+ lines.push(`- ${item.framework}: ${item.signals.join(', ')}`);
927
+ }
928
+ }
929
+
739
930
  lines.push('');
740
931
  lines.push('## Structure');
741
932
  lines.push(`- Top-level directories: ${scan.structure.top_level_directories.length > 0 ? scan.structure.top_level_directories.join(', ') : 'none detected'}`);
@@ -750,6 +941,14 @@ function renderProjectMap(scan) {
750
941
  lines.push('## Docs');
751
942
  lines.push(`- README present: ${scan.docs.has_readme ? 'yes' : 'no'}`);
752
943
  lines.push(`- Docs files: ${scan.docs.files.length > 0 ? scan.docs.files.join(', ') : 'none detected'}`);
944
+ lines.push(`- Decision log: ${hasDecisionLog ? 'present' : 'missing'}`);
945
+ lines.push(`- AI onboarding prompt: ${hasAiPrompt ? 'present' : 'missing'}`);
946
+
947
+ lines.push('');
948
+ lines.push('## High-Signal Files');
949
+ for (const file of highSignalFiles) {
950
+ lines.push(`- ${file}`);
951
+ }
753
952
 
754
953
  lines.push('');
755
954
  lines.push('## Risks');
@@ -771,6 +970,16 @@ function renderProjectMap(scan) {
771
970
  lines.push('- None');
772
971
  }
773
972
 
973
+ lines.push('');
974
+ lines.push('## Do Not Read First');
975
+ if (scan.skipped_paths.length > 0) {
976
+ for (const skippedPath of scan.skipped_paths) {
977
+ lines.push(`- ${skippedPath}`);
978
+ }
979
+ } else {
980
+ lines.push('- None detected, but still prioritize docs and config files before source trees.');
981
+ }
982
+
774
983
  lines.push('');
775
984
  return lines.join('\n');
776
985
  }
@@ -789,7 +998,7 @@ function writeProjectScanArtifacts(projectRoot, scan) {
789
998
  }
790
999
 
791
1000
  function runAnalyze(targetDir) {
792
- const projectRoot = path.resolve(process.cwd(), targetDir);
1001
+ const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
793
1002
 
794
1003
  if (!fs.existsSync(projectRoot)) {
795
1004
  throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
@@ -797,16 +1006,47 @@ function runAnalyze(targetDir) {
797
1006
 
798
1007
  const scan = buildProjectScan(projectRoot);
799
1008
  const artifacts = writeProjectScanArtifacts(projectRoot, scan);
1009
+ updateStateForAnalyze(projectRoot, CLI_VERSION);
800
1010
 
801
1011
  console.log(`Project analysis completed for ${projectRoot}`);
802
- console.log(`Wrote ${path.relative(projectRoot, artifacts.jsonPath)}`);
803
- console.log(`Wrote ${path.relative(projectRoot, artifacts.mdPath)}`);
1012
+ console.log(`Wrote ${relativePosixPath(projectRoot, artifacts.jsonPath)}`);
1013
+ console.log(`Wrote ${relativePosixPath(projectRoot, artifacts.mdPath)}`);
804
1014
  console.log(`Detected primary stack: ${scan.stack.primary}`);
805
1015
  console.log(`Detected package manager: ${scan.project.package_manager}`);
806
1016
  }
807
1017
 
1018
+ function runMigrate(targetDir) {
1019
+ const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
1020
+
1021
+ if (!fs.existsSync(projectRoot)) {
1022
+ throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
1023
+ }
1024
+
1025
+ const packageJson = loadPackageJson(projectRoot);
1026
+ const projectName = packageJson.name || path.basename(projectRoot) || 'Quiver Project';
1027
+ const packageRoot = path.resolve(__dirname, '../..');
1028
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'quiver-migrate-'));
1029
+
1030
+ try {
1031
+ const templateRoot = packTemplate(packageRoot, tempRoot);
1032
+ mergeDirectoryTree(templateRoot, path.join(projectRoot, 'docs-template'));
1033
+ initializeProjectDocs({
1034
+ projectRoot,
1035
+ projectName,
1036
+ cliVersion: CLI_VERSION,
1037
+ migrateMode: true,
1038
+ });
1039
+ updateStateForMigrate(projectRoot, projectName, CLI_VERSION);
1040
+
1041
+ console.log(`Quiver migration completed for ${projectRoot}`);
1042
+ console.log('Missing workflow files were restored without overwriting existing project files.');
1043
+ } finally {
1044
+ fs.rmSync(tempRoot, { recursive: true, force: true });
1045
+ }
1046
+ }
1047
+
808
1048
  function runDoctor(targetDir) {
809
- const projectRoot = path.resolve(process.cwd(), targetDir);
1049
+ const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
810
1050
 
811
1051
  if (!fs.existsSync(projectRoot)) {
812
1052
  throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
@@ -819,6 +1059,7 @@ function runDoctor(targetDir) {
819
1059
 
820
1060
  const projectSlug = generatedSpecs[0];
821
1061
  const requiredFiles = [
1062
+ 'AGENTS.md',
822
1063
  'README.md',
823
1064
  'docs/INDEX.md',
824
1065
  'docs/AI_CONTEXT.md',
@@ -852,32 +1093,64 @@ function runDoctor(targetDir) {
852
1093
  const missingFiles = assertFilesExist(projectRoot, requiredFiles);
853
1094
  const nonExecutableScripts = assertExecutablesExist(projectRoot, requiredExecutables);
854
1095
  const pkg = loadPackageJson(projectRoot);
855
- const requiredScripts = ['check:slice', 'check:pr', 'start:slice', 'cleanup:slice'];
856
- const missingScripts = requiredScripts.filter((name) => typeof pkg.scripts?.[name] !== 'string');
1096
+ const workflowScriptGroups = [
1097
+ { label: 'migrate', node: 'quiver:migrate', legacy: 'migrate' },
1098
+ { label: 'start-slice', node: 'quiver:start-slice', legacy: 'start:slice' },
1099
+ { label: 'check-slice', node: 'quiver:check-slice', legacy: 'check:slice' },
1100
+ { label: 'check-pr', node: 'quiver:check-pr', legacy: 'check:pr' },
1101
+ { label: 'cleanup-slice', node: 'quiver:cleanup-slice', legacy: 'cleanup:slice' },
1102
+ { label: 'check-scope', node: 'quiver:check-scope', legacy: 'check:scope' },
1103
+ { label: 'refresh-active-slices', node: 'quiver:refresh-active-slices', legacy: 'refresh:active-slices' },
1104
+ ];
1105
+ const missingScripts = workflowScriptGroups
1106
+ .filter((group) => typeof pkg.scripts?.[group.node] !== 'string' && typeof pkg.scripts?.[group.legacy] !== 'string')
1107
+ .map((group) => `${group.node} (or legacy ${group.legacy})`);
1108
+ const legacyOnlyScripts = workflowScriptGroups
1109
+ .filter((group) => typeof pkg.scripts?.[group.node] !== 'string' && typeof pkg.scripts?.[group.legacy] === 'string')
1110
+ .map((group) => group.label);
1111
+ const missingNodeNativeScripts = ['quiver:migrate', 'quiver:analyze', 'quiver:doctor']
1112
+ .filter((name) => typeof pkg.scripts?.[name] !== 'string');
857
1113
  const hasScanArtifacts = fs.existsSync(path.join(projectRoot, 'docs', 'PROJECT_SCAN.json'))
858
1114
  && fs.existsSync(path.join(projectRoot, 'docs', 'PROJECT_MAP.md'));
859
-
860
- const problems = [
1115
+ const quiverState = readState(projectRoot);
1116
+ const hasQuiverState = Boolean(quiverState);
1117
+ const stateWarnings = hasQuiverState ? [] : ['missing Quiver state metadata: .quiver/state.json'];
1118
+ const migrationProblems = [
861
1119
  ...missingFiles.map((file) => `missing file: ${file}`),
862
1120
  ...nonExecutableScripts.map((file) => `missing executable bit: ${file}`),
863
1121
  ...missingScripts.map((name) => `missing package.json script: ${name}`),
864
1122
  ];
1123
+ const softWarnings = collectDoctorWarnings(projectRoot);
865
1124
 
866
- if (problems.length > 0) {
867
- throw new Error(formatError(`doctor failed:\n- ${problems.join('\n- ')}`));
1125
+ if (migrationProblems.length > 0) {
1126
+ throw new Error(formatError(`doctor failed:\n- ${migrationProblems.join('\n- ')}\n- Run migration first: npx create-quiver migrate`));
868
1127
  }
869
1128
 
870
1129
  console.log(`Quiver doctor passed for ${projectRoot}`);
871
1130
  console.log(`Generated project slug: ${projectSlug}`);
872
1131
  console.log('Next steps:');
873
- if (!hasScanArtifacts) {
874
- console.log('- Analyze the project first: npx create-quiver analyze --dir .');
1132
+ for (const warning of stateWarnings) {
1133
+ console.log(`- Warning: ${warning}`);
1134
+ }
1135
+ for (const scriptName of missingNodeNativeScripts) {
1136
+ console.log(`- Warning: missing Node-native script: ${scriptName}`);
1137
+ }
1138
+ if (legacyOnlyScripts.length > 0) {
1139
+ console.log(`- Warning: legacy Bash workflow scripts detected for ${legacyOnlyScripts.join(', ')}. Run npx create-quiver migrate to add quiver:* npm scripts.`);
1140
+ }
1141
+ for (const warning of softWarnings) {
1142
+ console.log(`- Warning: ${warning}`);
1143
+ }
1144
+ if (!hasQuiverState) {
1145
+ console.log('- Run migration first: npx create-quiver migrate');
1146
+ } else if (!hasScanArtifacts) {
1147
+ console.log('- Analyze the project first: npx create-quiver analyze');
875
1148
  } else {
876
- console.log('- Ask your AI agent: Read docs/AI_ONBOARDING_PROMPT.md and execute it.');
1149
+ console.log('- Ask your AI agent: Read AGENTS.md, then docs/AI_ONBOARDING_PROMPT.md and execute it.');
877
1150
  }
878
- console.log(`- Start a slice: bash tools/scripts/start-slice.sh specs/${projectSlug}/slices/slice-template/slice.json`);
879
- console.log('- Validate a slice: bash tools/scripts/check-slice-readiness.sh');
880
- console.log('- Validate the PR gate: bash tools/scripts/check-pr-readiness.sh');
1151
+ console.log(`- Start a slice: npx create-quiver start-slice specs/${projectSlug}/slices/slice-template/slice.json`);
1152
+ console.log(`- Validate a slice: npx create-quiver check-slice specs/${projectSlug}/slices/slice-template/slice.json`);
1153
+ console.log(`- Validate the PR gate: npx create-quiver check-pr specs/${projectSlug}/slices/slice-template/slice.json`);
881
1154
  }
882
1155
 
883
1156
  function printInitNextSteps(targetDir, projectName) {
@@ -885,10 +1158,10 @@ function printInitNextSteps(targetDir, projectName) {
885
1158
 
886
1159
  console.log('');
887
1160
  console.log('Next steps:');
888
- console.log(`- Review ${path.join(targetDir, 'docs', 'INDEX.md')}`);
1161
+ console.log(`- Review AGENTS.md, then ${path.join(targetDir, 'docs', 'INDEX.md')}`);
889
1162
  console.log(`- Review ${path.join(targetDir, 'docs', 'WORKFLOW.md')}`);
890
1163
  console.log(`- Create your first slice from ${path.join(targetDir, 'specs', projectSlug, 'slices', 'slice-template', 'slice.json')}`);
891
- console.log(`- Launch slice work with ${path.join(targetDir, 'tools', 'scripts', 'start-slice.sh')}`);
1164
+ console.log(`- Launch slice work with npx create-quiver start-slice specs/${projectSlug}/slices/slice-template/slice.json`);
892
1165
  }
893
1166
 
894
1167
  async function run(argv) {
@@ -904,13 +1177,57 @@ async function run(argv) {
904
1177
  return;
905
1178
  }
906
1179
 
1180
+ if (args.mode === 'migrate') {
1181
+ runMigrate(args.targetDir);
1182
+ return;
1183
+ }
1184
+
907
1185
  if (args.mode === 'doctor') {
908
1186
  runDoctor(args.targetDir);
909
1187
  return;
910
1188
  }
911
1189
 
1190
+ if (args.mode === 'start-slice') {
1191
+ startSlice(path.resolve(process.cwd(), args.targetDir), { allowDraft: args.allowDraft });
1192
+ return;
1193
+ }
1194
+
1195
+ if (args.mode === 'check-slice') {
1196
+ checkSliceReadiness(path.resolve(process.cwd(), args.targetDir), {
1197
+ gate: args.gate,
1198
+ strictOverlap: args.strictOverlap,
1199
+ });
1200
+ return;
1201
+ }
1202
+
1203
+ if (args.mode === 'check-pr') {
1204
+ checkPrReadiness(path.resolve(process.cwd(), args.targetDir));
1205
+ return;
1206
+ }
1207
+
1208
+ if (args.mode === 'cleanup-slice') {
1209
+ cleanupSlice(path.resolve(process.cwd(), args.targetDir), {
1210
+ closeBaseline: args.closeBaseline,
1211
+ discard: args.discard,
1212
+ dryRun: args.dryRun,
1213
+ force: args.force,
1214
+ });
1215
+ return;
1216
+ }
1217
+
1218
+ if (args.mode === 'check-scope') {
1219
+ checkScope(path.resolve(process.cwd(), args.targetDir), { strict: args.strict });
1220
+ return;
1221
+ }
1222
+
1223
+ if (args.mode === 'refresh-active-slices') {
1224
+ const outputPath = refreshActiveSlicesBoard(path.resolve(process.cwd(), '.'));
1225
+ console.log(`Active slices refreshed: ${outputPath}`);
1226
+ return;
1227
+ }
1228
+
912
1229
  const packageRoot = path.resolve(__dirname, '../..');
913
- const targetDir = path.resolve(process.cwd(), args.targetDir);
1230
+ const targetDir = resolveTargetRoot(process.cwd(), args.targetDir);
914
1231
  const projectName = args.projectName || path.basename(targetDir) || 'Quiver Project';
915
1232
  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'quiver-create-'));
916
1233
 
@@ -929,5 +1246,8 @@ async function run(argv) {
929
1246
  }
930
1247
 
931
1248
  module.exports = {
1249
+ runAnalyze,
1250
+ runDoctor,
1251
+ runMigrate,
932
1252
  run,
933
1253
  };
@@ -0,0 +1,9 @@
1
+ const { runAnalyze } = require('../index');
2
+
3
+ function analyzeProject(targetDir) {
4
+ return runAnalyze(targetDir);
5
+ }
6
+
7
+ module.exports = {
8
+ analyzeProject,
9
+ };