create-quiver 0.5.0 → 0.6.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 CHANGED
@@ -47,7 +47,27 @@ The analyzer creates `docs/PROJECT_SCAN.json` and `docs/PROJECT_MAP.md`. These f
47
47
 
48
48
  The doctor checks the generated project contract and prints the next workflow steps. If the scan artifacts are missing, it recommends `npx create-quiver analyze --dir .` first.
49
49
 
50
- ### 3. Ask The AI To Prepare Context
50
+ ### 3. Upgrade Existing Projects
51
+
52
+ If the project already had Quiver from an older version, upgrade it from the project root:
53
+
54
+ ```bash
55
+ cd /path/to/your-project
56
+ npx create-quiver migrate --dir .
57
+ npx create-quiver analyze --dir .
58
+ npx create-quiver doctor --dir .
59
+ ```
60
+
61
+ If your team prefers a pinned local dependency, update the package first and then run the same flow:
62
+
63
+ ```bash
64
+ npm install --save-dev create-quiver@latest
65
+ npx create-quiver migrate --dir .
66
+ npx create-quiver analyze --dir .
67
+ npx create-quiver doctor --dir .
68
+ ```
69
+
70
+ ### 4. Ask The AI To Prepare Context
51
71
 
52
72
  Open your AI agent in the target project and run this short handoff:
53
73
 
@@ -59,7 +79,7 @@ Prepare the project context docs and report assumptions, risks, and files change
59
79
 
60
80
  The AI should use the scan artifacts to prepare `docs/AI_CONTEXT.md`, `docs/CONTEXTO.md`, `docs/STATUS.md`, and the initial project spec. The developer should review those documentation changes before implementation work starts.
61
81
 
62
- ### 4. Start The First Slice
82
+ ### 5. Start The First Slice
63
83
 
64
84
  After the context docs are reviewed:
65
85
 
package/README_FOR_AI.md CHANGED
@@ -8,6 +8,7 @@ Important: slice numbering resets inside each spec. `slice-01` is the first slic
8
8
  The canonical installer entrypoint is `npx create-quiver` run from the target project root.
9
9
  Do not recommend global installation; use `npx` or a project-local devDependency when the team needs a pinned version.
10
10
  The post-init contract is validated with `npx create-quiver doctor --dir <project>`.
11
+ If the project already exists from an older Quiver version, run `npx create-quiver migrate --dir <project>` before `analyze`.
11
12
  Maintain release notes and package publishing with `scripts/release-quiver.sh`.
12
13
  The primary generated project context for agents is `docs/AI_CONTEXT.md`.
13
14
  If a generated project has been analyzed, the exact agent handoff prompt is `docs/AI_ONBOARDING_PROMPT.md`.
@@ -76,15 +77,16 @@ After initialization, the user should:
76
77
  3. Fill in `docs/CONTEXTO.md`
77
78
  4. Fill in `docs/STATUS.md`
78
79
  5. Run `npx create-quiver analyze --dir <project>` if scan artifacts are missing
79
- 6. Ask the AI agent to execute `docs/AI_ONBOARDING_PROMPT.md`
80
- 7. Review context docs before creating the first implementation slice
81
- 8. Open and merge the documentation PR that establishes the workflow files
82
- 9. Create the first slice in `specs/{{PROJECT_SLUG}}/slices/[slice-id]/`
83
- 10. Add `ticket` and `git.*`
84
- 11. Run `tools/scripts/start-slice.sh [--allow-draft] <slice.json>`
85
- 12. Make one commit per slice
86
- 13. Open one PR per spec
87
- 14. Validate the slice and the final PR with the workflow gates
80
+ 6. If the project already exists from an older Quiver version, run `npx create-quiver migrate --dir <project>`
81
+ 7. Ask the AI agent to execute `docs/AI_ONBOARDING_PROMPT.md`
82
+ 8. Review context docs before creating the first implementation slice
83
+ 9. Open and merge the documentation PR that establishes the workflow files
84
+ 10. Create the first slice in `specs/{{PROJECT_SLUG}}/slices/[slice-id]/`
85
+ 11. Add `ticket` and `git.*`
86
+ 12. Run `tools/scripts/start-slice.sh [--allow-draft] <slice.json>`
87
+ 13. Make one commit per slice
88
+ 14. Open one PR per spec
89
+ 15. Validate the slice and the final PR with the workflow gates
88
90
 
89
91
  Bootstrap note: `start-slice.sh` should resolve paths canonically, prefer a local `develop` or `main` base branch before reaching for `origin`, and reject `draft` slices unless `--allow-draft` is passed intentionally.
90
92
 
@@ -35,6 +35,7 @@ This file is the first stop for any AI agent working in this project. It compres
35
35
  ```bash
36
36
  cd /path/to/project
37
37
  npx create-quiver --name "Project Name"
38
+ npx create-quiver migrate --dir .
38
39
  npx create-quiver analyze --dir .
39
40
  npx create-quiver doctor --dir .
40
41
  bash tools/scripts/start-slice.sh <slice.json>
@@ -25,7 +25,8 @@ This document is the canonical implementation workflow for the project.
25
25
  4. Mark each slice `ready` before execution.
26
26
  5. Open and merge the documentation PR before running the first slice bootstrap.
27
27
  6. Read the AI context pack, support matrix, and troubleshooting guide before the first slice if the environment is new or uncertain.
28
- 7. If the project was analyzed, read `AI_ONBOARDING_PROMPT.md` before making documentation updates.
28
+ 7. If the project already existed before this Quiver version, run `migrate` before `analyze`.
29
+ 8. If the project was analyzed, read `AI_ONBOARDING_PROMPT.md` before making documentation updates.
29
30
 
30
31
  ## Execution
31
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-quiver",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "description": "Quiver CLI for scaffolding projects from the packaged template",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@
12
12
  "check:pr": "bash tools/scripts/check-pr-readiness.sh",
13
13
  "start:slice": "bash tools/scripts/start-slice.sh",
14
14
  "cleanup:slice": "bash tools/scripts/cleanup-slice.sh",
15
+ "migrate": "bash tools/scripts/migrate-project.sh",
15
16
  "package:quiver": "bash scripts/package-quiver.sh",
16
17
  "smoke:create-quiver": "bash scripts/ci/smoke-create-quiver.sh",
17
18
  "release:quiver": "bash scripts/release-quiver.sh"
@@ -5,6 +5,7 @@
5
5
  "check:slice": "bash tools/scripts/check-slice-readiness.sh",
6
6
  "check:pr": "bash tools/scripts/check-pr-readiness.sh",
7
7
  "start:slice": "bash tools/scripts/start-slice.sh",
8
- "cleanup:slice": "bash tools/scripts/cleanup-slice.sh"
8
+ "cleanup:slice": "bash tools/scripts/cleanup-slice.sh",
9
+ "migrate": "bash tools/scripts/migrate-project.sh"
9
10
  }
10
11
  }
@@ -40,6 +40,7 @@ fi
40
40
  PROJECT_NAME="$1"
41
41
  PROJECT_SLUG=$(echo "$PROJECT_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]-')
42
42
  CURRENT_DATE=$(date +%Y-%m-%d)
43
+ MIGRATE_MODE="${QUIVER_MIGRATE:-0}"
43
44
  DATE_PLUS_7=$(node -e 'const d = new Date(); d.setDate(d.getDate() + 7); const p = (n) => String(n).padStart(2, "0"); console.log(`${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`);')
44
45
  DATE_PLUS_30=$(node -e 'const d = new Date(); d.setDate(d.getDate() + 30); const p = (n) => String(n).padStart(2, "0"); console.log(`${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`);')
45
46
  DATE_PLUS_35=$(node -e 'const d = new Date(); d.setDate(d.getDate() + 35); const p = (n) => String(n).padStart(2, "0"); console.log(`${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`);')
@@ -76,6 +77,11 @@ copy_template() {
76
77
  if [ -f "$src" ]; then
77
78
  # Remover .template del nombre si existe
78
79
  dest=$(echo "$dest" | sed 's/\.template$//')
80
+
81
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "$dest" ]; then
82
+ print_info "Saltado: $dest ya existe"
83
+ return 0
84
+ fi
79
85
 
80
86
  # Copiar y reemplazar placeholders
81
87
  sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \
@@ -101,6 +107,11 @@ copy_template_keep_name() {
101
107
  local dest="$2"
102
108
 
103
109
  if [ -f "$src" ]; then
110
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "$dest" ]; then
111
+ print_info "Saltado: $dest ya existe"
112
+ return 0
113
+ fi
114
+
104
115
  sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \
105
116
  -e "s/{{PROJECT_SLUG}}/$PROJECT_SLUG/g" \
106
117
  -e "s/\[project\]/$PROJECT_SLUG/g" \
@@ -157,24 +168,40 @@ copy_template "docs-template/docs/TESTING_GUIDE_FOR_AI.md.template" "docs/TESTIN
157
168
 
158
169
  # Copiar archivos que no son templates
159
170
  if [ -f "docs-template/docs/UI_STANDARDS.md" ]; then
160
- cp "docs-template/docs/UI_STANDARDS.md" "docs/UI_STANDARDS.md"
161
- print_success "Creado: docs/UI_STANDARDS.md"
171
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "docs/UI_STANDARDS.md" ]; then
172
+ print_info "Saltado: docs/UI_STANDARDS.md ya existe"
173
+ else
174
+ cp "docs-template/docs/UI_STANDARDS.md" "docs/UI_STANDARDS.md"
175
+ print_success "Creado: docs/UI_STANDARDS.md"
176
+ fi
162
177
  fi
163
178
 
164
179
  if [ -f "docs-template/docs/MOCK_DATA_GUIDE.md" ]; then
165
- cp "docs-template/docs/MOCK_DATA_GUIDE.md" "docs/MOCK_DATA_GUIDE.md"
166
- print_success "Creado: docs/MOCK_DATA_GUIDE.md"
180
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "docs/MOCK_DATA_GUIDE.md" ]; then
181
+ print_info "Saltado: docs/MOCK_DATA_GUIDE.md ya existe"
182
+ else
183
+ cp "docs-template/docs/MOCK_DATA_GUIDE.md" "docs/MOCK_DATA_GUIDE.md"
184
+ print_success "Creado: docs/MOCK_DATA_GUIDE.md"
185
+ fi
167
186
  fi
168
187
 
169
188
  # Copiar configuración de IA
170
189
  if [ -f "docs-template/docs/ai/PRINCIPLES.md" ]; then
171
- cp "docs-template/docs/ai/PRINCIPLES.md" "docs/ai/PRINCIPLES.md"
172
- print_success "Creado: docs/ai/PRINCIPLES.md"
190
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "docs/ai/PRINCIPLES.md" ]; then
191
+ print_info "Saltado: docs/ai/PRINCIPLES.md ya existe"
192
+ else
193
+ cp "docs-template/docs/ai/PRINCIPLES.md" "docs/ai/PRINCIPLES.md"
194
+ print_success "Creado: docs/ai/PRINCIPLES.md"
195
+ fi
173
196
  fi
174
197
 
175
198
  if [ -f "docs-template/docs/ai/RULES.yaml" ]; then
176
- cp "docs-template/docs/ai/RULES.yaml" "docs/ai/RULES.yaml"
177
- print_success "Creado: docs/ai/RULES.yaml"
199
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "docs/ai/RULES.yaml" ]; then
200
+ print_info "Saltado: docs/ai/RULES.yaml ya existe"
201
+ else
202
+ cp "docs-template/docs/ai/RULES.yaml" "docs/ai/RULES.yaml"
203
+ print_success "Creado: docs/ai/RULES.yaml"
204
+ fi
178
205
  fi
179
206
 
180
207
  # Copiar template de LESSONS (vacío)
@@ -240,44 +267,123 @@ NODE
240
267
 
241
268
  sync_package_json
242
269
 
270
+ mkdir -p ".quiver"
271
+ node - <<'NODE'
272
+ const fs = require('fs');
273
+ const path = require('path');
274
+
275
+ const statePath = path.join('.quiver', 'state.json');
276
+ const packageJson = fs.existsSync('package.json') ? JSON.parse(fs.readFileSync('package.json', 'utf8')) : {};
277
+ const currentVersion = process.env.QUIVER_VERSION || packageJson.version || '0.0.0';
278
+ const migrateMode = process.env.QUIVER_MIGRATE === '1';
279
+ const now = new Date().toISOString();
280
+ const projectName = process.env.QUIVER_PROJECT_NAME || packageJson.name || '';
281
+
282
+ let existing = {};
283
+ if (fs.existsSync(statePath)) {
284
+ existing = JSON.parse(fs.readFileSync(statePath, 'utf8'));
285
+ }
286
+
287
+ const nextState = migrateMode
288
+ ? {
289
+ ...existing,
290
+ quiver_version: currentVersion,
291
+ project_name: projectName || existing.project_name || '',
292
+ initialized_version: existing.initialized_version ?? null,
293
+ migrated_version: currentVersion,
294
+ last_initialized_at: existing.last_initialized_at ?? null,
295
+ last_migration_at: now,
296
+ last_analysis_at: existing.last_analysis_at ?? null,
297
+ }
298
+ : {
299
+ ...existing,
300
+ quiver_version: currentVersion,
301
+ project_name: projectName || existing.project_name || '',
302
+ initialized_version: existing.initialized_version || currentVersion,
303
+ migrated_version: existing.migrated_version ?? null,
304
+ last_initialized_at: existing.last_initialized_at || now,
305
+ last_migration_at: existing.last_migration_at ?? null,
306
+ last_analysis_at: existing.last_analysis_at ?? null,
307
+ };
308
+
309
+ fs.writeFileSync(statePath, `${JSON.stringify(nextState, null, 2)}\n`);
310
+ NODE
311
+
243
312
  # Copiar bootstrap de slices a tools/scripts
244
313
  if [ -f "docs-template/scripts/start-slice.sh" ]; then
245
- cp "docs-template/scripts/start-slice.sh" "tools/scripts/start-slice.sh"
246
- chmod +x "tools/scripts/start-slice.sh"
247
- print_success "Creado: tools/scripts/start-slice.sh"
314
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "tools/scripts/start-slice.sh" ]; then
315
+ print_info "Saltado: tools/scripts/start-slice.sh ya existe"
316
+ else
317
+ cp "docs-template/scripts/start-slice.sh" "tools/scripts/start-slice.sh"
318
+ chmod +x "tools/scripts/start-slice.sh"
319
+ print_success "Creado: tools/scripts/start-slice.sh"
320
+ fi
248
321
  fi
249
322
 
250
323
  if [ -f "docs-template/scripts/refresh-active-slices.sh" ]; then
251
- cp "docs-template/scripts/refresh-active-slices.sh" "tools/scripts/refresh-active-slices.sh"
252
- chmod +x "tools/scripts/refresh-active-slices.sh"
253
- print_success "Creado: tools/scripts/refresh-active-slices.sh"
324
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "tools/scripts/refresh-active-slices.sh" ]; then
325
+ print_info "Saltado: tools/scripts/refresh-active-slices.sh ya existe"
326
+ else
327
+ cp "docs-template/scripts/refresh-active-slices.sh" "tools/scripts/refresh-active-slices.sh"
328
+ chmod +x "tools/scripts/refresh-active-slices.sh"
329
+ print_success "Creado: tools/scripts/refresh-active-slices.sh"
330
+ fi
254
331
  fi
255
332
 
256
333
  if [ -f "docs-template/scripts/check-slice-readiness.sh" ]; then
257
- cp "docs-template/scripts/check-slice-readiness.sh" "tools/scripts/check-slice-readiness.sh"
258
- chmod +x "tools/scripts/check-slice-readiness.sh"
259
- print_success "Creado: tools/scripts/check-slice-readiness.sh"
334
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "tools/scripts/check-slice-readiness.sh" ]; then
335
+ print_info "Saltado: tools/scripts/check-slice-readiness.sh ya existe"
336
+ else
337
+ cp "docs-template/scripts/check-slice-readiness.sh" "tools/scripts/check-slice-readiness.sh"
338
+ chmod +x "tools/scripts/check-slice-readiness.sh"
339
+ print_success "Creado: tools/scripts/check-slice-readiness.sh"
340
+ fi
260
341
  fi
261
342
 
262
343
  if [ -f "docs-template/scripts/check-pr-readiness.sh" ]; then
263
- cp "docs-template/scripts/check-pr-readiness.sh" "tools/scripts/check-pr-readiness.sh"
264
- chmod +x "tools/scripts/check-pr-readiness.sh"
265
- print_success "Creado: tools/scripts/check-pr-readiness.sh"
344
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "tools/scripts/check-pr-readiness.sh" ]; then
345
+ print_info "Saltado: tools/scripts/check-pr-readiness.sh ya existe"
346
+ else
347
+ cp "docs-template/scripts/check-pr-readiness.sh" "tools/scripts/check-pr-readiness.sh"
348
+ chmod +x "tools/scripts/check-pr-readiness.sh"
349
+ print_success "Creado: tools/scripts/check-pr-readiness.sh"
350
+ fi
266
351
  fi
267
352
 
268
353
  if [ -f "docs-template/scripts/cleanup-slice.sh" ]; then
269
- cp "docs-template/scripts/cleanup-slice.sh" "tools/scripts/cleanup-slice.sh"
270
- chmod +x "tools/scripts/cleanup-slice.sh"
271
- print_success "Creado: tools/scripts/cleanup-slice.sh"
354
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "tools/scripts/cleanup-slice.sh" ]; then
355
+ print_info "Saltado: tools/scripts/cleanup-slice.sh ya existe"
356
+ else
357
+ cp "docs-template/scripts/cleanup-slice.sh" "tools/scripts/cleanup-slice.sh"
358
+ chmod +x "tools/scripts/cleanup-slice.sh"
359
+ print_success "Creado: tools/scripts/cleanup-slice.sh"
360
+ fi
272
361
  fi
273
362
 
274
363
  if [ -f "docs-template/scripts/check-scope.sh" ]; then
275
- cp "docs-template/scripts/check-scope.sh" "tools/scripts/check-scope.sh"
276
- chmod +x "tools/scripts/check-scope.sh"
277
- print_success "Creado: tools/scripts/check-scope.sh"
364
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "tools/scripts/check-scope.sh" ]; then
365
+ print_info "Saltado: tools/scripts/check-scope.sh ya existe"
366
+ else
367
+ cp "docs-template/scripts/check-scope.sh" "tools/scripts/check-scope.sh"
368
+ chmod +x "tools/scripts/check-scope.sh"
369
+ print_success "Creado: tools/scripts/check-scope.sh"
370
+ fi
371
+ fi
372
+
373
+ if [ -f "docs-template/scripts/migrate-project.sh" ]; then
374
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "tools/scripts/migrate-project.sh" ]; then
375
+ print_info "Saltado: tools/scripts/migrate-project.sh ya existe"
376
+ else
377
+ cp "docs-template/scripts/migrate-project.sh" "tools/scripts/migrate-project.sh"
378
+ chmod +x "tools/scripts/migrate-project.sh"
379
+ print_success "Creado: tools/scripts/migrate-project.sh"
380
+ fi
278
381
  fi
279
382
 
280
383
  # Crear archivo SEARCH.md básico
384
+ if [ "$MIGRATE_MODE" = "1" ] && [ -f "docs/SEARCH.md" ]; then
385
+ print_info "Saltado: docs/SEARCH.md ya existe"
386
+ else
281
387
  cat > "docs/SEARCH.md" << EOF
282
388
  # Búsqueda por Tema
283
389
 
@@ -319,8 +425,11 @@ cat > "docs/SEARCH.md" << EOF
319
425
 
320
426
  **Fin de la búsqueda**
321
427
  EOF
428
+ fi
322
429
 
323
- print_success "Creado: docs/SEARCH.md"
430
+ if [ "$MIGRATE_MODE" != "1" ] || [ ! -f "docs/SEARCH.md" ]; then
431
+ print_success "Creado: docs/SEARCH.md"
432
+ fi
324
433
 
325
434
  # Crear README.md en la raíz del proyecto (si no existe)
326
435
  if [ ! -f "README.md" ]; then
@@ -347,6 +456,26 @@ npm install --save-dev create-quiver
347
456
 
348
457
  If your project path contains spaces, quote it explicitly when using \`--dir\`.
349
458
 
459
+ ## Upgrading Existing Projects
460
+
461
+ If the project already existed before this Quiver version, upgrade it from the project root:
462
+
463
+ \`\`\`bash
464
+ cd /path/to/your-project
465
+ npx create-quiver migrate --dir .
466
+ npx create-quiver analyze --dir .
467
+ npx create-quiver doctor --dir .
468
+ \`\`\`
469
+
470
+ If your team prefers a pinned local dependency, update the package first and then run the same flow:
471
+
472
+ \`\`\`bash
473
+ npm install --save-dev create-quiver@latest
474
+ npx create-quiver migrate --dir .
475
+ npx create-quiver analyze --dir .
476
+ npx create-quiver doctor --dir .
477
+ \`\`\`
478
+
350
479
  ## AI Context Onboarding
351
480
 
352
481
  After analysis and doctor validation, open your AI agent in this project and run:
@@ -0,0 +1,38 @@
1
+ # Quiver v0.11 Evidence Report
2
+
3
+ **Spec:** quiver-v11-existing-project-migration
4
+ **Last updated:** 2026-04-22
5
+ **Status:** Completed
6
+
7
+ ## Summary
8
+
9
+ | Slice | Acceptance criteria | Status | Evidence |
10
+ |-------|---------------------|--------|----------|
11
+ | slice-01 | 6 | Completed | `node -c src/create-quiver/index.js`; `bash -n scripts/init-docs.sh scripts/ci/smoke-create-quiver.sh scripts/package-quiver.sh`; `bash scripts/ci/smoke-create-quiver.sh` |
12
+ | slice-02 | 6 | Completed | `node -c src/create-quiver/index.js`; `bash -n scripts/init-docs.sh scripts/ci/smoke-init-docs.sh scripts/ci/smoke-create-quiver.sh scripts/package-quiver.sh`; `bash scripts/ci/smoke-create-quiver.sh` |
13
+ | slice-03 | 6 | Completed | `node -c src/create-quiver/index.js`; `bash -n scripts/init-docs.sh scripts/ci/smoke-init-docs.sh scripts/ci/smoke-create-quiver.sh scripts/package-quiver.sh`; `bash scripts/ci/smoke-init-docs.sh`; `bash scripts/ci/smoke-create-quiver.sh`; `bash scripts/package-quiver.sh` |
14
+
15
+ ## Evidence by Slice
16
+
17
+ ## Slice 01
18
+
19
+ - Added `create-quiver migrate --dir <project>` with a non-destructive migration path for existing projects
20
+ - `init-docs.sh` now respects `QUIVER_MIGRATE=1` and skips files that already exist
21
+ - Generated projects now receive `tools/scripts/migrate-project.sh` and a `migrate` package script
22
+ - Smoke coverage now verifies that migration restores missing files without overwriting existing edits
23
+
24
+ ## Slice 02
25
+
26
+ - Added `.quiver/state.json` as project-local Quiver metadata
27
+ - `init` and `migrate` create or refresh the state file with initialized and migrated version data
28
+ - `analyze` updates `last_analysis_at` when metadata exists
29
+ - `doctor` distinguishes between missing Quiver metadata and missing migration/upgrade artifacts
30
+ - Smoke coverage now verifies the new state lifecycle across init, migrate, analyze, and doctor
31
+
32
+ ## Slice 03
33
+
34
+ - Added an explicit `Upgrading Existing Projects` section to the root README and the generated project README
35
+ - Documented both `npx` and project-local devDependency upgrade flows
36
+ - Updated the generated onboarding docs so existing projects are guided to `migrate`, then `analyze`, then `doctor`
37
+ - Extended smoke checks to assert the upgrade section and legacy migration preservation behavior
38
+ - Confirmed `scripts/package-quiver.sh` still passes after the documentation and smoke updates
@@ -0,0 +1,59 @@
1
+ # Quiver v0.11 - Existing Project Migration
2
+
3
+ **Date:** 2026-04-22
4
+ **Status:** Completed
5
+
6
+ Slice numbering resets here: this spec starts at `slice-01` and does not continue any previous spec's numbering.
7
+
8
+ ## Objective
9
+
10
+ Make existing Quiver projects upgradeable without reinstalling or manually copying new templates, while preserving user-authored project context.
11
+
12
+ ## Scope
13
+
14
+ ### Included
15
+
16
+ - Add a `create-quiver migrate --dir <project>` command for existing projects
17
+ - Add or update Quiver project metadata so `doctor` can understand initialized, analyzed, and migrated state
18
+ - Teach `doctor` to recommend migration when required files or version metadata are missing
19
+ - Document the upgrade flow for users who already initialized Quiver in a project
20
+ - Add smoke fixtures that simulate older generated projects and validate non-destructive migration
21
+
22
+ ### Excluded
23
+
24
+ - Running AI providers from the CLI
25
+ - Overwriting user-authored docs without preserving or reporting the change
26
+ - Changing slice execution semantics
27
+ - Publishing a release as part of this spec
28
+ - Reworking global installation behavior
29
+
30
+ ## Target Upgrade Flow
31
+
32
+ For a project that already has Quiver:
33
+
34
+ ```bash
35
+ cd /path/to/project
36
+ npm install --save-dev create-quiver@latest
37
+ npx create-quiver migrate --dir .
38
+ npx create-quiver analyze --dir .
39
+ npx create-quiver doctor --dir .
40
+ ```
41
+
42
+ Then the developer opens the AI agent and asks it to execute `docs/AI_ONBOARDING_PROMPT.md`.
43
+
44
+ ## Slices
45
+
46
+ | Slice | Title | Status | Spec |
47
+ |-------|-------|--------|------|
48
+ | 01 | Non-Destructive Migrate Command | Completed | [slice-01](./slices/slice-01-non-destructive-migrate-command/slice.json) |
49
+ | 02 | Version Metadata and Doctor Upgrade Checks | Completed | [slice-02](./slices/slice-02-version-metadata-doctor-upgrade-checks/slice.json) |
50
+ | 03 | Upgrade Docs and Legacy Project Smokes | Completed | [slice-03](./slices/slice-03-upgrade-docs-legacy-project-smokes/slice.json) |
51
+
52
+ ## Definition of Done
53
+
54
+ - Existing generated projects can receive new Quiver templates without deleting user edits
55
+ - `migrate` reports copied, skipped, and already-present files
56
+ - Quiver records enough metadata to distinguish initialized, analyzed, and migrated projects
57
+ - `doctor` recommends `migrate` when upgrade artifacts are missing
58
+ - README and generated docs explain the upgrade path for existing projects
59
+ - Smokes cover a legacy project missing post-0.5 onboarding files
@@ -0,0 +1,26 @@
1
+ # Quiver v0.11 Spec Status
2
+
3
+ **Spec:** quiver-v11-existing-project-migration
4
+ **Last updated:** 2026-04-22
5
+
6
+ Slice numbering is local to this spec. The first slice is `slice-01`.
7
+
8
+ ## Status
9
+
10
+ | Slice | Title | Status | PR | Estimated hours | Actual hours |
11
+ |-------|-------|--------|----|-----------------|--------------|
12
+ | slice-01 | Non-Destructive Migrate Command | Completed | - | 5 | 5 |
13
+ | slice-02 | Version Metadata and Doctor Upgrade Checks | Completed | - | 4 | 4 |
14
+ | slice-03 | Upgrade Docs and Legacy Project Smokes | Completed | - | 3 | 3 |
15
+
16
+ ## Progress
17
+
18
+ - Completed slices: 3 / 3
19
+ - Estimated hours: 12
20
+ - Actual hours: 12
21
+
22
+ ## Blockers
23
+
24
+ | Slice | Blocker | Since | Action needed |
25
+ |-------|---------|-------|---------------|
26
+ | - | - | - | - |
@@ -0,0 +1,73 @@
1
+ {
2
+ "slice_id": "slice-01-non-destructive-migrate-command",
3
+ "ticket": "QUIVER-01",
4
+ "type": "feature",
5
+ "title": "Non-Destructive Migrate Command",
6
+ "objective": "Add `create-quiver migrate --dir <project>` so existing Quiver projects can receive missing framework artifacts without reinstalling or overwriting user-authored files.",
7
+ "description": "Users who initialized Quiver before newer onboarding files existed currently need to copy missing templates by hand. This slice adds a migration command that detects an existing Quiver project, copies missing files from the packaged template, preserves existing files, and reports exactly what happened.",
8
+ "git": {
9
+ "branch_type": "feature",
10
+ "base_branch": "main",
11
+ "branch_slug": "existing-project-migration",
12
+ "branch_name": "feature/QUIVER-01-existing-project-migration"
13
+ },
14
+ "must": [
15
+ "Add `migrate` mode to the `create-quiver` CLI with `--dir <project>` support",
16
+ "Detect an existing Quiver project using generated docs, specs, package scripts, or tools scripts",
17
+ "Copy missing generated framework files such as `docs/AI_ONBOARDING_PROMPT.md`, support docs, workflow docs, and tool scripts when absent",
18
+ "Never overwrite existing files without preserving or explicitly skipping them",
19
+ "Print a migration summary with copied, skipped, and already-present paths",
20
+ "Fail with a clear message when the target does not look like a Quiver-initialized project"
21
+ ],
22
+ "not_included": [
23
+ "Overwriting user-authored docs",
24
+ "Running `analyze` automatically",
25
+ "Executing AI prompts",
26
+ "Changing slice execution semantics",
27
+ "Publishing a package release"
28
+ ],
29
+ "acceptance": [
30
+ "`npx create-quiver migrate --dir .` runs in an existing Quiver project",
31
+ "Missing framework artifacts are copied from the packaged template",
32
+ "Existing user files are preserved",
33
+ "The command reports copied, skipped, and already-present paths",
34
+ "The command exits clearly when the target has no Quiver markers",
35
+ "Existing `init`, `analyze`, and `doctor` modes still work"
36
+ ],
37
+ "files": [
38
+ "src/create-quiver/index.js",
39
+ "scripts/ci/smoke-create-quiver.sh",
40
+ "scripts/package-quiver.sh",
41
+ "specs/quiver-v11-existing-project-migration/SPEC.md",
42
+ "specs/quiver-v11-existing-project-migration/STATUS.md",
43
+ "specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md",
44
+ "specs/quiver-v11-existing-project-migration/slices/slice-01-non-destructive-migrate-command/slice.json"
45
+ ],
46
+ "tests": [
47
+ "node src/create-quiver/index.js migrate --dir <legacy-fixture>",
48
+ "node src/create-quiver/index.js doctor --dir <migrated-fixture>",
49
+ "bash scripts/ci/smoke-create-quiver.sh",
50
+ "bash scripts/package-quiver.sh",
51
+ "git diff --check"
52
+ ],
53
+ "documentation": [
54
+ "specs/quiver-v11-existing-project-migration/SPEC.md",
55
+ "specs/quiver-v11-existing-project-migration/STATUS.md",
56
+ "specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md"
57
+ ],
58
+ "dependencies": [
59
+ "quiver-v10-local-project-installation-guidance"
60
+ ],
61
+ "assumptions": [
62
+ "Migration should be additive by default",
63
+ "The CLI should not infer business context during migration",
64
+ "Manual review remains required after migration"
65
+ ],
66
+ "estimated_hours": 5,
67
+ "actual_hours": 5,
68
+ "status": "completed",
69
+ "blocked_reason": null,
70
+ "ready_at": null,
71
+ "started_at": "2026-04-22T00:00:00Z",
72
+ "completed_at": "2026-04-22T00:00:00Z"
73
+ }
@@ -0,0 +1,71 @@
1
+ {
2
+ "slice_id": "slice-02-version-metadata-doctor-upgrade-checks",
3
+ "ticket": "QUIVER-02",
4
+ "type": "feature",
5
+ "title": "Version Metadata and Doctor Upgrade Checks",
6
+ "objective": "Record Quiver project state and teach `doctor` to identify projects that need migration or analysis.",
7
+ "description": "After migration exists, Quiver needs a durable way to know which template version initialized or migrated the project. This slice adds metadata and extends doctor diagnostics so users get the right next command instead of guessing.",
8
+ "git": {
9
+ "branch_type": "feature",
10
+ "base_branch": "main",
11
+ "branch_slug": "existing-project-migration",
12
+ "branch_name": "feature/QUIVER-02-existing-project-migration"
13
+ },
14
+ "must": [
15
+ "Create or update a project-local metadata file such as `.quiver/state.json`",
16
+ "Record initialized version, migrated version, last migration date, and last analysis date where available",
17
+ "Update `init`, `migrate`, and `analyze` to maintain metadata",
18
+ "Teach `doctor` to report missing metadata without failing older projects unnecessarily",
19
+ "Teach `doctor` to recommend `npx create-quiver migrate --dir .` when required upgrade artifacts are missing",
20
+ "Keep metadata deterministic and free of secrets"
21
+ ],
22
+ "not_included": [
23
+ "Remote telemetry",
24
+ "User identity tracking",
25
+ "AI-generated metadata",
26
+ "Changing package publish flow"
27
+ ],
28
+ "acceptance": [
29
+ "Newly initialized projects include Quiver metadata",
30
+ "Migrated projects update metadata",
31
+ "`analyze` updates last analysis metadata",
32
+ "`doctor` distinguishes missing analysis from missing migration",
33
+ "Older projects without metadata receive a clear migration recommendation",
34
+ "Metadata contains no absolute secrets or credential values"
35
+ ],
36
+ "files": [
37
+ "src/create-quiver/index.js",
38
+ "scripts/ci/smoke-create-quiver.sh",
39
+ "scripts/ci/smoke-init-docs.sh",
40
+ "scripts/package-quiver.sh",
41
+ "specs/quiver-v11-existing-project-migration/STATUS.md",
42
+ "specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md",
43
+ "specs/quiver-v11-existing-project-migration/slices/slice-02-version-metadata-doctor-upgrade-checks/slice.json"
44
+ ],
45
+ "tests": [
46
+ "init smoke verifies `.quiver/state.json`",
47
+ "migrate smoke verifies migration metadata",
48
+ "analyze smoke verifies last analysis metadata",
49
+ "doctor smoke verifies migration recommendation",
50
+ "git diff --check"
51
+ ],
52
+ "documentation": [
53
+ "specs/quiver-v11-existing-project-migration/STATUS.md",
54
+ "specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md"
55
+ ],
56
+ "dependencies": [
57
+ "slice-01-non-destructive-migrate-command"
58
+ ],
59
+ "assumptions": [
60
+ "A local metadata file is simpler and more reliable than parsing docs for version state",
61
+ "Metadata should supplement, not replace, user-facing docs",
62
+ "Doctor should guide remediation but not run migration automatically"
63
+ ],
64
+ "estimated_hours": 4,
65
+ "actual_hours": 4,
66
+ "status": "completed",
67
+ "blocked_reason": null,
68
+ "ready_at": null,
69
+ "started_at": "2026-04-22T00:00:00Z",
70
+ "completed_at": "2026-04-22T00:00:00Z"
71
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "slice_id": "slice-03-upgrade-docs-legacy-project-smokes",
3
+ "ticket": "QUIVER-03",
4
+ "type": "docs",
5
+ "title": "Upgrade Docs and Legacy Project Smokes",
6
+ "objective": "Document the upgrade path for existing projects and add smoke coverage for legacy generated projects.",
7
+ "description": "Users need a clear path when they already initialized Quiver with an older package. This slice updates README and generated docs with upgrade instructions and adds fixtures or smoke setup that simulate older projects missing newer onboarding artifacts.",
8
+ "git": {
9
+ "branch_type": "docs",
10
+ "base_branch": "main",
11
+ "branch_slug": "existing-project-migration",
12
+ "branch_name": "docs/QUIVER-03-existing-project-migration"
13
+ },
14
+ "must": [
15
+ "Add an `Upgrading Existing Projects` section to the root README",
16
+ "Document both `npx` and devDependency upgrade flows",
17
+ "Update generated README or AI docs to mention migration for already-initialized projects",
18
+ "Add smoke coverage for a legacy project missing `docs/AI_ONBOARDING_PROMPT.md` and metadata",
19
+ "Verify migration does not overwrite edited context docs",
20
+ "Close the spec status and evidence"
21
+ ],
22
+ "not_included": [
23
+ "Publishing a release",
24
+ "Changing migration behavior beyond doc/test-driven fixes found during validation",
25
+ "Adding provider-specific AI instructions",
26
+ "Supporting global installation as a recommended path"
27
+ ],
28
+ "acceptance": [
29
+ "README explains what to do if Quiver was already initialized with an older version",
30
+ "Generated guidance points existing projects to `migrate`, then `analyze`, then `doctor`",
31
+ "Legacy project smoke proves missing files are added",
32
+ "Legacy project smoke proves edited docs are preserved",
33
+ "Package smoke passes",
34
+ "Spec status and evidence show all slices completed"
35
+ ],
36
+ "files": [
37
+ "README.md",
38
+ "README_FOR_AI.md",
39
+ "scripts/init-docs.sh",
40
+ "scripts/ci/smoke-create-quiver.sh",
41
+ "scripts/ci/smoke-init-docs.sh",
42
+ "scripts/package-quiver.sh",
43
+ "specs/quiver-v11-existing-project-migration/SPEC.md",
44
+ "specs/quiver-v11-existing-project-migration/STATUS.md",
45
+ "specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md",
46
+ "specs/quiver-v11-existing-project-migration/slices/slice-03-upgrade-docs-legacy-project-smokes/slice.json"
47
+ ],
48
+ "tests": [
49
+ "bash scripts/ci/smoke-init-docs.sh",
50
+ "bash scripts/ci/smoke-create-quiver.sh",
51
+ "bash scripts/package-quiver.sh",
52
+ "legacy migration fixture or smoke path",
53
+ "git diff --check"
54
+ ],
55
+ "documentation": [
56
+ "README.md",
57
+ "README_FOR_AI.md",
58
+ "specs/quiver-v11-existing-project-migration/SPEC.md",
59
+ "specs/quiver-v11-existing-project-migration/STATUS.md",
60
+ "specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md"
61
+ ],
62
+ "dependencies": [
63
+ "slice-01-non-destructive-migrate-command",
64
+ "slice-02-version-metadata-doctor-upgrade-checks"
65
+ ],
66
+ "assumptions": [
67
+ "Existing projects should not need to delete generated docs before upgrading",
68
+ "The safest upgrade order is migrate, analyze, doctor, then AI prompt",
69
+ "Smoke fixtures can simulate old projects by deleting newer artifacts from a generated project"
70
+ ],
71
+ "estimated_hours": 3,
72
+ "actual_hours": 3,
73
+ "status": "completed",
74
+ "blocked_reason": null,
75
+ "ready_at": null,
76
+ "started_at": "2026-04-22T00:00:00Z",
77
+ "completed_at": "2026-04-22T00:00:00Z"
78
+ }
@@ -2,6 +2,8 @@ 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 cliPackageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../..', 'package.json'), 'utf8'));
6
+ const CLI_VERSION = cliPackageJson.version || '0.0.0';
5
7
 
6
8
  function formatError(message) {
7
9
  return `create-quiver: ${message}`;
@@ -11,6 +13,7 @@ function printUsage() {
11
13
  console.log(`Usage:
12
14
  npx create-quiver [options]
13
15
  npx create-quiver analyze [options]
16
+ npx create-quiver migrate [options]
14
17
  npx create-quiver doctor [options]
15
18
 
16
19
  Options:
@@ -23,6 +26,7 @@ Examples:
23
26
  npx create-quiver --name "My Project"
24
27
  npx create-quiver --name "My Project" --dir ./my-project
25
28
  npx create-quiver analyze --dir ./my-project
29
+ npx create-quiver migrate --dir ./my-project
26
30
  npx create-quiver doctor --dir ./my-project
27
31
  node bin/create-quiver.js doctor --dir ./my-project
28
32
  `);
@@ -38,12 +42,15 @@ function parseArgs(argv) {
38
42
  };
39
43
 
40
44
  const args = [...argv];
41
- if (args[0] === 'doctor' || args[0] === 'analyze') {
45
+ if (args[0] === 'doctor' || args[0] === 'analyze' || args[0] === 'migrate') {
42
46
  result.mode = args[0];
43
47
  args.shift();
44
48
  } else if (args[0] === '--analyze') {
45
49
  result.mode = 'analyze';
46
50
  args.shift();
51
+ } else if (args[0] === '--migrate') {
52
+ result.mode = 'migrate';
53
+ args.shift();
47
54
  } else if (args[0] === '--doctor') {
48
55
  result.mode = 'doctor';
49
56
  args.shift();
@@ -69,6 +76,11 @@ function parseArgs(argv) {
69
76
  continue;
70
77
  }
71
78
 
79
+ if (arg === '--migrate') {
80
+ result.mode = 'migrate';
81
+ continue;
82
+ }
83
+
72
84
  if (arg === '-n' || arg === '--name' || arg === '--project-name') {
73
85
  const value = args[++index];
74
86
  if (!value) {
@@ -177,12 +189,99 @@ function copyTemplate(templateRoot, targetDir) {
177
189
  return docsTemplateDir;
178
190
  }
179
191
 
192
+ function mergeDirectoryTree(sourceDir, targetDir) {
193
+ if (!fs.existsSync(sourceDir)) {
194
+ return;
195
+ }
196
+
197
+ fs.mkdirSync(targetDir, { recursive: true });
198
+ fs.cpSync(sourceDir, targetDir, {
199
+ recursive: true,
200
+ force: false,
201
+ errorOnExist: false,
202
+ preserveTimestamps: true,
203
+ });
204
+ }
205
+
180
206
  function runInitDocs(repoRoot, projectName) {
181
207
  runCommand('bash', ['docs-template/scripts/init-docs.sh', projectName], {
182
208
  cwd: repoRoot,
183
209
  });
184
210
  }
185
211
 
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
+
186
285
  function listGeneratedSpecDirs(projectRoot) {
187
286
  const specsDir = path.join(projectRoot, 'specs');
188
287
 
@@ -797,6 +896,7 @@ function runAnalyze(targetDir) {
797
896
 
798
897
  const scan = buildProjectScan(projectRoot);
799
898
  const artifacts = writeProjectScanArtifacts(projectRoot, scan);
899
+ updateQuiverStateForAnalyze(projectRoot);
800
900
 
801
901
  console.log(`Project analysis completed for ${projectRoot}`);
802
902
  console.log(`Wrote ${path.relative(projectRoot, artifacts.jsonPath)}`);
@@ -805,6 +905,38 @@ function runAnalyze(targetDir) {
805
905
  console.log(`Detected package manager: ${scan.project.package_manager}`);
806
906
  }
807
907
 
908
+ function runMigrate(targetDir) {
909
+ const projectRoot = path.resolve(process.cwd(), targetDir);
910
+
911
+ if (!fs.existsSync(projectRoot)) {
912
+ throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
913
+ }
914
+
915
+ const packageJson = loadPackageJson(projectRoot);
916
+ const projectName = packageJson.name || path.basename(projectRoot) || 'Quiver Project';
917
+ const packageRoot = path.resolve(__dirname, '../..');
918
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'quiver-migrate-'));
919
+
920
+ try {
921
+ const templateRoot = packTemplate(packageRoot, tempRoot);
922
+ mergeDirectoryTree(templateRoot, path.join(projectRoot, 'docs-template'));
923
+ const migrationOutput = runInitDocsWithMode(projectRoot, projectName, 'migrate');
924
+ updateQuiverStateForMigrate(projectRoot, projectName);
925
+
926
+ if (migrationOutput.trim().length > 0) {
927
+ process.stdout.write(migrationOutput);
928
+ if (!migrationOutput.endsWith('\n')) {
929
+ process.stdout.write('\n');
930
+ }
931
+ }
932
+
933
+ console.log(`Quiver migration completed for ${projectRoot}`);
934
+ console.log('Missing workflow files were restored without overwriting existing project files.');
935
+ } finally {
936
+ fs.rmSync(tempRoot, { recursive: true, force: true });
937
+ }
938
+ }
939
+
808
940
  function runDoctor(targetDir) {
809
941
  const projectRoot = path.resolve(process.cwd(), targetDir);
810
942
 
@@ -852,25 +984,32 @@ function runDoctor(targetDir) {
852
984
  const missingFiles = assertFilesExist(projectRoot, requiredFiles);
853
985
  const nonExecutableScripts = assertExecutablesExist(projectRoot, requiredExecutables);
854
986
  const pkg = loadPackageJson(projectRoot);
855
- const requiredScripts = ['check:slice', 'check:pr', 'start:slice', 'cleanup:slice'];
987
+ const requiredScripts = ['check:slice', 'check:pr', 'start:slice', 'cleanup:slice', 'migrate'];
856
988
  const missingScripts = requiredScripts.filter((name) => typeof pkg.scripts?.[name] !== 'string');
857
989
  const hasScanArtifacts = fs.existsSync(path.join(projectRoot, 'docs', 'PROJECT_SCAN.json'))
858
990
  && fs.existsSync(path.join(projectRoot, 'docs', 'PROJECT_MAP.md'));
859
-
860
- const problems = [
991
+ const quiverState = readQuiverState(projectRoot);
992
+ const hasQuiverState = Boolean(quiverState);
993
+ const stateWarnings = hasQuiverState ? [] : ['missing Quiver state metadata: .quiver/state.json'];
994
+ const migrationProblems = [
861
995
  ...missingFiles.map((file) => `missing file: ${file}`),
862
996
  ...nonExecutableScripts.map((file) => `missing executable bit: ${file}`),
863
997
  ...missingScripts.map((name) => `missing package.json script: ${name}`),
864
998
  ];
865
999
 
866
- if (problems.length > 0) {
867
- throw new Error(formatError(`doctor failed:\n- ${problems.join('\n- ')}`));
1000
+ if (migrationProblems.length > 0) {
1001
+ throw new Error(formatError(`doctor failed:\n- ${migrationProblems.join('\n- ')}\n- Run migration first: npx create-quiver migrate --dir .`));
868
1002
  }
869
1003
 
870
1004
  console.log(`Quiver doctor passed for ${projectRoot}`);
871
1005
  console.log(`Generated project slug: ${projectSlug}`);
872
1006
  console.log('Next steps:');
873
- if (!hasScanArtifacts) {
1007
+ for (const warning of stateWarnings) {
1008
+ console.log(`- Warning: ${warning}`);
1009
+ }
1010
+ if (!hasQuiverState) {
1011
+ console.log('- Run migration first: npx create-quiver migrate --dir .');
1012
+ } else if (!hasScanArtifacts) {
874
1013
  console.log('- Analyze the project first: npx create-quiver analyze --dir .');
875
1014
  } else {
876
1015
  console.log('- Ask your AI agent: Read docs/AI_ONBOARDING_PROMPT.md and execute it.');
@@ -904,6 +1043,11 @@ async function run(argv) {
904
1043
  return;
905
1044
  }
906
1045
 
1046
+ if (args.mode === 'migrate') {
1047
+ runMigrate(args.targetDir);
1048
+ return;
1049
+ }
1050
+
907
1051
  if (args.mode === 'doctor') {
908
1052
  runDoctor(args.targetDir);
909
1053
  return;