specsmd 0.0.0-dev.78 → 0.0.0-dev.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -236
- package/flows/aidlc/scripts/{artifact-validator.js → artifact-validator.cjs} +3 -3
- package/flows/aidlc/scripts/{bolt-complete.js → bolt-complete.cjs} +3 -3
- package/flows/aidlc/scripts/{status-integrity.js → status-integrity.cjs} +5 -5
- package/flows/aidlc/skills/construction/bolt-start.md +3 -3
- package/flows/aidlc/skills/construction/prototype-apply.md +6 -0
- package/flows/aidlc/skills/inception/vibe-to-spec.md +4 -0
- package/flows/fire/agents/builder/agent.md +19 -17
- package/flows/fire/agents/builder/skills/code-review/SKILL.md +9 -6
- package/flows/fire/agents/builder/skills/code-review/references/auto-fix-rules.md +6 -0
- package/flows/fire/agents/builder/skills/run-execute/SKILL.md +181 -62
- package/flows/fire/agents/builder/skills/run-execute/scripts/{complete-run.js → complete-run.cjs} +274 -75
- package/flows/fire/agents/builder/skills/run-execute/scripts/{init-run.js → init-run.cjs} +22 -19
- package/flows/fire/agents/builder/skills/run-execute/scripts/update-phase.cjs +239 -0
- package/flows/fire/agents/builder/skills/run-plan/SKILL.md +44 -46
- package/flows/fire/agents/builder/skills/run-status/SKILL.md +3 -1
- package/flows/fire/agents/builder/skills/walkthrough-generate/SKILL.md +131 -7
- package/flows/fire/agents/builder/skills/walkthrough-generate/templates/walkthrough.md.hbs +99 -0
- package/flows/fire/agents/orchestrator/agent.md +22 -5
- package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +2 -1
- package/flows/fire/agents/orchestrator/skills/route/SKILL.md +11 -6
- package/flows/fire/agents/orchestrator/skills/status/SKILL.md +609 -13
- package/flows/fire/agents/planner/agent.md +5 -0
- package/flows/fire/agents/planner/skills/design-doc-generate/SKILL.md +1 -0
- package/flows/fire/agents/planner/skills/intent-capture/SKILL.md +1 -0
- package/flows/fire/agents/planner/skills/work-item-decompose/SKILL.md +1 -0
- package/flows/fire/commands/fire-builder.md +1 -1
- package/flows/fire/commands/fire-planner.md +1 -1
- package/flows/fire/commands/fire.md +1 -1
- package/flows/fire/memory-bank.yaml +29 -12
- package/flows/fire/quick-start.md +16 -0
- package/lib/constants.js +6 -3
- package/lib/installer.js +14 -0
- package/package.json +1 -1
- package/flows/fire/agents/builder/skills/walkthrough-generate/scripts/render-walkthrough.ts +0 -755
- package/flows/fire/agents/orchestrator/skills/project-migrate/SKILL.md +0 -234
package/flows/fire/agents/builder/skills/run-execute/scripts/{complete-run.js → complete-run.cjs}
RENAMED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Supports both single and batch/wide runs.
|
|
7
7
|
*
|
|
8
|
-
* For single runs: Completes the run and
|
|
8
|
+
* For single runs: Completes the run and removes from runs.active[].
|
|
9
9
|
* For batch/wide runs:
|
|
10
10
|
* - --complete-item: Marks current work item done, moves to next
|
|
11
11
|
* - --complete-run: Marks all items done and finalizes entire run
|
|
12
12
|
*
|
|
13
13
|
* Usage:
|
|
14
|
-
* Complete current item: node complete-run.
|
|
15
|
-
* Complete entire run: node complete-run.
|
|
16
|
-
* Complete (single/auto): node complete-run.
|
|
14
|
+
* Complete current item: node complete-run.cjs <rootPath> <runId> --complete-item [options]
|
|
15
|
+
* Complete entire run: node complete-run.cjs <rootPath> <runId> --complete-run [options]
|
|
16
|
+
* Complete (single/auto): node complete-run.cjs <rootPath> <runId> [options]
|
|
17
17
|
*
|
|
18
18
|
* Options:
|
|
19
19
|
* --files-created=JSON - JSON array of {path, purpose}
|
|
@@ -102,6 +102,128 @@ function validateFireProject(rootPath, runId) {
|
|
|
102
102
|
return { statePath, runPath, runLogPath };
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// Frontmatter Helpers
|
|
107
|
+
// =============================================================================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Parse YAML frontmatter from markdown content.
|
|
111
|
+
* Returns { frontmatter: object, body: string } or null if no frontmatter.
|
|
112
|
+
*/
|
|
113
|
+
function parseFrontmatter(content) {
|
|
114
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
115
|
+
if (!match) return null;
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const frontmatter = yaml.parse(match[1]);
|
|
119
|
+
const body = content.slice(match[0].length);
|
|
120
|
+
return { frontmatter, body };
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Reconstruct markdown content from frontmatter and body.
|
|
128
|
+
*/
|
|
129
|
+
function buildMarkdownWithFrontmatter(frontmatter, body) {
|
|
130
|
+
return `---\n${yaml.stringify(frontmatter)}---${body}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// =============================================================================
|
|
134
|
+
// Markdown Frontmatter Sync
|
|
135
|
+
// =============================================================================
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Update work item markdown file frontmatter with new status.
|
|
139
|
+
*/
|
|
140
|
+
function updateWorkItemMarkdown(rootPath, intentId, workItemId, status, runId, completedAt) {
|
|
141
|
+
const filePath = path.join(rootPath, '.specs-fire', 'intents', intentId, 'work-items', `${workItemId}.md`);
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(filePath)) {
|
|
144
|
+
// File doesn't exist - not an error, just skip
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
150
|
+
const parsed = parseFrontmatter(content);
|
|
151
|
+
|
|
152
|
+
if (!parsed) {
|
|
153
|
+
// No valid frontmatter - skip
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Update frontmatter fields
|
|
158
|
+
parsed.frontmatter.status = status;
|
|
159
|
+
if (runId) parsed.frontmatter.run_id = runId;
|
|
160
|
+
if (completedAt && status === 'completed') {
|
|
161
|
+
parsed.frontmatter.completed_at = completedAt;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const newContent = buildMarkdownWithFrontmatter(parsed.frontmatter, parsed.body);
|
|
165
|
+
fs.writeFileSync(filePath, newContent);
|
|
166
|
+
return true;
|
|
167
|
+
} catch (err) {
|
|
168
|
+
// Log but don't fail - markdown sync is best-effort
|
|
169
|
+
console.error(`Warning: Could not update work item markdown ${filePath}: ${err.message}`);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Update intent brief.md frontmatter based on work item statuses.
|
|
176
|
+
*/
|
|
177
|
+
function updateIntentMarkdown(rootPath, intentId, state) {
|
|
178
|
+
const filePath = path.join(rootPath, '.specs-fire', 'intents', intentId, 'brief.md');
|
|
179
|
+
|
|
180
|
+
if (!fs.existsSync(filePath)) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
// Determine intent status from its work items
|
|
186
|
+
const intent = state.intents?.find(i => i.id === intentId);
|
|
187
|
+
if (!intent || !Array.isArray(intent.work_items)) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const allCompleted = intent.work_items.every(wi => wi.status === 'completed');
|
|
192
|
+
const anyInProgress = intent.work_items.some(wi => wi.status === 'in_progress');
|
|
193
|
+
|
|
194
|
+
let newStatus = 'pending';
|
|
195
|
+
if (allCompleted) {
|
|
196
|
+
newStatus = 'completed';
|
|
197
|
+
} else if (anyInProgress || intent.work_items.some(wi => wi.status === 'completed')) {
|
|
198
|
+
newStatus = 'in_progress';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
202
|
+
const parsed = parseFrontmatter(content);
|
|
203
|
+
|
|
204
|
+
if (!parsed) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Only update if status actually changed
|
|
209
|
+
if (parsed.frontmatter.status === newStatus) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
parsed.frontmatter.status = newStatus;
|
|
214
|
+
if (allCompleted) {
|
|
215
|
+
parsed.frontmatter.completed_at = new Date().toISOString();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const newContent = buildMarkdownWithFrontmatter(parsed.frontmatter, parsed.body);
|
|
219
|
+
fs.writeFileSync(filePath, newContent);
|
|
220
|
+
return true;
|
|
221
|
+
} catch (err) {
|
|
222
|
+
console.error(`Warning: Could not update intent markdown ${filePath}: ${err.message}`);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
105
227
|
// =============================================================================
|
|
106
228
|
// State Operations
|
|
107
229
|
// =============================================================================
|
|
@@ -140,6 +262,10 @@ function writeState(statePath, state) {
|
|
|
140
262
|
// Run Log Operations
|
|
141
263
|
// =============================================================================
|
|
142
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Update run.md using proper YAML parsing instead of fragile regex.
|
|
267
|
+
* This ensures frontmatter updates work regardless of field order or formatting.
|
|
268
|
+
*/
|
|
143
269
|
function updateRunLog(runLogPath, activeRun, params, completedTime, isFullCompletion) {
|
|
144
270
|
let content;
|
|
145
271
|
try {
|
|
@@ -152,35 +278,62 @@ function updateRunLog(runLogPath, activeRun, params, completedTime, isFullComple
|
|
|
152
278
|
);
|
|
153
279
|
}
|
|
154
280
|
|
|
155
|
-
//
|
|
281
|
+
// Parse frontmatter using YAML (robust approach)
|
|
282
|
+
const parsed = parseFrontmatter(content);
|
|
283
|
+
if (!parsed) {
|
|
284
|
+
throw fireError(
|
|
285
|
+
'Invalid run.md format - no valid YAML frontmatter found.',
|
|
286
|
+
'COMPLETE_032',
|
|
287
|
+
'Ensure run.md has valid ---\\n...\\n--- frontmatter.'
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let { frontmatter, body } = parsed;
|
|
292
|
+
|
|
293
|
+
// Update frontmatter fields
|
|
156
294
|
if (isFullCompletion) {
|
|
157
|
-
|
|
158
|
-
|
|
295
|
+
frontmatter.status = 'completed';
|
|
296
|
+
frontmatter.completed = completedTime;
|
|
159
297
|
}
|
|
160
298
|
|
|
161
|
-
|
|
299
|
+
frontmatter.current_item = activeRun.current_item || null;
|
|
300
|
+
|
|
301
|
+
// Update work_items array in frontmatter
|
|
162
302
|
if (activeRun.work_items && Array.isArray(activeRun.work_items)) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
303
|
+
frontmatter.work_items = activeRun.work_items.map(item => ({
|
|
304
|
+
id: item.id,
|
|
305
|
+
intent: item.intent,
|
|
306
|
+
mode: item.mode,
|
|
307
|
+
status: item.status,
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Update markdown body sections
|
|
312
|
+
// Update Work Items section
|
|
313
|
+
if (activeRun.work_items && Array.isArray(activeRun.work_items)) {
|
|
314
|
+
const workItemsLines = activeRun.work_items.map((item, i) =>
|
|
315
|
+
`${i + 1}. **${item.id}** (${item.mode}) — ${item.status}`
|
|
316
|
+
).join('\n');
|
|
172
317
|
|
|
173
|
-
|
|
174
|
-
|
|
318
|
+
body = body.replace(
|
|
319
|
+
/## Work Items\n[\s\S]*?(?=\n## )/,
|
|
320
|
+
`## Work Items\n${workItemsLines}\n\n`
|
|
321
|
+
);
|
|
175
322
|
|
|
176
323
|
// Update Current Item section
|
|
177
324
|
if (activeRun.current_item) {
|
|
178
325
|
const currentItem = activeRun.work_items.find(i => i.id === activeRun.current_item);
|
|
179
326
|
if (currentItem) {
|
|
180
|
-
|
|
327
|
+
body = body.replace(
|
|
328
|
+
/## Current Item\n[^\n]+/,
|
|
329
|
+
`## Current Item\n${currentItem.id} (${currentItem.mode})`
|
|
330
|
+
);
|
|
181
331
|
}
|
|
182
332
|
} else {
|
|
183
|
-
|
|
333
|
+
body = body.replace(
|
|
334
|
+
/## Current Item\n[^\n]+/,
|
|
335
|
+
`## Current Item\n(all completed)`
|
|
336
|
+
);
|
|
184
337
|
}
|
|
185
338
|
}
|
|
186
339
|
|
|
@@ -199,14 +352,14 @@ function updateRunLog(runLogPath, activeRun, params, completedTime, isFullComple
|
|
|
199
352
|
: '(none)';
|
|
200
353
|
|
|
201
354
|
// Replace placeholder sections
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
355
|
+
body = body.replace('## Files Created\n(none yet)', `## Files Created\n${filesCreatedText}`);
|
|
356
|
+
body = body.replace('## Files Modified\n(none yet)', `## Files Modified\n${filesModifiedText}`);
|
|
357
|
+
body = body.replace('## Decisions\n(none yet)', `## Decisions\n${decisionsText}`);
|
|
205
358
|
|
|
206
359
|
// Add summary if not present
|
|
207
|
-
if (!
|
|
360
|
+
if (!body.includes('## Summary')) {
|
|
208
361
|
const itemCount = activeRun.work_items ? activeRun.work_items.length : 1;
|
|
209
|
-
|
|
362
|
+
body += `
|
|
210
363
|
|
|
211
364
|
## Summary
|
|
212
365
|
|
|
@@ -220,8 +373,11 @@ function updateRunLog(runLogPath, activeRun, params, completedTime, isFullComple
|
|
|
220
373
|
}
|
|
221
374
|
}
|
|
222
375
|
|
|
376
|
+
// Reconstruct content with updated frontmatter and body
|
|
377
|
+
const newContent = buildMarkdownWithFrontmatter(frontmatter, body);
|
|
378
|
+
|
|
223
379
|
try {
|
|
224
|
-
fs.writeFileSync(runLogPath,
|
|
380
|
+
fs.writeFileSync(runLogPath, newContent);
|
|
225
381
|
} catch (err) {
|
|
226
382
|
throw fireError(
|
|
227
383
|
`Failed to write run log: ${err.message}`,
|
|
@@ -248,25 +404,22 @@ function completeCurrentItem(rootPath, runId, params = {}) {
|
|
|
248
404
|
const { statePath, runLogPath } = validateFireProject(rootPath, runId);
|
|
249
405
|
const state = readState(statePath);
|
|
250
406
|
|
|
251
|
-
|
|
407
|
+
// Find run in active runs list
|
|
408
|
+
const activeRuns = state.runs?.active || [];
|
|
409
|
+
const runIndex = activeRuns.findIndex(r => r.id === runId);
|
|
410
|
+
|
|
411
|
+
if (runIndex === -1) {
|
|
252
412
|
throw fireError(
|
|
253
|
-
|
|
413
|
+
`Run "${runId}" not found in active runs.`,
|
|
254
414
|
'COMPLETE_040',
|
|
255
415
|
'The run may have already been completed or was never started.'
|
|
256
416
|
);
|
|
257
417
|
}
|
|
258
418
|
|
|
259
|
-
|
|
260
|
-
throw fireError(
|
|
261
|
-
`Run ID mismatch. Active run is "${state.active_run.id}" but trying to complete "${runId}".`,
|
|
262
|
-
'COMPLETE_041',
|
|
263
|
-
`Complete the active run "${state.active_run.id}" first.`
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
|
|
419
|
+
const activeRun = activeRuns[runIndex];
|
|
267
420
|
const completedTime = new Date().toISOString();
|
|
268
|
-
const workItems =
|
|
269
|
-
const currentItemId =
|
|
421
|
+
const workItems = activeRun.work_items || [];
|
|
422
|
+
const currentItemId = activeRun.current_item;
|
|
270
423
|
|
|
271
424
|
// Find and mark current item as completed
|
|
272
425
|
let currentItemIndex = -1;
|
|
@@ -292,17 +445,39 @@ function completeCurrentItem(rootPath, runId, params = {}) {
|
|
|
292
445
|
for (let i = currentItemIndex + 1; i < workItems.length; i++) {
|
|
293
446
|
if (workItems[i].status === 'pending') {
|
|
294
447
|
workItems[i].status = 'in_progress';
|
|
448
|
+
workItems[i].current_phase = 'plan';
|
|
295
449
|
nextItem = workItems[i];
|
|
296
450
|
break;
|
|
297
451
|
}
|
|
298
452
|
}
|
|
299
453
|
|
|
300
|
-
// Update
|
|
301
|
-
|
|
302
|
-
|
|
454
|
+
// Update active run in list
|
|
455
|
+
activeRun.work_items = workItems;
|
|
456
|
+
activeRun.current_item = nextItem ? nextItem.id : null;
|
|
457
|
+
state.runs.active[runIndex] = activeRun;
|
|
303
458
|
|
|
304
459
|
// Update run log
|
|
305
|
-
updateRunLog(runLogPath,
|
|
460
|
+
updateRunLog(runLogPath, activeRun, completionParams, completedTime, false);
|
|
461
|
+
|
|
462
|
+
// Sync markdown frontmatter for completed work item
|
|
463
|
+
const completedWorkItem = workItems.find(wi => wi.id === currentItemId);
|
|
464
|
+
if (completedWorkItem) {
|
|
465
|
+
updateWorkItemMarkdown(
|
|
466
|
+
rootPath,
|
|
467
|
+
completedWorkItem.intent,
|
|
468
|
+
currentItemId,
|
|
469
|
+
'completed',
|
|
470
|
+
runId,
|
|
471
|
+
completedTime
|
|
472
|
+
);
|
|
473
|
+
// Update intent status based on its work items
|
|
474
|
+
updateIntentMarkdown(rootPath, completedWorkItem.intent, state);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Also update next item's markdown to in_progress
|
|
478
|
+
if (nextItem) {
|
|
479
|
+
updateWorkItemMarkdown(rootPath, nextItem.intent, nextItem.id, 'in_progress', null, null);
|
|
480
|
+
}
|
|
306
481
|
|
|
307
482
|
// Save state
|
|
308
483
|
writeState(statePath, state);
|
|
@@ -335,25 +510,32 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
335
510
|
const { statePath, runLogPath } = validateFireProject(rootPath, runId);
|
|
336
511
|
const state = readState(statePath);
|
|
337
512
|
|
|
338
|
-
if
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
513
|
+
// Initialize runs structure if needed
|
|
514
|
+
if (!state.runs) {
|
|
515
|
+
state.runs = { active: [], completed: [] };
|
|
516
|
+
}
|
|
517
|
+
if (!Array.isArray(state.runs.active)) {
|
|
518
|
+
state.runs.active = [];
|
|
519
|
+
}
|
|
520
|
+
if (!Array.isArray(state.runs.completed)) {
|
|
521
|
+
state.runs.completed = [];
|
|
344
522
|
}
|
|
345
523
|
|
|
346
|
-
|
|
524
|
+
// Find run in active runs list
|
|
525
|
+
const runIndex = state.runs.active.findIndex(r => r.id === runId);
|
|
526
|
+
|
|
527
|
+
if (runIndex === -1) {
|
|
347
528
|
throw fireError(
|
|
348
|
-
`Run
|
|
349
|
-
'
|
|
350
|
-
|
|
529
|
+
`Run "${runId}" not found in active runs.`,
|
|
530
|
+
'COMPLETE_040',
|
|
531
|
+
'The run may have already been completed or was never started.'
|
|
351
532
|
);
|
|
352
533
|
}
|
|
353
534
|
|
|
535
|
+
const activeRun = state.runs.active[runIndex];
|
|
354
536
|
const completedTime = new Date().toISOString();
|
|
355
|
-
const workItems =
|
|
356
|
-
const scope =
|
|
537
|
+
const workItems = activeRun.work_items || [];
|
|
538
|
+
const scope = activeRun.scope || 'single';
|
|
357
539
|
|
|
358
540
|
// Mark all items as completed
|
|
359
541
|
for (const item of workItems) {
|
|
@@ -363,11 +545,11 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
363
545
|
}
|
|
364
546
|
}
|
|
365
547
|
|
|
366
|
-
|
|
367
|
-
|
|
548
|
+
activeRun.work_items = workItems;
|
|
549
|
+
activeRun.current_item = null;
|
|
368
550
|
|
|
369
551
|
// Update run log
|
|
370
|
-
updateRunLog(runLogPath,
|
|
552
|
+
updateRunLog(runLogPath, activeRun, completionParams, completedTime, true);
|
|
371
553
|
|
|
372
554
|
// Build completed run record
|
|
373
555
|
const completedRun = {
|
|
@@ -378,17 +560,15 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
378
560
|
intent: i.intent,
|
|
379
561
|
mode: i.mode,
|
|
380
562
|
})),
|
|
563
|
+
started: activeRun.started,
|
|
381
564
|
completed: completedTime,
|
|
382
565
|
};
|
|
383
566
|
|
|
384
|
-
// Get existing runs history or initialize
|
|
385
|
-
const existingRuns = state.runs || { completed: [] };
|
|
386
|
-
const existingCompleted = Array.isArray(existingRuns.completed) ? existingRuns.completed : [];
|
|
387
|
-
|
|
388
567
|
// Check for duplicate (idempotency)
|
|
389
|
-
const alreadyRecorded =
|
|
568
|
+
const alreadyRecorded = state.runs.completed.some(r => r.id === runId);
|
|
390
569
|
|
|
391
|
-
// Update work item status in intents
|
|
570
|
+
// Update work item status in intents (state.yaml)
|
|
571
|
+
const affectedIntents = new Set();
|
|
392
572
|
if (Array.isArray(state.intents)) {
|
|
393
573
|
for (const workItem of workItems) {
|
|
394
574
|
for (const intent of state.intents) {
|
|
@@ -397,6 +577,8 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
397
577
|
if (wi.id === workItem.id) {
|
|
398
578
|
wi.status = 'completed';
|
|
399
579
|
wi.run_id = runId;
|
|
580
|
+
wi.completed_at = completedTime;
|
|
581
|
+
affectedIntents.add(intent.id);
|
|
400
582
|
break;
|
|
401
583
|
}
|
|
402
584
|
}
|
|
@@ -405,15 +587,32 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
405
587
|
}
|
|
406
588
|
}
|
|
407
589
|
|
|
408
|
-
//
|
|
409
|
-
state.
|
|
410
|
-
|
|
411
|
-
completed
|
|
412
|
-
}
|
|
590
|
+
// Remove from active runs and add to completed
|
|
591
|
+
state.runs.active.splice(runIndex, 1);
|
|
592
|
+
if (!alreadyRecorded) {
|
|
593
|
+
state.runs.completed.push(completedRun);
|
|
594
|
+
}
|
|
413
595
|
|
|
414
|
-
// Save state
|
|
596
|
+
// Save state first (so markdown sync has correct state)
|
|
415
597
|
writeState(statePath, state);
|
|
416
598
|
|
|
599
|
+
// Sync markdown frontmatter for all completed work items
|
|
600
|
+
for (const workItem of workItems) {
|
|
601
|
+
updateWorkItemMarkdown(
|
|
602
|
+
rootPath,
|
|
603
|
+
workItem.intent,
|
|
604
|
+
workItem.id,
|
|
605
|
+
'completed',
|
|
606
|
+
runId,
|
|
607
|
+
workItem.completed_at || completedTime
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Update intent markdown for all affected intents
|
|
612
|
+
for (const intentId of affectedIntents) {
|
|
613
|
+
updateIntentMarkdown(rootPath, intentId, state);
|
|
614
|
+
}
|
|
615
|
+
|
|
417
616
|
return {
|
|
418
617
|
success: true,
|
|
419
618
|
runId: runId,
|
|
@@ -480,9 +679,9 @@ function parseArgs(args) {
|
|
|
480
679
|
|
|
481
680
|
function printUsage() {
|
|
482
681
|
console.error('Usage:');
|
|
483
|
-
console.error(' Complete current item: node complete-run.
|
|
484
|
-
console.error(' Complete entire run: node complete-run.
|
|
485
|
-
console.error(' Auto (single runs): node complete-run.
|
|
682
|
+
console.error(' Complete current item: node complete-run.cjs <rootPath> <runId> --complete-item [options]');
|
|
683
|
+
console.error(' Complete entire run: node complete-run.cjs <rootPath> <runId> --complete-run [options]');
|
|
684
|
+
console.error(' Auto (single runs): node complete-run.cjs <rootPath> <runId> [options]');
|
|
486
685
|
console.error('');
|
|
487
686
|
console.error('Arguments:');
|
|
488
687
|
console.error(' rootPath - Project root directory');
|
|
@@ -500,8 +699,8 @@ function printUsage() {
|
|
|
500
699
|
console.error(' --coverage=N - Coverage percentage');
|
|
501
700
|
console.error('');
|
|
502
701
|
console.error('Examples:');
|
|
503
|
-
console.error(' node complete-run.
|
|
504
|
-
console.error(' node complete-run.
|
|
702
|
+
console.error(' node complete-run.cjs /project run-003 --complete-item');
|
|
703
|
+
console.error(' node complete-run.cjs /project run-003 --complete-run --tests=5 --coverage=85');
|
|
505
704
|
}
|
|
506
705
|
|
|
507
706
|
// =============================================================================
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
* - existing run folders in .specs-fire/runs/
|
|
12
12
|
*
|
|
13
13
|
* Usage:
|
|
14
|
-
* Single item: node init-run.
|
|
15
|
-
* Batch/Wide: node init-run.
|
|
14
|
+
* Single item: node init-run.cjs <rootPath> <workItemId> <intentId> <mode>
|
|
15
|
+
* Batch/Wide: node init-run.cjs <rootPath> --batch '<workItemsJson>'
|
|
16
16
|
*
|
|
17
17
|
* Examples:
|
|
18
|
-
* node init-run.
|
|
19
|
-
* node init-run.
|
|
18
|
+
* node init-run.cjs /project login-endpoint user-auth confirm
|
|
19
|
+
* node init-run.cjs /project --batch '[{"id":"wi-1","intent":"int-1","mode":"autopilot"}]'
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
const fs = require('fs');
|
|
@@ -317,13 +317,15 @@ function initRun(rootPath, workItems, scope) {
|
|
|
317
317
|
// Read state
|
|
318
318
|
const state = readState(statePath);
|
|
319
319
|
|
|
320
|
-
//
|
|
321
|
-
if (state.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
320
|
+
// Initialize runs structure if needed
|
|
321
|
+
if (!state.runs) {
|
|
322
|
+
state.runs = { active: [], completed: [] };
|
|
323
|
+
}
|
|
324
|
+
if (!Array.isArray(state.runs.active)) {
|
|
325
|
+
state.runs.active = [];
|
|
326
|
+
}
|
|
327
|
+
if (!Array.isArray(state.runs.completed)) {
|
|
328
|
+
state.runs.completed = [];
|
|
327
329
|
}
|
|
328
330
|
|
|
329
331
|
// Generate run ID (checks both history AND file system)
|
|
@@ -337,22 +339,23 @@ function initRun(rootPath, workItems, scope) {
|
|
|
337
339
|
const startTime = new Date().toISOString();
|
|
338
340
|
createRunLog(runPath, runId, workItems, detectedScope, startTime);
|
|
339
341
|
|
|
340
|
-
// Prepare work items for state with status tracking
|
|
342
|
+
// Prepare work items for state with status and phase tracking
|
|
341
343
|
const stateWorkItems = workItems.map((item, index) => ({
|
|
342
344
|
id: item.id,
|
|
343
345
|
intent: item.intent,
|
|
344
346
|
mode: item.mode,
|
|
345
347
|
status: index === 0 ? 'in_progress' : 'pending',
|
|
348
|
+
current_phase: index === 0 ? 'plan' : null,
|
|
346
349
|
}));
|
|
347
350
|
|
|
348
|
-
//
|
|
349
|
-
state.
|
|
351
|
+
// Add to active runs list (supports multiple parallel runs)
|
|
352
|
+
state.runs.active.push({
|
|
350
353
|
id: runId,
|
|
351
354
|
scope: detectedScope,
|
|
352
355
|
work_items: stateWorkItems,
|
|
353
356
|
current_item: workItems[0].id,
|
|
354
357
|
started: startTime,
|
|
355
|
-
};
|
|
358
|
+
});
|
|
356
359
|
|
|
357
360
|
// Save state
|
|
358
361
|
writeState(statePath, state);
|
|
@@ -375,8 +378,8 @@ function initRun(rootPath, workItems, scope) {
|
|
|
375
378
|
|
|
376
379
|
function printUsage() {
|
|
377
380
|
console.error('Usage:');
|
|
378
|
-
console.error(' Single item: node init-run.
|
|
379
|
-
console.error(' Batch/Wide: node init-run.
|
|
381
|
+
console.error(' Single item: node init-run.cjs <rootPath> <workItemId> <intentId> <mode>');
|
|
382
|
+
console.error(' Batch/Wide: node init-run.cjs <rootPath> --batch \'<workItemsJson>\' [--scope=<scope>]');
|
|
380
383
|
console.error('');
|
|
381
384
|
console.error('Arguments:');
|
|
382
385
|
console.error(' rootPath - Project root directory');
|
|
@@ -392,8 +395,8 @@ function printUsage() {
|
|
|
392
395
|
console.error(' [{"id": "wi-1", "intent": "int-1", "mode": "autopilot"}, ...]');
|
|
393
396
|
console.error('');
|
|
394
397
|
console.error('Examples:');
|
|
395
|
-
console.error(' node init-run.
|
|
396
|
-
console.error(' node init-run.
|
|
398
|
+
console.error(' node init-run.cjs /project login-endpoint user-auth confirm');
|
|
399
|
+
console.error(' node init-run.cjs /project --batch \'[{"id":"wi-1","intent":"int-1","mode":"autopilot"}]\'');
|
|
397
400
|
}
|
|
398
401
|
|
|
399
402
|
if (require.main === module) {
|