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.
- package/.github/workflows/ci.yml +7 -30
- package/AGENTS.md.template +41 -0
- package/CHANGELOG.md +5 -0
- package/README.md +37 -13
- package/README_FOR_AI.md +27 -7
- package/ROADMAP.md +78 -0
- package/docs/AI_CONTEXT.md.template +19 -26
- 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 +17 -4
- package/package.json +2 -1
- package/package.template.json +11 -0
- package/scripts/cleanup-slice.sh +2 -172
- package/scripts/init-docs.sh +97 -24
- 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-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 +299 -123
- 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,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
|
|
29
|
-
npx create-quiver migrate
|
|
30
|
-
npx create-quiver doctor
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
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('##
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
853
|
+
lines.push('## Suggested Reading Order');
|
|
854
|
+
for (const item of readingOrder) {
|
|
855
|
+
lines.push(`- ${item}`);
|
|
856
|
+
}
|
|
811
857
|
|
|
812
|
-
if (
|
|
813
|
-
lines.push('');
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1009
|
+
updateStateForAnalyze(projectRoot, CLI_VERSION);
|
|
900
1010
|
|
|
901
1011
|
console.log(`Project analysis completed for ${projectRoot}`);
|
|
902
|
-
console.log(`Wrote ${
|
|
903
|
-
console.log(`Wrote ${
|
|
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 =
|
|
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
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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 =
|
|
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
|
|
988
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
1018
|
-
console.log(
|
|
1019
|
-
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`);
|
|
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
|
|
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 =
|
|
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
|
};
|