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.
- package/.github/workflows/ci.yml +7 -30
- package/AGENTS.md.template +41 -0
- package/CHANGELOG.md +5 -0
- package/README.md +53 -9
- package/README_FOR_AI.md +36 -14
- package/ROADMAP.md +78 -0
- package/docs/AI_CONTEXT.md.template +19 -25
- package/docs/AI_ONBOARDING_PROMPT.md.template +12 -0
- package/docs/CONTEXTO.md.template +4 -17
- package/docs/DECISIONS.md.template +18 -0
- package/docs/DEEP.md.template +34 -0
- package/docs/DOCUMENTATION_GUIDE.md.template +9 -7
- package/docs/GITFLOW_PR_GUIDE.md.template +7 -0
- package/docs/INDEX.md.template +9 -0
- package/docs/QUICK.md.template +27 -0
- package/docs/STANDARD.md.template +49 -0
- package/docs/STATUS.md.template +2 -2
- package/docs/SUPPORT_MATRIX.md.template +7 -4
- package/docs/TESTING_GUIDE_FOR_AI.md.template +4 -3
- package/docs/TROUBLESHOOTING.md.template +14 -0
- package/docs/WORKFLOW.md.template +19 -5
- package/package.json +3 -1
- package/package.template.json +13 -1
- package/scripts/cleanup-slice.sh +2 -172
- package/scripts/init-docs.sh +246 -44
- package/scripts/package-quiver.sh +5 -0
- package/scripts/start-slice.sh +3 -425
- package/specs/[project-name]/EVIDENCE_REPORT.md.template +3 -1
- package/specs/[project-name]/slices/slice-template/slice.json +2 -2
- package/specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md +38 -0
- package/specs/quiver-v11-existing-project-migration/SPEC.md +59 -0
- package/specs/quiver-v11-existing-project-migration/STATUS.md +26 -0
- package/specs/quiver-v11-existing-project-migration/slices/slice-01-non-destructive-migrate-command/slice.json +73 -0
- package/specs/quiver-v11-existing-project-migration/slices/slice-02-version-metadata-doctor-upgrade-checks/slice.json +71 -0
- package/specs/quiver-v11-existing-project-migration/slices/slice-03-upgrade-docs-legacy-project-smokes/slice.json +78 -0
- package/specs/quiver-v12-cross-platform-native-runtime/EVIDENCE_REPORT.md +30 -0
- package/specs/quiver-v12-cross-platform-native-runtime/SPEC.md +86 -0
- package/specs/quiver-v12-cross-platform-native-runtime/STATUS.md +29 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +69 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +76 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +74 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +81 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +78 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-06-cross-platform-ci-release-readiness/slice.json +74 -0
- package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +28 -0
- package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +68 -0
- package/specs/quiver-v13-token-efficient-ai-context/STATUS.md +26 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-01-token-efficient-ai-modes-guidance/slice.json +65 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-02-decision-log-context-checkpoint/slice.json +64 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-03-project-map-reading-order/slice.json +66 -0
- package/specs/quiver-v14-tiered-context-pack/EVIDENCE_REPORT.md +42 -0
- package/specs/quiver-v14-tiered-context-pack/SPEC.md +116 -0
- package/specs/quiver-v14-tiered-context-pack/STATUS.md +35 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-01-tiered-context-pack/slice.json +77 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-02-agents-md-router/slice.json +74 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-03-active-slice-lifecycle/slice.json +74 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-04-dedup-frontmatter/slice.json +83 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-05-doctor-smokes-tiered-pack/slice.json +84 -0
- package/src/create-quiver/index.js +360 -40
- package/src/create-quiver/lib/analyze.js +9 -0
- package/src/create-quiver/lib/doctor.js +212 -0
- package/src/create-quiver/lib/git.js +154 -0
- package/src/create-quiver/lib/init-docs.js +616 -0
- package/src/create-quiver/lib/lifecycle.js +478 -0
- package/src/create-quiver/lib/paths.js +19 -0
- package/src/create-quiver/lib/readiness.js +300 -0
- package/src/create-quiver/lib/scope.js +5 -0
- package/src/create-quiver/lib/slice.js +194 -0
- 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
|
|
26
|
-
npx create-quiver
|
|
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
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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('##
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
853
|
+
lines.push('## Suggested Reading Order');
|
|
854
|
+
for (const item of readingOrder) {
|
|
855
|
+
lines.push(`- ${item}`);
|
|
856
|
+
}
|
|
712
857
|
|
|
713
|
-
if (
|
|
714
|
-
lines.push('');
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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 (
|
|
908
|
+
if (relevantScripts.length > 0) {
|
|
732
909
|
lines.push('');
|
|
733
910
|
lines.push('### package.json scripts');
|
|
734
|
-
for (const [name, command] of
|
|
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 =
|
|
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 ${
|
|
803
|
-
console.log(`Wrote ${
|
|
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 =
|
|
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
|
|
856
|
-
|
|
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
|
|
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 (
|
|
867
|
-
throw new Error(formatError(`doctor failed:\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
|
-
|
|
874
|
-
console.log(
|
|
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:
|
|
879
|
-
console.log(
|
|
880
|
-
console.log(
|
|
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
|
|
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 =
|
|
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
|
};
|