create-quiver 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.github/workflows/ci.yml +7 -30
  2. package/AGENTS.md.template +41 -0
  3. package/CHANGELOG.md +5 -0
  4. package/README.md +53 -9
  5. package/README_FOR_AI.md +36 -14
  6. package/ROADMAP.md +78 -0
  7. package/docs/AI_CONTEXT.md.template +19 -25
  8. package/docs/AI_ONBOARDING_PROMPT.md.template +12 -0
  9. package/docs/CONTEXTO.md.template +4 -17
  10. package/docs/DECISIONS.md.template +18 -0
  11. package/docs/DEEP.md.template +34 -0
  12. package/docs/DOCUMENTATION_GUIDE.md.template +9 -7
  13. package/docs/GITFLOW_PR_GUIDE.md.template +7 -0
  14. package/docs/INDEX.md.template +9 -0
  15. package/docs/QUICK.md.template +27 -0
  16. package/docs/STANDARD.md.template +49 -0
  17. package/docs/STATUS.md.template +2 -2
  18. package/docs/SUPPORT_MATRIX.md.template +7 -4
  19. package/docs/TESTING_GUIDE_FOR_AI.md.template +4 -3
  20. package/docs/TROUBLESHOOTING.md.template +14 -0
  21. package/docs/WORKFLOW.md.template +19 -5
  22. package/package.json +3 -1
  23. package/package.template.json +13 -1
  24. package/scripts/cleanup-slice.sh +2 -172
  25. package/scripts/init-docs.sh +246 -44
  26. package/scripts/package-quiver.sh +5 -0
  27. package/scripts/start-slice.sh +3 -425
  28. package/specs/[project-name]/EVIDENCE_REPORT.md.template +3 -1
  29. package/specs/[project-name]/slices/slice-template/slice.json +2 -2
  30. package/specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md +38 -0
  31. package/specs/quiver-v11-existing-project-migration/SPEC.md +59 -0
  32. package/specs/quiver-v11-existing-project-migration/STATUS.md +26 -0
  33. package/specs/quiver-v11-existing-project-migration/slices/slice-01-non-destructive-migrate-command/slice.json +73 -0
  34. package/specs/quiver-v11-existing-project-migration/slices/slice-02-version-metadata-doctor-upgrade-checks/slice.json +71 -0
  35. package/specs/quiver-v11-existing-project-migration/slices/slice-03-upgrade-docs-legacy-project-smokes/slice.json +78 -0
  36. package/specs/quiver-v12-cross-platform-native-runtime/EVIDENCE_REPORT.md +30 -0
  37. package/specs/quiver-v12-cross-platform-native-runtime/SPEC.md +86 -0
  38. package/specs/quiver-v12-cross-platform-native-runtime/STATUS.md +29 -0
  39. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +69 -0
  40. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +76 -0
  41. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +74 -0
  42. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +81 -0
  43. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +78 -0
  44. package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-06-cross-platform-ci-release-readiness/slice.json +74 -0
  45. package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +28 -0
  46. package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +68 -0
  47. package/specs/quiver-v13-token-efficient-ai-context/STATUS.md +26 -0
  48. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-01-token-efficient-ai-modes-guidance/slice.json +65 -0
  49. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-02-decision-log-context-checkpoint/slice.json +64 -0
  50. package/specs/quiver-v13-token-efficient-ai-context/slices/slice-03-project-map-reading-order/slice.json +66 -0
  51. package/specs/quiver-v14-tiered-context-pack/EVIDENCE_REPORT.md +42 -0
  52. package/specs/quiver-v14-tiered-context-pack/SPEC.md +116 -0
  53. package/specs/quiver-v14-tiered-context-pack/STATUS.md +35 -0
  54. package/specs/quiver-v14-tiered-context-pack/slices/slice-01-tiered-context-pack/slice.json +77 -0
  55. package/specs/quiver-v14-tiered-context-pack/slices/slice-02-agents-md-router/slice.json +74 -0
  56. package/specs/quiver-v14-tiered-context-pack/slices/slice-03-active-slice-lifecycle/slice.json +74 -0
  57. package/specs/quiver-v14-tiered-context-pack/slices/slice-04-dedup-frontmatter/slice.json +83 -0
  58. package/specs/quiver-v14-tiered-context-pack/slices/slice-05-doctor-smokes-tiered-pack/slice.json +84 -0
  59. package/src/create-quiver/index.js +360 -40
  60. package/src/create-quiver/lib/analyze.js +9 -0
  61. package/src/create-quiver/lib/doctor.js +212 -0
  62. package/src/create-quiver/lib/git.js +154 -0
  63. package/src/create-quiver/lib/init-docs.js +616 -0
  64. package/src/create-quiver/lib/lifecycle.js +478 -0
  65. package/src/create-quiver/lib/paths.js +19 -0
  66. package/src/create-quiver/lib/readiness.js +300 -0
  67. package/src/create-quiver/lib/scope.js +5 -0
  68. package/src/create-quiver/lib/slice.js +194 -0
  69. package/src/create-quiver/lib/state.js +89 -0
@@ -2,428 +2,6 @@
2
2
 
3
3
  set -euo pipefail
4
4
 
5
- usage() {
6
- cat <<'EOF'
7
- Uso:
8
- bash tools/scripts/start-slice.sh [--allow-draft] <ruta-al-slice.json>
9
-
10
- Lee la metadata git del slice, valida la rama declarada y crea un worktree
11
- afuera de la raiz trackeada del repo.
12
-
13
- Variables opcionales:
14
- SLICE_WORKTREES_DIR Directorio base para los worktrees.
15
- Default: <repo-parent>/.worktrees/<repo-name>
16
- --allow-draft Permite bootstraps intencionales de slices en draft.
17
- EOF
18
- }
19
-
20
- append_unique_line() {
21
- local file_path="$1"
22
- local line="$2"
23
-
24
- mkdir -p "$(dirname "$file_path")"
25
- touch "$file_path"
26
-
27
- if ! grep -Fxq "$line" "$file_path"; then
28
- printf '%s\n' "$line" >> "$file_path"
29
- fi
30
- }
31
-
32
- ensure_local_exclude() {
33
- local workdir="$1"
34
- local pattern="$2"
35
- local exclude_path
36
- local git_dir
37
-
38
- git_dir="$(git -C "$workdir" rev-parse --absolute-git-dir)"
39
- exclude_path="$git_dir/info/exclude"
40
- append_unique_line "$exclude_path" "$pattern"
41
- }
42
-
43
- canonicalize_dir() {
44
- local dir_path="$1"
45
- (cd "$dir_path" && pwd -P)
46
- }
47
-
48
- resolve_base_ref() {
49
- local base_branch="$1"
50
-
51
- if git show-ref --verify --quiet "refs/heads/$base_branch"; then
52
- printf '%s\n' "$base_branch"
53
- return 0
54
- fi
55
-
56
- if git show-ref --verify --quiet "refs/remotes/origin/$base_branch"; then
57
- printf '%s\n' "origin/$base_branch"
58
- return 0
59
- fi
60
-
61
- if git ls-remote --exit-code --heads origin "$base_branch" >/dev/null 2>&1; then
62
- if git fetch origin "$base_branch:refs/remotes/origin/$base_branch" >/dev/null 2>&1; then
63
- printf '%s\n' "origin/$base_branch"
64
- return 0
65
- fi
66
-
67
- echo "Error: origin existe pero no se pudo actualizar '$base_branch'. Revisa conectividad o crea la rama local '$base_branch' antes de correr start-slice.sh." >&2
68
- return 1
69
- fi
70
-
71
- echo "Error: no se encontró '$base_branch' como rama local ni como ref remota 'origin/$base_branch'. Crea la rama base localmente o configura origin antes de correr start-slice.sh." >&2
72
- return 1
73
- }
74
-
75
- slice_alias() {
76
- node - "$1" <<'NODE'
77
- const ticket = String(process.argv[2] || '').trim();
78
- const parts = ticket.split('-').filter(Boolean);
79
- const prefix = (parts[0] || 'GEN').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
80
- const suffix = (parts[parts.length - 1] || '00').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
81
- const short = prefix.length <= 3 ? prefix : prefix.slice(0, 3);
82
- process.stdout.write(`${short || 'GEN'}-${suffix || '00'}`);
83
- NODE
84
- }
85
-
86
- write_worktree_context() {
87
- local target_worktree="$1"
88
- local target_branch="$2"
89
-
90
- if [[ "$target_worktree" != "$repo_root" ]]; then
91
- ensure_local_exclude "$target_worktree" "WORKTREE_CONTEXT.md"
92
- fi
93
-
94
- node - "$slice_abs" "$target_worktree" "$target_branch" "$spec_family" "$spec_slug" <<'NODE' > "$target_worktree/WORKTREE_CONTEXT.md"
95
- const fs = require('fs');
96
-
97
- const [slicePath, worktreePath, branchName, specFamily, specSlug] = process.argv.slice(2);
98
- const slice = JSON.parse(fs.readFileSync(slicePath, 'utf8'));
99
-
100
- function toAlias(ticket) {
101
- const parts = String(ticket || '').split('-').filter(Boolean);
102
- const prefix = (parts[0] || 'GEN').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
103
- const suffix = (parts[parts.length - 1] || '00').replace(/[^A-Za-z0-9]/g, '').toUpperCase();
104
- const short = prefix.length <= 3 ? prefix : prefix.slice(0, 3);
105
- return `${short || 'GEN'}-${suffix || '00'}`;
106
- }
107
-
108
- function listBlock(items) {
109
- if (!Array.isArray(items) || items.length === 0) {
110
- return '- n/a';
111
- }
112
-
113
- return items.map((item) => `- ${item}`).join('\n');
114
- }
115
-
116
- const alias = toAlias(slice.ticket);
117
- const status = slice.status || 'pending';
118
- const title = slice.title || slice.slice_id;
119
- const objective = slice.objective || 'Sin objetivo declarado.';
120
-
121
- const lines = [
122
- '# Worktree Context',
123
- '',
124
- '> Archivo generado localmente por `tools/scripts/start-slice.sh`.',
125
- '> No se trackea en git.',
126
- '',
127
- `**Alias:** ${alias}`,
128
- `**Spec:** ${specSlug}`,
129
- `**Spec family:** ${specFamily}`,
130
- `**Slice:** ${slice.slice_id}`,
131
- `**Ticket:** ${slice.ticket}`,
132
- `**Branch:** ${branchName}`,
133
- `**Worktree:** ${worktreePath}`,
134
- `**Status:** ${status}`,
135
- '',
136
- '## Title',
137
- '',
138
- title,
139
- '',
140
- '## Objective',
141
- '',
142
- objective,
143
- '',
144
- '## Routes',
145
- '',
146
- listBlock(slice.ui_scope?.routes),
147
- '',
148
- '## Components',
149
- '',
150
- listBlock(slice.ui_scope?.components),
151
- '',
152
- '## Allowed Files',
153
- '',
154
- listBlock(slice.files),
155
- '',
156
- '## Constraints',
157
- '',
158
- listBlock(slice.not_included),
159
- '',
160
- '## Expected Validation',
161
- '',
162
- listBlock(slice.acceptance),
163
- ''
164
- ];
165
-
166
- process.stdout.write(`${lines.join('\n')}\n`);
167
- NODE
168
- }
169
-
170
- refresh_active_slices_board() {
171
- if [[ -x "$repo_root/tools/scripts/refresh-active-slices.sh" ]]; then
172
- "$repo_root/tools/scripts/refresh-active-slices.sh" >/dev/null 2>&1 || true
173
- fi
174
- }
175
-
176
- allow_draft="0"
177
- args=()
178
- for arg in "$@"; do
179
- case "$arg" in
180
- -h|--help)
181
- usage
182
- exit 0
183
- ;;
184
- --allow-draft)
185
- allow_draft="1"
186
- ;;
187
- *)
188
- args+=("$arg")
189
- ;;
190
- esac
191
- done
192
-
193
- [[ "${ALLOW_DRAFT_SLICE:-}" == "1" ]] && allow_draft="1"
194
-
195
- if [[ ${#args[@]} -ne 1 ]]; then
196
- usage
197
- exit 1
198
- fi
199
-
200
- if ! command -v git >/dev/null 2>&1; then
201
- echo "Error: git no esta disponible en PATH." >&2
202
- exit 1
203
- fi
204
-
205
- if ! command -v node >/dev/null 2>&1; then
206
- echo "Error: node no esta disponible en PATH." >&2
207
- exit 1
208
- fi
209
-
210
- slice_input="${args[0]}"
211
- repo_root="$(canonicalize_dir "$(git rev-parse --show-toplevel)")"
212
-
213
- if [[ ! -f "$slice_input" ]]; then
214
- echo "Error: no existe el slice '$slice_input'." >&2
215
- exit 1
216
- fi
217
-
218
- slice_abs="$(canonicalize_dir "$(dirname "$slice_input")")/$(basename "$slice_input")"
219
- slice_rel="${slice_abs#$repo_root/}"
220
-
221
- case "$slice_rel" in
222
- specs-fix/*/slices/*/slice.json)
223
- spec_family="specs-fix"
224
- spec_slug="$(printf '%s\n' "$slice_rel" | cut -d/ -f2)"
225
- ;;
226
- specs/*/slices/*/slice.json)
227
- spec_family="specs"
228
- spec_slug="$(printf '%s\n' "$slice_rel" | cut -d/ -f2)"
229
- ;;
230
- *)
231
- echo "Error: el slice debe vivir dentro de specs/ o specs-fix/." >&2
232
- exit 1
233
- ;;
234
- esac
235
-
236
- slice_meta=()
237
- while IFS= read -r line; do
238
- slice_meta+=("$line")
239
- done < <(node - "$slice_abs" <<'NODE'
240
- const fs = require('fs');
241
-
242
- const slicePath = process.argv[2];
243
-
244
- function fail(message) {
245
- console.error(`Error: ${message}`);
246
- process.exit(1);
247
- }
248
-
249
- let json;
250
- try {
251
- json = JSON.parse(fs.readFileSync(slicePath, 'utf8'));
252
- } catch (error) {
253
- fail(`No se pudo parsear '${slicePath}' como JSON: ${error.message}`);
254
- }
255
-
256
- const ticket = typeof json.ticket === 'string' ? json.ticket.trim() : '';
257
- const git = json.git ?? {};
258
- const branchType = typeof git.branch_type === 'string' ? git.branch_type.trim() : '';
259
- const baseBranch = typeof git.base_branch === 'string' ? git.base_branch.trim() : '';
260
- const branchSlug = typeof git.branch_slug === 'string' ? git.branch_slug.trim() : '';
261
- const branchName = typeof git.branch_name === 'string' ? git.branch_name.trim() : '';
262
- const sliceId = typeof json.slice_id === 'string' ? json.slice_id.trim() : '';
263
-
264
- if (!sliceId) {
265
- fail('Falta "slice_id" en el slice.');
266
- }
267
-
268
- if (!ticket) {
269
- fail('Falta "ticket" en el slice.');
270
- }
271
-
272
- if (!branchType || !baseBranch || !branchSlug || !branchName) {
273
- fail('El bloque "git" debe incluir "branch_type", "base_branch", "branch_slug" y "branch_name".');
274
- }
275
-
276
- const expectedBaseByType = {
277
- feature: 'develop',
278
- bugfix: 'develop',
279
- hotfix: 'main'
280
- };
281
-
282
- if (!expectedBaseByType[branchType]) {
283
- fail(`git.branch_type invalido: "${branchType}". Usa "feature", "bugfix" o "hotfix".`);
284
- }
285
-
286
- const expectedBaseBranch = expectedBaseByType[branchType];
287
- if (baseBranch !== expectedBaseBranch) {
288
- fail(`git.base_branch invalido para ${branchType}. Esperado: "${expectedBaseBranch}".`);
289
- }
290
-
291
- const expectedBranchName = `${branchType}/${ticket}-${branchSlug}`;
292
- if (branchName !== expectedBranchName) {
293
- fail(`git.branch_name invalido. Esperado: "${expectedBranchName}".`);
294
- }
295
-
296
- console.log(sliceId);
297
- console.log(ticket);
298
- console.log(branchType);
299
- console.log(baseBranch);
300
- console.log(branchSlug);
301
- console.log(branchName);
302
- console.log(String(json.status || 'draft'));
303
- NODE
304
- )
305
-
306
- if [[ ${#slice_meta[@]} -ne 7 ]]; then
307
- echo "Error: no se pudo leer la metadata git del slice." >&2
308
- exit 1
309
- fi
310
-
311
- slice_id="${slice_meta[0]}"
312
- ticket="${slice_meta[1]}"
313
- branch_type="${slice_meta[2]}"
314
- base_branch="${slice_meta[3]}"
315
- branch_slug="${slice_meta[4]}"
316
- branch_name="${slice_meta[5]}"
317
- slice_status="${slice_meta[6]}"
318
-
319
- if [[ "$slice_status" == "blocked" ]]; then
320
- echo "Error: el slice esta bloqueado (status=blocked). Resolve el bloqueante antes de iniciar." >&2
321
- exit 1
322
- fi
323
-
324
- if [[ "$slice_status" == "cancelled" ]]; then
325
- echo "Error: el slice esta cancelado (status=cancelled)." >&2
326
- exit 1
327
- fi
328
-
329
- if [[ "$slice_status" == "completed" ]]; then
330
- echo "WARN: el slice ya figura como completed. Si realmente corresponde reejecutarlo, cambia el status a in_progress."
331
- fi
332
-
333
- if [[ "$slice_status" == "draft" && "$allow_draft" != "1" ]]; then
334
- echo "Error: el slice esta en estado 'draft'. Marca el slice como 'ready' o usa --allow-draft para un bootstrap intencional." >&2
335
- exit 1
336
- fi
337
-
338
- if [[ "$slice_status" == "draft" ]]; then
339
- echo "WARN: bootstrap intencional para un slice en draft."
340
- fi
341
-
342
- repo_name="$(basename "$repo_root")"
343
- repo_parent="$(dirname "$repo_root")"
344
- worktrees_root="${SLICE_WORKTREES_DIR:-$repo_parent/.worktrees/$repo_name}"
345
- safe_branch_name="$(printf '%s' "$branch_name" | sed 's#[^A-Za-z0-9._-]#-#g')"
346
- worktree_path="$worktrees_root/$safe_branch_name"
347
-
348
- git worktree prune >/dev/null 2>&1 || true
349
-
350
- existing_worktree_path="$(
351
- node - "$branch_name" <<'NODE'
352
- const cp = require('child_process');
353
-
354
- const branchRef = `refs/heads/${process.argv[2]}`;
355
- const text = cp.execSync('git worktree list --porcelain', {
356
- encoding: 'utf8',
357
- stdio: ['ignore', 'pipe', 'pipe']
358
- });
359
-
360
- let currentPath = '';
361
- for (const line of text.trim().split('\n')) {
362
- if (line.startsWith('worktree ')) {
363
- currentPath = line.slice('worktree '.length);
364
- continue;
365
- }
366
-
367
- if (line === `branch ${branchRef}`) {
368
- process.stdout.write(currentPath);
369
- break;
370
- }
371
- }
372
- NODE
373
- )"
374
-
375
- if [[ -n "$existing_worktree_path" && ! -d "$existing_worktree_path" ]]; then
376
- existing_worktree_path=""
377
- fi
378
-
379
- if [[ -n "$existing_worktree_path" ]]; then
380
- write_worktree_context "$existing_worktree_path" "$branch_name"
381
- refresh_active_slices_board
382
- cat <<EOF
383
- La rama ya tiene un worktree asociado.
384
- Alias: $(slice_alias "$ticket")
385
- Spec: $spec_slug
386
- Slice: $slice_id
387
- Ticket: $ticket
388
- Rama: $branch_name
389
- Base: $base_branch
390
- Worktree: $existing_worktree_path
391
- EOF
392
- exit 0
393
- fi
394
-
395
- if [[ -e "$worktree_path" && ! -f "$worktree_path/.git" && ! -d "$worktree_path/.git" ]]; then
396
- echo "Error: la ruta '$worktree_path' ya existe y no parece un worktree git." >&2
397
- exit 1
398
- fi
399
-
400
- mkdir -p "$worktrees_root"
401
-
402
- git fetch origin --prune >/dev/null 2>&1 || true
403
-
404
- if git show-ref --verify --quiet "refs/heads/$branch_name"; then
405
- git worktree add "$worktree_path" "$branch_name"
406
- elif git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then
407
- git fetch origin "$branch_name:$branch_name" >/dev/null 2>&1
408
- git worktree add "$worktree_path" "$branch_name"
409
- else
410
- base_ref="$(resolve_base_ref "$base_branch")"
411
- git worktree add -b "$branch_name" "$worktree_path" "$base_ref"
412
- fi
413
-
414
- write_worktree_context "$worktree_path" "$branch_name"
415
- refresh_active_slices_board
416
-
417
- cat <<EOF
418
- Slice listo para trabajar.
419
- Alias: $(slice_alias "$ticket")
420
- Spec: $spec_slug
421
- Slice: $slice_id
422
- Ticket: $ticket
423
- Tipo de rama: $branch_type
424
- Base: $base_branch
425
- Slug: $branch_slug
426
- Rama: $branch_name
427
- Worktree: $worktree_path
428
- Contexto: $worktree_path/WORKTREE_CONTEXT.md
429
- EOF
5
+ repo_root="$(git rev-parse --show-toplevel)"
6
+ cd "$repo_root"
7
+ exec npx create-quiver start-slice "$@"
@@ -12,4 +12,6 @@
12
12
 
13
13
  ## Evidence by Slice
14
14
 
15
- _No evidence recorded yet._
15
+ - Prefer summarized command evidence over pasted long logs.
16
+ - For each command, record: command, exit code, first relevant error or success signal, and log path if one exists.
17
+ - Keep full logs in files or artifacts when they are too long to fit comfortably here.
@@ -8,8 +8,8 @@
8
8
  "git": {
9
9
  "branch_type": "feature",
10
10
  "base_branch": "develop",
11
- "branch_slug": "[kebab-case-slug]",
12
- "branch_name": "feature/ABC-123-[kebab-case-slug]"
11
+ "branch_slug": "slice-template",
12
+ "branch_name": "feature/ABC-123-slice-template"
13
13
  },
14
14
  "ui_scope": {
15
15
  "routes": [
@@ -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
+ }