create-quiver 0.9.0 → 0.10.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/README.md +312 -124
- package/README_FOR_AI.md +59 -45
- package/ROADMAP.md +12 -11
- package/docs/AI_ONBOARDING_PROMPT.md.template +120 -52
- package/docs/COMMANDS.md.template +41 -6
- package/docs/GITFLOW_PR_GUIDE.md.template +11 -0
- package/docs/STANDARD.md.template +1 -1
- package/docs/SUPPORT_MATRIX.md.template +4 -0
- package/docs/TROUBLESHOOTING.md.template +29 -1
- package/docs/WORKFLOW.md.template +1 -1
- package/package.json +6 -1
- package/package.template.json +11 -6
- package/scripts/check-pr-readiness.sh +1 -1
- package/scripts/check-scope.sh +0 -1
- package/scripts/check-slice-readiness.sh +3 -4
- package/scripts/init-docs.sh +55 -9
- package/specs/quiver-v19-self-install-dev-dep/EVIDENCE_REPORT.md +2 -2
- package/specs/quiver-v19-self-install-dev-dep/STATUS.md +4 -4
- package/specs/quiver-v19-self-install-dev-dep/slices/slice-01-auto-install-dev-dep/slice.json +4 -4
- package/specs/quiver-v20-ai-cli-orchestration/EVIDENCE_REPORT.md +23 -0
- package/specs/quiver-v20-ai-cli-orchestration/EXECUTION_PLAN.md +57 -0
- package/specs/quiver-v20-ai-cli-orchestration/SPEC.md +202 -0
- package/specs/quiver-v20-ai-cli-orchestration/STATUS.md +35 -0
- package/specs/quiver-v20-ai-cli-orchestration/pr.md +100 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/slice.json +54 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/slice.json +55 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/CLOSURE_BRIEF.md +40 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/EXECUTION_BRIEF.md +60 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/slice.json +54 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/EXECUTION_BRIEF.md +62 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/slice.json +62 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/slice.json +59 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/slice.json +59 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/EXECUTION_BRIEF.md +64 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/slice.json +65 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/EXECUTION_BRIEF.md +66 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/slice.json +63 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/EXECUTION_BRIEF.md +64 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/slice.json +77 -0
- package/specs/quiver-v21-ai-first-layout/EVIDENCE_REPORT.md +31 -0
- package/specs/quiver-v21-ai-first-layout/EXECUTION_PLAN.md +185 -0
- package/specs/quiver-v21-ai-first-layout/SPEC.md +212 -0
- package/specs/quiver-v21-ai-first-layout/STATUS.md +37 -0
- package/specs/quiver-v21-ai-first-layout/pr.md +110 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/slice.json +45 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/slice.json +57 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/EXECUTION_BRIEF.md +60 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/slice.json +58 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/slice.json +64 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/slice.json +64 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/EXECUTION_BRIEF.md +60 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/slice.json +65 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/EXECUTION_BRIEF.md +62 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/slice.json +66 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/slice.json +67 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/EXECUTION_BRIEF.md +66 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/slice.json +62 -0
- package/src/create-quiver/commands/ai.js +442 -0
- package/src/create-quiver/index.js +421 -84
- package/src/create-quiver/lib/ai/context-packs.js +158 -0
- package/src/create-quiver/lib/ai/execution-plan.js +254 -0
- package/src/create-quiver/lib/ai/executor.js +323 -0
- package/src/create-quiver/lib/ai/github.js +329 -0
- package/src/create-quiver/lib/ai/phase-gates.js +72 -0
- package/src/create-quiver/lib/ai/preflight.js +58 -0
- package/src/create-quiver/lib/ai/prompt-transport.js +81 -0
- package/src/create-quiver/lib/ai/prompts.js +39 -0
- package/src/create-quiver/lib/ai/providers.js +314 -0
- package/src/create-quiver/lib/ai/safety.js +151 -0
- package/src/create-quiver/lib/ai/spec-generator.js +314 -0
- package/src/create-quiver/lib/ai/spec-templates.js +715 -0
- package/src/create-quiver/lib/doctor.js +114 -0
- package/src/create-quiver/lib/git.js +21 -0
- package/src/create-quiver/lib/init-docs.js +286 -25
- package/src/create-quiver/lib/init-layout.js +426 -0
- package/src/create-quiver/lib/lifecycle.js +2 -2
- package/src/create-quiver/lib/paths.js +63 -2
- package/src/create-quiver/lib/project-scan.js +66 -0
- package/src/create-quiver/lib/readiness.js +4 -2
- package/src/create-quiver/lib/scope.js +125 -0
- package/src/create-quiver/lib/slice-graph.js +6 -0
- package/src/create-quiver/lib/slice.js +51 -8
- package/src/create-quiver/lib/state.js +18 -1
- package/src/create-quiver/lib/template-resolver.js +74 -0
- package/.claude/settings.local.json +0 -52
|
@@ -3,16 +3,27 @@ const os = require('os');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { execFileSync } = require('child_process');
|
|
5
5
|
const { checkHandoff, scaffoldHandoff } = require('./lib/handoff');
|
|
6
|
-
const {
|
|
6
|
+
const { collectDoctorReport } = require('./lib/doctor');
|
|
7
|
+
const { runDoctor: runAiDoctor, runExecuteSlice: runAiExecuteSlice, runOnboard, runPlan: runAiPlan, runPr: runAiPr } = require('./commands/ai');
|
|
7
8
|
const { runGraph } = require('./commands/graph');
|
|
8
9
|
const { runNext } = require('./commands/next');
|
|
9
10
|
const { runPlan } = require('./commands/plan');
|
|
11
|
+
const { buildInitLayout, formatInitLayoutPlan } = require('./lib/init-layout');
|
|
10
12
|
const { initializeProjectDocs, installSelfAsDevDep } = require('./lib/init-docs');
|
|
11
13
|
const { checkPrReadiness, checkScope, checkSliceReadiness } = require('./lib/readiness');
|
|
12
14
|
const { cleanupSlice, refreshActiveSlicesBoard, startSlice } = require('./lib/lifecycle');
|
|
13
15
|
const { relativePosixPath, resolveTargetRoot } = require('./lib/paths');
|
|
16
|
+
const {
|
|
17
|
+
CURRENT_SCAN_RELATIVE_PATH,
|
|
18
|
+
PROJECT_MAP_RELATIVE_PATH,
|
|
19
|
+
hasProjectScanArtifact,
|
|
20
|
+
projectScanPaths,
|
|
21
|
+
writeProjectScanJson,
|
|
22
|
+
} = require('./lib/project-scan');
|
|
23
|
+
const { resolveTemplateRoot } = require('./lib/template-resolver');
|
|
14
24
|
const {
|
|
15
25
|
hasQuiverInitializationEvidence,
|
|
26
|
+
inspectLegacyMigrationLayout,
|
|
16
27
|
readState,
|
|
17
28
|
updateStateForAnalyze,
|
|
18
29
|
updateStateForMigrate,
|
|
@@ -27,8 +38,10 @@ function formatError(message) {
|
|
|
27
38
|
function printUsage() {
|
|
28
39
|
console.log(`Usage:
|
|
29
40
|
npx create-quiver [options]
|
|
41
|
+
npx create-quiver init [options]
|
|
30
42
|
npx create-quiver analyze [options]
|
|
31
43
|
npx create-quiver plan [options]
|
|
44
|
+
npx create-quiver ai <task> [options]
|
|
32
45
|
npx create-quiver graph [options]
|
|
33
46
|
npx create-quiver next [options]
|
|
34
47
|
npx create-quiver migrate [options]
|
|
@@ -54,14 +67,26 @@ Options:
|
|
|
54
67
|
--all-ready List every ready slice returned by next
|
|
55
68
|
--auto-start Prompt for confirmation and run start-slice on next
|
|
56
69
|
--unicode Prefer Unicode output when supported
|
|
70
|
+
--minimal Plan or run the minimal init profile
|
|
71
|
+
--full Plan or run the full compatibility init profile
|
|
72
|
+
--legacy-scripts Include legacy Bash wrappers in init profile
|
|
73
|
+
--include-templates Export packaged templates in init profile
|
|
74
|
+
--dry-run Preview init or AI work without executing writes/providers
|
|
57
75
|
-y, --yes Skip prompts and use the provided inputs
|
|
58
76
|
-h, --help Show this help message
|
|
59
77
|
|
|
60
78
|
Examples:
|
|
79
|
+
npx create-quiver init --name "My Project"
|
|
80
|
+
npx create-quiver init --name "My Project" --dry-run
|
|
61
81
|
npx create-quiver --name "My Project"
|
|
62
82
|
npx create-quiver --name "My Project" --dir ./my-project
|
|
63
83
|
cd ./my-project && npx create-quiver analyze
|
|
64
84
|
cd ./my-project && npx create-quiver plan --json
|
|
85
|
+
cd ./my-project && npx create-quiver ai onboard --dry-run
|
|
86
|
+
cd ./my-project && npx create-quiver ai plan --phase acceptance --input requirements.md --dry-run
|
|
87
|
+
cd ./my-project && npx create-quiver ai execute-slice --slice specs/my-project/slices/slice-01/slice.json --dry-run
|
|
88
|
+
cd ./my-project && npx create-quiver ai doctor --dry-run --ssh-host-alias github-work --identity-file ~/.ssh/github-work
|
|
89
|
+
cd ./my-project && npx create-quiver ai pr --dry-run --ssh-host-alias github-work --identity-file ~/.ssh/github-work
|
|
65
90
|
cd ./my-project && npx create-quiver graph --show-conflicts
|
|
66
91
|
cd ./my-project && npx create-quiver graph --format mermaid
|
|
67
92
|
cd ./my-project && npx create-quiver graph --format dot
|
|
@@ -86,6 +111,7 @@ function parseArgs(argv) {
|
|
|
86
111
|
const result = {
|
|
87
112
|
help: false,
|
|
88
113
|
force: false,
|
|
114
|
+
explicitInit: false,
|
|
89
115
|
mode: 'init',
|
|
90
116
|
allowDraft: false,
|
|
91
117
|
closeBaseline: false,
|
|
@@ -105,12 +131,28 @@ function parseArgs(argv) {
|
|
|
105
131
|
showConflicts: false,
|
|
106
132
|
level: null,
|
|
107
133
|
unicode: false,
|
|
134
|
+
aiCommand: '',
|
|
135
|
+
aiPhase: 'acceptance',
|
|
136
|
+
aiProvider: 'codex',
|
|
137
|
+
aiRole: '',
|
|
138
|
+
aiContext: '',
|
|
139
|
+
aiInput: '',
|
|
140
|
+
aiSlice: '',
|
|
141
|
+
aiTimeout: null,
|
|
142
|
+
aiSshHostAlias: '',
|
|
143
|
+
aiIdentityFile: '',
|
|
144
|
+
aiRemote: 'origin',
|
|
145
|
+
initFull: false,
|
|
146
|
+
initIncludeTemplates: false,
|
|
147
|
+
initLegacyScripts: false,
|
|
148
|
+
initMinimal: false,
|
|
108
149
|
};
|
|
109
150
|
|
|
110
151
|
const args = [...argv];
|
|
111
|
-
const commandModes = new Set(['plan', 'graph', 'next', 'doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'check-handoff', 'new-handoff', 'cleanup-slice', 'check-scope', 'refresh-active-slices']);
|
|
152
|
+
const commandModes = new Set(['init', 'plan', 'graph', 'next', 'doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'check-handoff', 'new-handoff', 'cleanup-slice', 'check-scope', 'refresh-active-slices', 'ai']);
|
|
112
153
|
if (commandModes.has(args[0])) {
|
|
113
154
|
result.mode = args[0];
|
|
155
|
+
result.explicitInit = args[0] === 'init';
|
|
114
156
|
args.shift();
|
|
115
157
|
} else if (args[0] === '--analyze') {
|
|
116
158
|
result.mode = 'analyze';
|
|
@@ -189,6 +231,26 @@ function parseArgs(argv) {
|
|
|
189
231
|
continue;
|
|
190
232
|
}
|
|
191
233
|
|
|
234
|
+
if (arg === '--minimal') {
|
|
235
|
+
result.initMinimal = true;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (arg === '--full') {
|
|
240
|
+
result.initFull = true;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (arg === '--legacy-scripts') {
|
|
245
|
+
result.initLegacyScripts = true;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (arg === '--include-templates') {
|
|
250
|
+
result.initIncludeTemplates = true;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
192
254
|
if (arg === '--strict') {
|
|
193
255
|
result.strict = true;
|
|
194
256
|
continue;
|
|
@@ -251,6 +313,100 @@ function parseArgs(argv) {
|
|
|
251
313
|
continue;
|
|
252
314
|
}
|
|
253
315
|
|
|
316
|
+
if (arg === '--provider') {
|
|
317
|
+
const value = args[++index];
|
|
318
|
+
if (!value) {
|
|
319
|
+
throw new Error(formatError('missing value for --provider'));
|
|
320
|
+
}
|
|
321
|
+
result.aiProvider = value;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (arg === '--role') {
|
|
326
|
+
const value = args[++index];
|
|
327
|
+
if (!value) {
|
|
328
|
+
throw new Error(formatError('missing value for --role'));
|
|
329
|
+
}
|
|
330
|
+
result.aiRole = value;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (arg === '--context') {
|
|
335
|
+
const value = args[++index];
|
|
336
|
+
if (!value) {
|
|
337
|
+
throw new Error(formatError('missing value for --context'));
|
|
338
|
+
}
|
|
339
|
+
result.aiContext = value;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (arg === '--input') {
|
|
344
|
+
const value = args[++index];
|
|
345
|
+
if (!value) {
|
|
346
|
+
throw new Error(formatError('missing value for --input'));
|
|
347
|
+
}
|
|
348
|
+
result.aiInput = value;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (arg === '--slice') {
|
|
353
|
+
const value = args[++index];
|
|
354
|
+
if (!value) {
|
|
355
|
+
throw new Error(formatError('missing value for --slice'));
|
|
356
|
+
}
|
|
357
|
+
result.aiSlice = value;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (arg === '--timeout') {
|
|
362
|
+
const value = args[++index];
|
|
363
|
+
if (typeof value === 'undefined') {
|
|
364
|
+
throw new Error(formatError('missing value for --timeout'));
|
|
365
|
+
}
|
|
366
|
+
const parsed = Number.parseInt(value, 10);
|
|
367
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
368
|
+
throw new Error(formatError('invalid value for --timeout'));
|
|
369
|
+
}
|
|
370
|
+
result.aiTimeout = parsed;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (arg === '--ssh-host-alias') {
|
|
375
|
+
const value = args[++index];
|
|
376
|
+
if (!value) {
|
|
377
|
+
throw new Error(formatError('missing value for --ssh-host-alias'));
|
|
378
|
+
}
|
|
379
|
+
result.aiSshHostAlias = value;
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (arg === '--identity-file') {
|
|
384
|
+
const value = args[++index];
|
|
385
|
+
if (!value) {
|
|
386
|
+
throw new Error(formatError('missing value for --identity-file'));
|
|
387
|
+
}
|
|
388
|
+
result.aiIdentityFile = value;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (arg === '--remote') {
|
|
393
|
+
const value = args[++index];
|
|
394
|
+
if (!value) {
|
|
395
|
+
throw new Error(formatError('missing value for --remote'));
|
|
396
|
+
}
|
|
397
|
+
result.aiRemote = value;
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (arg === '--phase') {
|
|
402
|
+
const value = args[++index];
|
|
403
|
+
if (!value) {
|
|
404
|
+
throw new Error(formatError('missing value for --phase'));
|
|
405
|
+
}
|
|
406
|
+
result.aiPhase = value;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
254
410
|
if (arg === '--spec') {
|
|
255
411
|
const value = args[++index];
|
|
256
412
|
if (!value) {
|
|
@@ -306,6 +462,13 @@ function parseArgs(argv) {
|
|
|
306
462
|
if (positional.length > 0) {
|
|
307
463
|
throw new Error(formatError('plan does not accept positional arguments; use --spec <slug>'));
|
|
308
464
|
}
|
|
465
|
+
} else if (result.mode === 'ai') {
|
|
466
|
+
if (!result.aiCommand && positional.length > 0) {
|
|
467
|
+
result.aiCommand = positional.shift();
|
|
468
|
+
}
|
|
469
|
+
if (positional.length > 0) {
|
|
470
|
+
throw new Error(formatError('ai does not accept extra positional arguments'));
|
|
471
|
+
}
|
|
309
472
|
} else if (result.mode === 'refresh-active-slices') {
|
|
310
473
|
if (positional.length > 0) {
|
|
311
474
|
throw new Error(formatError('refresh-active-slices does not accept positional arguments'));
|
|
@@ -340,6 +503,43 @@ function runCommand(command, args, options = {}) {
|
|
|
340
503
|
});
|
|
341
504
|
}
|
|
342
505
|
|
|
506
|
+
function copyPackageFallback(packageRoot, tempRoot) {
|
|
507
|
+
const fallbackDir = path.join(tempRoot, 'package-fallback');
|
|
508
|
+
const ignoredRoots = new Set([
|
|
509
|
+
'.git',
|
|
510
|
+
'.worktrees',
|
|
511
|
+
'examples',
|
|
512
|
+
'package-lock.json',
|
|
513
|
+
'tests',
|
|
514
|
+
]);
|
|
515
|
+
const ignoredPrefixes = [
|
|
516
|
+
'scripts/ci',
|
|
517
|
+
'specs/quiver-v01',
|
|
518
|
+
'specs/quiver-v02-bootstrap-hardening',
|
|
519
|
+
'specs/quiver-v03-adoption-verification',
|
|
520
|
+
'specs/quiver-v04-zero-friction-installation',
|
|
521
|
+
];
|
|
522
|
+
|
|
523
|
+
fs.cpSync(packageRoot, fallbackDir, {
|
|
524
|
+
recursive: true,
|
|
525
|
+
filter: (sourcePath) => {
|
|
526
|
+
const relativePath = relativePosixPath(packageRoot, sourcePath);
|
|
527
|
+
if (!relativePath || relativePath === '.') {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const firstSegment = relativePath.split('/')[0];
|
|
532
|
+
if (ignoredRoots.has(firstSegment) || ignoredRoots.has(relativePath)) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return !ignoredPrefixes.some((prefix) => relativePath === prefix || relativePath.startsWith(`${prefix}/`));
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
return fallbackDir;
|
|
541
|
+
}
|
|
542
|
+
|
|
343
543
|
function packTemplate(packageRoot, tempRoot) {
|
|
344
544
|
const packDir = path.join(tempRoot, 'pack');
|
|
345
545
|
const extractDir = path.join(tempRoot, 'extract');
|
|
@@ -349,24 +549,32 @@ function packTemplate(packageRoot, tempRoot) {
|
|
|
349
549
|
fs.mkdirSync(extractDir, { recursive: true });
|
|
350
550
|
fs.mkdirSync(npmCache, { recursive: true });
|
|
351
551
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
552
|
+
try {
|
|
553
|
+
const packOutput = runCommand('npm', ['pack', '--json', '--pack-destination', packDir], {
|
|
554
|
+
cwd: packageRoot,
|
|
555
|
+
env: {
|
|
556
|
+
...process.env,
|
|
557
|
+
npm_config_cache: npmCache,
|
|
558
|
+
},
|
|
559
|
+
});
|
|
359
560
|
|
|
360
|
-
|
|
361
|
-
|
|
561
|
+
const packInfo = JSON.parse(packOutput.trim());
|
|
562
|
+
const tarballPath = path.join(packDir, packInfo[0].filename);
|
|
362
563
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
564
|
+
if (!fs.existsSync(tarballPath)) {
|
|
565
|
+
throw new Error(formatError(`pack output not found at ${tarballPath}`));
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
runCommand('tar', ['-xzf', tarballPath, '-C', extractDir]);
|
|
366
569
|
|
|
367
|
-
|
|
570
|
+
return path.join(extractDir, 'package');
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (error && error.code === 'ENOENT') {
|
|
573
|
+
return copyPackageFallback(packageRoot, tempRoot);
|
|
574
|
+
}
|
|
368
575
|
|
|
369
|
-
|
|
576
|
+
throw error;
|
|
577
|
+
}
|
|
370
578
|
}
|
|
371
579
|
|
|
372
580
|
function ensureDir(dirPath) {
|
|
@@ -385,6 +593,10 @@ function copyTemplate(templateRoot, targetDir) {
|
|
|
385
593
|
return docsTemplateDir;
|
|
386
594
|
}
|
|
387
595
|
|
|
596
|
+
function exportTemplatesToLegacyRoot(templateRoot, targetDir) {
|
|
597
|
+
return copyTemplate(templateRoot, targetDir);
|
|
598
|
+
}
|
|
599
|
+
|
|
388
600
|
function mergeDirectoryTree(sourceDir, targetDir) {
|
|
389
601
|
if (!fs.existsSync(sourceDir)) {
|
|
390
602
|
return;
|
|
@@ -399,12 +611,22 @@ function mergeDirectoryTree(sourceDir, targetDir) {
|
|
|
399
611
|
});
|
|
400
612
|
}
|
|
401
613
|
|
|
402
|
-
function runInitDocs(repoRoot, projectName) {
|
|
614
|
+
function runInitDocs(repoRoot, projectName, options = {}) {
|
|
615
|
+
const templateRoot = options.templateRoot
|
|
616
|
+
? { path: options.templateRoot }
|
|
617
|
+
: resolveTemplateRoot(repoRoot, {
|
|
618
|
+
packageRoot: path.resolve(__dirname, '../..'),
|
|
619
|
+
});
|
|
620
|
+
|
|
403
621
|
initializeProjectDocs({
|
|
404
622
|
projectRoot: repoRoot,
|
|
405
623
|
projectName,
|
|
406
624
|
cliVersion: CLI_VERSION,
|
|
625
|
+
includeTemplates: options.includeTemplates === true,
|
|
626
|
+
legacyScripts: options.legacyScripts === true,
|
|
407
627
|
migrateMode: false,
|
|
628
|
+
profile: options.profile || 'default',
|
|
629
|
+
templateRoot: templateRoot.path,
|
|
408
630
|
});
|
|
409
631
|
}
|
|
410
632
|
|
|
@@ -426,6 +648,10 @@ function assertFilesExist(root, relativePaths) {
|
|
|
426
648
|
}
|
|
427
649
|
|
|
428
650
|
function assertExecutablesExist(root, relativePaths) {
|
|
651
|
+
if (process.platform === 'win32') {
|
|
652
|
+
return [];
|
|
653
|
+
}
|
|
654
|
+
|
|
429
655
|
return relativePaths.filter((relativePath) => {
|
|
430
656
|
const absolutePath = path.join(root, relativePath);
|
|
431
657
|
|
|
@@ -931,8 +1157,8 @@ function renderProjectMap(scan) {
|
|
|
931
1157
|
'docs/INDEX.md',
|
|
932
1158
|
'docs/AI_CONTEXT.md',
|
|
933
1159
|
'docs/DECISIONS.md',
|
|
934
|
-
|
|
935
|
-
|
|
1160
|
+
CURRENT_SCAN_RELATIVE_PATH,
|
|
1161
|
+
PROJECT_MAP_RELATIVE_PATH,
|
|
936
1162
|
'docs/AI_ONBOARDING_PROMPT.md',
|
|
937
1163
|
'docs/CONTEXTO.md',
|
|
938
1164
|
'docs/WORKFLOW.md',
|
|
@@ -952,8 +1178,7 @@ function renderProjectMap(scan) {
|
|
|
952
1178
|
'README.md',
|
|
953
1179
|
'docs/INDEX.md',
|
|
954
1180
|
'docs/AI_CONTEXT.md',
|
|
955
|
-
|
|
956
|
-
'docs/PROJECT_MAP.md',
|
|
1181
|
+
PROJECT_MAP_RELATIVE_PATH,
|
|
957
1182
|
hasDecisionLog ? 'docs/DECISIONS.md' : 'docs/DECISIONS.md (create with migrate if missing)',
|
|
958
1183
|
'docs/CONTEXTO.md',
|
|
959
1184
|
'docs/WORKFLOW.md',
|
|
@@ -993,7 +1218,7 @@ function renderProjectMap(scan) {
|
|
|
993
1218
|
lines.push('## Entry Points');
|
|
994
1219
|
lines.push(`- Project overview: ${scan.docs.has_readme ? 'README.md' : 'docs/CONTEXTO.md'}`);
|
|
995
1220
|
lines.push(`- AI context: ${hasDecisionLog ? 'docs/AI_CONTEXT.md + docs/DECISIONS.md' : 'docs/AI_CONTEXT.md'}`);
|
|
996
|
-
lines.push(
|
|
1221
|
+
lines.push(`- Analysis outputs: ${CURRENT_SCAN_RELATIVE_PATH}, ${PROJECT_MAP_RELATIVE_PATH}`);
|
|
997
1222
|
lines.push(`- Workflow contract: docs/WORKFLOW.md`);
|
|
998
1223
|
lines.push(`- Spec contract: specs/${projectSlug}/SPEC.md`);
|
|
999
1224
|
if (sourceDirs.length > 0) {
|
|
@@ -1108,16 +1333,13 @@ function renderProjectMap(scan) {
|
|
|
1108
1333
|
}
|
|
1109
1334
|
|
|
1110
1335
|
function writeProjectScanArtifacts(projectRoot, scan) {
|
|
1111
|
-
const
|
|
1112
|
-
ensureDir(
|
|
1113
|
-
|
|
1114
|
-
const jsonPath = path.join(docsDir, 'PROJECT_SCAN.json');
|
|
1115
|
-
const mdPath = path.join(docsDir, 'PROJECT_MAP.md');
|
|
1336
|
+
const scanPaths = projectScanPaths(projectRoot);
|
|
1337
|
+
ensureDir(path.dirname(scanPaths.projectMapPath));
|
|
1116
1338
|
|
|
1117
|
-
|
|
1118
|
-
fs.writeFileSync(
|
|
1339
|
+
const jsonPath = writeProjectScanJson(projectRoot, scan);
|
|
1340
|
+
fs.writeFileSync(scanPaths.projectMapPath, `${renderProjectMap(scan)}\n`);
|
|
1119
1341
|
|
|
1120
|
-
return { jsonPath, mdPath };
|
|
1342
|
+
return { jsonPath, mdPath: scanPaths.projectMapPath };
|
|
1121
1343
|
}
|
|
1122
1344
|
|
|
1123
1345
|
function runAnalyze(targetDir) {
|
|
@@ -1138,7 +1360,7 @@ function runAnalyze(targetDir) {
|
|
|
1138
1360
|
console.log(`Detected package manager: ${scan.project.package_manager}`);
|
|
1139
1361
|
}
|
|
1140
1362
|
|
|
1141
|
-
function runMigrate(targetDir) {
|
|
1363
|
+
function runMigrate(targetDir, options = {}) {
|
|
1142
1364
|
const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
|
|
1143
1365
|
|
|
1144
1366
|
if (!fs.existsSync(projectRoot)) {
|
|
@@ -1153,6 +1375,7 @@ function runMigrate(targetDir) {
|
|
|
1153
1375
|
const projectName = packageJson.name || path.basename(projectRoot) || 'Quiver Project';
|
|
1154
1376
|
const packageRoot = path.resolve(__dirname, '../..');
|
|
1155
1377
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'quiver-migrate-'));
|
|
1378
|
+
const legacyLayout = inspectLegacyMigrationLayout(projectRoot);
|
|
1156
1379
|
|
|
1157
1380
|
try {
|
|
1158
1381
|
const templateRoot = packTemplate(packageRoot, tempRoot);
|
|
@@ -1161,11 +1384,14 @@ function runMigrate(targetDir) {
|
|
|
1161
1384
|
projectRoot,
|
|
1162
1385
|
projectName,
|
|
1163
1386
|
cliVersion: CLI_VERSION,
|
|
1387
|
+
legacyScripts: true,
|
|
1164
1388
|
migrateMode: true,
|
|
1389
|
+
profile: 'full',
|
|
1390
|
+
templateRoot,
|
|
1165
1391
|
});
|
|
1166
1392
|
updateStateForMigrate(projectRoot, projectName, CLI_VERSION);
|
|
1167
1393
|
|
|
1168
|
-
if (!
|
|
1394
|
+
if (!options.skipInstall) {
|
|
1169
1395
|
const installResult = installSelfAsDevDep(projectRoot, CLI_VERSION);
|
|
1170
1396
|
if (installResult === 'installed') {
|
|
1171
1397
|
console.log(`Added create-quiver@${CLI_VERSION} as dev dependency`);
|
|
@@ -1176,6 +1402,9 @@ function runMigrate(targetDir) {
|
|
|
1176
1402
|
|
|
1177
1403
|
console.log(`Quiver migration completed for ${projectRoot}`);
|
|
1178
1404
|
console.log('Missing workflow files were restored without overwriting existing project files.');
|
|
1405
|
+
if (legacyLayout.hasLegacyLayout) {
|
|
1406
|
+
console.log(`Legacy layout detected and preserved: ${legacyLayout.legacyPaths.join(', ')}`);
|
|
1407
|
+
}
|
|
1179
1408
|
} finally {
|
|
1180
1409
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
1181
1410
|
}
|
|
@@ -1192,47 +1421,42 @@ function runDoctor(targetDir) {
|
|
|
1192
1421
|
throw new Error(formatError('doctor requires a project previously initialized by Quiver.\nRun init first: npx create-quiver --name "Project Name"'));
|
|
1193
1422
|
}
|
|
1194
1423
|
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1424
|
+
const doctorReport = collectDoctorReport(projectRoot);
|
|
1425
|
+
const specSlugs = doctorReport.specSlugs;
|
|
1426
|
+
const specRequiredFiles = specSlugs.flatMap((projectSlug) => [
|
|
1427
|
+
`specs/${projectSlug}/SPEC.md`,
|
|
1428
|
+
`specs/${projectSlug}/STATUS.md`,
|
|
1429
|
+
`specs/${projectSlug}/EVIDENCE_REPORT.md`,
|
|
1430
|
+
]);
|
|
1431
|
+
const newLayoutRequiredFiles = [
|
|
1202
1432
|
'AGENTS.md',
|
|
1203
1433
|
'README.md',
|
|
1204
|
-
'docs/INDEX.md',
|
|
1205
1434
|
'docs/AI_CONTEXT.md',
|
|
1206
1435
|
'docs/AI_ONBOARDING_PROMPT.md',
|
|
1207
|
-
'docs/
|
|
1436
|
+
'docs/COMMANDS.md',
|
|
1208
1437
|
'docs/WORKFLOW.md',
|
|
1209
|
-
'docs/SUPPORT_MATRIX.md',
|
|
1210
|
-
'docs/TROUBLESHOOTING.md',
|
|
1211
|
-
'docs/TESTING_GUIDE_FOR_AI.md',
|
|
1212
|
-
'docs/ai/PRINCIPLES.md',
|
|
1213
|
-
'docs/ai/RULES.yaml',
|
|
1214
|
-
'docs/ai/LESSONS.md',
|
|
1215
|
-
`specs/${projectSlug}/SPEC.md`,
|
|
1216
|
-
`specs/${projectSlug}/STATUS.md`,
|
|
1217
|
-
`specs/${projectSlug}/EVIDENCE_REPORT.md`,
|
|
1218
1438
|
'package.json',
|
|
1219
|
-
'.
|
|
1220
|
-
'.
|
|
1221
|
-
'.
|
|
1222
|
-
|
|
1439
|
+
'.quiver/state.json',
|
|
1440
|
+
'.quiver/config.json',
|
|
1441
|
+
'.quiver/.gitignore',
|
|
1442
|
+
...specRequiredFiles,
|
|
1223
1443
|
];
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1444
|
+
const requiredFiles = doctorReport.layout === 'legacy'
|
|
1445
|
+
? ['package.json', ...specRequiredFiles]
|
|
1446
|
+
: newLayoutRequiredFiles;
|
|
1447
|
+
const legacyScriptsDir = path.join(projectRoot, 'tools', 'scripts');
|
|
1448
|
+
const requiredExecutables = fs.existsSync(legacyScriptsDir)
|
|
1449
|
+
? [
|
|
1450
|
+
'tools/scripts/start-slice.sh',
|
|
1451
|
+
'tools/scripts/check-slice-readiness.sh',
|
|
1452
|
+
'tools/scripts/check-pr-readiness.sh',
|
|
1453
|
+
'tools/scripts/cleanup-slice.sh',
|
|
1454
|
+
'tools/scripts/check-scope.sh',
|
|
1455
|
+
]
|
|
1456
|
+
: [];
|
|
1233
1457
|
const missingFiles = assertFilesExist(projectRoot, requiredFiles);
|
|
1234
1458
|
const nonExecutableScripts = assertExecutablesExist(projectRoot, requiredExecutables);
|
|
1235
|
-
const pkg = loadPackageJson(projectRoot);
|
|
1459
|
+
const pkg = fs.existsSync(path.join(projectRoot, 'package.json')) ? loadPackageJson(projectRoot) : {};
|
|
1236
1460
|
const workflowScriptGroups = [
|
|
1237
1461
|
{ label: 'migrate', node: 'quiver:migrate', legacy: 'migrate' },
|
|
1238
1462
|
{ label: 'start-slice', node: 'quiver:start-slice', legacy: 'start:slice' },
|
|
@@ -1250,8 +1474,15 @@ function runDoctor(targetDir) {
|
|
|
1250
1474
|
.map((group) => group.label);
|
|
1251
1475
|
const missingNodeNativeScripts = ['quiver:migrate', 'quiver:analyze', 'quiver:doctor']
|
|
1252
1476
|
.filter((name) => typeof pkg.scripts?.[name] !== 'string');
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1477
|
+
const missingAiScripts = [
|
|
1478
|
+
'quiver:ai:onboard',
|
|
1479
|
+
'quiver:ai:plan',
|
|
1480
|
+
'quiver:ai:execute-slice',
|
|
1481
|
+
'quiver:ai:pr',
|
|
1482
|
+
'quiver:ai:doctor',
|
|
1483
|
+
].filter((name) => typeof pkg.scripts?.[name] !== 'string');
|
|
1484
|
+
const hasScanArtifacts = hasProjectScanArtifact(projectRoot)
|
|
1485
|
+
&& fs.existsSync(path.join(projectRoot, PROJECT_MAP_RELATIVE_PATH));
|
|
1255
1486
|
const quiverState = readState(projectRoot);
|
|
1256
1487
|
const hasQuiverState = Boolean(quiverState);
|
|
1257
1488
|
const stateWarnings = hasQuiverState ? [] : ['missing Quiver state metadata: .quiver/state.json'];
|
|
@@ -1260,21 +1491,35 @@ function runDoctor(targetDir) {
|
|
|
1260
1491
|
...nonExecutableScripts.map((file) => `missing executable bit: ${file}`),
|
|
1261
1492
|
...missingScripts.map((name) => `missing package.json script: ${name}`),
|
|
1262
1493
|
];
|
|
1263
|
-
const softWarnings =
|
|
1494
|
+
const softWarnings = doctorReport.warnings;
|
|
1264
1495
|
|
|
1265
1496
|
if (migrationProblems.length > 0) {
|
|
1266
1497
|
throw new Error(formatError(`doctor failed:\n- ${migrationProblems.join('\n- ')}\n- Run migration first: npx create-quiver migrate`));
|
|
1267
1498
|
}
|
|
1268
1499
|
|
|
1269
1500
|
console.log(`Quiver doctor passed for ${projectRoot}`);
|
|
1270
|
-
console.log(`
|
|
1501
|
+
console.log(`Layout: ${doctorReport.layout}`);
|
|
1502
|
+
if (specSlugs.length > 0) {
|
|
1503
|
+
console.log(`Specs: ${specSlugs.join(', ')}`);
|
|
1504
|
+
} else {
|
|
1505
|
+
console.log('Specs: none yet');
|
|
1506
|
+
}
|
|
1507
|
+
if (doctorReport.legacySignals.length > 0) {
|
|
1508
|
+
console.log(`Legacy signals: ${doctorReport.legacySignals.join(', ')}`);
|
|
1509
|
+
}
|
|
1271
1510
|
console.log('Next steps:');
|
|
1511
|
+
for (const recommendation of doctorReport.recommendations) {
|
|
1512
|
+
console.log(`- ${recommendation}`);
|
|
1513
|
+
}
|
|
1272
1514
|
for (const warning of stateWarnings) {
|
|
1273
1515
|
console.log(`- Warning: ${warning}`);
|
|
1274
1516
|
}
|
|
1275
1517
|
for (const scriptName of missingNodeNativeScripts) {
|
|
1276
1518
|
console.log(`- Warning: missing Node-native script: ${scriptName}`);
|
|
1277
1519
|
}
|
|
1520
|
+
for (const scriptName of missingAiScripts) {
|
|
1521
|
+
console.log(`- Warning: missing AI orchestration script: ${scriptName}`);
|
|
1522
|
+
}
|
|
1278
1523
|
if (legacyOnlyScripts.length > 0) {
|
|
1279
1524
|
console.log(`- Warning: legacy Bash workflow scripts detected for ${legacyOnlyScripts.join(', ')}. Run npx create-quiver migrate to add quiver:* npm scripts.`);
|
|
1280
1525
|
}
|
|
@@ -1289,20 +1534,23 @@ function runDoctor(targetDir) {
|
|
|
1289
1534
|
console.log('- Ask your AI agent: Read AGENTS.md, then docs/AI_ONBOARDING_PROMPT.md and execute it.');
|
|
1290
1535
|
}
|
|
1291
1536
|
console.log('- Check the next ready slice: npx create-quiver next');
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1537
|
+
if (specSlugs.length > 0) {
|
|
1538
|
+
const projectSlug = specSlugs[0];
|
|
1539
|
+
console.log(`- Start a slice: npx create-quiver start-slice specs/${projectSlug}/slices/<slice-id>/slice.json`);
|
|
1540
|
+
console.log(`- Validate a slice: npx create-quiver check-slice specs/${projectSlug}/slices/<slice-id>/slice.json`);
|
|
1541
|
+
console.log(`- Validate the PR gate: npx create-quiver check-pr specs/${projectSlug}/slices/<slice-id>/slice.json`);
|
|
1542
|
+
} else {
|
|
1543
|
+
console.log('- Create real specs and slices only after acceptance criteria and the technical plan are approved.');
|
|
1544
|
+
}
|
|
1295
1545
|
}
|
|
1296
1546
|
|
|
1297
1547
|
function printInitNextSteps(targetDir, projectName) {
|
|
1298
|
-
const projectSlug = toProjectSlug(projectName);
|
|
1299
|
-
|
|
1300
1548
|
console.log('');
|
|
1301
1549
|
console.log('Next steps:');
|
|
1302
|
-
console.log(`- Review AGENTS.md, then ${path.join(targetDir, 'docs', '
|
|
1550
|
+
console.log(`- Review AGENTS.md, then ${path.join(targetDir, 'docs', 'AI_ONBOARDING_PROMPT.md')}`);
|
|
1303
1551
|
console.log(`- Review ${path.join(targetDir, 'docs', 'WORKFLOW.md')}`);
|
|
1304
|
-
console.log(
|
|
1305
|
-
console.log(
|
|
1552
|
+
console.log('- Analyze the project with npx create-quiver analyze');
|
|
1553
|
+
console.log('- Create real specs and slices after acceptance criteria and the technical plan are approved.');
|
|
1306
1554
|
}
|
|
1307
1555
|
|
|
1308
1556
|
async function run(argv) {
|
|
@@ -1328,6 +1576,72 @@ async function run(argv) {
|
|
|
1328
1576
|
return;
|
|
1329
1577
|
}
|
|
1330
1578
|
|
|
1579
|
+
if (args.mode === 'ai') {
|
|
1580
|
+
if (!args.aiCommand) {
|
|
1581
|
+
throw new Error(formatError('missing ai subcommand. Use: npx create-quiver ai onboard | plan | execute-slice | doctor | pr'));
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
if (args.aiCommand === 'onboard') {
|
|
1585
|
+
await runOnboard(process.cwd(), {
|
|
1586
|
+
context: args.aiContext || undefined,
|
|
1587
|
+
dryRun: args.dryRun,
|
|
1588
|
+
input: args.aiInput || undefined,
|
|
1589
|
+
provider: args.aiProvider,
|
|
1590
|
+
role: args.aiRole,
|
|
1591
|
+
timeout: args.aiTimeout,
|
|
1592
|
+
});
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
if (args.aiCommand === 'plan') {
|
|
1597
|
+
await runAiPlan(process.cwd(), {
|
|
1598
|
+
context: args.aiContext || undefined,
|
|
1599
|
+
dryRun: args.dryRun,
|
|
1600
|
+
input: args.aiInput || undefined,
|
|
1601
|
+
phase: args.aiPhase,
|
|
1602
|
+
provider: args.aiProvider,
|
|
1603
|
+
role: args.aiRole,
|
|
1604
|
+
specSlug: args.specSlug || undefined,
|
|
1605
|
+
timeout: args.aiTimeout,
|
|
1606
|
+
});
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
if (args.aiCommand === 'execute-slice') {
|
|
1611
|
+
await runAiExecuteSlice(process.cwd(), {
|
|
1612
|
+
context: args.aiContext || undefined,
|
|
1613
|
+
dryRun: args.dryRun,
|
|
1614
|
+
provider: args.aiProvider,
|
|
1615
|
+
role: args.aiRole,
|
|
1616
|
+
slice: args.aiSlice || undefined,
|
|
1617
|
+
timeout: args.aiTimeout,
|
|
1618
|
+
});
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
if (args.aiCommand === 'doctor') {
|
|
1623
|
+
await runAiDoctor(process.cwd(), {
|
|
1624
|
+
dryRun: args.dryRun,
|
|
1625
|
+
remote: args.aiRemote || undefined,
|
|
1626
|
+
sshHostAlias: args.aiSshHostAlias || undefined,
|
|
1627
|
+
identityFile: args.aiIdentityFile || undefined,
|
|
1628
|
+
});
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
if (args.aiCommand === 'pr') {
|
|
1633
|
+
await runAiPr(process.cwd(), {
|
|
1634
|
+
dryRun: args.dryRun,
|
|
1635
|
+
remote: args.aiRemote || undefined,
|
|
1636
|
+
sshHostAlias: args.aiSshHostAlias || undefined,
|
|
1637
|
+
identityFile: args.aiIdentityFile || undefined,
|
|
1638
|
+
});
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
throw new Error(formatError(`unsupported ai subcommand: ${args.aiCommand}. Supported tasks: onboard, plan, execute-slice, doctor, pr`));
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1331
1645
|
if (args.mode === 'graph') {
|
|
1332
1646
|
runGraph(process.cwd(), {
|
|
1333
1647
|
format: args.format,
|
|
@@ -1350,7 +1664,7 @@ async function run(argv) {
|
|
|
1350
1664
|
}
|
|
1351
1665
|
|
|
1352
1666
|
if (args.mode === 'migrate') {
|
|
1353
|
-
runMigrate(args.targetDir);
|
|
1667
|
+
runMigrate(args.targetDir, { skipInstall: args.skipInstall });
|
|
1354
1668
|
return;
|
|
1355
1669
|
}
|
|
1356
1670
|
|
|
@@ -1360,12 +1674,12 @@ async function run(argv) {
|
|
|
1360
1674
|
}
|
|
1361
1675
|
|
|
1362
1676
|
if (args.mode === 'start-slice') {
|
|
1363
|
-
startSlice(
|
|
1677
|
+
startSlice(args.targetDir, { allowDraft: args.allowDraft });
|
|
1364
1678
|
return;
|
|
1365
1679
|
}
|
|
1366
1680
|
|
|
1367
1681
|
if (args.mode === 'check-slice') {
|
|
1368
|
-
checkSliceReadiness(
|
|
1682
|
+
checkSliceReadiness(args.targetDir, {
|
|
1369
1683
|
gate: args.gate,
|
|
1370
1684
|
strictOverlap: args.strictOverlap,
|
|
1371
1685
|
});
|
|
@@ -1373,7 +1687,7 @@ async function run(argv) {
|
|
|
1373
1687
|
}
|
|
1374
1688
|
|
|
1375
1689
|
if (args.mode === 'check-pr') {
|
|
1376
|
-
checkPrReadiness(
|
|
1690
|
+
checkPrReadiness(args.targetDir);
|
|
1377
1691
|
return;
|
|
1378
1692
|
}
|
|
1379
1693
|
|
|
@@ -1397,7 +1711,7 @@ async function run(argv) {
|
|
|
1397
1711
|
}
|
|
1398
1712
|
|
|
1399
1713
|
if (args.mode === 'cleanup-slice') {
|
|
1400
|
-
cleanupSlice(
|
|
1714
|
+
cleanupSlice(args.targetDir, {
|
|
1401
1715
|
closeBaseline: args.closeBaseline,
|
|
1402
1716
|
discard: args.discard,
|
|
1403
1717
|
dryRun: args.dryRun,
|
|
@@ -1407,7 +1721,7 @@ async function run(argv) {
|
|
|
1407
1721
|
}
|
|
1408
1722
|
|
|
1409
1723
|
if (args.mode === 'check-scope') {
|
|
1410
|
-
checkScope(
|
|
1724
|
+
checkScope(args.targetDir, { strict: args.strict });
|
|
1411
1725
|
return;
|
|
1412
1726
|
}
|
|
1413
1727
|
|
|
@@ -1420,14 +1734,37 @@ async function run(argv) {
|
|
|
1420
1734
|
const packageRoot = path.resolve(__dirname, '../..');
|
|
1421
1735
|
const targetDir = resolveTargetRoot(process.cwd(), args.targetDir);
|
|
1422
1736
|
const projectName = args.projectName || path.basename(targetDir) || 'Quiver Project';
|
|
1737
|
+
const initLayout = buildInitLayout(targetDir, {
|
|
1738
|
+
compatibilityAlias: !args.explicitInit,
|
|
1739
|
+
dryRun: args.dryRun,
|
|
1740
|
+
full: args.initFull,
|
|
1741
|
+
includeTemplates: args.initIncludeTemplates,
|
|
1742
|
+
legacyScripts: args.initLegacyScripts,
|
|
1743
|
+
minimal: args.initMinimal,
|
|
1744
|
+
projectName,
|
|
1745
|
+
skipInstall: args.skipInstall,
|
|
1746
|
+
});
|
|
1747
|
+
|
|
1748
|
+
if (args.dryRun) {
|
|
1749
|
+
console.log(formatInitLayoutPlan(initLayout));
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1423
1753
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'quiver-create-'));
|
|
1424
1754
|
|
|
1425
1755
|
try {
|
|
1426
1756
|
ensureDir(targetDir);
|
|
1427
1757
|
|
|
1428
1758
|
const templateRoot = packTemplate(packageRoot, tempRoot);
|
|
1429
|
-
|
|
1430
|
-
|
|
1759
|
+
if (initLayout.profile === 'full') {
|
|
1760
|
+
exportTemplatesToLegacyRoot(templateRoot, targetDir);
|
|
1761
|
+
}
|
|
1762
|
+
runInitDocs(targetDir, projectName, {
|
|
1763
|
+
includeTemplates: args.initIncludeTemplates,
|
|
1764
|
+
legacyScripts: args.initLegacyScripts,
|
|
1765
|
+
profile: initLayout.profile,
|
|
1766
|
+
templateRoot,
|
|
1767
|
+
});
|
|
1431
1768
|
|
|
1432
1769
|
if (!args.skipInstall) {
|
|
1433
1770
|
const installResult = installSelfAsDevDep(targetDir, CLI_VERSION);
|