create-quiver 0.6.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 (63) 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 +37 -13
  5. package/README_FOR_AI.md +27 -7
  6. package/ROADMAP.md +78 -0
  7. package/docs/AI_CONTEXT.md.template +19 -26
  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 +17 -4
  22. package/package.json +2 -1
  23. package/package.template.json +11 -0
  24. package/scripts/cleanup-slice.sh +2 -172
  25. package/scripts/init-docs.sh +97 -24
  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-v12-cross-platform-native-runtime/EVIDENCE_REPORT.md +30 -0
  31. package/specs/quiver-v12-cross-platform-native-runtime/SPEC.md +86 -0
  32. package/specs/quiver-v12-cross-platform-native-runtime/STATUS.md +29 -0
  33. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +69 -0
  34. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +76 -0
  35. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +74 -0
  36. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +81 -0
  37. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +78 -0
  38. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-06-cross-platform-ci-release-readiness/slice.json +74 -0
  39. package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +28 -0
  40. package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +68 -0
  41. package/specs/quiver-v13-token-efficient-ai-context/STATUS.md +26 -0
  42. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-01-token-efficient-ai-modes-guidance/slice.json +65 -0
  43. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-02-decision-log-context-checkpoint/slice.json +64 -0
  44. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-03-project-map-reading-order/slice.json +66 -0
  45. package/specs/quiver-v14-tiered-context-pack/EVIDENCE_REPORT.md +42 -0
  46. package/specs/quiver-v14-tiered-context-pack/SPEC.md +116 -0
  47. package/specs/quiver-v14-tiered-context-pack/STATUS.md +35 -0
  48. package/specs/quiver-v14-tiered-context-pack/slices/slice-01-tiered-context-pack/slice.json +77 -0
  49. package/specs/quiver-v14-tiered-context-pack/slices/slice-02-agents-md-router/slice.json +74 -0
  50. package/specs/quiver-v14-tiered-context-pack/slices/slice-03-active-slice-lifecycle/slice.json +74 -0
  51. package/specs/quiver-v14-tiered-context-pack/slices/slice-04-dedup-frontmatter/slice.json +83 -0
  52. package/specs/quiver-v14-tiered-context-pack/slices/slice-05-doctor-smokes-tiered-pack/slice.json +84 -0
  53. package/src/create-quiver/index.js +299 -123
  54. package/src/create-quiver/lib/analyze.js +9 -0
  55. package/src/create-quiver/lib/doctor.js +212 -0
  56. package/src/create-quiver/lib/git.js +154 -0
  57. package/src/create-quiver/lib/init-docs.js +616 -0
  58. package/src/create-quiver/lib/lifecycle.js +478 -0
  59. package/src/create-quiver/lib/paths.js +19 -0
  60. package/src/create-quiver/lib/readiness.js +300 -0
  61. package/src/create-quiver/lib/scope.js +5 -0
  62. package/src/create-quiver/lib/slice.js +194 -0
  63. package/src/create-quiver/lib/state.js +89 -0
@@ -2,6 +2,16 @@ 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');
5
15
  const cliPackageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../..', 'package.json'), 'utf8'));
6
16
  const CLI_VERSION = cliPackageJson.version || '0.0.0';
7
17
 
@@ -15,6 +25,12 @@ function printUsage() {
15
25
  npx create-quiver analyze [options]
16
26
  npx create-quiver migrate [options]
17
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
18
34
 
19
35
  Options:
20
36
  -n, --name <project-name> Project name to generate
@@ -25,9 +41,15 @@ Options:
25
41
  Examples:
26
42
  npx create-quiver --name "My Project"
27
43
  npx create-quiver --name "My Project" --dir ./my-project
28
- npx create-quiver analyze --dir ./my-project
29
- npx create-quiver migrate --dir ./my-project
30
- 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
31
53
  node bin/create-quiver.js doctor --dir ./my-project
32
54
  `);
33
55
  }
@@ -37,12 +59,20 @@ function parseArgs(argv) {
37
59
  help: false,
38
60
  force: false,
39
61
  mode: 'init',
62
+ allowDraft: false,
63
+ closeBaseline: false,
64
+ discard: false,
65
+ dryRun: false,
66
+ gate: 'execution',
40
67
  projectName: '',
41
68
  targetDir: '.',
69
+ strict: false,
70
+ strictOverlap: false,
42
71
  };
43
72
 
44
73
  const args = [...argv];
45
- if (args[0] === 'doctor' || args[0] === 'analyze' || args[0] === 'migrate') {
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])) {
46
76
  result.mode = args[0];
47
77
  args.shift();
48
78
  } else if (args[0] === '--analyze') {
@@ -81,6 +111,45 @@ function parseArgs(argv) {
81
111
  continue;
82
112
  }
83
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
+
84
153
  if (arg === '-n' || arg === '--name' || arg === '--project-name') {
85
154
  const value = args[++index];
86
155
  if (!value) {
@@ -114,6 +183,10 @@ function parseArgs(argv) {
114
183
  if (positional.length > 0) {
115
184
  result.targetDir = positional.shift();
116
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
+ }
117
190
  } else {
118
191
  if (positional.length > 0) {
119
192
  result.targetDir = positional.shift();
@@ -204,84 +277,14 @@ function mergeDirectoryTree(sourceDir, targetDir) {
204
277
  }
205
278
 
206
279
  function runInitDocs(repoRoot, projectName) {
207
- runCommand('bash', ['docs-template/scripts/init-docs.sh', projectName], {
208
- cwd: repoRoot,
280
+ initializeProjectDocs({
281
+ projectRoot: repoRoot,
282
+ projectName,
283
+ cliVersion: CLI_VERSION,
284
+ migrateMode: false,
209
285
  });
210
286
  }
211
287
 
212
- function runInitDocsWithMode(repoRoot, projectName, mode) {
213
- return runCommand('bash', ['docs-template/scripts/init-docs.sh', projectName], {
214
- cwd: repoRoot,
215
- env: {
216
- ...process.env,
217
- QUIVER_PROJECT_NAME: projectName,
218
- QUIVER_MIGRATE: mode === 'migrate' ? '1' : '0',
219
- QUIVER_VERSION: CLI_VERSION,
220
- },
221
- });
222
- }
223
-
224
- function writeQuiverState(projectRoot, nextState) {
225
- const stateDir = path.join(projectRoot, '.quiver');
226
- const statePath = path.join(stateDir, 'state.json');
227
- fs.mkdirSync(stateDir, { recursive: true });
228
- fs.writeFileSync(statePath, `${JSON.stringify(nextState, null, 2)}\n`);
229
- return statePath;
230
- }
231
-
232
- function readQuiverState(projectRoot) {
233
- return readJsonIfExists(path.join(projectRoot, '.quiver', 'state.json'));
234
- }
235
-
236
- function createInitialQuiverState(projectName) {
237
- const now = new Date().toISOString();
238
-
239
- return {
240
- project_name: projectName,
241
- quiver_version: CLI_VERSION,
242
- initialized_version: CLI_VERSION,
243
- migrated_version: null,
244
- last_initialized_at: now,
245
- last_migration_at: null,
246
- last_analysis_at: null,
247
- };
248
- }
249
-
250
- function updateQuiverStateForAnalyze(projectRoot) {
251
- const currentState = readQuiverState(projectRoot);
252
-
253
- if (!currentState) {
254
- return null;
255
- }
256
-
257
- const nextState = {
258
- ...currentState,
259
- quiver_version: CLI_VERSION,
260
- last_analysis_at: new Date().toISOString(),
261
- };
262
-
263
- writeQuiverState(projectRoot, nextState);
264
- return nextState;
265
- }
266
-
267
- function updateQuiverStateForMigrate(projectRoot, projectName) {
268
- const currentState = readQuiverState(projectRoot);
269
- const now = new Date().toISOString();
270
- const nextState = {
271
- ...(currentState || {}),
272
- project_name: projectName,
273
- quiver_version: CLI_VERSION,
274
- initialized_version: currentState?.initialized_version ?? null,
275
- migrated_version: CLI_VERSION,
276
- last_initialized_at: currentState?.last_initialized_at ?? null,
277
- last_migration_at: now,
278
- last_analysis_at: currentState?.last_analysis_at ?? null,
279
- };
280
-
281
- writeQuiverState(projectRoot, nextState);
282
- return nextState;
283
- }
284
-
285
288
  function listGeneratedSpecDirs(projectRoot) {
286
289
  const specsDir = path.join(projectRoot, 'specs');
287
290
 
@@ -791,6 +794,49 @@ function buildProjectScan(projectRoot) {
791
794
 
792
795
  function renderProjectMap(scan) {
793
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
+ ];
794
840
 
795
841
  lines.push('# Project Map');
796
842
  lines.push('');
@@ -804,37 +850,83 @@ function renderProjectMap(scan) {
804
850
  }
805
851
 
806
852
  lines.push('');
807
- lines.push('## Stack');
808
- lines.push(`- Primary: ${scan.stack.primary}`);
809
- lines.push(`- Frameworks: ${scan.stack.frameworks.length > 0 ? scan.stack.frameworks.join(', ') : 'none detected'}`);
810
- 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
+ }
811
857
 
812
- if (scan.stack.evidence.length > 0) {
813
- lines.push('');
814
- lines.push('### Evidence');
815
- for (const item of scan.stack.evidence) {
816
- 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}/...`);
817
866
  }
818
867
  }
819
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}`);
887
+ }
888
+ } else {
889
+ lines.push('- none detected');
890
+ }
891
+
820
892
  lines.push('');
821
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');
822
898
  lines.push('| Command | Value |');
823
899
  lines.push('|---------|-------|');
824
- lines.push(`| Install | ${escapeMarkdownCell(scan.commands.install || 'npm install')} |`);
825
- lines.push(`| dev | ${escapeMarkdownCell(scan.commands.common.dev || 'not defined')} |`);
826
- lines.push(`| build | ${escapeMarkdownCell(scan.commands.common.build || 'not defined')} |`);
827
- lines.push(`| test | ${escapeMarkdownCell(scan.commands.common.test || 'not defined')} |`);
828
- lines.push(`| lint | ${escapeMarkdownCell(scan.commands.common.lint || 'not defined')} |`);
900
+ for (const [name, value] of likelyTestCommands) {
901
+ lines.push(`| ${name} | ${escapeMarkdownCell(value)} |`);
902
+ }
829
903
 
830
- if (Object.keys(scan.commands.scripts).length > 0) {
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);
907
+
908
+ if (relevantScripts.length > 0) {
831
909
  lines.push('');
832
910
  lines.push('### package.json scripts');
833
- for (const [name, command] of Object.entries(scan.commands.scripts)) {
911
+ for (const [name, command] of relevantScripts) {
834
912
  lines.push(`- ${name}: \`${command}\``);
835
913
  }
836
914
  }
837
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
+
838
930
  lines.push('');
839
931
  lines.push('## Structure');
840
932
  lines.push(`- Top-level directories: ${scan.structure.top_level_directories.length > 0 ? scan.structure.top_level_directories.join(', ') : 'none detected'}`);
@@ -849,6 +941,14 @@ function renderProjectMap(scan) {
849
941
  lines.push('## Docs');
850
942
  lines.push(`- README present: ${scan.docs.has_readme ? 'yes' : 'no'}`);
851
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
+ }
852
952
 
853
953
  lines.push('');
854
954
  lines.push('## Risks');
@@ -870,6 +970,16 @@ function renderProjectMap(scan) {
870
970
  lines.push('- None');
871
971
  }
872
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
+
873
983
  lines.push('');
874
984
  return lines.join('\n');
875
985
  }
@@ -888,7 +998,7 @@ function writeProjectScanArtifacts(projectRoot, scan) {
888
998
  }
889
999
 
890
1000
  function runAnalyze(targetDir) {
891
- const projectRoot = path.resolve(process.cwd(), targetDir);
1001
+ const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
892
1002
 
893
1003
  if (!fs.existsSync(projectRoot)) {
894
1004
  throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
@@ -896,17 +1006,17 @@ function runAnalyze(targetDir) {
896
1006
 
897
1007
  const scan = buildProjectScan(projectRoot);
898
1008
  const artifacts = writeProjectScanArtifacts(projectRoot, scan);
899
- updateQuiverStateForAnalyze(projectRoot);
1009
+ updateStateForAnalyze(projectRoot, CLI_VERSION);
900
1010
 
901
1011
  console.log(`Project analysis completed for ${projectRoot}`);
902
- console.log(`Wrote ${path.relative(projectRoot, artifacts.jsonPath)}`);
903
- console.log(`Wrote ${path.relative(projectRoot, artifacts.mdPath)}`);
1012
+ console.log(`Wrote ${relativePosixPath(projectRoot, artifacts.jsonPath)}`);
1013
+ console.log(`Wrote ${relativePosixPath(projectRoot, artifacts.mdPath)}`);
904
1014
  console.log(`Detected primary stack: ${scan.stack.primary}`);
905
1015
  console.log(`Detected package manager: ${scan.project.package_manager}`);
906
1016
  }
907
1017
 
908
1018
  function runMigrate(targetDir) {
909
- const projectRoot = path.resolve(process.cwd(), targetDir);
1019
+ const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
910
1020
 
911
1021
  if (!fs.existsSync(projectRoot)) {
912
1022
  throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
@@ -920,15 +1030,13 @@ function runMigrate(targetDir) {
920
1030
  try {
921
1031
  const templateRoot = packTemplate(packageRoot, tempRoot);
922
1032
  mergeDirectoryTree(templateRoot, path.join(projectRoot, 'docs-template'));
923
- const migrationOutput = runInitDocsWithMode(projectRoot, projectName, 'migrate');
924
- updateQuiverStateForMigrate(projectRoot, projectName);
925
-
926
- if (migrationOutput.trim().length > 0) {
927
- process.stdout.write(migrationOutput);
928
- if (!migrationOutput.endsWith('\n')) {
929
- process.stdout.write('\n');
930
- }
931
- }
1033
+ initializeProjectDocs({
1034
+ projectRoot,
1035
+ projectName,
1036
+ cliVersion: CLI_VERSION,
1037
+ migrateMode: true,
1038
+ });
1039
+ updateStateForMigrate(projectRoot, projectName, CLI_VERSION);
932
1040
 
933
1041
  console.log(`Quiver migration completed for ${projectRoot}`);
934
1042
  console.log('Missing workflow files were restored without overwriting existing project files.');
@@ -938,7 +1046,7 @@ function runMigrate(targetDir) {
938
1046
  }
939
1047
 
940
1048
  function runDoctor(targetDir) {
941
- const projectRoot = path.resolve(process.cwd(), targetDir);
1049
+ const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
942
1050
 
943
1051
  if (!fs.existsSync(projectRoot)) {
944
1052
  throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
@@ -951,6 +1059,7 @@ function runDoctor(targetDir) {
951
1059
 
952
1060
  const projectSlug = generatedSpecs[0];
953
1061
  const requiredFiles = [
1062
+ 'AGENTS.md',
954
1063
  'README.md',
955
1064
  'docs/INDEX.md',
956
1065
  'docs/AI_CONTEXT.md',
@@ -984,11 +1093,26 @@ function runDoctor(targetDir) {
984
1093
  const missingFiles = assertFilesExist(projectRoot, requiredFiles);
985
1094
  const nonExecutableScripts = assertExecutablesExist(projectRoot, requiredExecutables);
986
1095
  const pkg = loadPackageJson(projectRoot);
987
- const requiredScripts = ['check:slice', 'check:pr', 'start:slice', 'cleanup:slice', 'migrate'];
988
- 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');
989
1113
  const hasScanArtifacts = fs.existsSync(path.join(projectRoot, 'docs', 'PROJECT_SCAN.json'))
990
1114
  && fs.existsSync(path.join(projectRoot, 'docs', 'PROJECT_MAP.md'));
991
- const quiverState = readQuiverState(projectRoot);
1115
+ const quiverState = readState(projectRoot);
992
1116
  const hasQuiverState = Boolean(quiverState);
993
1117
  const stateWarnings = hasQuiverState ? [] : ['missing Quiver state metadata: .quiver/state.json'];
994
1118
  const migrationProblems = [
@@ -996,9 +1120,10 @@ function runDoctor(targetDir) {
996
1120
  ...nonExecutableScripts.map((file) => `missing executable bit: ${file}`),
997
1121
  ...missingScripts.map((name) => `missing package.json script: ${name}`),
998
1122
  ];
1123
+ const softWarnings = collectDoctorWarnings(projectRoot);
999
1124
 
1000
1125
  if (migrationProblems.length > 0) {
1001
- throw new Error(formatError(`doctor failed:\n- ${migrationProblems.join('\n- ')}\n- Run migration first: npx create-quiver migrate --dir .`));
1126
+ throw new Error(formatError(`doctor failed:\n- ${migrationProblems.join('\n- ')}\n- Run migration first: npx create-quiver migrate`));
1002
1127
  }
1003
1128
 
1004
1129
  console.log(`Quiver doctor passed for ${projectRoot}`);
@@ -1007,16 +1132,25 @@ function runDoctor(targetDir) {
1007
1132
  for (const warning of stateWarnings) {
1008
1133
  console.log(`- Warning: ${warning}`);
1009
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
+ }
1010
1144
  if (!hasQuiverState) {
1011
- console.log('- Run migration first: npx create-quiver migrate --dir .');
1145
+ console.log('- Run migration first: npx create-quiver migrate');
1012
1146
  } else if (!hasScanArtifacts) {
1013
- console.log('- Analyze the project first: npx create-quiver analyze --dir .');
1147
+ console.log('- Analyze the project first: npx create-quiver analyze');
1014
1148
  } else {
1015
- 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.');
1016
1150
  }
1017
- console.log(`- Start a slice: bash tools/scripts/start-slice.sh specs/${projectSlug}/slices/slice-template/slice.json`);
1018
- console.log('- Validate a slice: bash tools/scripts/check-slice-readiness.sh');
1019
- 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`);
1020
1154
  }
1021
1155
 
1022
1156
  function printInitNextSteps(targetDir, projectName) {
@@ -1024,10 +1158,10 @@ function printInitNextSteps(targetDir, projectName) {
1024
1158
 
1025
1159
  console.log('');
1026
1160
  console.log('Next steps:');
1027
- console.log(`- Review ${path.join(targetDir, 'docs', 'INDEX.md')}`);
1161
+ console.log(`- Review AGENTS.md, then ${path.join(targetDir, 'docs', 'INDEX.md')}`);
1028
1162
  console.log(`- Review ${path.join(targetDir, 'docs', 'WORKFLOW.md')}`);
1029
1163
  console.log(`- Create your first slice from ${path.join(targetDir, 'specs', projectSlug, 'slices', 'slice-template', 'slice.json')}`);
1030
- 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`);
1031
1165
  }
1032
1166
 
1033
1167
  async function run(argv) {
@@ -1053,8 +1187,47 @@ async function run(argv) {
1053
1187
  return;
1054
1188
  }
1055
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
+
1056
1229
  const packageRoot = path.resolve(__dirname, '../..');
1057
- const targetDir = path.resolve(process.cwd(), args.targetDir);
1230
+ const targetDir = resolveTargetRoot(process.cwd(), args.targetDir);
1058
1231
  const projectName = args.projectName || path.basename(targetDir) || 'Quiver Project';
1059
1232
  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'quiver-create-'));
1060
1233
 
@@ -1073,5 +1246,8 @@ async function run(argv) {
1073
1246
  }
1074
1247
 
1075
1248
  module.exports = {
1249
+ runAnalyze,
1250
+ runDoctor,
1251
+ runMigrate,
1076
1252
  run,
1077
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
+ };