create-quiver 0.7.0 → 0.8.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/.claude/settings.local.json +45 -0
- package/.github/workflows/ci.yml +2 -2
- package/BACKLOG.md +139 -0
- package/CHANGELOG.md +13 -1
- package/README.md +31 -1
- package/README_FOR_AI.md +25 -13
- package/ROADMAP.md +28 -6
- package/docs/AI_ONBOARDING_PROMPT.md.template +4 -0
- package/docs/COMMANDS.md.template +25 -0
- package/docs/INDEX.md.template +2 -0
- package/docs/SUPPORT_MATRIX.md.template +9 -0
- package/docs/WORKFLOW.md.template +4 -0
- package/docs/examples/graph.md.template +62 -0
- package/docs/examples/next.md.template +27 -0
- package/docs/examples/plan.md.template +28 -0
- package/package.json +5 -2
- package/package.template.json +8 -3
- package/scripts/check-slice-readiness.sh +6 -4
- package/scripts/init-docs.sh +57 -9
- package/specs/[project-name]/HANDOFF.md.template +37 -0
- package/specs/[project-name]/slices/slice-template/slice.json +5 -0
- package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-01-project-scan-command/slice.json +1 -1
- package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-02-ai-onboarding-prompt/slice.json +1 -1
- package/specs/quiver-v08-agent-onboarding-analysis/slices/slice-03-doctor-readme-adoption-flow/slice.json +1 -1
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-01-cross-platform-support-contract/slice.json +1 -1
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-02-node-init-docs-runtime/slice.json +1 -1
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-03-node-migrate-analyze-doctor-flow/slice.json +1 -1
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-04-node-slice-lifecycle-commands/slice.json +1 -1
- package/specs/quiver-v12-cross-platform-native-runtime/slices/slice-05-generated-project-scripts-and-migration/slice.json +1 -1
- package/specs/quiver-v13-token-efficient-ai-context/EVIDENCE_REPORT.md +1 -1
- package/specs/quiver-v13-token-efficient-ai-context/SPEC.md +1 -1
- package/specs/quiver-v15-init-required-before-migrate/EVIDENCE_REPORT.md +26 -0
- package/specs/quiver-v15-init-required-before-migrate/SPEC.md +66 -0
- package/specs/quiver-v15-init-required-before-migrate/STATUS.md +26 -0
- package/specs/quiver-v15-init-required-before-migrate/slices/slice-01-migrate-initialization-precondition/slice.json +65 -0
- package/specs/quiver-v15-init-required-before-migrate/slices/slice-02-doctor-not-initialized-guidance/slice.json +61 -0
- package/specs/quiver-v15-init-required-before-migrate/slices/slice-03-docs-smokes-init-before-migrate/slice.json +64 -0
- package/specs/quiver-v16-handoff-contract/EVIDENCE_REPORT.md +26 -0
- package/specs/quiver-v16-handoff-contract/SPEC.md +68 -0
- package/specs/quiver-v16-handoff-contract/STATUS.md +26 -0
- package/specs/quiver-v16-handoff-contract/slices/slice-01-handoff-template-and-contract/slice.json +66 -0
- package/specs/quiver-v16-handoff-contract/slices/slice-02-check-handoff-command/slice.json +70 -0
- package/specs/quiver-v16-handoff-contract/slices/slice-03-handoff-scaffold-optional/slice.json +67 -0
- package/specs/quiver-v17-orchestration-foundation/EVIDENCE_REPORT.md +32 -0
- package/specs/quiver-v17-orchestration-foundation/SPEC.md +79 -0
- package/specs/quiver-v17-orchestration-foundation/STATUS.md +31 -0
- package/specs/quiver-v17-orchestration-foundation/slices/slice-01-ci-matrix-verified/slice.json +68 -0
- package/specs/quiver-v17-orchestration-foundation/slices/slice-02-slice-graph-library/slice.json +65 -0
- package/specs/quiver-v17-orchestration-foundation/slices/slice-03-depends-on-validation/slice.json +72 -0
- package/specs/quiver-v18-slice-orchestration/EVIDENCE_REPORT.md +38 -0
- package/specs/quiver-v18-slice-orchestration/SPEC.md +91 -0
- package/specs/quiver-v18-slice-orchestration/STATUS.md +33 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-01-plan-command/slice.json +79 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-02-graph-mvp-tree/slice.json +75 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-03-graph-extended-formats/slice.json +70 -0
- package/specs/quiver-v18-slice-orchestration/slices/slice-04-next-command/slice.json +73 -0
- package/specs/quiver-v18-stabilization/EVIDENCE_REPORT.md +26 -0
- package/specs/quiver-v18-stabilization/SPEC.md +62 -0
- package/specs/quiver-v18-stabilization/STATUS.md +30 -0
- package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/CLOSURE_BRIEF.md +29 -0
- package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/EXECUTION_BRIEF.md +134 -0
- package/specs/quiver-v18-stabilization/slices/slice-01-fix-legacy-dependency-resolution/slice.json +56 -0
- package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/CLOSURE_BRIEF.md +29 -0
- package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/EXECUTION_BRIEF.md +118 -0
- package/specs/quiver-v18-stabilization/slices/slice-02-roadmap-and-branch-cleanup/slice.json +57 -0
- package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/CLOSURE_BRIEF.md +23 -0
- package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/EXECUTION_BRIEF.md +73 -0
- package/specs/quiver-v18-stabilization/slices/slice-03-publish-drafts-branch/slice.json +49 -0
- package/src/create-quiver/commands/graph.js +97 -0
- package/src/create-quiver/commands/next.js +134 -0
- package/src/create-quiver/commands/plan.js +205 -0
- package/src/create-quiver/index.js +179 -2
- package/src/create-quiver/lib/handoff.js +104 -0
- package/src/create-quiver/lib/init-docs.js +71 -13
- package/src/create-quiver/lib/json.js +14 -0
- package/src/create-quiver/lib/lifecycle.js +3 -2
- package/src/create-quiver/lib/readiness.js +55 -1
- package/src/create-quiver/lib/renderers/dot.js +129 -0
- package/src/create-quiver/lib/renderers/mermaid.js +119 -0
- package/src/create-quiver/lib/renderers/tree.js +116 -0
- package/src/create-quiver/lib/slice-graph.js +453 -0
- package/src/create-quiver/lib/slice.js +2 -1
- package/src/create-quiver/lib/state.js +50 -0
|
@@ -2,12 +2,17 @@ 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 { checkHandoff, scaffoldHandoff } = require('./lib/handoff');
|
|
5
6
|
const { collectDoctorWarnings } = require('./lib/doctor');
|
|
7
|
+
const { runGraph } = require('./commands/graph');
|
|
8
|
+
const { runNext } = require('./commands/next');
|
|
9
|
+
const { runPlan } = require('./commands/plan');
|
|
6
10
|
const { initializeProjectDocs } = require('./lib/init-docs');
|
|
7
11
|
const { checkPrReadiness, checkScope, checkSliceReadiness } = require('./lib/readiness');
|
|
8
12
|
const { cleanupSlice, refreshActiveSlicesBoard, startSlice } = require('./lib/lifecycle');
|
|
9
13
|
const { relativePosixPath, resolveTargetRoot } = require('./lib/paths');
|
|
10
14
|
const {
|
|
15
|
+
hasQuiverInitializationEvidence,
|
|
11
16
|
readState,
|
|
12
17
|
updateStateForAnalyze,
|
|
13
18
|
updateStateForMigrate,
|
|
@@ -23,11 +28,16 @@ function printUsage() {
|
|
|
23
28
|
console.log(`Usage:
|
|
24
29
|
npx create-quiver [options]
|
|
25
30
|
npx create-quiver analyze [options]
|
|
31
|
+
npx create-quiver plan [options]
|
|
32
|
+
npx create-quiver graph [options]
|
|
33
|
+
npx create-quiver next [options]
|
|
26
34
|
npx create-quiver migrate [options]
|
|
27
35
|
npx create-quiver doctor [options]
|
|
28
36
|
npx create-quiver start-slice [options] <slice.json>
|
|
29
37
|
npx create-quiver check-slice [options] <slice.json>
|
|
30
38
|
npx create-quiver check-pr <slice.json>
|
|
39
|
+
npx create-quiver check-handoff <handoff.md>
|
|
40
|
+
npx create-quiver new-handoff <spec-slug>
|
|
31
41
|
npx create-quiver cleanup-slice [options] <slice.json>
|
|
32
42
|
npx create-quiver check-scope [options] <slice.json>
|
|
33
43
|
npx create-quiver refresh-active-slices
|
|
@@ -35,6 +45,15 @@ function printUsage() {
|
|
|
35
45
|
Options:
|
|
36
46
|
-n, --name <project-name> Project name to generate
|
|
37
47
|
-d, --dir <target-dir> Target directory to scaffold into or inspect
|
|
48
|
+
--spec <slug> Restrict plan output to one spec
|
|
49
|
+
--format <name> Graph output format (tree, mermaid, dot)
|
|
50
|
+
--show-conflicts Show shared file paths in graph output
|
|
51
|
+
--level <n> Restrict graph output to one level
|
|
52
|
+
--json Emit machine-readable JSON
|
|
53
|
+
--only-ready Show only slices with no pending dependencies
|
|
54
|
+
--all-ready List every ready slice returned by next
|
|
55
|
+
--auto-start Prompt for confirmation and run start-slice on next
|
|
56
|
+
--unicode Prefer Unicode output when supported
|
|
38
57
|
-y, --yes Skip prompts and use the provided inputs
|
|
39
58
|
-h, --help Show this help message
|
|
40
59
|
|
|
@@ -42,11 +61,20 @@ Examples:
|
|
|
42
61
|
npx create-quiver --name "My Project"
|
|
43
62
|
npx create-quiver --name "My Project" --dir ./my-project
|
|
44
63
|
cd ./my-project && npx create-quiver analyze
|
|
64
|
+
cd ./my-project && npx create-quiver plan --json
|
|
65
|
+
cd ./my-project && npx create-quiver graph --show-conflicts
|
|
66
|
+
cd ./my-project && npx create-quiver graph --format mermaid
|
|
67
|
+
cd ./my-project && npx create-quiver graph --format dot
|
|
68
|
+
cd ./my-project && npx create-quiver next
|
|
69
|
+
cd ./my-project && npx create-quiver next --all-ready
|
|
70
|
+
cd ./my-project && npx create-quiver next --auto-start
|
|
45
71
|
cd ./my-project && npx create-quiver migrate
|
|
46
72
|
cd ./my-project && npx create-quiver doctor
|
|
47
73
|
cd ./my-project && npx create-quiver start-slice specs/my-project/slices/slice-01/slice.json
|
|
48
74
|
cd ./my-project && npx create-quiver check-slice specs/my-project/slices/slice-01/slice.json
|
|
49
75
|
cd ./my-project && npx create-quiver check-pr specs/my-project/slices/slice-01/slice.json
|
|
76
|
+
cd ./my-project && npx create-quiver check-handoff specs/my-project/HANDOFF.md
|
|
77
|
+
cd ./my-project && npx create-quiver new-handoff my-spec
|
|
50
78
|
cd ./my-project && npx create-quiver cleanup-slice specs/my-project/slices/slice-01/slice.json
|
|
51
79
|
cd ./my-project && npx create-quiver check-scope specs/my-project/slices/slice-01/slice.json
|
|
52
80
|
cd ./my-project && npx create-quiver refresh-active-slices
|
|
@@ -68,10 +96,19 @@ function parseArgs(argv) {
|
|
|
68
96
|
targetDir: '.',
|
|
69
97
|
strict: false,
|
|
70
98
|
strictOverlap: false,
|
|
99
|
+
json: false,
|
|
100
|
+
onlyReady: false,
|
|
101
|
+
allReady: false,
|
|
102
|
+
autoStart: false,
|
|
103
|
+
specSlug: '',
|
|
104
|
+
format: 'tree',
|
|
105
|
+
showConflicts: false,
|
|
106
|
+
level: null,
|
|
107
|
+
unicode: false,
|
|
71
108
|
};
|
|
72
109
|
|
|
73
110
|
const args = [...argv];
|
|
74
|
-
const commandModes = new Set(['doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'cleanup-slice', 'check-scope', 'refresh-active-slices']);
|
|
111
|
+
const commandModes = new Set(['plan', 'graph', 'next', 'doctor', 'analyze', 'migrate', 'start-slice', 'check-slice', 'check-pr', 'check-handoff', 'new-handoff', 'cleanup-slice', 'check-scope', 'refresh-active-slices']);
|
|
75
112
|
if (commandModes.has(args[0])) {
|
|
76
113
|
result.mode = args[0];
|
|
77
114
|
args.shift();
|
|
@@ -84,6 +121,12 @@ function parseArgs(argv) {
|
|
|
84
121
|
} else if (args[0] === '--doctor') {
|
|
85
122
|
result.mode = 'doctor';
|
|
86
123
|
args.shift();
|
|
124
|
+
} else if (args[0] === '--check-handoff') {
|
|
125
|
+
result.mode = 'check-handoff';
|
|
126
|
+
args.shift();
|
|
127
|
+
} else if (args[0] === '--new-handoff') {
|
|
128
|
+
result.mode = 'new-handoff';
|
|
129
|
+
args.shift();
|
|
87
130
|
}
|
|
88
131
|
|
|
89
132
|
const positional = [];
|
|
@@ -111,6 +154,16 @@ function parseArgs(argv) {
|
|
|
111
154
|
continue;
|
|
112
155
|
}
|
|
113
156
|
|
|
157
|
+
if (arg === '--check-handoff') {
|
|
158
|
+
result.mode = 'check-handoff';
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (arg === '--new-handoff') {
|
|
163
|
+
result.mode = 'new-handoff';
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
114
167
|
if (arg === '--allow-draft') {
|
|
115
168
|
result.allowDraft = true;
|
|
116
169
|
continue;
|
|
@@ -141,6 +194,67 @@ function parseArgs(argv) {
|
|
|
141
194
|
continue;
|
|
142
195
|
}
|
|
143
196
|
|
|
197
|
+
if (arg === '--json') {
|
|
198
|
+
result.json = true;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (arg === '--show-conflicts') {
|
|
203
|
+
result.showConflicts = true;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (arg === '--format') {
|
|
208
|
+
const value = args[++index];
|
|
209
|
+
if (!value) {
|
|
210
|
+
throw new Error(formatError('missing value for --format'));
|
|
211
|
+
}
|
|
212
|
+
result.format = value;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (arg === '--level') {
|
|
217
|
+
const value = args[++index];
|
|
218
|
+
if (typeof value === 'undefined') {
|
|
219
|
+
throw new Error(formatError('missing value for --level'));
|
|
220
|
+
}
|
|
221
|
+
const parsed = Number.parseInt(value, 10);
|
|
222
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
223
|
+
throw new Error(formatError('invalid value for --level'));
|
|
224
|
+
}
|
|
225
|
+
result.level = parsed;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (arg === '--only-ready') {
|
|
230
|
+
result.onlyReady = true;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (arg === '--all-ready') {
|
|
235
|
+
result.allReady = true;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (arg === '--auto-start') {
|
|
240
|
+
result.autoStart = true;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (arg === '--unicode') {
|
|
245
|
+
result.unicode = true;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (arg === '--spec') {
|
|
250
|
+
const value = args[++index];
|
|
251
|
+
if (!value) {
|
|
252
|
+
throw new Error(formatError('missing value for --spec'));
|
|
253
|
+
}
|
|
254
|
+
result.specSlug = value;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
144
258
|
if (arg === '--gate') {
|
|
145
259
|
const value = args[++index];
|
|
146
260
|
if (!value) {
|
|
@@ -183,6 +297,10 @@ function parseArgs(argv) {
|
|
|
183
297
|
if (positional.length > 0) {
|
|
184
298
|
result.targetDir = positional.shift();
|
|
185
299
|
}
|
|
300
|
+
} else if (result.mode === 'plan') {
|
|
301
|
+
if (positional.length > 0) {
|
|
302
|
+
throw new Error(formatError('plan does not accept positional arguments; use --spec <slug>'));
|
|
303
|
+
}
|
|
186
304
|
} else if (result.mode === 'refresh-active-slices') {
|
|
187
305
|
if (positional.length > 0) {
|
|
188
306
|
throw new Error(formatError('refresh-active-slices does not accept positional arguments'));
|
|
@@ -1022,6 +1140,10 @@ function runMigrate(targetDir) {
|
|
|
1022
1140
|
throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
|
|
1023
1141
|
}
|
|
1024
1142
|
|
|
1143
|
+
if (!hasQuiverInitializationEvidence(projectRoot)) {
|
|
1144
|
+
throw new Error(formatError('migrate requires a project previously initialized by Quiver.\nRun: npx create-quiver --name "Project Name"'));
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1025
1147
|
const packageJson = loadPackageJson(projectRoot);
|
|
1026
1148
|
const projectName = packageJson.name || path.basename(projectRoot) || 'Quiver Project';
|
|
1027
1149
|
const packageRoot = path.resolve(__dirname, '../..');
|
|
@@ -1052,6 +1174,10 @@ function runDoctor(targetDir) {
|
|
|
1052
1174
|
throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
|
|
1053
1175
|
}
|
|
1054
1176
|
|
|
1177
|
+
if (!hasQuiverInitializationEvidence(projectRoot)) {
|
|
1178
|
+
throw new Error(formatError('doctor requires a project previously initialized by Quiver.\nRun init first: npx create-quiver --name "Project Name"'));
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1055
1181
|
const generatedSpecs = listGeneratedSpecDirs(projectRoot);
|
|
1056
1182
|
if (generatedSpecs.length !== 1) {
|
|
1057
1183
|
throw new Error(formatError(`expected exactly one generated spec directory, found ${generatedSpecs.length || 0}`));
|
|
@@ -1144,10 +1270,11 @@ function runDoctor(targetDir) {
|
|
|
1144
1270
|
if (!hasQuiverState) {
|
|
1145
1271
|
console.log('- Run migration first: npx create-quiver migrate');
|
|
1146
1272
|
} else if (!hasScanArtifacts) {
|
|
1147
|
-
|
|
1273
|
+
console.log('- Analyze the project first: npx create-quiver analyze');
|
|
1148
1274
|
} else {
|
|
1149
1275
|
console.log('- Ask your AI agent: Read AGENTS.md, then docs/AI_ONBOARDING_PROMPT.md and execute it.');
|
|
1150
1276
|
}
|
|
1277
|
+
console.log('- Check the next ready slice: npx create-quiver next');
|
|
1151
1278
|
console.log(`- Start a slice: npx create-quiver start-slice specs/${projectSlug}/slices/slice-template/slice.json`);
|
|
1152
1279
|
console.log(`- Validate a slice: npx create-quiver check-slice specs/${projectSlug}/slices/slice-template/slice.json`);
|
|
1153
1280
|
console.log(`- Validate the PR gate: npx create-quiver check-pr specs/${projectSlug}/slices/slice-template/slice.json`);
|
|
@@ -1177,6 +1304,37 @@ async function run(argv) {
|
|
|
1177
1304
|
return;
|
|
1178
1305
|
}
|
|
1179
1306
|
|
|
1307
|
+
if (args.mode === 'plan') {
|
|
1308
|
+
runPlan(process.cwd(), {
|
|
1309
|
+
json: args.json,
|
|
1310
|
+
onlyReady: args.onlyReady,
|
|
1311
|
+
specSlug: args.specSlug,
|
|
1312
|
+
unicode: args.unicode,
|
|
1313
|
+
});
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
if (args.mode === 'graph') {
|
|
1318
|
+
runGraph(process.cwd(), {
|
|
1319
|
+
format: args.format,
|
|
1320
|
+
json: args.json,
|
|
1321
|
+
level: args.level,
|
|
1322
|
+
showConflicts: args.showConflicts,
|
|
1323
|
+
unicode: args.unicode,
|
|
1324
|
+
});
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
if (args.mode === 'next') {
|
|
1329
|
+
await runNext(process.cwd(), {
|
|
1330
|
+
allReady: args.allReady,
|
|
1331
|
+
autoStart: args.autoStart,
|
|
1332
|
+
json: args.json,
|
|
1333
|
+
specSlug: args.specSlug,
|
|
1334
|
+
});
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1180
1338
|
if (args.mode === 'migrate') {
|
|
1181
1339
|
runMigrate(args.targetDir);
|
|
1182
1340
|
return;
|
|
@@ -1205,6 +1363,25 @@ async function run(argv) {
|
|
|
1205
1363
|
return;
|
|
1206
1364
|
}
|
|
1207
1365
|
|
|
1366
|
+
if (args.mode === 'check-handoff') {
|
|
1367
|
+
const repoRoot = process.cwd();
|
|
1368
|
+
const handoffInput = args.targetDir;
|
|
1369
|
+
if (!handoffInput || handoffInput === '.') {
|
|
1370
|
+
throw new Error(formatError('missing handoff path. Use: npx create-quiver check-handoff specs/<spec-slug>/HANDOFF.md'));
|
|
1371
|
+
}
|
|
1372
|
+
const resolved = checkHandoff(handoffInput, repoRoot);
|
|
1373
|
+
console.log(`PASS: Handoff validated at ${resolved.relativePath}`);
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
if (args.mode === 'new-handoff') {
|
|
1378
|
+
const repoRoot = process.cwd();
|
|
1379
|
+
const handoffSlug = args.targetDir;
|
|
1380
|
+
const resolved = scaffoldHandoff(handoffSlug, repoRoot);
|
|
1381
|
+
console.log(`PASS: Handoff scaffolded at ${resolved.relativePath}`);
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1208
1385
|
if (args.mode === 'cleanup-slice') {
|
|
1209
1386
|
cleanupSlice(path.resolve(process.cwd(), args.targetDir), {
|
|
1210
1387
|
closeBaseline: args.closeBaseline,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const REQUIRED_HEADINGS = [
|
|
5
|
+
'## Background',
|
|
6
|
+
'## What you will change',
|
|
7
|
+
'## Validation checklist',
|
|
8
|
+
'## Out of scope',
|
|
9
|
+
'## Expected deliverable',
|
|
10
|
+
'## Constraints',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const HANDOFF_TEMPLATE_PATH = path.resolve(__dirname, '..', '..', '..', 'specs', '[project-name]', 'HANDOFF.md.template');
|
|
14
|
+
|
|
15
|
+
function normalizePosixPath(filePath, pathLib = path) {
|
|
16
|
+
return filePath.split(pathLib.sep).join('/');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveHandoffPath(repoRoot, handoffInput, pathLib = path) {
|
|
20
|
+
const absolutePath = pathLib.resolve(repoRoot, handoffInput);
|
|
21
|
+
const relativePath = normalizePosixPath(pathLib.relative(repoRoot, absolutePath), pathLib);
|
|
22
|
+
|
|
23
|
+
if (relativePath.startsWith('..') || pathLib.isAbsolute(relativePath)) {
|
|
24
|
+
throw new Error(`create-quiver: handoff must live at specs/<spec-slug>/HANDOFF.md (got ${normalizePosixPath(handoffInput, pathLib)})`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const match = relativePath.match(/^specs\/([^/]+)\/HANDOFF\.md$/);
|
|
28
|
+
if (!match) {
|
|
29
|
+
throw new Error(`create-quiver: handoff must live at specs/<spec-slug>/HANDOFF.md (got ${relativePath})`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
absolutePath,
|
|
34
|
+
relativePath,
|
|
35
|
+
specSlug: match[1],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readHandoffSections(text) {
|
|
40
|
+
return String(text || '')
|
|
41
|
+
.split(/\r?\n/)
|
|
42
|
+
.map((line) => line.trim())
|
|
43
|
+
.filter((line) => line.startsWith('## '))
|
|
44
|
+
.map((line) => line.replace(/\s+$/, ''));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function validateHandoffSections(text) {
|
|
48
|
+
const sections = new Set(readHandoffSections(text));
|
|
49
|
+
return REQUIRED_HEADINGS.filter((heading) => !sections.has(heading));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function checkHandoff(handoffInput, repoRoot = process.cwd()) {
|
|
53
|
+
const resolved = resolveHandoffPath(repoRoot, handoffInput);
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(resolved.absolutePath)) {
|
|
56
|
+
throw new Error(`create-quiver: missing handoff file: ${resolved.relativePath}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const text = fs.readFileSync(resolved.absolutePath, 'utf8');
|
|
60
|
+
const missingSections = validateHandoffSections(text);
|
|
61
|
+
if (missingSections.length > 0) {
|
|
62
|
+
throw new Error(`create-quiver: handoff is missing required sections: ${missingSections.join(', ')}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return resolved;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function scaffoldHandoff(specSlug, repoRoot = process.cwd()) {
|
|
69
|
+
const trimmedSlug = String(specSlug || '').trim();
|
|
70
|
+
if (!trimmedSlug) {
|
|
71
|
+
throw new Error('create-quiver: missing handoff slug. Use: npx create-quiver new-handoff <spec-slug>');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!fs.existsSync(HANDOFF_TEMPLATE_PATH)) {
|
|
75
|
+
throw new Error('create-quiver: missing handoff template at specs/[project-name]/HANDOFF.md.template');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const resolved = resolveHandoffPath(repoRoot, path.join('specs', trimmedSlug, 'HANDOFF.md'));
|
|
79
|
+
if (fs.existsSync(resolved.absolutePath)) {
|
|
80
|
+
throw new Error(`create-quiver: handoff already exists at ${resolved.relativePath}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const templateText = fs.readFileSync(HANDOFF_TEMPLATE_PATH, 'utf8');
|
|
84
|
+
const projectName = trimmedSlug.replace(/-/g, ' ');
|
|
85
|
+
const currentDate = new Date().toISOString().slice(0, 10);
|
|
86
|
+
const renderedText = templateText
|
|
87
|
+
.replace(/{{PROJECT_NAME}}/g, projectName)
|
|
88
|
+
.replace(/{{PROJECT_SLUG}}/g, trimmedSlug)
|
|
89
|
+
.replace(/{{FECHA}}/g, currentDate);
|
|
90
|
+
|
|
91
|
+
fs.mkdirSync(path.dirname(resolved.absolutePath), { recursive: true });
|
|
92
|
+
fs.writeFileSync(resolved.absolutePath, renderedText);
|
|
93
|
+
|
|
94
|
+
return checkHandoff(resolved.relativePath, repoRoot);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = {
|
|
98
|
+
REQUIRED_HEADINGS,
|
|
99
|
+
checkHandoff,
|
|
100
|
+
readHandoffSections,
|
|
101
|
+
scaffoldHandoff,
|
|
102
|
+
resolveHandoffPath,
|
|
103
|
+
validateHandoffSections,
|
|
104
|
+
};
|
|
@@ -61,6 +61,9 @@ function renderTemplate(text, replacements) {
|
|
|
61
61
|
.replace(/{{PRIMARY_DEV}}/g, replacements.primaryDev || 'not defined')
|
|
62
62
|
.replace(/{{PRIMARY_TEST}}/g, replacements.primaryTest || 'not defined')
|
|
63
63
|
.replace(/{{ANALYZE_COMMAND}}/g, replacements.analyzeCommand || 'npx create-quiver analyze')
|
|
64
|
+
.replace(/{{PLAN_COMMAND}}/g, replacements.planCommand || 'npx create-quiver plan')
|
|
65
|
+
.replace(/{{GRAPH_COMMAND}}/g, replacements.graphCommand || 'npx create-quiver graph')
|
|
66
|
+
.replace(/{{NEXT_COMMAND}}/g, replacements.nextCommand || 'npx create-quiver next')
|
|
64
67
|
.replace(/{{DOCTOR_COMMAND}}/g, replacements.doctorCommand || 'npx create-quiver doctor')
|
|
65
68
|
.replace(/{{START_SLICE_COMMAND}}/g, replacements.startSliceCommand || 'npx create-quiver start-slice <slice.json>')
|
|
66
69
|
.replace(/{{CHECK_SLICE_COMMAND}}/g, replacements.checkSliceCommand || 'npx create-quiver check-slice <slice.json>')
|
|
@@ -227,12 +230,22 @@ function buildReadme(projectName, projectSlug) {
|
|
|
227
230
|
|
|
228
231
|
## Quick Start
|
|
229
232
|
|
|
230
|
-
Run Quiver from this project root. Do not install it globally.
|
|
233
|
+
Run Quiver from this project root. Do not install it globally.
|
|
231
234
|
|
|
232
235
|
\`\`\`bash
|
|
233
236
|
npm install
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
{{ANALYZE_COMMAND}}
|
|
238
|
+
{{PLAN_COMMAND}}
|
|
239
|
+
{{GRAPH_COMMAND}}
|
|
240
|
+
{{DOCTOR_COMMAND}}
|
|
241
|
+
{{NEXT_COMMAND}}
|
|
242
|
+
\`\`\`
|
|
243
|
+
|
|
244
|
+
Exportable graph formats are available when you need a PR-ready Mermaid block or Graphviz source:
|
|
245
|
+
|
|
246
|
+
\`\`\`bash
|
|
247
|
+
{{GRAPH_COMMAND}} --format mermaid
|
|
248
|
+
{{GRAPH_COMMAND}} --format dot
|
|
236
249
|
\`\`\`
|
|
237
250
|
|
|
238
251
|
If this project needs a pinned Quiver version, install it as a devDependency:
|
|
@@ -251,17 +264,28 @@ The generated project includes \`quiver:*\` npm scripts that call the Node CLI a
|
|
|
251
264
|
|
|
252
265
|
\`\`\`bash
|
|
253
266
|
npm run quiver:analyze
|
|
267
|
+
npm run quiver:plan
|
|
268
|
+
npm run quiver:graph
|
|
269
|
+
npm run quiver:next
|
|
254
270
|
npm run quiver:doctor
|
|
255
271
|
npm run quiver:migrate
|
|
256
272
|
npm run quiver:start-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
257
273
|
npm run quiver:check-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
258
274
|
npm run quiver:check-pr -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
275
|
+
npm run quiver:check-handoff -- specs/${projectSlug}/HANDOFF.md
|
|
259
276
|
npm run quiver:cleanup-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
260
277
|
npm run quiver:check-scope -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
261
278
|
npm run quiver:refresh-active-slices
|
|
262
279
|
\`\`\`
|
|
263
280
|
|
|
281
|
+
The \`quiver:graph\` script prints the tree view by default; use \`npx create-quiver graph --format mermaid\` for PR-ready Markdown and \`--format dot\` when you want Graphviz source.
|
|
282
|
+
The \`quiver:next\` script points to the next ready slice and can auto-start it behind a confirmation prompt.
|
|
283
|
+
Use \`npx create-quiver next --all-ready\` when you want the full ready level instead of a single suggestion.
|
|
264
284
|
The legacy Bash wrappers remain in \`tools/scripts/\` for compatibility, but new project-level automation should prefer the \`quiver:*\` scripts and the direct \`npx create-quiver ...\` commands below.
|
|
285
|
+
\`npm run quiver:migrate\` is only for projects that were already initialized by Quiver.
|
|
286
|
+
\`npm run check-handoff -- specs/${projectSlug}/HANDOFF.md\` is available as a legacy-friendly alias for the handoff validator.
|
|
287
|
+
If a new bounded transfer is needed, scaffold \`specs/${projectSlug}/HANDOFF.md\` with \`npx create-quiver new-handoff ${projectSlug}\` and validate it with \`npx create-quiver check-handoff specs/${projectSlug}/HANDOFF.md\`.
|
|
288
|
+
For exceptional context transfers between agents or phases, a dedicated \`HANDOFF.md\` can live alongside the usual spec and docs files.
|
|
265
289
|
|
|
266
290
|
## Cross-Platform Support
|
|
267
291
|
|
|
@@ -274,8 +298,19 @@ If the project already existed before this Quiver version, upgrade it from the p
|
|
|
274
298
|
\`\`\`bash
|
|
275
299
|
cd /path/to/your-project
|
|
276
300
|
npx create-quiver migrate
|
|
277
|
-
|
|
278
|
-
|
|
301
|
+
{{ANALYZE_COMMAND}}
|
|
302
|
+
{{PLAN_COMMAND}}
|
|
303
|
+
{{GRAPH_COMMAND}}
|
|
304
|
+
{{NEXT_COMMAND}}
|
|
305
|
+
{{DOCTOR_COMMAND}}
|
|
306
|
+
\`\`\`
|
|
307
|
+
|
|
308
|
+
Use \`{{GRAPH_COMMAND}} --format mermaid\` for GitHub-friendly graph embeds or \`{{GRAPH_COMMAND}} --format dot\` for Graphviz pipelines.
|
|
309
|
+
|
|
310
|
+
If the project never ran Quiver initialization before, do not use \`migrate\` as bootstrap. Run:
|
|
311
|
+
|
|
312
|
+
\`\`\`bash
|
|
313
|
+
npx create-quiver --name "Project Name"
|
|
279
314
|
\`\`\`
|
|
280
315
|
|
|
281
316
|
If your team prefers a pinned local dependency, update the package first and then run the same flow:
|
|
@@ -283,10 +318,15 @@ If your team prefers a pinned local dependency, update the package first and the
|
|
|
283
318
|
\`\`\`bash
|
|
284
319
|
npm install --save-dev create-quiver@latest
|
|
285
320
|
npx create-quiver migrate
|
|
286
|
-
|
|
287
|
-
|
|
321
|
+
{{ANALYZE_COMMAND}}
|
|
322
|
+
{{PLAN_COMMAND}}
|
|
323
|
+
{{GRAPH_COMMAND}}
|
|
324
|
+
{{NEXT_COMMAND}}
|
|
325
|
+
{{DOCTOR_COMMAND}}
|
|
288
326
|
\`\`\`
|
|
289
327
|
|
|
328
|
+
The tree output remains the default, but Mermaid and DOT are available on demand for exported docs and slide decks.
|
|
329
|
+
|
|
290
330
|
## AI Context Onboarding
|
|
291
331
|
|
|
292
332
|
Read \`AGENTS.md\` first, then open \`docs/AI_ONBOARDING_PROMPT.md\` after analysis.
|
|
@@ -300,6 +340,7 @@ Prepare the project context docs and report assumptions, risks, and files change
|
|
|
300
340
|
\`\`\`
|
|
301
341
|
|
|
302
342
|
Review the AI changes to docs/AI_CONTEXT.md, docs/CONTEXTO.md, docs/STATUS.md, and specs/${projectSlug}/SPEC.md before starting implementation work. Use \`docs/PROJECT_MAP.md\` for stack and command details.
|
|
343
|
+
If the work was explicitly transferred through a handoff artifact, read \`specs/${projectSlug}/HANDOFF.md\` before implementation.
|
|
303
344
|
|
|
304
345
|
## Decision Log
|
|
305
346
|
|
|
@@ -309,15 +350,21 @@ Record durable decisions in \`docs/DECISIONS.md\` so future AI agents do not re-
|
|
|
309
350
|
|
|
310
351
|
1. Review or refine specs/${projectSlug}/SPEC.md.
|
|
311
352
|
2. Create the first slice from specs/${projectSlug}/slices/slice-template/slice.json.
|
|
312
|
-
3.
|
|
313
|
-
4.
|
|
314
|
-
5.
|
|
353
|
+
3. Review the plan with \`{{PLAN_COMMAND}}\` or \`npm run quiver:plan\`.
|
|
354
|
+
4. Inspect parallel lots with \`{{GRAPH_COMMAND}}\` or \`npm run quiver:graph\`.
|
|
355
|
+
5. Check the next ready slice with \`{{NEXT_COMMAND}}\` or \`npm run quiver:next\`.
|
|
356
|
+
6. Start work with \`{{START_SLICE_COMMAND}}\` or \`npm run quiver:start-slice -- <slice.json>\`.
|
|
357
|
+
7. Make one commit per slice.
|
|
358
|
+
8. Open one PR per spec.
|
|
315
359
|
|
|
316
360
|
## Verification Checklist
|
|
317
361
|
|
|
318
362
|
- [ ] npm install completes
|
|
319
|
-
- [ ]
|
|
320
|
-
- [ ]
|
|
363
|
+
- [ ] {{ANALYZE_COMMAND}} completes
|
|
364
|
+
- [ ] {{PLAN_COMMAND}} completes
|
|
365
|
+
- [ ] {{GRAPH_COMMAND}} completes
|
|
366
|
+
- [ ] {{NEXT_COMMAND}} completes
|
|
367
|
+
- [ ] {{DOCTOR_COMMAND}} completes
|
|
321
368
|
- [ ] AI agent executed docs/AI_ONBOARDING_PROMPT.md
|
|
322
369
|
- [ ] Context docs were reviewed before the first slice
|
|
323
370
|
|
|
@@ -326,6 +373,9 @@ Record durable decisions in \`docs/DECISIONS.md\` so future AI agents do not re-
|
|
|
326
373
|
- [AI Context](./docs/AI_CONTEXT.md) - Contexto resumido para IA
|
|
327
374
|
- [Decision Log](./docs/DECISIONS.md) - Decisiones durables del proyecto
|
|
328
375
|
- [AI Onboarding Prompt](./docs/AI_ONBOARDING_PROMPT.md) - Handoff exacto para agentes después del análisis
|
|
376
|
+
- [Handoff](./specs/${projectSlug}/HANDOFF.md) - Transferencia excepcional entre agentes o fases
|
|
377
|
+
- [Check Handoff](./docs/WORKFLOW.md) - Valida el handoff con \`npx create-quiver check-handoff\`
|
|
378
|
+
- [Commands](./docs/COMMANDS.md) - Tabla canónica de comandos de orquestación
|
|
329
379
|
- [Contexto](./docs/CONTEXTO.md) - Qué es ${projectName}
|
|
330
380
|
- [Workflow](./docs/WORKFLOW.md) - Cómo implementar
|
|
331
381
|
- [Support Matrix](./docs/SUPPORT_MATRIX.md) - Qué entornos están soportados
|
|
@@ -382,6 +432,7 @@ function initializeProjectDocs(options) {
|
|
|
382
432
|
});
|
|
383
433
|
const templateCopies = [
|
|
384
434
|
['docs/INDEX.md.template', 'docs/INDEX.md'],
|
|
435
|
+
['docs/COMMANDS.md.template', 'docs/COMMANDS.md'],
|
|
385
436
|
['docs/DECISIONS.md.template', 'docs/DECISIONS.md'],
|
|
386
437
|
['docs/AI_CONTEXT.md.template', 'docs/AI_CONTEXT.md', frontMatterFor('Agent-facing project context pack', 'onboarding, implementation, review')],
|
|
387
438
|
['docs/AI_ONBOARDING_PROMPT.md.template', 'docs/AI_ONBOARDING_PROMPT.md', frontMatterFor('AI onboarding handoff prompt', 'onboarding after analysis')],
|
|
@@ -398,6 +449,7 @@ function initializeProjectDocs(options) {
|
|
|
398
449
|
['docs/TESTING_GUIDE_FOR_AI.md.template', 'docs/TESTING_GUIDE_FOR_AI.md'],
|
|
399
450
|
['docs/ai/LESSONS.md.template', 'docs/ai/LESSONS.md', frontMatterFor('Slice learnings log', 'after slice completion')],
|
|
400
451
|
['specs/[project-name]/SPEC.md.template', `specs/${replacements.projectSlug}/SPEC.md`],
|
|
452
|
+
['specs/[project-name]/HANDOFF.md.template', `specs/${replacements.projectSlug}/HANDOFF.md`],
|
|
401
453
|
['specs/[project-name]/STATUS.md.template', `specs/${replacements.projectSlug}/STATUS.md`],
|
|
402
454
|
['specs/[project-name]/EVIDENCE_REPORT.md.template', `specs/${replacements.projectSlug}/EVIDENCE_REPORT.md`],
|
|
403
455
|
['specs/[project-name]/slices/slice-template/slice.json', `specs/${replacements.projectSlug}/slices/slice-template/slice.json`],
|
|
@@ -479,6 +531,9 @@ function initializeProjectDocs(options) {
|
|
|
479
531
|
primaryDev: packageScripts.dev || packageScripts.start || 'not defined',
|
|
480
532
|
primaryTest: packageScripts.test || 'not defined',
|
|
481
533
|
analyzeCommand: 'npx create-quiver analyze',
|
|
534
|
+
planCommand: 'npx create-quiver plan',
|
|
535
|
+
graphCommand: 'npx create-quiver graph',
|
|
536
|
+
nextCommand: 'npx create-quiver next',
|
|
482
537
|
doctorCommand: 'npx create-quiver doctor',
|
|
483
538
|
startSliceCommand: 'npx create-quiver start-slice <slice.json>',
|
|
484
539
|
checkSliceCommand: 'npx create-quiver check-slice <slice.json>',
|
|
@@ -492,6 +547,9 @@ function initializeProjectDocs(options) {
|
|
|
492
547
|
['docs/QUICK.md.template', 'docs/ai/QUICK.md'],
|
|
493
548
|
['docs/STANDARD.md.template', 'docs/ai/STANDARD.md'],
|
|
494
549
|
['docs/DEEP.md.template', 'docs/ai/DEEP.md'],
|
|
550
|
+
['docs/examples/plan.md.template', 'docs/examples/plan.md'],
|
|
551
|
+
['docs/examples/graph.md.template', 'docs/examples/graph.md'],
|
|
552
|
+
['docs/examples/next.md.template', 'docs/examples/next.md'],
|
|
495
553
|
];
|
|
496
554
|
|
|
497
555
|
for (const [source, destination] of tierCopies) {
|
|
@@ -597,7 +655,7 @@ function initializeProjectDocs(options) {
|
|
|
597
655
|
|
|
598
656
|
const readmePath = path.join(projectRoot, 'README.md');
|
|
599
657
|
if (!fs.existsSync(readmePath)) {
|
|
600
|
-
fs.writeFileSync(readmePath, `${buildReadme(projectName, replacements.projectSlug)}\n`);
|
|
658
|
+
fs.writeFileSync(readmePath, `${renderTemplate(buildReadme(projectName, replacements.projectSlug), replacements)}\n`);
|
|
601
659
|
operations.push({ source: 'README.md template', destination: 'README.md', result: 'created' });
|
|
602
660
|
} else {
|
|
603
661
|
operations.push({ source: 'README.md template', destination: 'README.md', result: 'skipped' });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function stripJsonComments(text) {
|
|
2
|
+
return String(text || '')
|
|
3
|
+
.replace(/^\s*\/\/.*$/gm, '')
|
|
4
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function parseJsonWithComments(text) {
|
|
8
|
+
return JSON.parse(stripJsonComments(text));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
parseJsonWithComments,
|
|
13
|
+
stripJsonComments,
|
|
14
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { branchDelete, catFileExists, currentBranch, fetchBranch, fetchRemote, hasLocalBranch, hasRemoteBranch, lsRemoteHeads, mergeBaseIsAncestor, revListCount, runGit, statusPorcelain, worktreeAdd, worktreeList, worktreePrune, worktreeRemove } = require('./git');
|
|
4
|
+
const { parseJsonWithComments } = require('./json');
|
|
4
5
|
const { writeFrontMatter } = require('./init-docs');
|
|
5
6
|
const { resolveTargetRoot } = require('./paths');
|
|
6
7
|
const { activeSlicePath, renderActiveSlice, resolveSliceContext, safeBranchName, toAlias, validateSliceMetaForStart, worktreesRootForRepo } = require('./slice');
|
|
@@ -131,7 +132,7 @@ function refreshActiveSlicesBoard(repoRoot) {
|
|
|
131
132
|
continue;
|
|
132
133
|
}
|
|
133
134
|
if (entry.isFile() && entry.name === 'slice.json' && fullPath.includes(`${path.sep}slices${path.sep}`)) {
|
|
134
|
-
const json =
|
|
135
|
+
const json = parseJsonWithComments(fs.readFileSync(fullPath, 'utf8'));
|
|
135
136
|
const relPath = path.relative(repoRoot, fullPath);
|
|
136
137
|
const parts = relPath.split(path.sep);
|
|
137
138
|
const specFamily = parts[0];
|
|
@@ -181,7 +182,7 @@ function refreshActiveSlicesBoard(repoRoot) {
|
|
|
181
182
|
const liveSlicePath = path.join(worktreePath, slice.relPath);
|
|
182
183
|
if (fs.existsSync(liveSlicePath)) {
|
|
183
184
|
try {
|
|
184
|
-
const liveJson =
|
|
185
|
+
const liveJson = parseJsonWithComments(fs.readFileSync(liveSlicePath, 'utf8'));
|
|
185
186
|
liveStatus = liveJson.status || liveStatus;
|
|
186
187
|
} catch {
|
|
187
188
|
// ignore
|