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 +22 -2
- package/README_FOR_AI.md +11 -9
- package/docs/AI_CONTEXT.md.template +1 -0
- package/docs/WORKFLOW.md.template +2 -1
- package/package.json +2 -1
- package/package.template.json +2 -1
- package/scripts/init-docs.sh +156 -27
- package/specs/quiver-v11-existing-project-migration/EVIDENCE_REPORT.md +38 -0
- package/specs/quiver-v11-existing-project-migration/SPEC.md +59 -0
- package/specs/quiver-v11-existing-project-migration/STATUS.md +26 -0
- package/specs/quiver-v11-existing-project-migration/slices/slice-01-non-destructive-migrate-command/slice.json +73 -0
- package/specs/quiver-v11-existing-project-migration/slices/slice-02-version-metadata-doctor-upgrade-checks/slice.json +71 -0
- package/specs/quiver-v11-existing-project-migration/slices/slice-03-upgrade-docs-legacy-project-smokes/slice.json +78 -0
- package/src/create-quiver/index.js +151 -7
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.
|
|
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
|
-
###
|
|
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.
|
|
80
|
-
7.
|
|
81
|
-
8.
|
|
82
|
-
9.
|
|
83
|
-
10.
|
|
84
|
-
11.
|
|
85
|
-
12.
|
|
86
|
-
13.
|
|
87
|
-
14.
|
|
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
|
|
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.
|
|
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"
|
package/package.template.json
CHANGED
|
@@ -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
|
}
|
package/scripts/init-docs.sh
CHANGED
|
@@ -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
|
-
|
|
161
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
867
|
-
throw new Error(formatError(`doctor failed:\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
|
-
|
|
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;
|