@girardmedia/bootspring 2.1.0 → 2.1.2
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/cli/build.js
CHANGED
|
@@ -165,6 +165,10 @@ async function run(args) {
|
|
|
165
165
|
case 'all':
|
|
166
166
|
return buildLoop(projectRoot, parsedArgs);
|
|
167
167
|
|
|
168
|
+
case 'migrate-todo':
|
|
169
|
+
case 'migrate':
|
|
170
|
+
return migrateTodoFormat(projectRoot, parsedArgs);
|
|
171
|
+
|
|
168
172
|
case 'backfill':
|
|
169
173
|
case 'recover':
|
|
170
174
|
case 'hydrate':
|
|
@@ -350,7 +354,7 @@ ${c.bold}Phase:${c.reset} ${nextTask.phase || 'MVP'}
|
|
|
350
354
|
${c.bold}Source:${c.reset} ${nextTask.source || 'PRD.md'}
|
|
351
355
|
|
|
352
356
|
${c.cyan}${c.bold}Tell Claude Code:${c.reset}
|
|
353
|
-
"Read planning/
|
|
357
|
+
"Read planning/TODO.md, find ${nextTask.id}, and implement it"
|
|
354
358
|
|
|
355
359
|
${c.dim}When done, run: bootspring build done${c.reset}
|
|
356
360
|
`);
|
|
@@ -462,7 +466,7 @@ async function markTaskDone(projectRoot, _args = {}) {
|
|
|
462
466
|
console.log(`
|
|
463
467
|
${c.cyan}${c.bold}▶ Next: ${nextTask.title}${c.reset} (${nextTask.id})
|
|
464
468
|
|
|
465
|
-
${c.bold}Continue building:${c.reset} Read planning/
|
|
469
|
+
${c.bold}Continue building:${c.reset} Read planning/TODO.md, implement ${nextTask.id}
|
|
466
470
|
${c.dim}Then run: bootspring build done${c.reset}
|
|
467
471
|
`);
|
|
468
472
|
} else {
|
|
@@ -545,7 +549,7 @@ ${c.bold}Progress:${c.reset} ${stats.completed}/${stats.total} tasks (${Math.rou
|
|
|
545
549
|
${c.bold}Git Mode:${c.reset} ${noCommit ? 'Disabled' : noPush ? 'Commit only' : 'Commit + Push'}
|
|
546
550
|
|
|
547
551
|
${c.bold}Workflow:${c.reset}
|
|
548
|
-
1. Read planning/
|
|
552
|
+
1. Read planning/TODO.md, find ${currentTask?.id || 'current task'}
|
|
549
553
|
2. Implement the task
|
|
550
554
|
3. Run: ${c.cyan}bootspring build done${c.reset}${!noCommit ? `
|
|
551
555
|
(Auto-commits: "feat(${currentTask?.id}): ${currentTask?.title?.slice(0, 40) || 'task'}...")${!noPush ? `
|
|
@@ -1225,6 +1229,171 @@ ${c.bold}Next steps:${c.reset}
|
|
|
1225
1229
|
`);
|
|
1226
1230
|
}
|
|
1227
1231
|
|
|
1232
|
+
/**
|
|
1233
|
+
* Extract non-task supplementary content from TASK_QUEUE.md.
|
|
1234
|
+
* Captures context notes, architecture diagrams, integration guidelines,
|
|
1235
|
+
* and any section that isn't the task table or per-task detail blocks.
|
|
1236
|
+
*/
|
|
1237
|
+
function extractSupplementaryContent(queueContent) {
|
|
1238
|
+
const lines = queueContent.split('\n');
|
|
1239
|
+
const sections = [];
|
|
1240
|
+
let currentSection = null;
|
|
1241
|
+
let inTaskTable = false;
|
|
1242
|
+
let inTaskDetail = false;
|
|
1243
|
+
|
|
1244
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1245
|
+
const line = lines[i];
|
|
1246
|
+
|
|
1247
|
+
// Detect task table (starts with | Position | or | # |)
|
|
1248
|
+
if (/^\|\s*(Position|#)\s*\|/.test(line)) {
|
|
1249
|
+
inTaskTable = true;
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
// Still in task table (rows starting with |)
|
|
1253
|
+
if (inTaskTable && /^\|/.test(line)) {
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
if (inTaskTable && !/^\|/.test(line)) {
|
|
1257
|
+
inTaskTable = false;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// Detect per-task detail blocks (### task-XXX: or ### bs-XXX:)
|
|
1261
|
+
if (/^###\s+(task-\d+|bs-\d+)\s*:/.test(line)) {
|
|
1262
|
+
inTaskDetail = true;
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
// End task detail at next ### or ## heading
|
|
1266
|
+
if (inTaskDetail && /^#{2,3}\s+/.test(line) && !/^###\s+(task-\d+|bs-\d+)\s*:/.test(line)) {
|
|
1267
|
+
inTaskDetail = false;
|
|
1268
|
+
}
|
|
1269
|
+
if (inTaskDetail) continue;
|
|
1270
|
+
|
|
1271
|
+
// Skip the header, queue status heading, and generated boilerplate
|
|
1272
|
+
if (/^#\s+.*Implementation Queue/.test(line)) continue;
|
|
1273
|
+
if (/^>\s*(Ordered task queue|Last Updated|Current Version)/.test(line)) continue;
|
|
1274
|
+
if (/^## Queue Status/.test(line)) { inTaskTable = true; continue; }
|
|
1275
|
+
if (/^## Task Details/.test(line)) { inTaskDetail = true; continue; }
|
|
1276
|
+
if (/^\*Generated by/.test(line)) continue;
|
|
1277
|
+
if (/^---$/.test(line.trim())) continue;
|
|
1278
|
+
|
|
1279
|
+
// Detect section headings for supplementary content
|
|
1280
|
+
if (/^##\s+/.test(line) && !/^## (Queue Status|Task Details|Execution Guidelines)/.test(line)) {
|
|
1281
|
+
if (currentSection) sections.push(currentSection);
|
|
1282
|
+
currentSection = { heading: line, lines: [] };
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// Execution Guidelines is useful — keep it
|
|
1287
|
+
if (/^## Execution Guidelines/.test(line)) {
|
|
1288
|
+
if (currentSection) sections.push(currentSection);
|
|
1289
|
+
currentSection = { heading: line, lines: [] };
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
if (currentSection) {
|
|
1294
|
+
currentSection.lines.push(line);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
if (currentSection) sections.push(currentSection);
|
|
1299
|
+
|
|
1300
|
+
// Filter out empty sections and build output
|
|
1301
|
+
const output = sections
|
|
1302
|
+
.filter(s => s.lines.some(l => l.trim().length > 0))
|
|
1303
|
+
.map(s => `${s.heading}\n\n${s.lines.join('\n').trim()}`)
|
|
1304
|
+
.join('\n\n---\n\n');
|
|
1305
|
+
|
|
1306
|
+
return output.trim();
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* Migrate from TASK_QUEUE.md to TODO.md as single source of truth.
|
|
1311
|
+
*/
|
|
1312
|
+
async function migrateTodoFormat(projectRoot, args = {}) {
|
|
1313
|
+
const planningDir = path.join(projectRoot, 'planning');
|
|
1314
|
+
const queuePath = path.join(planningDir, 'TASK_QUEUE.md');
|
|
1315
|
+
const todoPath = path.join(planningDir, 'TODO.md');
|
|
1316
|
+
const keepQueue = Boolean(args['keep-queue'] || args.keepQueue);
|
|
1317
|
+
|
|
1318
|
+
console.log(`
|
|
1319
|
+
${c.cyan}${c.bold}Migrating to TODO.md as Single Source of Truth${c.reset}
|
|
1320
|
+
`);
|
|
1321
|
+
|
|
1322
|
+
// Step 1: Ensure we have build state with tasks
|
|
1323
|
+
let state = buildState.load(projectRoot);
|
|
1324
|
+
|
|
1325
|
+
if (!state || !state.implementationQueue || state.implementationQueue.length === 0) {
|
|
1326
|
+
if (fs.existsSync(queuePath)) {
|
|
1327
|
+
console.log(`${c.dim}No build state — syncing tasks from TASK_QUEUE.md first...${c.reset}`);
|
|
1328
|
+
try {
|
|
1329
|
+
const taskExtractor = require('../core/task-extractor');
|
|
1330
|
+
const result = taskExtractor.syncFromTaskQueue(projectRoot);
|
|
1331
|
+
if (result.total > 0) {
|
|
1332
|
+
console.log(`${c.green}✓${c.reset} Synced ${result.total} tasks from TASK_QUEUE.md`);
|
|
1333
|
+
state = buildState.load(projectRoot);
|
|
1334
|
+
}
|
|
1335
|
+
} catch {
|
|
1336
|
+
// Fall through
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
if (!state || !state.implementationQueue || state.implementationQueue.length === 0) {
|
|
1341
|
+
console.log(`${c.red}No tasks found. Run 'bootspring seed build' first or ensure TASK_QUEUE.md exists.${c.reset}`);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// Step 2: Extract non-task content from TASK_QUEUE.md (context notes, diagrams, guidelines)
|
|
1347
|
+
let supplementaryContent = '';
|
|
1348
|
+
if (fs.existsSync(queuePath)) {
|
|
1349
|
+
const queueContent = fs.readFileSync(queuePath, 'utf-8');
|
|
1350
|
+
supplementaryContent = extractSupplementaryContent(queueContent);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// Step 3: Generate enriched TODO.md with full task metadata
|
|
1354
|
+
const planningTemplate = require('../generators/templates/build-planning.template');
|
|
1355
|
+
let todo = planningTemplate.generateTodo(state.implementationQueue, {
|
|
1356
|
+
projectName: state.projectName
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1359
|
+
// Append supplementary content if any was extracted
|
|
1360
|
+
if (supplementaryContent) {
|
|
1361
|
+
todo += `\n---\n\n## Reference (from TASK_QUEUE.md)\n\n${supplementaryContent}\n`;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
fs.writeFileSync(todoPath, todo);
|
|
1365
|
+
|
|
1366
|
+
const stats = buildState.getStats(projectRoot);
|
|
1367
|
+
console.log(`${c.green}✓${c.reset} Generated TODO.md with ${state.implementationQueue.length} tasks (${stats?.completed || 0} completed)`);
|
|
1368
|
+
if (supplementaryContent) {
|
|
1369
|
+
console.log(`${c.green}✓${c.reset} Preserved supplementary content (context notes, diagrams, guidelines)`);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// Step 4: Archive TASK_QUEUE.md unless --keep-queue
|
|
1373
|
+
if (fs.existsSync(queuePath) && !keepQueue) {
|
|
1374
|
+
const archivePath = path.join(planningDir, 'TASK_QUEUE.md.bak');
|
|
1375
|
+
fs.renameSync(queuePath, archivePath);
|
|
1376
|
+
console.log(`${c.green}✓${c.reset} Archived TASK_QUEUE.md → TASK_QUEUE.md.bak`);
|
|
1377
|
+
} else if (fs.existsSync(queuePath) && keepQueue) {
|
|
1378
|
+
console.log(`${c.dim}Kept TASK_QUEUE.md (--keep-queue)${c.reset}`);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
console.log(`
|
|
1382
|
+
${c.green}${c.bold}Migration complete${c.reset}
|
|
1383
|
+
|
|
1384
|
+
${c.bold}What changed:${c.reset}
|
|
1385
|
+
- ${c.cyan}planning/TODO.md${c.reset} is now the single source of truth
|
|
1386
|
+
- Task IDs, source, description, acceptance criteria, and status are all inline
|
|
1387
|
+
- Non-task content (context notes, diagrams, guidelines) preserved in Reference section
|
|
1388
|
+
- Build loop reads TODO.md instead of TASK_QUEUE.md
|
|
1389
|
+
${!keepQueue && fs.existsSync(path.join(planningDir, 'TASK_QUEUE.md.bak')) ? ` - Old TASK_QUEUE.md archived as TASK_QUEUE.md.bak
|
|
1390
|
+
` : ''}
|
|
1391
|
+
${c.bold}Next steps:${c.reset}
|
|
1392
|
+
Run ${c.cyan}bootspring build status${c.reset} to verify
|
|
1393
|
+
Run ${c.cyan}bootspring build next${c.reset} to continue building
|
|
1394
|
+
`);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1228
1397
|
/**
|
|
1229
1398
|
* Show help
|
|
1230
1399
|
*/
|
|
@@ -1252,6 +1421,7 @@ ${c.bold}Commands:${c.reset}
|
|
|
1252
1421
|
${c.cyan}bootspring build status${c.reset} View detailed progress
|
|
1253
1422
|
${c.cyan}bootspring build task${c.reset} View current task details
|
|
1254
1423
|
${c.cyan}bootspring build plan${c.reset} View the full master plan
|
|
1424
|
+
${c.cyan}bootspring build migrate-todo${c.reset} Migrate TASK_QUEUE.md → TODO.md (single source of truth)
|
|
1255
1425
|
${c.cyan}bootspring build backfill${c.reset} Recover preseed + SEED + TASK_QUEUE artifacts
|
|
1256
1426
|
${c.cyan}bootspring build reset${c.reset} Start over
|
|
1257
1427
|
|
|
@@ -52,7 +52,7 @@ function generate(config) {
|
|
|
52
52
|
sections.push('');
|
|
53
53
|
sections.push('This loops through all tasks:');
|
|
54
54
|
sections.push('1. Run `bootspring build task` (current task comes from `planning/BUILD_STATE.json`)');
|
|
55
|
-
sections.push('2. Read that task\'s acceptance criteria in `planning/
|
|
55
|
+
sections.push('2. Read that task\'s acceptance criteria in `planning/TODO.md`');
|
|
56
56
|
sections.push('3. Implement and validate the task');
|
|
57
57
|
sections.push('4. Run `bootspring build done` (auto-queues next)');
|
|
58
58
|
sections.push('');
|
|
@@ -153,8 +153,7 @@ function generatePlanningAgents(_config) {
|
|
|
153
153
|
sections.push('');
|
|
154
154
|
sections.push('| File | Purpose |');
|
|
155
155
|
sections.push('|------|---------|');
|
|
156
|
-
sections.push('| `
|
|
157
|
-
sections.push('| `TODO.md` | Full task checklist |');
|
|
156
|
+
sections.push('| `TODO.md` | **Source of truth** — task checklist with acceptance criteria |');
|
|
158
157
|
sections.push('| `BUILD_STATE.json` | Build state and current task ID (do not edit) |');
|
|
159
158
|
sections.push('| `CONTEXT.md` | Build context summary |');
|
|
160
159
|
sections.push('');
|
|
@@ -162,7 +161,7 @@ function generatePlanningAgents(_config) {
|
|
|
162
161
|
sections.push('## Workflow');
|
|
163
162
|
sections.push('');
|
|
164
163
|
sections.push('1. Run `bootspring build task` to identify the current task ID');
|
|
165
|
-
sections.push('2. Read the matching
|
|
164
|
+
sections.push('2. Read the matching task in `TODO.md` for acceptance criteria');
|
|
166
165
|
sections.push('3. Implement in the main codebase (not here)');
|
|
167
166
|
sections.push('4. Ensure acceptance criteria are met');
|
|
168
167
|
sections.push('5. Run `npm run lint && npm run test`');
|
|
@@ -93,7 +93,7 @@ Run \`bootspring build task\` to get the current \`in_progress\` task from \`BUI
|
|
|
93
93
|
## Workflow
|
|
94
94
|
|
|
95
95
|
1. Run \`bootspring build task\` to identify the current task ID
|
|
96
|
-
2. Read the matching
|
|
96
|
+
2. Read the matching task in \`TODO.md\` for acceptance criteria
|
|
97
97
|
3. Implement in the main codebase (not in this folder)
|
|
98
98
|
4. Ensure acceptance criteria are met
|
|
99
99
|
5. Run quality checks: \`npm run lint && npm run test\`
|
|
@@ -104,8 +104,7 @@ Run \`bootspring build task\` to get the current \`in_progress\` task from \`BUI
|
|
|
104
104
|
|
|
105
105
|
| File | Purpose |
|
|
106
106
|
|------|---------|
|
|
107
|
-
| \`
|
|
108
|
-
| \`TODO.md\` | Full task checklist |
|
|
107
|
+
| \`TODO.md\` | **Source of truth** — task checklist with acceptance criteria |
|
|
109
108
|
| \`BUILD_STATE.json\` | Build state and current task ID (do not edit directly) |
|
|
110
109
|
| \`CONTEXT.md\` | Build context summary |
|
|
111
110
|
|
|
@@ -254,7 +253,19 @@ function generateTodo(tasks, options = {}) {
|
|
|
254
253
|
|
|
255
254
|
let content = `# ${projectName} - Build Todo
|
|
256
255
|
|
|
257
|
-
>
|
|
256
|
+
> Single source of truth for autonomous build execution
|
|
257
|
+
> Updated: ${new Date().toISOString().split('T')[0]}
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Program Status
|
|
262
|
+
|
|
263
|
+
| Metric | Value |
|
|
264
|
+
|---|---:|
|
|
265
|
+
| Total Tasks | ${tasks.length} |
|
|
266
|
+
| Completed | ${tasks.filter(t => t.status === 'completed').length} |
|
|
267
|
+
| Remaining | ${tasks.filter(t => t.status === 'pending').length} |
|
|
268
|
+
| In Progress | ${tasks.filter(t => t.status === 'in_progress').length} |
|
|
258
269
|
|
|
259
270
|
---
|
|
260
271
|
|
|
@@ -265,17 +276,42 @@ function generateTodo(tasks, options = {}) {
|
|
|
265
276
|
const total = phaseTasks.length;
|
|
266
277
|
const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
|
|
267
278
|
|
|
268
|
-
content += `## ${formatPhaseName(phaseName)} (${percent}%)
|
|
279
|
+
content += `## ${formatPhaseName(phaseName)} (${completed}/${total} — ${percent}%)
|
|
269
280
|
|
|
270
281
|
`;
|
|
271
282
|
|
|
272
283
|
for (const task of phaseTasks) {
|
|
273
284
|
const checkbox = task.status === 'completed' ? '[x]' : '[ ]';
|
|
274
|
-
|
|
285
|
+
const statusTag = task.status === 'completed' ? ' (`completed`)' :
|
|
286
|
+
task.status === 'in_progress' ? ' (`in_progress`)' :
|
|
287
|
+
task.status === 'blocked' ? ' (`blocked`)' :
|
|
288
|
+
task.status === 'skipped' ? ' (`skipped`)' : '';
|
|
289
|
+
content += `- ${checkbox} \`${task.id}\` ${task.title}${statusTag}\n`;
|
|
290
|
+
|
|
291
|
+
// Source traceability
|
|
292
|
+
if (task.source) {
|
|
293
|
+
const sourceStr = task.sourceSection
|
|
294
|
+
? `${task.source} (${task.sourceSection})`
|
|
295
|
+
: task.source;
|
|
296
|
+
content += ` - **Source:** ${sourceStr}\n`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Description
|
|
300
|
+
if (task.description) {
|
|
301
|
+
content += ` - **Description:** ${task.description}\n`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Complexity and dependencies
|
|
305
|
+
if (task.estimatedComplexity && task.estimatedComplexity !== 'medium') {
|
|
306
|
+
content += ` - **Complexity:** ${task.estimatedComplexity}\n`;
|
|
307
|
+
}
|
|
308
|
+
if (task.dependencies && task.dependencies.length > 0) {
|
|
309
|
+
content += ` - **Dependencies:** ${task.dependencies.join(', ')}\n`;
|
|
310
|
+
}
|
|
275
311
|
|
|
276
|
-
//
|
|
312
|
+
// Acceptance criteria as sub-items (all of them — this is the source of truth)
|
|
277
313
|
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
278
|
-
for (const criteria of task.acceptanceCriteria
|
|
314
|
+
for (const criteria of task.acceptanceCriteria) {
|
|
279
315
|
const subCheckbox = task.status === 'completed' ? '[x]' : '[ ]';
|
|
280
316
|
content += ` - ${subCheckbox} ${criteria}\n`;
|
|
281
317
|
}
|
|
@@ -285,18 +321,7 @@ function generateTodo(tasks, options = {}) {
|
|
|
285
321
|
content += '\n---\n\n';
|
|
286
322
|
}
|
|
287
323
|
|
|
288
|
-
content +=
|
|
289
|
-
|
|
290
|
-
| Metric | Value |
|
|
291
|
-
|--------|-------|
|
|
292
|
-
| Total Tasks | ${tasks.length} |
|
|
293
|
-
| Completed | ${tasks.filter(t => t.status === 'completed').length} |
|
|
294
|
-
| Remaining | ${tasks.filter(t => t.status === 'pending').length} |
|
|
295
|
-
| In Progress | ${tasks.filter(t => t.status === 'in_progress').length} |
|
|
296
|
-
|
|
297
|
-
---
|
|
298
|
-
|
|
299
|
-
*Updated: ${new Date().toISOString()}*
|
|
324
|
+
content += `*Updated: ${new Date().toISOString()}*
|
|
300
325
|
`;
|
|
301
326
|
|
|
302
327
|
return content;
|
|
@@ -643,27 +668,28 @@ function updateFromState(projectRoot, state) {
|
|
|
643
668
|
|
|
644
669
|
const tasks = state.implementationQueue;
|
|
645
670
|
const planningDir = path.join(projectRoot, 'planning');
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
? fs.readFileSync(queuePath, 'utf-8')
|
|
649
|
-
: '';
|
|
650
|
-
const existingVersionMatch = existingQueue.match(/\*\*Current Version:\*\*\s*([^\n]+)/i)
|
|
651
|
-
|| existingQueue.match(/>\s*Current Version:\s*([^\n]+)/i);
|
|
652
|
-
const queueVersion = existingVersionMatch
|
|
653
|
-
? existingVersionMatch[1].trim()
|
|
654
|
-
: DEFAULT_QUEUE_VERSION;
|
|
655
|
-
|
|
656
|
-
// Regenerate TODO.md with current status
|
|
671
|
+
|
|
672
|
+
// Regenerate TODO.md — the single source of truth
|
|
657
673
|
const todo = generateTodo(tasks, { projectName: state.projectName });
|
|
658
674
|
fs.writeFileSync(path.join(planningDir, 'TODO.md'), todo);
|
|
659
675
|
|
|
660
|
-
//
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
676
|
+
// Legacy: also regenerate TASK_QUEUE.md if it already exists (backward compat)
|
|
677
|
+
const queuePath = path.join(planningDir, 'TASK_QUEUE.md');
|
|
678
|
+
if (fs.existsSync(queuePath)) {
|
|
679
|
+
const existingQueue = fs.readFileSync(queuePath, 'utf-8');
|
|
680
|
+
const existingVersionMatch = existingQueue.match(/\*\*Current Version:\*\*\s*([^\n]+)/i)
|
|
681
|
+
|| existingQueue.match(/>\s*Current Version:\s*([^\n]+)/i);
|
|
682
|
+
const queueVersion = existingVersionMatch
|
|
683
|
+
? existingVersionMatch[1].trim()
|
|
684
|
+
: DEFAULT_QUEUE_VERSION;
|
|
685
|
+
|
|
686
|
+
const queue = generateImplementationQueue(tasks, {
|
|
687
|
+
projectName: state.projectName,
|
|
688
|
+
lastUpdated: new Date().toISOString().split('T')[0],
|
|
689
|
+
queueVersion
|
|
690
|
+
});
|
|
691
|
+
fs.writeFileSync(queuePath, queue);
|
|
692
|
+
}
|
|
667
693
|
}
|
|
668
694
|
|
|
669
695
|
module.exports = {
|
|
@@ -33,7 +33,7 @@ function generate(config) {
|
|
|
33
33
|
sections.push('### Before Writing ANY Code');
|
|
34
34
|
sections.push('1. Read `planning/MASTER_PLAN.md` - Overall vision and phases');
|
|
35
35
|
sections.push('2. Run `bootspring build task` - Current task ID comes from `planning/BUILD_STATE.json`');
|
|
36
|
-
sections.push('3. Read `planning/
|
|
36
|
+
sections.push('3. Read `planning/TODO.md` - Find the task ID and its acceptance criteria');
|
|
37
37
|
sections.push('4. Read `planning/CONTEXT.md` - Current build context');
|
|
38
38
|
sections.push('5. Understand the acceptance criteria before implementing');
|
|
39
39
|
sections.push('');
|
|
@@ -53,9 +53,8 @@ function generate(config) {
|
|
|
53
53
|
sections.push('| File | Purpose | When to Read |');
|
|
54
54
|
sections.push('|------|---------|--------------|');
|
|
55
55
|
sections.push('| `MASTER_PLAN.md` | Overall vision, phases, success criteria | **Always read first** |');
|
|
56
|
-
sections.push('| `
|
|
56
|
+
sections.push('| `TODO.md` | **Source of truth** — all tasks with acceptance criteria | **Before each task** |');
|
|
57
57
|
sections.push('| `CONTEXT.md` | Current build context and decisions | Before each task |');
|
|
58
|
-
sections.push('| `TODO.md` | Checkbox task list | Update after completing |');
|
|
59
58
|
sections.push('| `BUILD_STATE.json` | Machine state - source of current task ID | Reference only |');
|
|
60
59
|
sections.push('');
|
|
61
60
|
sections.push('---');
|
|
@@ -231,7 +230,7 @@ function generate(config) {
|
|
|
231
230
|
sections.push('│ ↓ │');
|
|
232
231
|
sections.push('│ bootspring build task → get in_progress task ID │');
|
|
233
232
|
sections.push('│ ↓ │');
|
|
234
|
-
sections.push('│ Read planning/
|
|
233
|
+
sections.push('│ Read planning/TODO.md for acceptance criteria │');
|
|
235
234
|
sections.push('│ ↓ │');
|
|
236
235
|
sections.push('│ Implement task (meet acceptance criteria) │');
|
|
237
236
|
sections.push('│ ↓ │');
|