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.
- package/.github/workflows/ci.yml +7 -30
- package/AGENTS.md.template +41 -0
- package/CHANGELOG.md +5 -0
- package/README.md +53 -9
- package/README_FOR_AI.md +36 -14
- package/ROADMAP.md +78 -0
- package/docs/AI_CONTEXT.md.template +19 -25
- package/docs/AI_ONBOARDING_PROMPT.md.template +12 -0
- package/docs/CONTEXTO.md.template +4 -17
- package/docs/DECISIONS.md.template +18 -0
- package/docs/DEEP.md.template +34 -0
- package/docs/DOCUMENTATION_GUIDE.md.template +9 -7
- package/docs/GITFLOW_PR_GUIDE.md.template +7 -0
- package/docs/INDEX.md.template +9 -0
- package/docs/QUICK.md.template +27 -0
- package/docs/STANDARD.md.template +49 -0
- package/docs/STATUS.md.template +2 -2
- package/docs/SUPPORT_MATRIX.md.template +7 -4
- package/docs/TESTING_GUIDE_FOR_AI.md.template +4 -3
- package/docs/TROUBLESHOOTING.md.template +14 -0
- package/docs/WORKFLOW.md.template +19 -5
- package/package.json +3 -1
- package/package.template.json +13 -1
- package/scripts/cleanup-slice.sh +2 -172
- package/scripts/init-docs.sh +246 -44
- package/scripts/package-quiver.sh +5 -0
- package/scripts/start-slice.sh +3 -425
- package/specs/[project-name]/EVIDENCE_REPORT.md.template +3 -1
- package/specs/[project-name]/slices/slice-template/slice.json +2 -2
- package/specs/quiver-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/specs/quiver-v12-cross-platform-native-runtime/EVIDENCE_REPORT.md +30 -0
- package/specs/quiver-v12-cross-platform-native-runtime/SPEC.md +86 -0
- package/specs/quiver-v12-cross-platform-native-runtime/STATUS.md +29 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +69 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +76 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +74 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +81 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +78 -0
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-06-cross-platform-ci-release-readiness/slice.json +74 -0
- package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +28 -0
- package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +68 -0
- package/specs/quiver-v13-token-efficient-ai-context/STATUS.md +26 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-01-token-efficient-ai-modes-guidance/slice.json +65 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-02-decision-log-context-checkpoint/slice.json +64 -0
- package/specs/quiver-v13-token-efficient-ai-context/slices/slice-03-project-map-reading-order/slice.json +66 -0
- package/specs/quiver-v14-tiered-context-pack/EVIDENCE_REPORT.md +42 -0
- package/specs/quiver-v14-tiered-context-pack/SPEC.md +116 -0
- package/specs/quiver-v14-tiered-context-pack/STATUS.md +35 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-01-tiered-context-pack/slice.json +77 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-02-agents-md-router/slice.json +74 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-03-active-slice-lifecycle/slice.json +74 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-04-dedup-frontmatter/slice.json +83 -0
- package/specs/quiver-v14-tiered-context-pack/slices/slice-05-doctor-smokes-tiered-pack/slice.json +84 -0
- package/src/create-quiver/index.js +360 -40
- package/src/create-quiver/lib/analyze.js +9 -0
- package/src/create-quiver/lib/doctor.js +212 -0
- package/src/create-quiver/lib/git.js +154 -0
- package/src/create-quiver/lib/init-docs.js +616 -0
- package/src/create-quiver/lib/lifecycle.js +478 -0
- package/src/create-quiver/lib/paths.js +19 -0
- package/src/create-quiver/lib/readiness.js +300 -0
- package/src/create-quiver/lib/scope.js +5 -0
- package/src/create-quiver/lib/slice.js +194 -0
- package/src/create-quiver/lib/state.js +89 -0
package/scripts/start-slice.sh
CHANGED
|
@@ -2,428 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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": "
|
|
12
|
-
"branch_name": "feature/ABC-123-
|
|
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
|
+
}
|