specsmd 0.0.0-dev.53 → 0.0.0-dev.55
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/agent.md +4 -1
- package/flows/fire/agents/builder/skills/run-execute/SKILL.md +23 -7
- package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.js +384 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.js +319 -0
- package/flows/fire/agents/builder/skills/run-execute/templates/test-report.md.hbs +81 -0
- package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +15 -6
- package/flows/fire/agents/orchestrator/skills/project-init/templates/coding-standards.md.hbs +149 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/system-architecture.md.hbs +101 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/tech-stack.md.hbs +136 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/testing-standards.md.hbs +94 -0
- package/flows/fire/memory-bank.yaml +12 -3
- package/package.json +1 -1
- package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.ts +0 -806
- package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.ts +0 -575
|
@@ -155,18 +155,21 @@ Each run creates a folder with its artifacts:
|
|
|
155
155
|
.specs-fire/runs/{run-id}/
|
|
156
156
|
├── plan.md # Approved implementation plan (confirm/validate modes)
|
|
157
157
|
├── run.md # Run log (metadata, files changed, decisions)
|
|
158
|
+
├── test-report.md # Test results, coverage, and acceptance validation
|
|
158
159
|
└── walkthrough.md # Implementation walkthrough (for human review)
|
|
159
160
|
```
|
|
160
161
|
|
|
161
|
-
**The
|
|
162
|
+
**The quartet**:
|
|
162
163
|
- **plan.md** — What we intended to do (approved at checkpoint)
|
|
163
164
|
- **run.md** — What happened during execution
|
|
165
|
+
- **test-report.md** — Test results and acceptance criteria validation
|
|
164
166
|
- **walkthrough.md** — Human-readable summary after completion
|
|
165
167
|
|
|
166
168
|
| Artifact | Location | Template |
|
|
167
169
|
|----------|----------|----------|
|
|
168
170
|
| Plan | `.specs-fire/runs/{run-id}/plan.md` | `skills/run-execute/templates/plan.md.hbs` |
|
|
169
171
|
| Run Log | `.specs-fire/runs/{run-id}/run.md` | (generated by script) |
|
|
172
|
+
| Test Report | `.specs-fire/runs/{run-id}/test-report.md` | `skills/run-execute/templates/test-report.md.hbs` |
|
|
170
173
|
| Walkthrough | `.specs-fire/runs/{run-id}/walkthrough.md` | `skills/walkthrough-generate/templates/walkthrough.md.hbs` |
|
|
171
174
|
|
|
172
175
|
---
|
|
@@ -57,7 +57,7 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
57
57
|
</mandate>
|
|
58
58
|
|
|
59
59
|
<step n="1" title="Initialize Run">
|
|
60
|
-
<action script="scripts/init-run.
|
|
60
|
+
<action script="scripts/init-run.js">Create run record</action>
|
|
61
61
|
<action>Generate run ID: run-{NNN}</action>
|
|
62
62
|
<action>Create folder: .specs-fire/runs/{run-id}/</action>
|
|
63
63
|
<action>Set active_run in state.yaml</action>
|
|
@@ -66,7 +66,11 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
66
66
|
<step n="2" title="Load Context">
|
|
67
67
|
<action>Read work item from .specs-fire/intents/{intent}/work-items/{id}.md</action>
|
|
68
68
|
<action>Read intent brief for broader context</action>
|
|
69
|
-
<action>
|
|
69
|
+
<action>Load ALL project standards:</action>
|
|
70
|
+
<substep>.specs-fire/standards/tech-stack.md — Technology choices</substep>
|
|
71
|
+
<substep>.specs-fire/standards/coding-standards.md — Code style and patterns</substep>
|
|
72
|
+
<substep>.specs-fire/standards/testing-standards.md — Testing strategy and coverage</substep>
|
|
73
|
+
<substep>.specs-fire/standards/system-architecture.md — Architecture context and constraints</substep>
|
|
70
74
|
<action>Determine execution mode from work item</action>
|
|
71
75
|
</step>
|
|
72
76
|
|
|
@@ -163,6 +167,12 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
163
167
|
</step>
|
|
164
168
|
|
|
165
169
|
<step n="6" title="Run Tests">
|
|
170
|
+
<action>Load testing standards from .specs-fire/standards/testing-standards.md</action>
|
|
171
|
+
<action>Write tests following testing standards:</action>
|
|
172
|
+
<substep>Unit tests for new/modified functions</substep>
|
|
173
|
+
<substep>Integration tests for API endpoints or workflows</substep>
|
|
174
|
+
<substep>Follow test naming and structure conventions</substep>
|
|
175
|
+
|
|
166
176
|
<action>Run test suite</action>
|
|
167
177
|
<check if="tests fail">
|
|
168
178
|
<output>
|
|
@@ -171,11 +181,15 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
171
181
|
<action>Fix failing tests</action>
|
|
172
182
|
<action>Re-run tests</action>
|
|
173
183
|
</check>
|
|
174
|
-
|
|
184
|
+
|
|
185
|
+
<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>
|
|
175
189
|
</step>
|
|
176
190
|
|
|
177
191
|
<step n="7" title="Complete Run">
|
|
178
|
-
<action script="scripts/complete-run.
|
|
192
|
+
<action script="scripts/complete-run.js">Finalize run record</action>
|
|
179
193
|
<action>Update state.yaml (clear active_run, mark work item complete)</action>
|
|
180
194
|
<action>Generate run log: .specs-fire/runs/{run-id}/run.md</action>
|
|
181
195
|
</step>
|
|
@@ -193,7 +207,9 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
193
207
|
Tests added: {count}
|
|
194
208
|
Coverage: {percentage}%
|
|
195
209
|
|
|
196
|
-
|
|
210
|
+
Artifacts:
|
|
211
|
+
- Test Report: .specs-fire/runs/{run-id}/test-report.md
|
|
212
|
+
- Walkthrough: .specs-fire/runs/{run-id}/walkthrough.md
|
|
197
213
|
|
|
198
214
|
Continue to next work item? [Y/n]
|
|
199
215
|
</output>
|
|
@@ -208,8 +224,8 @@ Before executing scripts, ensure required dependencies are installed:
|
|
|
208
224
|
|
|
209
225
|
| Script | Purpose |
|
|
210
226
|
|--------|---------|
|
|
211
|
-
| `scripts/init-run.
|
|
212
|
-
| `scripts/complete-run.
|
|
227
|
+
| `scripts/init-run.js` | Initialize run record and folder |
|
|
228
|
+
| `scripts/complete-run.js` | Finalize run and update state |
|
|
213
229
|
|
|
214
230
|
---
|
|
215
231
|
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FIRE Run Completion Script
|
|
5
|
+
*
|
|
6
|
+
* Finalizes a run by:
|
|
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
|
|
11
|
+
*
|
|
12
|
+
* Usage: node complete-run.js <rootPath> <runId> [--files-created=JSON] [--files-modified=JSON] [--decisions=JSON] [--tests=N] [--coverage=N]
|
|
13
|
+
*
|
|
14
|
+
* Example: node complete-run.js /project run-003 --tests=5 --coverage=85
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const yaml = require('yaml');
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Error Helper
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
function fireError(message, code, suggestion) {
|
|
26
|
+
const err = new Error(`FIRE Error [${code}]: ${message} ${suggestion}`);
|
|
27
|
+
err.code = code;
|
|
28
|
+
err.suggestion = suggestion;
|
|
29
|
+
return err;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Validation
|
|
34
|
+
// =============================================================================
|
|
35
|
+
|
|
36
|
+
function validateInputs(rootPath, runId) {
|
|
37
|
+
if (!rootPath || typeof rootPath !== 'string' || rootPath.trim() === '') {
|
|
38
|
+
throw fireError('rootPath is required.', 'COMPLETE_001', 'Provide a valid project root path.');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!runId || typeof runId !== 'string' || runId.trim() === '') {
|
|
42
|
+
throw fireError('runId is required.', 'COMPLETE_002', 'Provide the run ID to complete.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(rootPath)) {
|
|
46
|
+
throw fireError(
|
|
47
|
+
`Project root not found: "${rootPath}".`,
|
|
48
|
+
'COMPLETE_003',
|
|
49
|
+
'Ensure the path exists and is accessible.'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function validateFireProject(rootPath, runId) {
|
|
55
|
+
const fireDir = path.join(rootPath, '.specs-fire');
|
|
56
|
+
const statePath = path.join(fireDir, 'state.yaml');
|
|
57
|
+
const runsPath = path.join(fireDir, 'runs');
|
|
58
|
+
const runPath = path.join(runsPath, runId);
|
|
59
|
+
const runLogPath = path.join(runPath, 'run.md');
|
|
60
|
+
|
|
61
|
+
if (!fs.existsSync(fireDir)) {
|
|
62
|
+
throw fireError(
|
|
63
|
+
`FIRE project not initialized at: "${rootPath}".`,
|
|
64
|
+
'COMPLETE_010',
|
|
65
|
+
'Run fire-init first to initialize the project.'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!fs.existsSync(statePath)) {
|
|
70
|
+
throw fireError(
|
|
71
|
+
`State file not found at: "${statePath}".`,
|
|
72
|
+
'COMPLETE_011',
|
|
73
|
+
'The project may be corrupted. Try re-initializing.'
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(runPath)) {
|
|
78
|
+
throw fireError(
|
|
79
|
+
`Run folder not found: "${runPath}".`,
|
|
80
|
+
'COMPLETE_012',
|
|
81
|
+
`Ensure run "${runId}" was properly initialized.`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!fs.existsSync(runLogPath)) {
|
|
86
|
+
throw fireError(
|
|
87
|
+
`Run log not found: "${runLogPath}".`,
|
|
88
|
+
'COMPLETE_013',
|
|
89
|
+
`The run may have been partially initialized.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { statePath, runPath, runLogPath };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// =============================================================================
|
|
97
|
+
// State Operations
|
|
98
|
+
// =============================================================================
|
|
99
|
+
|
|
100
|
+
function readState(statePath) {
|
|
101
|
+
try {
|
|
102
|
+
const content = fs.readFileSync(statePath, 'utf8');
|
|
103
|
+
const state = yaml.parse(content);
|
|
104
|
+
if (!state || typeof state !== 'object') {
|
|
105
|
+
throw fireError('State file is empty or invalid.', 'COMPLETE_020', 'Check state.yaml format.');
|
|
106
|
+
}
|
|
107
|
+
return state;
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (err.code && err.code.startsWith('COMPLETE_')) throw err;
|
|
110
|
+
throw fireError(
|
|
111
|
+
`Failed to read state file: ${err.message}`,
|
|
112
|
+
'COMPLETE_021',
|
|
113
|
+
'Check file permissions and YAML syntax.'
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function writeState(statePath, state) {
|
|
119
|
+
try {
|
|
120
|
+
fs.writeFileSync(statePath, yaml.stringify(state));
|
|
121
|
+
} catch (err) {
|
|
122
|
+
throw fireError(
|
|
123
|
+
`Failed to write state file: ${err.message}`,
|
|
124
|
+
'COMPLETE_022',
|
|
125
|
+
'Check file permissions and disk space.'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// =============================================================================
|
|
131
|
+
// Run Log Operations
|
|
132
|
+
// =============================================================================
|
|
133
|
+
|
|
134
|
+
function updateRunLog(runLogPath, params, completedTime) {
|
|
135
|
+
let content;
|
|
136
|
+
try {
|
|
137
|
+
content = fs.readFileSync(runLogPath, 'utf8');
|
|
138
|
+
} catch (err) {
|
|
139
|
+
throw fireError(
|
|
140
|
+
`Failed to read run log: ${err.message}`,
|
|
141
|
+
'COMPLETE_030',
|
|
142
|
+
'Check file permissions.'
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Update status
|
|
147
|
+
content = content.replace(/status: in_progress/, 'status: completed');
|
|
148
|
+
content = content.replace(/completed: null/, `completed: ${completedTime}`);
|
|
149
|
+
|
|
150
|
+
// Format file lists
|
|
151
|
+
const filesCreatedText = params.filesCreated.length > 0
|
|
152
|
+
? params.filesCreated.map(f => `- \`${f.path}\`: ${f.purpose || '(no purpose)'}`).join('\n')
|
|
153
|
+
: '(none)';
|
|
154
|
+
|
|
155
|
+
const filesModifiedText = params.filesModified.length > 0
|
|
156
|
+
? params.filesModified.map(f => `- \`${f.path}\`: ${f.changes || '(no changes)'}`).join('\n')
|
|
157
|
+
: '(none)';
|
|
158
|
+
|
|
159
|
+
const decisionsText = params.decisions.length > 0
|
|
160
|
+
? params.decisions.map(d => `- **${d.decision}**: ${d.choice} (${d.rationale || 'no rationale'})`).join('\n')
|
|
161
|
+
: '(none)';
|
|
162
|
+
|
|
163
|
+
// Replace placeholder sections
|
|
164
|
+
content = content.replace('## Files Created\n(none yet)', `## Files Created\n${filesCreatedText}`);
|
|
165
|
+
content = content.replace('## Files Modified\n(none yet)', `## Files Modified\n${filesModifiedText}`);
|
|
166
|
+
content = content.replace('## Decisions\n(none yet)', `## Decisions\n${decisionsText}`);
|
|
167
|
+
|
|
168
|
+
// Add summary if not present
|
|
169
|
+
if (!content.includes('## Summary')) {
|
|
170
|
+
content += `
|
|
171
|
+
|
|
172
|
+
## Summary
|
|
173
|
+
|
|
174
|
+
- Files created: ${params.filesCreated.length}
|
|
175
|
+
- Files modified: ${params.filesModified.length}
|
|
176
|
+
- Tests added: ${params.testsAdded}
|
|
177
|
+
- Coverage: ${params.coverage}%
|
|
178
|
+
- Completed: ${completedTime}
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
fs.writeFileSync(runLogPath, content);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
throw fireError(
|
|
186
|
+
`Failed to write run log: ${err.message}`,
|
|
187
|
+
'COMPLETE_031',
|
|
188
|
+
'Check file permissions.'
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// =============================================================================
|
|
194
|
+
// Main Function
|
|
195
|
+
// =============================================================================
|
|
196
|
+
|
|
197
|
+
function completeRun(rootPath, runId, params = {}) {
|
|
198
|
+
// Defaults for optional params
|
|
199
|
+
const completionParams = {
|
|
200
|
+
filesCreated: params.filesCreated || [],
|
|
201
|
+
filesModified: params.filesModified || [],
|
|
202
|
+
decisions: params.decisions || [],
|
|
203
|
+
testsAdded: params.testsAdded || 0,
|
|
204
|
+
coverage: params.coverage || 0,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Validate inputs
|
|
208
|
+
validateInputs(rootPath, runId);
|
|
209
|
+
|
|
210
|
+
// Validate FIRE project structure
|
|
211
|
+
const { statePath, runLogPath } = validateFireProject(rootPath, runId);
|
|
212
|
+
|
|
213
|
+
// Read state
|
|
214
|
+
const state = readState(statePath);
|
|
215
|
+
|
|
216
|
+
// Validate active run matches
|
|
217
|
+
if (!state.active_run) {
|
|
218
|
+
throw fireError(
|
|
219
|
+
'No active run found in state.yaml.',
|
|
220
|
+
'COMPLETE_040',
|
|
221
|
+
'The run may have already been completed or was never started.'
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (state.active_run.id !== runId) {
|
|
226
|
+
throw fireError(
|
|
227
|
+
`Run ID mismatch. Active run is "${state.active_run.id}" but trying to complete "${runId}".`,
|
|
228
|
+
'COMPLETE_041',
|
|
229
|
+
`Complete the active run "${state.active_run.id}" first.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
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
|
+
const completedTime = new Date().toISOString();
|
|
237
|
+
|
|
238
|
+
// Update run log
|
|
239
|
+
updateRunLog(runLogPath, completionParams, completedTime);
|
|
240
|
+
|
|
241
|
+
// Build completed run record
|
|
242
|
+
const completedRun = {
|
|
243
|
+
id: runId,
|
|
244
|
+
work_item: workItemId,
|
|
245
|
+
intent: intentId,
|
|
246
|
+
completed: completedTime,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Get existing runs history or initialize
|
|
250
|
+
const existingRuns = state.runs || { completed: [] };
|
|
251
|
+
const existingCompleted = Array.isArray(existingRuns.completed) ? existingRuns.completed : [];
|
|
252
|
+
|
|
253
|
+
// Check for duplicate (idempotency)
|
|
254
|
+
const alreadyRecorded = existingCompleted.some(r => r.id === runId);
|
|
255
|
+
|
|
256
|
+
// Update work item status in intents
|
|
257
|
+
if (Array.isArray(state.intents)) {
|
|
258
|
+
for (const intent of state.intents) {
|
|
259
|
+
if (intent.id === intentId && Array.isArray(intent.work_items)) {
|
|
260
|
+
for (const workItem of intent.work_items) {
|
|
261
|
+
if (workItem.id === workItemId) {
|
|
262
|
+
workItem.status = 'completed';
|
|
263
|
+
workItem.run_id = runId;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Update state
|
|
273
|
+
state.active_run = null;
|
|
274
|
+
state.runs = {
|
|
275
|
+
completed: alreadyRecorded ? existingCompleted : [...existingCompleted, completedRun],
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Save state
|
|
279
|
+
writeState(statePath, state);
|
|
280
|
+
|
|
281
|
+
// Return result
|
|
282
|
+
return {
|
|
283
|
+
success: true,
|
|
284
|
+
runId: runId,
|
|
285
|
+
workItemId: workItemId,
|
|
286
|
+
intentId: intentId,
|
|
287
|
+
completedAt: completedTime,
|
|
288
|
+
filesCreated: completionParams.filesCreated.length,
|
|
289
|
+
filesModified: completionParams.filesModified.length,
|
|
290
|
+
testsAdded: completionParams.testsAdded,
|
|
291
|
+
coverage: completionParams.coverage,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// =============================================================================
|
|
296
|
+
// CLI Argument Parsing
|
|
297
|
+
// =============================================================================
|
|
298
|
+
|
|
299
|
+
function parseArgs(args) {
|
|
300
|
+
const result = {
|
|
301
|
+
rootPath: args[0],
|
|
302
|
+
runId: args[1],
|
|
303
|
+
filesCreated: [],
|
|
304
|
+
filesModified: [],
|
|
305
|
+
decisions: [],
|
|
306
|
+
testsAdded: 0,
|
|
307
|
+
coverage: 0,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
for (let i = 2; i < args.length; i++) {
|
|
311
|
+
const arg = args[i];
|
|
312
|
+
if (arg.startsWith('--files-created=')) {
|
|
313
|
+
try {
|
|
314
|
+
result.filesCreated = JSON.parse(arg.substring('--files-created='.length));
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.error('Warning: Could not parse --files-created JSON');
|
|
317
|
+
}
|
|
318
|
+
} else if (arg.startsWith('--files-modified=')) {
|
|
319
|
+
try {
|
|
320
|
+
result.filesModified = JSON.parse(arg.substring('--files-modified='.length));
|
|
321
|
+
} catch (e) {
|
|
322
|
+
console.error('Warning: Could not parse --files-modified JSON');
|
|
323
|
+
}
|
|
324
|
+
} else if (arg.startsWith('--decisions=')) {
|
|
325
|
+
try {
|
|
326
|
+
result.decisions = JSON.parse(arg.substring('--decisions='.length));
|
|
327
|
+
} catch (e) {
|
|
328
|
+
console.error('Warning: Could not parse --decisions JSON');
|
|
329
|
+
}
|
|
330
|
+
} else if (arg.startsWith('--tests=')) {
|
|
331
|
+
result.testsAdded = parseInt(arg.substring('--tests='.length), 10) || 0;
|
|
332
|
+
} else if (arg.startsWith('--coverage=')) {
|
|
333
|
+
result.coverage = parseFloat(arg.substring('--coverage='.length)) || 0;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// =============================================================================
|
|
341
|
+
// CLI Interface
|
|
342
|
+
// =============================================================================
|
|
343
|
+
|
|
344
|
+
if (require.main === module) {
|
|
345
|
+
const args = process.argv.slice(2);
|
|
346
|
+
|
|
347
|
+
if (args.length < 2) {
|
|
348
|
+
console.error('Usage: node complete-run.js <rootPath> <runId> [options]');
|
|
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');
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const params = parseArgs(args);
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
const result = completeRun(params.rootPath, params.runId, {
|
|
370
|
+
filesCreated: params.filesCreated,
|
|
371
|
+
filesModified: params.filesModified,
|
|
372
|
+
decisions: params.decisions,
|
|
373
|
+
testsAdded: params.testsAdded,
|
|
374
|
+
coverage: params.coverage,
|
|
375
|
+
});
|
|
376
|
+
console.log(JSON.stringify(result, null, 2));
|
|
377
|
+
process.exit(0);
|
|
378
|
+
} catch (err) {
|
|
379
|
+
console.error(err.message);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
module.exports = { completeRun };
|