specsmd 0.0.0-dev.55 → 0.0.0-dev.57
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/flows/fire/agents/builder/skills/run-execute/SKILL.md +181 -23
- package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.js +242 -77
- package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.js +177 -42
- package/flows/fire/agents/builder/skills/run-plan/SKILL.md +59 -38
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Skill: Run Execute
|
|
2
2
|
|
|
3
|
-
Execute
|
|
3
|
+
Execute work items based on their assigned mode (autopilot, confirm, validate).
|
|
4
|
+
Supports both single-item and multi-item (batch/wide) runs.
|
|
4
5
|
|
|
5
6
|
---
|
|
6
7
|
|
|
@@ -33,6 +34,7 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
33
34
|
|
|
34
35
|
- Pending work item ready for execution
|
|
35
36
|
- Resumed from interrupted run
|
|
37
|
+
- Batch of work items passed from run-plan
|
|
36
38
|
|
|
37
39
|
---
|
|
38
40
|
|
|
@@ -45,26 +47,82 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
45
47
|
|
|
46
48
|
---
|
|
47
49
|
|
|
50
|
+
## Critical Requirements
|
|
51
|
+
|
|
52
|
+
### MUST Use Scripts - Never Bypass
|
|
53
|
+
|
|
54
|
+
**CRITICAL**: You MUST call the scripts. DO NOT use mkdir or manual file creation.
|
|
55
|
+
|
|
56
|
+
| Action | CORRECT | WRONG |
|
|
57
|
+
|--------|---------|-------|
|
|
58
|
+
| Initialize run | `node scripts/init-run.js ...` | `mkdir .specs-fire/runs/run-001` |
|
|
59
|
+
| Complete item | `node scripts/complete-run.js ... --complete-item` | Manual state editing |
|
|
60
|
+
| Complete run | `node scripts/complete-run.js ... --complete-run` | Manual state editing |
|
|
61
|
+
|
|
62
|
+
The scripts:
|
|
63
|
+
- Create run folder AND run.md together
|
|
64
|
+
- Update state.yaml atomically
|
|
65
|
+
- Track run history in runs.completed
|
|
66
|
+
- Handle batch run state transitions
|
|
67
|
+
|
|
68
|
+
### Batch Run Execution Flow
|
|
69
|
+
|
|
70
|
+
For runs with multiple work items:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
1. Call init-run.js ONCE at start (creates run.md with ALL items)
|
|
74
|
+
2. Execute each work item sequentially:
|
|
75
|
+
- Load item context
|
|
76
|
+
- Execute based on mode (autopilot/confirm/validate)
|
|
77
|
+
- Call complete-run.js --complete-item after each
|
|
78
|
+
3. Call complete-run.js --complete-run after final item
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
48
83
|
## Workflow
|
|
49
84
|
|
|
50
85
|
```xml
|
|
51
86
|
<skill name="run-execute">
|
|
52
87
|
|
|
53
88
|
<mandate>
|
|
89
|
+
USE SCRIPTS — Never bypass init-run.js or complete-run.js.
|
|
54
90
|
TRACK ALL FILE OPERATIONS — Every create, modify must be recorded.
|
|
55
91
|
NEVER skip tests — Tests are mandatory, not optional.
|
|
56
92
|
FOLLOW BROWNFIELD RULES — Read before write, match existing patterns.
|
|
57
93
|
</mandate>
|
|
58
94
|
|
|
59
95
|
<step n="1" title="Initialize Run">
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
96
|
+
<critical>
|
|
97
|
+
MUST call init-run.js script. DO NOT use mkdir directly.
|
|
98
|
+
The script creates BOTH the folder AND run.md file.
|
|
99
|
+
</critical>
|
|
100
|
+
|
|
101
|
+
<action>Prepare work items JSON array:</action>
|
|
102
|
+
<code>
|
|
103
|
+
# For single item:
|
|
104
|
+
node scripts/init-run.js {rootPath} {workItemId} {intentId} {mode}
|
|
105
|
+
|
|
106
|
+
# For batch/wide (multiple items):
|
|
107
|
+
node scripts/init-run.js {rootPath} --batch '[
|
|
108
|
+
{"id": "item-1", "intent": "intent-1", "mode": "autopilot"},
|
|
109
|
+
{"id": "item-2", "intent": "intent-1", "mode": "confirm"}
|
|
110
|
+
]' --scope=batch
|
|
111
|
+
</code>
|
|
112
|
+
|
|
113
|
+
<action>Parse script output for runId and runPath</action>
|
|
114
|
+
<action>Verify run.md was created in .specs-fire/runs/{run-id}/</action>
|
|
115
|
+
|
|
116
|
+
<check if="run.md not found">
|
|
117
|
+
<error>init-run.js failed to create run.md. Check script output.</error>
|
|
118
|
+
</check>
|
|
64
119
|
</step>
|
|
65
120
|
|
|
66
|
-
<step n="2" title="
|
|
67
|
-
<
|
|
121
|
+
<step n="2" title="Execute Work Items Loop">
|
|
122
|
+
<note>For batch runs, repeat steps 2-6 for each work item</note>
|
|
123
|
+
|
|
124
|
+
<action>Get current_item from state.yaml active_run</action>
|
|
125
|
+
<action>Load work item from .specs-fire/intents/{intent}/work-items/{id}.md</action>
|
|
68
126
|
<action>Read intent brief for broader context</action>
|
|
69
127
|
<action>Load ALL project standards:</action>
|
|
70
128
|
<substep>.specs-fire/standards/tech-stack.md — Technology choices</substep>
|
|
@@ -183,15 +241,36 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
183
241
|
</check>
|
|
184
242
|
|
|
185
243
|
<action>Validate acceptance criteria from work item</action>
|
|
186
|
-
<action>Measure coverage against target from testing-standards.md</action>
|
|
187
|
-
<action>Generate test report using template: templates/test-report.md.hbs</action>
|
|
188
|
-
<action>Save to: .specs-fire/runs/{run-id}/test-report.md</action>
|
|
189
244
|
</step>
|
|
190
245
|
|
|
191
|
-
<step n="7" title="Complete
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
246
|
+
<step n="7" title="Complete Current Work Item">
|
|
247
|
+
<critical>
|
|
248
|
+
MUST call complete-run.js script. Check if more items remain.
|
|
249
|
+
</critical>
|
|
250
|
+
|
|
251
|
+
<check if="batch run with more items pending">
|
|
252
|
+
<action>Call complete-run.js with --complete-item flag:</action>
|
|
253
|
+
<code>
|
|
254
|
+
node scripts/complete-run.js {rootPath} {runId} --complete-item
|
|
255
|
+
</code>
|
|
256
|
+
<action>Parse output for nextItem</action>
|
|
257
|
+
<output>
|
|
258
|
+
Work item {current_item} complete.
|
|
259
|
+
Next: {nextItem}
|
|
260
|
+
</output>
|
|
261
|
+
<goto step="2">Continue with next work item</goto>
|
|
262
|
+
</check>
|
|
263
|
+
|
|
264
|
+
<check if="last item or single item run">
|
|
265
|
+
<action>Call complete-run.js with --complete-run flag:</action>
|
|
266
|
+
<code>
|
|
267
|
+
node scripts/complete-run.js {rootPath} {runId} --complete-run \
|
|
268
|
+
--files-created='[{"path":"...","purpose":"..."}]' \
|
|
269
|
+
--files-modified='[{"path":"...","changes":"..."}]' \
|
|
270
|
+
--tests=5 --coverage=85
|
|
271
|
+
</code>
|
|
272
|
+
<goto step="8"/>
|
|
273
|
+
</check>
|
|
195
274
|
</step>
|
|
196
275
|
|
|
197
276
|
<step n="8" title="Generate Walkthrough">
|
|
@@ -200,18 +279,16 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
200
279
|
|
|
201
280
|
<step n="9" title="Report Completion">
|
|
202
281
|
<output>
|
|
203
|
-
Run {run-id} completed
|
|
282
|
+
Run {run-id} completed.
|
|
204
283
|
|
|
284
|
+
Work items completed: {count}
|
|
205
285
|
Files created: {count}
|
|
206
286
|
Files modified: {count}
|
|
207
287
|
Tests added: {count}
|
|
208
|
-
Coverage: {percentage}%
|
|
209
288
|
|
|
210
289
|
Artifacts:
|
|
211
|
-
-
|
|
290
|
+
- Run Log: .specs-fire/runs/{run-id}/run.md
|
|
212
291
|
- Walkthrough: .specs-fire/runs/{run-id}/walkthrough.md
|
|
213
|
-
|
|
214
|
-
Continue to next work item? [Y/n]
|
|
215
292
|
</output>
|
|
216
293
|
</step>
|
|
217
294
|
|
|
@@ -222,10 +299,71 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
222
299
|
|
|
223
300
|
## Scripts
|
|
224
301
|
|
|
225
|
-
| Script | Purpose |
|
|
226
|
-
|
|
227
|
-
| `scripts/init-run.js` | Initialize run record and folder |
|
|
228
|
-
| `scripts/complete-run.js` | Finalize run and update state |
|
|
302
|
+
| Script | Purpose | Usage |
|
|
303
|
+
|--------|---------|-------|
|
|
304
|
+
| `scripts/init-run.js` | Initialize run record and folder | Creates run.md with all work items |
|
|
305
|
+
| `scripts/complete-run.js` | Finalize run and update state | `--complete-item` or `--complete-run` |
|
|
306
|
+
|
|
307
|
+
### init-run.js Usage
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Single work item
|
|
311
|
+
node scripts/init-run.js /project work-item-id intent-id autopilot
|
|
312
|
+
|
|
313
|
+
# Batch/wide (multiple items)
|
|
314
|
+
node scripts/init-run.js /project --batch '[
|
|
315
|
+
{"id": "wi-1", "intent": "int-1", "mode": "autopilot"},
|
|
316
|
+
{"id": "wi-2", "intent": "int-1", "mode": "confirm"}
|
|
317
|
+
]' --scope=batch
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Output:**
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"success": true,
|
|
324
|
+
"runId": "run-001",
|
|
325
|
+
"runPath": "/project/.specs-fire/runs/run-001",
|
|
326
|
+
"scope": "batch",
|
|
327
|
+
"workItems": [...],
|
|
328
|
+
"currentItem": "wi-1"
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### complete-run.js Usage
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
# Complete current item (batch runs - moves to next item)
|
|
336
|
+
node scripts/complete-run.js /project run-001 --complete-item
|
|
337
|
+
|
|
338
|
+
# Complete entire run (single runs or final item in batch)
|
|
339
|
+
node scripts/complete-run.js /project run-001 --complete-run \
|
|
340
|
+
--files-created='[{"path":"src/new.ts","purpose":"New feature"}]' \
|
|
341
|
+
--files-modified='[{"path":"src/old.ts","changes":"Added import"}]' \
|
|
342
|
+
--tests=5 --coverage=85
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**--complete-item Output:**
|
|
346
|
+
```json
|
|
347
|
+
{
|
|
348
|
+
"success": true,
|
|
349
|
+
"runId": "run-001",
|
|
350
|
+
"completedItem": "wi-1",
|
|
351
|
+
"nextItem": "wi-2",
|
|
352
|
+
"remainingItems": 1,
|
|
353
|
+
"allItemsCompleted": false
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**--complete-run Output:**
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"success": true,
|
|
361
|
+
"runId": "run-001",
|
|
362
|
+
"scope": "batch",
|
|
363
|
+
"workItemsCompleted": 2,
|
|
364
|
+
"completedAt": "2026-01-20T..."
|
|
365
|
+
}
|
|
366
|
+
```
|
|
229
367
|
|
|
230
368
|
---
|
|
231
369
|
|
|
@@ -244,3 +382,23 @@ decisions:
|
|
|
244
382
|
- decision: Use JWT for tokens
|
|
245
383
|
rationale: Stateless, works with load balancer
|
|
246
384
|
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Run Folder Structure
|
|
389
|
+
|
|
390
|
+
After init-run.js creates a run:
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
.specs-fire/runs/run-001/
|
|
394
|
+
├── run.md # Created by init-run.js, updated by complete-run.js
|
|
395
|
+
├── plan.md # Created during confirm/validate mode (optional)
|
|
396
|
+
└── walkthrough.md # Created by walkthrough-generate skill
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The run.md contains:
|
|
400
|
+
- All work items with their statuses
|
|
401
|
+
- Current item being executed
|
|
402
|
+
- Files created/modified (after completion)
|
|
403
|
+
- Decisions made (after completion)
|
|
404
|
+
- Summary (after completion)
|
|
@@ -3,15 +3,24 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* FIRE Run Completion Script
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* 1. Recording the completed run in state.yaml runs.completed history
|
|
8
|
-
* 2. Updating work item status to completed
|
|
9
|
-
* 3. Clearing active_run
|
|
10
|
-
* 4. Updating run.md with completion data
|
|
6
|
+
* Supports both single and batch/wide runs.
|
|
11
7
|
*
|
|
12
|
-
*
|
|
8
|
+
* For single runs: Completes the run and clears active_run.
|
|
9
|
+
* For batch/wide runs:
|
|
10
|
+
* - --complete-item: Marks current work item done, moves to next
|
|
11
|
+
* - --complete-run: Marks all items done and finalizes entire run
|
|
13
12
|
*
|
|
14
|
-
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* Complete current item: node complete-run.js <rootPath> <runId> --complete-item [options]
|
|
15
|
+
* Complete entire run: node complete-run.js <rootPath> <runId> --complete-run [options]
|
|
16
|
+
* Complete (single/auto): node complete-run.js <rootPath> <runId> [options]
|
|
17
|
+
*
|
|
18
|
+
* Options:
|
|
19
|
+
* --files-created=JSON - JSON array of {path, purpose}
|
|
20
|
+
* --files-modified=JSON - JSON array of {path, changes}
|
|
21
|
+
* --decisions=JSON - JSON array of {decision, choice, rationale}
|
|
22
|
+
* --tests=N - Number of tests added
|
|
23
|
+
* --coverage=N - Coverage percentage
|
|
15
24
|
*/
|
|
16
25
|
|
|
17
26
|
const fs = require('fs');
|
|
@@ -131,7 +140,7 @@ function writeState(statePath, state) {
|
|
|
131
140
|
// Run Log Operations
|
|
132
141
|
// =============================================================================
|
|
133
142
|
|
|
134
|
-
function updateRunLog(runLogPath, params, completedTime) {
|
|
143
|
+
function updateRunLog(runLogPath, activeRun, params, completedTime, isFullCompletion) {
|
|
135
144
|
let content;
|
|
136
145
|
try {
|
|
137
146
|
content = fs.readFileSync(runLogPath, 'utf8');
|
|
@@ -143,40 +152,72 @@ function updateRunLog(runLogPath, params, completedTime) {
|
|
|
143
152
|
);
|
|
144
153
|
}
|
|
145
154
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
155
|
+
// If full completion, update run status
|
|
156
|
+
if (isFullCompletion) {
|
|
157
|
+
content = content.replace(/status: in_progress/, 'status: completed');
|
|
158
|
+
content = content.replace(/completed: null/, `completed: ${completedTime}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Update work items status in frontmatter
|
|
162
|
+
if (activeRun.work_items && Array.isArray(activeRun.work_items)) {
|
|
163
|
+
for (const item of activeRun.work_items) {
|
|
164
|
+
// Update status in YAML frontmatter
|
|
165
|
+
const statusPattern = new RegExp(`(id: ${item.id}\\n\\s+intent: [^\\n]+\\n\\s+mode: [^\\n]+\\n\\s+status: )(\\w+)`);
|
|
166
|
+
content = content.replace(statusPattern, `$1${item.status}`);
|
|
167
|
+
|
|
168
|
+
// Update status in markdown body
|
|
169
|
+
const bodyPattern = new RegExp(`(\\*\\*${item.id}\\*\\* \\([^)]+\\) — )(\\w+)`);
|
|
170
|
+
content = content.replace(bodyPattern, `$1${item.status}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Update current_item in frontmatter
|
|
174
|
+
content = content.replace(/current_item: [^\n]+/, `current_item: ${activeRun.current_item || 'none'}`);
|
|
175
|
+
|
|
176
|
+
// Update Current Item section
|
|
177
|
+
if (activeRun.current_item) {
|
|
178
|
+
const currentItem = activeRun.work_items.find(i => i.id === activeRun.current_item);
|
|
179
|
+
if (currentItem) {
|
|
180
|
+
content = content.replace(/## Current Item\n[^\n]+/, `## Current Item\n${currentItem.id} (${currentItem.mode})`);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
content = content.replace(/## Current Item\n[^\n]+/, `## Current Item\n(all completed)`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
149
186
|
|
|
150
|
-
// Format file lists
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
187
|
+
// Format file lists (only on full completion)
|
|
188
|
+
if (isFullCompletion) {
|
|
189
|
+
const filesCreatedText = params.filesCreated.length > 0
|
|
190
|
+
? params.filesCreated.map(f => `- \`${f.path}\`: ${f.purpose || '(no purpose)'}`).join('\n')
|
|
191
|
+
: '(none)';
|
|
154
192
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
193
|
+
const filesModifiedText = params.filesModified.length > 0
|
|
194
|
+
? params.filesModified.map(f => `- \`${f.path}\`: ${f.changes || '(no changes)'}`).join('\n')
|
|
195
|
+
: '(none)';
|
|
158
196
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
197
|
+
const decisionsText = params.decisions.length > 0
|
|
198
|
+
? params.decisions.map(d => `- **${d.decision}**: ${d.choice} (${d.rationale || 'no rationale'})`).join('\n')
|
|
199
|
+
: '(none)';
|
|
162
200
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
201
|
+
// Replace placeholder sections
|
|
202
|
+
content = content.replace('## Files Created\n(none yet)', `## Files Created\n${filesCreatedText}`);
|
|
203
|
+
content = content.replace('## Files Modified\n(none yet)', `## Files Modified\n${filesModifiedText}`);
|
|
204
|
+
content = content.replace('## Decisions\n(none yet)', `## Decisions\n${decisionsText}`);
|
|
167
205
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
206
|
+
// Add summary if not present
|
|
207
|
+
if (!content.includes('## Summary')) {
|
|
208
|
+
const itemCount = activeRun.work_items ? activeRun.work_items.length : 1;
|
|
209
|
+
content += `
|
|
171
210
|
|
|
172
211
|
## Summary
|
|
173
212
|
|
|
213
|
+
- Work items completed: ${itemCount}
|
|
174
214
|
- Files created: ${params.filesCreated.length}
|
|
175
215
|
- Files modified: ${params.filesModified.length}
|
|
176
216
|
- Tests added: ${params.testsAdded}
|
|
177
217
|
- Coverage: ${params.coverage}%
|
|
178
218
|
- Completed: ${completedTime}
|
|
179
219
|
`;
|
|
220
|
+
}
|
|
180
221
|
}
|
|
181
222
|
|
|
182
223
|
try {
|
|
@@ -191,11 +232,10 @@ function updateRunLog(runLogPath, params, completedTime) {
|
|
|
191
232
|
}
|
|
192
233
|
|
|
193
234
|
// =============================================================================
|
|
194
|
-
//
|
|
235
|
+
// Complete Current Item (for batch runs)
|
|
195
236
|
// =============================================================================
|
|
196
237
|
|
|
197
|
-
function
|
|
198
|
-
// Defaults for optional params
|
|
238
|
+
function completeCurrentItem(rootPath, runId, params = {}) {
|
|
199
239
|
const completionParams = {
|
|
200
240
|
filesCreated: params.filesCreated || [],
|
|
201
241
|
filesModified: params.filesModified || [],
|
|
@@ -204,16 +244,97 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
204
244
|
coverage: params.coverage || 0,
|
|
205
245
|
};
|
|
206
246
|
|
|
207
|
-
// Validate inputs
|
|
208
247
|
validateInputs(rootPath, runId);
|
|
209
|
-
|
|
210
|
-
// Validate FIRE project structure
|
|
211
248
|
const { statePath, runLogPath } = validateFireProject(rootPath, runId);
|
|
249
|
+
const state = readState(statePath);
|
|
250
|
+
|
|
251
|
+
if (!state.active_run) {
|
|
252
|
+
throw fireError(
|
|
253
|
+
'No active run found in state.yaml.',
|
|
254
|
+
'COMPLETE_040',
|
|
255
|
+
'The run may have already been completed or was never started.'
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (state.active_run.id !== runId) {
|
|
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
|
+
|
|
267
|
+
const completedTime = new Date().toISOString();
|
|
268
|
+
const workItems = state.active_run.work_items || [];
|
|
269
|
+
const currentItemId = state.active_run.current_item;
|
|
270
|
+
|
|
271
|
+
// Find and mark current item as completed
|
|
272
|
+
let currentItemIndex = -1;
|
|
273
|
+
for (let i = 0; i < workItems.length; i++) {
|
|
274
|
+
if (workItems[i].id === currentItemId) {
|
|
275
|
+
workItems[i].status = 'completed';
|
|
276
|
+
workItems[i].completed_at = completedTime;
|
|
277
|
+
currentItemIndex = i;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (currentItemIndex === -1) {
|
|
283
|
+
throw fireError(
|
|
284
|
+
`Current item "${currentItemId}" not found in work items.`,
|
|
285
|
+
'COMPLETE_050',
|
|
286
|
+
'The run state may be corrupted.'
|
|
287
|
+
);
|
|
288
|
+
}
|
|
212
289
|
|
|
213
|
-
//
|
|
290
|
+
// Find next pending item
|
|
291
|
+
let nextItem = null;
|
|
292
|
+
for (let i = currentItemIndex + 1; i < workItems.length; i++) {
|
|
293
|
+
if (workItems[i].status === 'pending') {
|
|
294
|
+
workItems[i].status = 'in_progress';
|
|
295
|
+
nextItem = workItems[i];
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Update state
|
|
301
|
+
state.active_run.work_items = workItems;
|
|
302
|
+
state.active_run.current_item = nextItem ? nextItem.id : null;
|
|
303
|
+
|
|
304
|
+
// Update run log
|
|
305
|
+
updateRunLog(runLogPath, state.active_run, completionParams, completedTime, false);
|
|
306
|
+
|
|
307
|
+
// Save state
|
|
308
|
+
writeState(statePath, state);
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
success: true,
|
|
312
|
+
runId: runId,
|
|
313
|
+
completedItem: currentItemId,
|
|
314
|
+
nextItem: nextItem ? nextItem.id : null,
|
|
315
|
+
remainingItems: workItems.filter(i => i.status === 'pending').length,
|
|
316
|
+
allItemsCompleted: nextItem === null,
|
|
317
|
+
completedAt: completedTime,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// =============================================================================
|
|
322
|
+
// Complete Entire Run
|
|
323
|
+
// =============================================================================
|
|
324
|
+
|
|
325
|
+
function completeRun(rootPath, runId, params = {}) {
|
|
326
|
+
const completionParams = {
|
|
327
|
+
filesCreated: params.filesCreated || [],
|
|
328
|
+
filesModified: params.filesModified || [],
|
|
329
|
+
decisions: params.decisions || [],
|
|
330
|
+
testsAdded: params.testsAdded || 0,
|
|
331
|
+
coverage: params.coverage || 0,
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
validateInputs(rootPath, runId);
|
|
335
|
+
const { statePath, runLogPath } = validateFireProject(rootPath, runId);
|
|
214
336
|
const state = readState(statePath);
|
|
215
337
|
|
|
216
|
-
// Validate active run matches
|
|
217
338
|
if (!state.active_run) {
|
|
218
339
|
throw fireError(
|
|
219
340
|
'No active run found in state.yaml.',
|
|
@@ -230,19 +351,33 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
230
351
|
);
|
|
231
352
|
}
|
|
232
353
|
|
|
233
|
-
// Extract work item and intent from active run
|
|
234
|
-
const workItemId = state.active_run.work_item;
|
|
235
|
-
const intentId = state.active_run.intent;
|
|
236
354
|
const completedTime = new Date().toISOString();
|
|
355
|
+
const workItems = state.active_run.work_items || [];
|
|
356
|
+
const scope = state.active_run.scope || 'single';
|
|
357
|
+
|
|
358
|
+
// Mark all items as completed
|
|
359
|
+
for (const item of workItems) {
|
|
360
|
+
if (item.status !== 'completed') {
|
|
361
|
+
item.status = 'completed';
|
|
362
|
+
item.completed_at = completedTime;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
state.active_run.work_items = workItems;
|
|
367
|
+
state.active_run.current_item = null;
|
|
237
368
|
|
|
238
369
|
// Update run log
|
|
239
|
-
updateRunLog(runLogPath, completionParams, completedTime);
|
|
370
|
+
updateRunLog(runLogPath, state.active_run, completionParams, completedTime, true);
|
|
240
371
|
|
|
241
372
|
// Build completed run record
|
|
242
373
|
const completedRun = {
|
|
243
374
|
id: runId,
|
|
244
|
-
|
|
245
|
-
|
|
375
|
+
scope: scope,
|
|
376
|
+
work_items: workItems.map(i => ({
|
|
377
|
+
id: i.id,
|
|
378
|
+
intent: i.intent,
|
|
379
|
+
mode: i.mode,
|
|
380
|
+
})),
|
|
246
381
|
completed: completedTime,
|
|
247
382
|
};
|
|
248
383
|
|
|
@@ -255,16 +390,17 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
255
390
|
|
|
256
391
|
// Update work item status in intents
|
|
257
392
|
if (Array.isArray(state.intents)) {
|
|
258
|
-
for (const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
workItem.
|
|
263
|
-
|
|
264
|
-
|
|
393
|
+
for (const workItem of workItems) {
|
|
394
|
+
for (const intent of state.intents) {
|
|
395
|
+
if (intent.id === workItem.intent && Array.isArray(intent.work_items)) {
|
|
396
|
+
for (const wi of intent.work_items) {
|
|
397
|
+
if (wi.id === workItem.id) {
|
|
398
|
+
wi.status = 'completed';
|
|
399
|
+
wi.run_id = runId;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
265
402
|
}
|
|
266
403
|
}
|
|
267
|
-
break;
|
|
268
404
|
}
|
|
269
405
|
}
|
|
270
406
|
}
|
|
@@ -278,12 +414,11 @@ function completeRun(rootPath, runId, params = {}) {
|
|
|
278
414
|
// Save state
|
|
279
415
|
writeState(statePath, state);
|
|
280
416
|
|
|
281
|
-
// Return result
|
|
282
417
|
return {
|
|
283
418
|
success: true,
|
|
284
419
|
runId: runId,
|
|
285
|
-
|
|
286
|
-
|
|
420
|
+
scope: scope,
|
|
421
|
+
workItemsCompleted: workItems.length,
|
|
287
422
|
completedAt: completedTime,
|
|
288
423
|
filesCreated: completionParams.filesCreated.length,
|
|
289
424
|
filesModified: completionParams.filesModified.length,
|
|
@@ -300,6 +435,8 @@ function parseArgs(args) {
|
|
|
300
435
|
const result = {
|
|
301
436
|
rootPath: args[0],
|
|
302
437
|
runId: args[1],
|
|
438
|
+
completeItem: false,
|
|
439
|
+
completeRunFlag: false,
|
|
303
440
|
filesCreated: [],
|
|
304
441
|
filesModified: [],
|
|
305
442
|
decisions: [],
|
|
@@ -309,7 +446,11 @@ function parseArgs(args) {
|
|
|
309
446
|
|
|
310
447
|
for (let i = 2; i < args.length; i++) {
|
|
311
448
|
const arg = args[i];
|
|
312
|
-
if (arg
|
|
449
|
+
if (arg === '--complete-item') {
|
|
450
|
+
result.completeItem = true;
|
|
451
|
+
} else if (arg === '--complete-run') {
|
|
452
|
+
result.completeRunFlag = true;
|
|
453
|
+
} else if (arg.startsWith('--files-created=')) {
|
|
313
454
|
try {
|
|
314
455
|
result.filesCreated = JSON.parse(arg.substring('--files-created='.length));
|
|
315
456
|
} catch (e) {
|
|
@@ -337,6 +478,32 @@ function parseArgs(args) {
|
|
|
337
478
|
return result;
|
|
338
479
|
}
|
|
339
480
|
|
|
481
|
+
function printUsage() {
|
|
482
|
+
console.error('Usage:');
|
|
483
|
+
console.error(' Complete current item: node complete-run.js <rootPath> <runId> --complete-item [options]');
|
|
484
|
+
console.error(' Complete entire run: node complete-run.js <rootPath> <runId> --complete-run [options]');
|
|
485
|
+
console.error(' Auto (single runs): node complete-run.js <rootPath> <runId> [options]');
|
|
486
|
+
console.error('');
|
|
487
|
+
console.error('Arguments:');
|
|
488
|
+
console.error(' rootPath - Project root directory');
|
|
489
|
+
console.error(' runId - Run ID to complete (e.g., run-003)');
|
|
490
|
+
console.error('');
|
|
491
|
+
console.error('Flags:');
|
|
492
|
+
console.error(' --complete-item - Complete only the current work item (batch/wide runs)');
|
|
493
|
+
console.error(' --complete-run - Complete the entire run');
|
|
494
|
+
console.error('');
|
|
495
|
+
console.error('Options:');
|
|
496
|
+
console.error(' --files-created=JSON - JSON array of {path, purpose}');
|
|
497
|
+
console.error(' --files-modified=JSON - JSON array of {path, changes}');
|
|
498
|
+
console.error(' --decisions=JSON - JSON array of {decision, choice, rationale}');
|
|
499
|
+
console.error(' --tests=N - Number of tests added');
|
|
500
|
+
console.error(' --coverage=N - Coverage percentage');
|
|
501
|
+
console.error('');
|
|
502
|
+
console.error('Examples:');
|
|
503
|
+
console.error(' node complete-run.js /project run-003 --complete-item');
|
|
504
|
+
console.error(' node complete-run.js /project run-003 --complete-run --tests=5 --coverage=85');
|
|
505
|
+
}
|
|
506
|
+
|
|
340
507
|
// =============================================================================
|
|
341
508
|
// CLI Interface
|
|
342
509
|
// =============================================================================
|
|
@@ -345,34 +512,32 @@ if (require.main === module) {
|
|
|
345
512
|
const args = process.argv.slice(2);
|
|
346
513
|
|
|
347
514
|
if (args.length < 2) {
|
|
348
|
-
|
|
349
|
-
console.error('');
|
|
350
|
-
console.error('Arguments:');
|
|
351
|
-
console.error(' rootPath - Project root directory');
|
|
352
|
-
console.error(' runId - Run ID to complete (e.g., run-003)');
|
|
353
|
-
console.error('');
|
|
354
|
-
console.error('Options:');
|
|
355
|
-
console.error(' --files-created=JSON - JSON array of {path, purpose}');
|
|
356
|
-
console.error(' --files-modified=JSON - JSON array of {path, changes}');
|
|
357
|
-
console.error(' --decisions=JSON - JSON array of {decision, choice, rationale}');
|
|
358
|
-
console.error(' --tests=N - Number of tests added');
|
|
359
|
-
console.error(' --coverage=N - Coverage percentage');
|
|
360
|
-
console.error('');
|
|
361
|
-
console.error('Example:');
|
|
362
|
-
console.error(' node complete-run.js /my/project run-003 --tests=5 --coverage=85');
|
|
515
|
+
printUsage();
|
|
363
516
|
process.exit(1);
|
|
364
517
|
}
|
|
365
518
|
|
|
366
519
|
const params = parseArgs(args);
|
|
367
520
|
|
|
368
521
|
try {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
522
|
+
let result;
|
|
523
|
+
if (params.completeItem) {
|
|
524
|
+
result = completeCurrentItem(params.rootPath, params.runId, {
|
|
525
|
+
filesCreated: params.filesCreated,
|
|
526
|
+
filesModified: params.filesModified,
|
|
527
|
+
decisions: params.decisions,
|
|
528
|
+
testsAdded: params.testsAdded,
|
|
529
|
+
coverage: params.coverage,
|
|
530
|
+
});
|
|
531
|
+
} else {
|
|
532
|
+
// Default: complete entire run
|
|
533
|
+
result = completeRun(params.rootPath, params.runId, {
|
|
534
|
+
filesCreated: params.filesCreated,
|
|
535
|
+
filesModified: params.filesModified,
|
|
536
|
+
decisions: params.decisions,
|
|
537
|
+
testsAdded: params.testsAdded,
|
|
538
|
+
coverage: params.coverage,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
376
541
|
console.log(JSON.stringify(result, null, 2));
|
|
377
542
|
process.exit(0);
|
|
378
543
|
} catch (err) {
|
|
@@ -381,4 +546,4 @@ if (require.main === module) {
|
|
|
381
546
|
}
|
|
382
547
|
}
|
|
383
548
|
|
|
384
|
-
module.exports = { completeRun };
|
|
549
|
+
module.exports = { completeRun, completeCurrentItem };
|
|
@@ -4,13 +4,19 @@
|
|
|
4
4
|
* FIRE Run Initialization Script
|
|
5
5
|
*
|
|
6
6
|
* Creates run record in state.yaml and run folder structure.
|
|
7
|
+
* Supports both single work item and batch/wide runs with multiple items.
|
|
8
|
+
*
|
|
7
9
|
* Ensures deterministic run ID generation by checking BOTH:
|
|
8
10
|
* - runs.completed history in state.yaml
|
|
9
11
|
* - existing run folders in .specs-fire/runs/
|
|
10
12
|
*
|
|
11
|
-
* Usage:
|
|
13
|
+
* Usage:
|
|
14
|
+
* Single item: node init-run.js <rootPath> <workItemId> <intentId> <mode>
|
|
15
|
+
* Batch/Wide: node init-run.js <rootPath> --batch '<workItemsJson>'
|
|
12
16
|
*
|
|
13
|
-
*
|
|
17
|
+
* Examples:
|
|
18
|
+
* node init-run.js /project login-endpoint user-auth confirm
|
|
19
|
+
* node init-run.js /project --batch '[{"id":"wi-1","intent":"int-1","mode":"autopilot"}]'
|
|
14
20
|
*/
|
|
15
21
|
|
|
16
22
|
const fs = require('fs');
|
|
@@ -33,35 +39,58 @@ function fireError(message, code, suggestion) {
|
|
|
33
39
|
// =============================================================================
|
|
34
40
|
|
|
35
41
|
const VALID_MODES = ['autopilot', 'confirm', 'validate'];
|
|
42
|
+
const VALID_SCOPES = ['single', 'batch', 'wide'];
|
|
36
43
|
|
|
37
|
-
function
|
|
44
|
+
function validateRootPath(rootPath) {
|
|
38
45
|
if (!rootPath || typeof rootPath !== 'string' || rootPath.trim() === '') {
|
|
39
46
|
throw fireError('rootPath is required.', 'INIT_001', 'Provide a valid project root path.');
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
if (!
|
|
43
|
-
throw fireError(
|
|
49
|
+
if (!fs.existsSync(rootPath)) {
|
|
50
|
+
throw fireError(
|
|
51
|
+
`Project root not found: "${rootPath}".`,
|
|
52
|
+
'INIT_040',
|
|
53
|
+
'Ensure the path exists and is accessible.'
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function validateWorkItem(item, index) {
|
|
59
|
+
if (!item.id || typeof item.id !== 'string' || item.id.trim() === '') {
|
|
60
|
+
throw fireError(
|
|
61
|
+
`Work item at index ${index} missing 'id'.`,
|
|
62
|
+
'INIT_010',
|
|
63
|
+
'Each work item must have an id.'
|
|
64
|
+
);
|
|
44
65
|
}
|
|
45
66
|
|
|
46
|
-
if (!
|
|
47
|
-
throw fireError(
|
|
67
|
+
if (!item.intent || typeof item.intent !== 'string' || item.intent.trim() === '') {
|
|
68
|
+
throw fireError(
|
|
69
|
+
`Work item "${item.id}" missing 'intent'.`,
|
|
70
|
+
'INIT_020',
|
|
71
|
+
'Each work item must have an intent.'
|
|
72
|
+
);
|
|
48
73
|
}
|
|
49
74
|
|
|
50
|
-
if (!mode || !VALID_MODES.includes(mode)) {
|
|
75
|
+
if (!item.mode || !VALID_MODES.includes(item.mode)) {
|
|
51
76
|
throw fireError(
|
|
52
|
-
`
|
|
77
|
+
`Work item "${item.id}" has invalid mode: "${item.mode}".`,
|
|
53
78
|
'INIT_030',
|
|
54
79
|
`Valid modes are: ${VALID_MODES.join(', ')}`
|
|
55
80
|
);
|
|
56
81
|
}
|
|
82
|
+
}
|
|
57
83
|
|
|
58
|
-
|
|
84
|
+
function validateWorkItems(workItems) {
|
|
85
|
+
if (!Array.isArray(workItems) || workItems.length === 0) {
|
|
59
86
|
throw fireError(
|
|
60
|
-
|
|
61
|
-
'
|
|
62
|
-
'
|
|
87
|
+
'Work items array is empty or invalid.',
|
|
88
|
+
'INIT_011',
|
|
89
|
+
'Provide at least one work item.'
|
|
63
90
|
);
|
|
64
91
|
}
|
|
92
|
+
|
|
93
|
+
workItems.forEach((item, index) => validateWorkItem(item, index));
|
|
65
94
|
}
|
|
66
95
|
|
|
67
96
|
function validateFireProject(rootPath) {
|
|
@@ -171,6 +200,19 @@ function generateRunId(runsPath, state) {
|
|
|
171
200
|
return `run-${String(nextNum).padStart(3, '0')}`;
|
|
172
201
|
}
|
|
173
202
|
|
|
203
|
+
// =============================================================================
|
|
204
|
+
// Scope Detection
|
|
205
|
+
// =============================================================================
|
|
206
|
+
|
|
207
|
+
function detectScope(workItems) {
|
|
208
|
+
if (workItems.length === 1) {
|
|
209
|
+
return 'single';
|
|
210
|
+
}
|
|
211
|
+
// For multiple items, default to batch
|
|
212
|
+
// (wide would be explicitly set by the caller if all compatible items are included)
|
|
213
|
+
return 'batch';
|
|
214
|
+
}
|
|
215
|
+
|
|
174
216
|
// =============================================================================
|
|
175
217
|
// Run Folder Creation
|
|
176
218
|
// =============================================================================
|
|
@@ -187,12 +229,21 @@ function createRunFolder(runPath) {
|
|
|
187
229
|
}
|
|
188
230
|
}
|
|
189
231
|
|
|
190
|
-
function createRunLog(runPath, runId,
|
|
232
|
+
function createRunLog(runPath, runId, workItems, scope, startTime) {
|
|
233
|
+
// Format work items for run.md
|
|
234
|
+
const workItemsList = workItems.map((item, index) => {
|
|
235
|
+
const status = index === 0 ? 'in_progress' : 'pending';
|
|
236
|
+
return ` - id: ${item.id}\n intent: ${item.intent}\n mode: ${item.mode}\n status: ${status}`;
|
|
237
|
+
}).join('\n');
|
|
238
|
+
|
|
239
|
+
const currentItem = workItems[0];
|
|
240
|
+
|
|
191
241
|
const runLog = `---
|
|
192
242
|
id: ${runId}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
243
|
+
scope: ${scope}
|
|
244
|
+
work_items:
|
|
245
|
+
${workItemsList}
|
|
246
|
+
current_item: ${currentItem.id}
|
|
196
247
|
status: in_progress
|
|
197
248
|
started: ${startTime}
|
|
198
249
|
completed: null
|
|
@@ -200,8 +251,14 @@ completed: null
|
|
|
200
251
|
|
|
201
252
|
# Run: ${runId}
|
|
202
253
|
|
|
203
|
-
##
|
|
204
|
-
${
|
|
254
|
+
## Scope
|
|
255
|
+
${scope} (${workItems.length} work item${workItems.length > 1 ? 's' : ''})
|
|
256
|
+
|
|
257
|
+
## Work Items
|
|
258
|
+
${workItems.map((item, i) => `${i + 1}. **${item.id}** (${item.mode}) — ${i === 0 ? 'in_progress' : 'pending'}`).join('\n')}
|
|
259
|
+
|
|
260
|
+
## Current Item
|
|
261
|
+
${currentItem.id} (${currentItem.mode})
|
|
205
262
|
|
|
206
263
|
## Files Created
|
|
207
264
|
(none yet)
|
|
@@ -229,9 +286,30 @@ ${workItemId}
|
|
|
229
286
|
// Main Function
|
|
230
287
|
// =============================================================================
|
|
231
288
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
289
|
+
/**
|
|
290
|
+
* Initialize a run with one or more work items.
|
|
291
|
+
*
|
|
292
|
+
* @param {string} rootPath - Project root directory
|
|
293
|
+
* @param {Array<{id: string, intent: string, mode: string}>} workItems - Work items to include in run
|
|
294
|
+
* @param {string} [scope] - Optional scope override ('single', 'batch', 'wide')
|
|
295
|
+
* @returns {object} Result with runId, runPath, workItems, scope, started
|
|
296
|
+
*/
|
|
297
|
+
function initRun(rootPath, workItems, scope) {
|
|
298
|
+
// Validate root path
|
|
299
|
+
validateRootPath(rootPath);
|
|
300
|
+
|
|
301
|
+
// Validate work items
|
|
302
|
+
validateWorkItems(workItems);
|
|
303
|
+
|
|
304
|
+
// Detect or validate scope
|
|
305
|
+
const detectedScope = scope || detectScope(workItems);
|
|
306
|
+
if (scope && !VALID_SCOPES.includes(scope)) {
|
|
307
|
+
throw fireError(
|
|
308
|
+
`Invalid scope: "${scope}".`,
|
|
309
|
+
'INIT_035',
|
|
310
|
+
`Valid scopes are: ${VALID_SCOPES.join(', ')}`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
235
313
|
|
|
236
314
|
// Validate FIRE project structure
|
|
237
315
|
const { statePath, runsPath } = validateFireProject(rootPath);
|
|
@@ -257,14 +335,22 @@ function initRun(rootPath, workItemId, intentId, mode) {
|
|
|
257
335
|
|
|
258
336
|
// Create run log
|
|
259
337
|
const startTime = new Date().toISOString();
|
|
260
|
-
createRunLog(runPath, runId,
|
|
338
|
+
createRunLog(runPath, runId, workItems, detectedScope, startTime);
|
|
339
|
+
|
|
340
|
+
// Prepare work items for state with status tracking
|
|
341
|
+
const stateWorkItems = workItems.map((item, index) => ({
|
|
342
|
+
id: item.id,
|
|
343
|
+
intent: item.intent,
|
|
344
|
+
mode: item.mode,
|
|
345
|
+
status: index === 0 ? 'in_progress' : 'pending',
|
|
346
|
+
}));
|
|
261
347
|
|
|
262
348
|
// Update state with active run
|
|
263
349
|
state.active_run = {
|
|
264
350
|
id: runId,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
351
|
+
scope: detectedScope,
|
|
352
|
+
work_items: stateWorkItems,
|
|
353
|
+
current_item: workItems[0].id,
|
|
268
354
|
started: startTime,
|
|
269
355
|
};
|
|
270
356
|
|
|
@@ -276,9 +362,9 @@ function initRun(rootPath, workItemId, intentId, mode) {
|
|
|
276
362
|
success: true,
|
|
277
363
|
runId: runId,
|
|
278
364
|
runPath: runPath,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
365
|
+
scope: detectedScope,
|
|
366
|
+
workItems: stateWorkItems,
|
|
367
|
+
currentItem: workItems[0].id,
|
|
282
368
|
started: startTime,
|
|
283
369
|
};
|
|
284
370
|
}
|
|
@@ -287,27 +373,76 @@ function initRun(rootPath, workItemId, intentId, mode) {
|
|
|
287
373
|
// CLI Interface
|
|
288
374
|
// =============================================================================
|
|
289
375
|
|
|
376
|
+
function printUsage() {
|
|
377
|
+
console.error('Usage:');
|
|
378
|
+
console.error(' Single item: node init-run.js <rootPath> <workItemId> <intentId> <mode>');
|
|
379
|
+
console.error(' Batch/Wide: node init-run.js <rootPath> --batch \'<workItemsJson>\' [--scope=<scope>]');
|
|
380
|
+
console.error('');
|
|
381
|
+
console.error('Arguments:');
|
|
382
|
+
console.error(' rootPath - Project root directory');
|
|
383
|
+
console.error(' workItemId - Work item ID (single mode)');
|
|
384
|
+
console.error(' intentId - Intent ID (single mode)');
|
|
385
|
+
console.error(' mode - Execution mode: autopilot, confirm, validate');
|
|
386
|
+
console.error('');
|
|
387
|
+
console.error('Options:');
|
|
388
|
+
console.error(' --batch - JSON array of work items');
|
|
389
|
+
console.error(' --scope - Override scope: single, batch, wide');
|
|
390
|
+
console.error('');
|
|
391
|
+
console.error('Work item JSON format:');
|
|
392
|
+
console.error(' [{"id": "wi-1", "intent": "int-1", "mode": "autopilot"}, ...]');
|
|
393
|
+
console.error('');
|
|
394
|
+
console.error('Examples:');
|
|
395
|
+
console.error(' node init-run.js /project login-endpoint user-auth confirm');
|
|
396
|
+
console.error(' node init-run.js /project --batch \'[{"id":"wi-1","intent":"int-1","mode":"autopilot"}]\'');
|
|
397
|
+
}
|
|
398
|
+
|
|
290
399
|
if (require.main === module) {
|
|
291
400
|
const args = process.argv.slice(2);
|
|
292
401
|
|
|
293
|
-
if (args.length <
|
|
294
|
-
|
|
295
|
-
console.error('');
|
|
296
|
-
console.error('Arguments:');
|
|
297
|
-
console.error(' rootPath - Project root directory');
|
|
298
|
-
console.error(' workItemId - Work item ID to execute');
|
|
299
|
-
console.error(' intentId - Intent ID containing the work item');
|
|
300
|
-
console.error(' mode - Execution mode (autopilot, confirm, validate)');
|
|
301
|
-
console.error('');
|
|
302
|
-
console.error('Example:');
|
|
303
|
-
console.error(' node init-run.js /my/project login-endpoint user-auth confirm');
|
|
402
|
+
if (args.length < 2) {
|
|
403
|
+
printUsage();
|
|
304
404
|
process.exit(1);
|
|
305
405
|
}
|
|
306
406
|
|
|
307
|
-
const
|
|
407
|
+
const rootPath = args[0];
|
|
408
|
+
let workItems = [];
|
|
409
|
+
let scope = null;
|
|
410
|
+
|
|
411
|
+
// Check if batch mode
|
|
412
|
+
if (args[1] === '--batch') {
|
|
413
|
+
if (args.length < 3) {
|
|
414
|
+
console.error('Error: --batch requires a JSON array of work items');
|
|
415
|
+
printUsage();
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
workItems = JSON.parse(args[2]);
|
|
421
|
+
} catch (err) {
|
|
422
|
+
console.error(`Error: Failed to parse work items JSON: ${err.message}`);
|
|
423
|
+
process.exit(1);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Check for --scope option
|
|
427
|
+
for (let i = 3; i < args.length; i++) {
|
|
428
|
+
if (args[i].startsWith('--scope=')) {
|
|
429
|
+
scope = args[i].substring('--scope='.length);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
// Single item mode (backwards compatible)
|
|
434
|
+
if (args.length < 4) {
|
|
435
|
+
printUsage();
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const [, workItemId, intentId, mode] = args;
|
|
440
|
+
workItems = [{ id: workItemId, intent: intentId, mode: mode }];
|
|
441
|
+
scope = 'single';
|
|
442
|
+
}
|
|
308
443
|
|
|
309
444
|
try {
|
|
310
|
-
const result = initRun(rootPath,
|
|
445
|
+
const result = initRun(rootPath, workItems, scope);
|
|
311
446
|
console.log(JSON.stringify(result, null, 2));
|
|
312
447
|
process.exit(0);
|
|
313
448
|
} catch (err) {
|
|
@@ -18,6 +18,29 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## Critical Clarifications
|
|
22
|
+
|
|
23
|
+
### Dependencies Mean Sequential Execution, NOT Separate Runs
|
|
24
|
+
|
|
25
|
+
**IMPORTANT**: When work items have dependencies:
|
|
26
|
+
- They execute **sequentially within the SAME run**
|
|
27
|
+
- They do **NOT** require separate runs
|
|
28
|
+
- The dependent item waits for its dependency to complete before starting
|
|
29
|
+
|
|
30
|
+
**Example**: If item 05 depends on item 04:
|
|
31
|
+
- **CORRECT**: ONE run with both items, 04 executes first, then 05
|
|
32
|
+
- **WRONG**: TWO separate runs
|
|
33
|
+
|
|
34
|
+
### All Options Can Include Multiple Items Per Run
|
|
35
|
+
|
|
36
|
+
| Option | Items Per Run | Execution |
|
|
37
|
+
|--------|---------------|-----------|
|
|
38
|
+
| Single | 1 item | One at a time, separate runs |
|
|
39
|
+
| Batch | Multiple items (same mode) | Sequential within run |
|
|
40
|
+
| Wide | All compatible items | Sequential within run |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
21
44
|
## Workflow
|
|
22
45
|
|
|
23
46
|
```xml
|
|
@@ -28,6 +51,7 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
28
51
|
SUGGEST smart groupings based on mode, dependencies, and user history.
|
|
29
52
|
LEARN from user choices to improve future recommendations.
|
|
30
53
|
NEVER force a scope - always let user choose.
|
|
54
|
+
DEPENDENCIES = SEQUENTIAL EXECUTION, NOT SEPARATE RUNS.
|
|
31
55
|
</mandate>
|
|
32
56
|
|
|
33
57
|
<step n="1" title="Discover Available Work">
|
|
@@ -71,10 +95,10 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
71
95
|
<action>Read workspace.run_scope_preference from state.yaml (if exists)</action>
|
|
72
96
|
|
|
73
97
|
<grouping-rules>
|
|
74
|
-
<rule>
|
|
75
|
-
<rule>
|
|
76
|
-
<rule>Cross-intent
|
|
77
|
-
<rule>Validate mode items
|
|
98
|
+
<rule>Dependencies = SEQUENTIAL execution in SAME run (NOT separate runs)</rule>
|
|
99
|
+
<rule>Different modes CAN be in same run (executed sequentially)</rule>
|
|
100
|
+
<rule>Cross-intent items allowed in same run if compatible</rule>
|
|
101
|
+
<rule>Validate mode items may benefit from running alone (more checkpoints)</rule>
|
|
78
102
|
</grouping-rules>
|
|
79
103
|
|
|
80
104
|
<generate-options>
|
|
@@ -84,22 +108,22 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
84
108
|
</option>
|
|
85
109
|
|
|
86
110
|
<option name="batch">
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
111
|
+
All pending items in ONE run
|
|
112
|
+
Execute sequentially (dependencies respected by order)
|
|
113
|
+
Checkpoints pause at confirm/validate items
|
|
114
|
+
Total runs: 1
|
|
90
115
|
</option>
|
|
91
116
|
|
|
92
117
|
<option name="wide">
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
Total runs: {minimum possible}
|
|
118
|
+
Same as batch - all items in one run
|
|
119
|
+
Total runs: 1
|
|
96
120
|
</option>
|
|
97
121
|
</generate-options>
|
|
98
122
|
</step>
|
|
99
123
|
|
|
100
124
|
<step n="4" title="Present Options">
|
|
101
125
|
<action>Determine recommended option based on:</action>
|
|
102
|
-
<substep>autonomy_bias (autonomous→
|
|
126
|
+
<substep>autonomy_bias (autonomous→batch, controlled→single)</substep>
|
|
103
127
|
<substep>run_scope_preference (user's historical choice)</substep>
|
|
104
128
|
<substep>Number of pending items (few items→single is fine)</substep>
|
|
105
129
|
|
|
@@ -115,20 +139,24 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
115
139
|
{/for}
|
|
116
140
|
{/for}
|
|
117
141
|
|
|
142
|
+
{if dependencies exist}
|
|
143
|
+
**Dependencies** (determines execution order):
|
|
144
|
+
- {dependent_item} depends on {dependency_item}
|
|
145
|
+
{/if}
|
|
146
|
+
|
|
118
147
|
---
|
|
119
148
|
|
|
120
149
|
**How would you like to execute?**
|
|
121
150
|
|
|
122
151
|
**[1] One at a time** — {single_count} separate runs
|
|
123
|
-
Most controlled, review after each
|
|
152
|
+
Most controlled, review after each run
|
|
124
153
|
|
|
125
|
-
**[2]
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
{/for}
|
|
154
|
+
**[2] Sequential chain** — 1 run with {count} items (Recommended)
|
|
155
|
+
Execute in order: {item1} → {item2} → ...
|
|
156
|
+
Checkpoints pause at confirm/validate items
|
|
129
157
|
|
|
130
|
-
**[3] All together** —
|
|
131
|
-
|
|
158
|
+
**[3] All together** — Same as [2]
|
|
159
|
+
1 run, sequential execution
|
|
132
160
|
|
|
133
161
|
Choose [1/2/3]:
|
|
134
162
|
</output>
|
|
@@ -139,13 +167,9 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
139
167
|
<set>run_scope = single</set>
|
|
140
168
|
<set>work_items_for_run = [first_pending_item]</set>
|
|
141
169
|
</check>
|
|
142
|
-
<check if="response == 2">
|
|
170
|
+
<check if="response == 2 or response == 3">
|
|
143
171
|
<set>run_scope = batch</set>
|
|
144
|
-
<set>work_items_for_run =
|
|
145
|
-
</check>
|
|
146
|
-
<check if="response == 3">
|
|
147
|
-
<set>run_scope = wide</set>
|
|
148
|
-
<set>work_items_for_run = all_compatible_items</set>
|
|
172
|
+
<set>work_items_for_run = all_pending_items_in_dependency_order</set>
|
|
149
173
|
</check>
|
|
150
174
|
</step>
|
|
151
175
|
|
|
@@ -170,14 +194,12 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
170
194
|
Starting run with {count} work item(s):
|
|
171
195
|
|
|
172
196
|
{for each item in work_items_for_run}
|
|
173
|
-
|
|
197
|
+
{index}. {item.title} ({item.mode})
|
|
174
198
|
{/for}
|
|
175
199
|
|
|
176
|
-
{if run_scope == batch or wide}
|
|
177
200
|
Items will execute sequentially within this run.
|
|
178
201
|
{if any item is confirm or validate}
|
|
179
|
-
Checkpoints will pause for approval
|
|
180
|
-
{/if}
|
|
202
|
+
Checkpoints will pause for approval at confirm/validate items.
|
|
181
203
|
{/if}
|
|
182
204
|
|
|
183
205
|
---
|
|
@@ -185,7 +207,7 @@ Plan the scope of a run by discovering available work items and suggesting group
|
|
|
185
207
|
Begin execution? [Y/n]
|
|
186
208
|
</output>
|
|
187
209
|
<check if="response == y">
|
|
188
|
-
<invoke-skill args="work_items_for_run">run-execute</invoke-skill>
|
|
210
|
+
<invoke-skill args="work_items_for_run, run_scope">run-execute</invoke-skill>
|
|
189
211
|
</check>
|
|
190
212
|
</step>
|
|
191
213
|
|
|
@@ -255,14 +277,13 @@ active_run:
|
|
|
255
277
|
```
|
|
256
278
|
1. Collect all pending items with their modes
|
|
257
279
|
2. Build dependency graph
|
|
258
|
-
3.
|
|
259
|
-
|
|
260
|
-
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
-
|
|
264
|
-
-
|
|
265
|
-
5. Return groupings with run counts
|
|
280
|
+
3. Sort items in dependency order (dependencies first)
|
|
281
|
+
4. For "single" option:
|
|
282
|
+
- Each item is its own run
|
|
283
|
+
5. For "batch" or "wide" option:
|
|
284
|
+
- ALL items in ONE run
|
|
285
|
+
- Execution order follows dependency graph
|
|
286
|
+
- Checkpoints pause at confirm/validate items
|
|
266
287
|
```
|
|
267
288
|
|
|
268
289
|
---
|
|
@@ -274,7 +295,7 @@ IF run_scope_history has 3+ same choices:
|
|
|
274
295
|
pre_selected = most_common_choice
|
|
275
296
|
|
|
276
297
|
ELSE IF autonomy_bias == autonomous:
|
|
277
|
-
recommended =
|
|
298
|
+
recommended = batch (all in one run)
|
|
278
299
|
|
|
279
300
|
ELSE IF autonomy_bias == controlled:
|
|
280
301
|
recommended = single
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specsmd",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.57",
|
|
4
4
|
"description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
|
|
5
5
|
"main": "lib/installer.js",
|
|
6
6
|
"bin": {
|