specsmd 0.0.0-dev.6 → 0.0.0-dev.60

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.
Files changed (73) hide show
  1. package/README.md +10 -2
  2. package/flows/aidlc/commands/construction-agent.md +5 -1
  3. package/flows/aidlc/commands/inception-agent.md +4 -0
  4. package/flows/aidlc/commands/master-agent.md +4 -0
  5. package/flows/aidlc/commands/operations-agent.md +4 -0
  6. package/flows/aidlc/memory-bank.yaml +2 -1
  7. package/{scripts → flows/aidlc/scripts}/artifact-validator.js +3 -3
  8. package/{scripts → flows/aidlc/scripts}/bolt-complete.js +35 -4
  9. package/{scripts → flows/aidlc/scripts}/status-integrity.js +4 -4
  10. package/flows/aidlc/skills/construction/bolt-list.md +1 -1
  11. package/flows/aidlc/skills/construction/bolt-start.md +2 -2
  12. package/flows/aidlc/skills/construction/bolt-status.md +1 -1
  13. package/flows/aidlc/skills/construction/prototype-apply.md +305 -0
  14. package/flows/aidlc/skills/inception/bolt-plan.md +15 -2
  15. package/flows/aidlc/skills/inception/vibe-to-spec.md +406 -0
  16. package/flows/aidlc/skills/master/analyze-context.md +1 -1
  17. package/flows/aidlc/templates/construction/bolt-template.md +22 -1
  18. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +73 -11
  19. package/flows/aidlc/templates/construction/bolt-types/simple-construction-bolt.md +5 -0
  20. package/flows/aidlc/templates/standards/decision-index-template.md +32 -0
  21. package/flows/fire/README.md +19 -0
  22. package/flows/fire/agents/builder/agent.md +272 -0
  23. package/flows/fire/agents/builder/skills/run-execute/SKILL.md +455 -0
  24. package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.js +549 -0
  25. package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.js +454 -0
  26. package/flows/fire/agents/builder/skills/run-execute/templates/plan.md.hbs +61 -0
  27. package/flows/fire/agents/builder/skills/run-execute/templates/test-report.md.hbs +81 -0
  28. package/flows/fire/agents/builder/skills/run-plan/SKILL.md +376 -0
  29. package/flows/fire/agents/builder/skills/run-status/SKILL.md +94 -0
  30. package/flows/fire/agents/builder/skills/walkthrough-generate/SKILL.md +140 -0
  31. package/flows/fire/agents/builder/skills/walkthrough-generate/scripts/render-walkthrough.ts +755 -0
  32. package/flows/fire/agents/builder/skills/walkthrough-generate/templates/walkthrough.md.hbs +77 -0
  33. package/flows/fire/agents/orchestrator/agent.md +113 -0
  34. package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +150 -0
  35. package/flows/fire/agents/orchestrator/skills/project-init/templates/coding-standards.md.hbs +149 -0
  36. package/flows/fire/agents/orchestrator/skills/project-init/templates/system-architecture.md.hbs +101 -0
  37. package/flows/fire/agents/orchestrator/skills/project-init/templates/tech-stack.md.hbs +136 -0
  38. package/flows/fire/agents/orchestrator/skills/project-init/templates/testing-standards.md.hbs +94 -0
  39. package/flows/fire/agents/orchestrator/skills/route/SKILL.md +123 -0
  40. package/flows/fire/agents/orchestrator/skills/status/SKILL.md +99 -0
  41. package/flows/fire/agents/planner/agent.md +122 -0
  42. package/flows/fire/agents/planner/skills/design-doc-generate/SKILL.md +213 -0
  43. package/flows/fire/agents/planner/skills/design-doc-generate/templates/design.md.hbs +76 -0
  44. package/flows/fire/agents/planner/skills/intent-capture/SKILL.md +155 -0
  45. package/flows/fire/agents/planner/skills/intent-capture/templates/brief.md.hbs +40 -0
  46. package/flows/fire/agents/planner/skills/work-item-decompose/SKILL.md +194 -0
  47. package/flows/fire/agents/planner/skills/work-item-decompose/templates/work-item.md.hbs +40 -0
  48. package/flows/fire/commands/fire-builder.md +56 -0
  49. package/flows/fire/commands/fire-planner.md +48 -0
  50. package/flows/fire/commands/fire.md +46 -0
  51. package/flows/fire/memory-bank.yaml +164 -0
  52. package/flows/fire/quick-start.md +130 -0
  53. package/flows/simple/README.md +190 -0
  54. package/flows/simple/agents/agent.md +404 -0
  55. package/flows/simple/commands/agent.md +60 -0
  56. package/flows/simple/context-config.yaml +34 -0
  57. package/flows/simple/memory-bank.yaml +66 -0
  58. package/flows/simple/quick-start.md +231 -0
  59. package/flows/simple/skills/design.md +96 -0
  60. package/flows/simple/skills/execute.md +190 -0
  61. package/flows/simple/skills/requirements.md +94 -0
  62. package/flows/simple/skills/tasks.md +136 -0
  63. package/flows/simple/templates/design-template.md +138 -0
  64. package/flows/simple/templates/requirements-template.md +85 -0
  65. package/flows/simple/templates/tasks-template.md +104 -0
  66. package/lib/analytics/tracker.js +6 -2
  67. package/lib/constants.js +17 -8
  68. package/lib/installer.js +5 -15
  69. package/lib/installers/KiroInstaller.js +55 -0
  70. package/lib/installers/OpenCodeInstaller.js +9 -1
  71. package/lib/installers/ToolInstaller.js +4 -1
  72. package/lib/installers/WindsurfInstaller.js +0 -54
  73. package/package.json +3 -52
@@ -0,0 +1,454 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FIRE Run Initialization Script
5
+ *
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
+ *
9
+ * Ensures deterministic run ID generation by checking BOTH:
10
+ * - runs.completed history in state.yaml
11
+ * - existing run folders in .specs-fire/runs/
12
+ *
13
+ * Usage:
14
+ * Single item: node init-run.js <rootPath> <workItemId> <intentId> <mode>
15
+ * Batch/Wide: node init-run.js <rootPath> --batch '<workItemsJson>'
16
+ *
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"}]'
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const yaml = require('yaml');
25
+
26
+ // =============================================================================
27
+ // Error Helper
28
+ // =============================================================================
29
+
30
+ function fireError(message, code, suggestion) {
31
+ const err = new Error(`FIRE Error [${code}]: ${message} ${suggestion}`);
32
+ err.code = code;
33
+ err.suggestion = suggestion;
34
+ return err;
35
+ }
36
+
37
+ // =============================================================================
38
+ // Validation
39
+ // =============================================================================
40
+
41
+ const VALID_MODES = ['autopilot', 'confirm', 'validate'];
42
+ const VALID_SCOPES = ['single', 'batch', 'wide'];
43
+
44
+ function validateRootPath(rootPath) {
45
+ if (!rootPath || typeof rootPath !== 'string' || rootPath.trim() === '') {
46
+ throw fireError('rootPath is required.', 'INIT_001', 'Provide a valid project root path.');
47
+ }
48
+
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
+ );
65
+ }
66
+
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
+ );
73
+ }
74
+
75
+ if (!item.mode || !VALID_MODES.includes(item.mode)) {
76
+ throw fireError(
77
+ `Work item "${item.id}" has invalid mode: "${item.mode}".`,
78
+ 'INIT_030',
79
+ `Valid modes are: ${VALID_MODES.join(', ')}`
80
+ );
81
+ }
82
+ }
83
+
84
+ function validateWorkItems(workItems) {
85
+ if (!Array.isArray(workItems) || workItems.length === 0) {
86
+ throw fireError(
87
+ 'Work items array is empty or invalid.',
88
+ 'INIT_011',
89
+ 'Provide at least one work item.'
90
+ );
91
+ }
92
+
93
+ workItems.forEach((item, index) => validateWorkItem(item, index));
94
+ }
95
+
96
+ function validateFireProject(rootPath) {
97
+ const fireDir = path.join(rootPath, '.specs-fire');
98
+ const statePath = path.join(fireDir, 'state.yaml');
99
+ const runsPath = path.join(fireDir, 'runs');
100
+
101
+ if (!fs.existsSync(fireDir)) {
102
+ throw fireError(
103
+ `FIRE project not initialized at: "${rootPath}".`,
104
+ 'INIT_041',
105
+ 'Run fire-init first to initialize the project.'
106
+ );
107
+ }
108
+
109
+ if (!fs.existsSync(statePath)) {
110
+ throw fireError(
111
+ `State file not found at: "${statePath}".`,
112
+ 'INIT_042',
113
+ 'The project may be corrupted. Try re-initializing.'
114
+ );
115
+ }
116
+
117
+ return { fireDir, statePath, runsPath };
118
+ }
119
+
120
+ // =============================================================================
121
+ // State Operations
122
+ // =============================================================================
123
+
124
+ function readState(statePath) {
125
+ try {
126
+ const content = fs.readFileSync(statePath, 'utf8');
127
+ const state = yaml.parse(content);
128
+ if (!state || typeof state !== 'object') {
129
+ throw fireError('State file is empty or invalid.', 'INIT_050', 'Check state.yaml format.');
130
+ }
131
+ return state;
132
+ } catch (err) {
133
+ if (err.code && err.code.startsWith('INIT_')) throw err;
134
+ throw fireError(
135
+ `Failed to read state file: ${err.message}`,
136
+ 'INIT_051',
137
+ 'Check file permissions and YAML syntax.'
138
+ );
139
+ }
140
+ }
141
+
142
+ function writeState(statePath, state) {
143
+ try {
144
+ fs.writeFileSync(statePath, yaml.stringify(state));
145
+ } catch (err) {
146
+ throw fireError(
147
+ `Failed to write state file: ${err.message}`,
148
+ 'INIT_052',
149
+ 'Check file permissions and disk space.'
150
+ );
151
+ }
152
+ }
153
+
154
+ // =============================================================================
155
+ // Run ID Generation (CRITICAL - checks both history and file system)
156
+ // =============================================================================
157
+
158
+ function generateRunId(runsPath, state) {
159
+ // Ensure runs directory exists
160
+ if (!fs.existsSync(runsPath)) {
161
+ fs.mkdirSync(runsPath, { recursive: true });
162
+ }
163
+
164
+ // Source 1: Get max from state.yaml runs.completed history
165
+ let maxFromHistory = 0;
166
+ if (state.runs && Array.isArray(state.runs.completed)) {
167
+ for (const run of state.runs.completed) {
168
+ if (run.id) {
169
+ const match = run.id.match(/^run-(\d+)$/);
170
+ if (match) {
171
+ const num = parseInt(match[1], 10);
172
+ if (num > maxFromHistory) maxFromHistory = num;
173
+ }
174
+ }
175
+ }
176
+ }
177
+
178
+ // Source 2: Get max from file system (defensive)
179
+ let maxFromFileSystem = 0;
180
+ try {
181
+ const entries = fs.readdirSync(runsPath);
182
+ for (const entry of entries) {
183
+ if (/^run-\d{3,}$/.test(entry)) {
184
+ const num = parseInt(entry.replace('run-', ''), 10);
185
+ if (num > maxFromFileSystem) maxFromFileSystem = num;
186
+ }
187
+ }
188
+ } catch (err) {
189
+ throw fireError(
190
+ `Failed to read runs directory: ${err.message}`,
191
+ 'INIT_060',
192
+ 'Check directory permissions.'
193
+ );
194
+ }
195
+
196
+ // Use MAX of both to ensure no duplicates
197
+ const maxNum = Math.max(maxFromHistory, maxFromFileSystem);
198
+ const nextNum = maxNum + 1;
199
+
200
+ return `run-${String(nextNum).padStart(3, '0')}`;
201
+ }
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
+
216
+ // =============================================================================
217
+ // Run Folder Creation
218
+ // =============================================================================
219
+
220
+ function createRunFolder(runPath) {
221
+ try {
222
+ fs.mkdirSync(runPath, { recursive: true });
223
+ } catch (err) {
224
+ throw fireError(
225
+ `Failed to create run folder: ${err.message}`,
226
+ 'INIT_070',
227
+ 'Check directory permissions and disk space.'
228
+ );
229
+ }
230
+ }
231
+
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
+
241
+ const runLog = `---
242
+ id: ${runId}
243
+ scope: ${scope}
244
+ work_items:
245
+ ${workItemsList}
246
+ current_item: ${currentItem.id}
247
+ status: in_progress
248
+ started: ${startTime}
249
+ completed: null
250
+ ---
251
+
252
+ # Run: ${runId}
253
+
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})
262
+
263
+ ## Files Created
264
+ (none yet)
265
+
266
+ ## Files Modified
267
+ (none yet)
268
+
269
+ ## Decisions
270
+ (none yet)
271
+ `;
272
+
273
+ const runLogPath = path.join(runPath, 'run.md');
274
+ try {
275
+ fs.writeFileSync(runLogPath, runLog);
276
+ } catch (err) {
277
+ throw fireError(
278
+ `Failed to create run log: ${err.message}`,
279
+ 'INIT_071',
280
+ 'Check file permissions.'
281
+ );
282
+ }
283
+ }
284
+
285
+ // =============================================================================
286
+ // Main Function
287
+ // =============================================================================
288
+
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
+ }
313
+
314
+ // Validate FIRE project structure
315
+ const { statePath, runsPath } = validateFireProject(rootPath);
316
+
317
+ // Read state
318
+ const state = readState(statePath);
319
+
320
+ // Check for existing active run
321
+ if (state.active_run) {
322
+ throw fireError(
323
+ `A run is already active: "${state.active_run.id}".`,
324
+ 'INIT_080',
325
+ `Complete or cancel run "${state.active_run.id}" before starting a new one.`
326
+ );
327
+ }
328
+
329
+ // Generate run ID (checks both history AND file system)
330
+ const runId = generateRunId(runsPath, state);
331
+ const runPath = path.join(runsPath, runId);
332
+
333
+ // Create run folder
334
+ createRunFolder(runPath);
335
+
336
+ // Create run log
337
+ const startTime = new Date().toISOString();
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
+ }));
347
+
348
+ // Update state with active run
349
+ state.active_run = {
350
+ id: runId,
351
+ scope: detectedScope,
352
+ work_items: stateWorkItems,
353
+ current_item: workItems[0].id,
354
+ started: startTime,
355
+ };
356
+
357
+ // Save state
358
+ writeState(statePath, state);
359
+
360
+ // Return result
361
+ return {
362
+ success: true,
363
+ runId: runId,
364
+ runPath: runPath,
365
+ scope: detectedScope,
366
+ workItems: stateWorkItems,
367
+ currentItem: workItems[0].id,
368
+ started: startTime,
369
+ };
370
+ }
371
+
372
+ // =============================================================================
373
+ // CLI Interface
374
+ // =============================================================================
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
+
399
+ if (require.main === module) {
400
+ const args = process.argv.slice(2);
401
+
402
+ if (args.length < 2) {
403
+ printUsage();
404
+ process.exit(1);
405
+ }
406
+
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
+ }
443
+
444
+ try {
445
+ const result = initRun(rootPath, workItems, scope);
446
+ console.log(JSON.stringify(result, null, 2));
447
+ process.exit(0);
448
+ } catch (err) {
449
+ console.error(err.message);
450
+ process.exit(1);
451
+ }
452
+ }
453
+
454
+ module.exports = { initRun };
@@ -0,0 +1,61 @@
1
+ ---
2
+ run: {{run_id}}
3
+ work_item: {{work_item_id}}
4
+ intent: {{intent_id}}
5
+ mode: {{mode}}
6
+ checkpoint: {{checkpoint_type}}
7
+ approved_at: {{approved_at}}
8
+ ---
9
+
10
+ # Implementation Plan: {{title}}
11
+
12
+ ## Approach
13
+
14
+ {{approach}}
15
+
16
+ ## Files to Create
17
+
18
+ | File | Purpose |
19
+ |------|---------|
20
+ {{#each files_to_create}}
21
+ | `{{this.path}}` | {{this.purpose}} |
22
+ {{/each}}
23
+ {{#unless files_to_create}}
24
+ | (none) | |
25
+ {{/unless}}
26
+
27
+ ## Files to Modify
28
+
29
+ | File | Changes |
30
+ |------|---------|
31
+ {{#each files_to_modify}}
32
+ | `{{this.path}}` | {{this.changes}} |
33
+ {{/each}}
34
+ {{#unless files_to_modify}}
35
+ | (none) | |
36
+ {{/unless}}
37
+
38
+ {{#if tests}}
39
+ ## Tests
40
+
41
+ | Test File | Coverage |
42
+ |-----------|----------|
43
+ {{#each tests}}
44
+ | `{{this.path}}` | {{this.coverage}} |
45
+ {{/each}}
46
+ {{/if}}
47
+
48
+ {{#if technical_details}}
49
+ ## Technical Details
50
+
51
+ {{technical_details}}
52
+ {{/if}}
53
+
54
+ {{#if design_doc}}
55
+ ## Based on Design Doc
56
+
57
+ Reference: `{{design_doc}}`
58
+ {{/if}}
59
+
60
+ ---
61
+ *Plan approved at checkpoint. Execution follows.*
@@ -0,0 +1,81 @@
1
+ ---
2
+ run: {{run_id}}
3
+ work_item: {{work_item_id}}
4
+ intent: {{intent_id}}
5
+ generated: {{generated_at}}
6
+ status: {{status}}
7
+ ---
8
+
9
+ # Test Report: {{title}}
10
+
11
+ ## Summary
12
+
13
+ | Category | Passed | Failed | Skipped | Coverage |
14
+ |----------|--------|--------|---------|----------|
15
+ | Unit | {{unit_passed}} | {{unit_failed}} | {{unit_skipped}} | {{unit_coverage}}% |
16
+ | Integration | {{integration_passed}} | {{integration_failed}} | {{integration_skipped}} | {{integration_coverage}}% |
17
+ | **Total** | {{total_passed}} | {{total_failed}} | {{total_skipped}} | {{total_coverage}}% |
18
+
19
+ ## Acceptance Criteria Validation
20
+
21
+ {{#each acceptance_criteria}}
22
+ - {{#if this.passed}}✅{{else}}❌{{/if}} **{{this.criterion}}** — {{this.status}}
23
+ {{/each}}
24
+
25
+ ## Tests Written
26
+
27
+ ### Unit Tests
28
+
29
+ {{#each unit_tests}}
30
+ - `{{this.file}}` — {{this.description}}
31
+ {{/each}}
32
+
33
+ ### Integration Tests
34
+
35
+ {{#each integration_tests}}
36
+ - `{{this.file}}` — {{this.description}}
37
+ {{/each}}
38
+
39
+ ## Test Commands
40
+
41
+ ```bash
42
+ # Run all tests
43
+ {{test_command}}
44
+
45
+ # Run with coverage
46
+ {{coverage_command}}
47
+ ```
48
+
49
+ ## Coverage Details
50
+
51
+ {{#if coverage_details}}
52
+ | Module | Statements | Branches | Functions | Lines |
53
+ |--------|------------|----------|-----------|-------|
54
+ {{#each coverage_details}}
55
+ | `{{this.module}}` | {{this.statements}}% | {{this.branches}}% | {{this.functions}}% | {{this.lines}}% |
56
+ {{/each}}
57
+ {{else}}
58
+ Coverage details not available.
59
+ {{/if}}
60
+
61
+ ## Issues Found
62
+
63
+ {{#if issues}}
64
+ | Issue | Severity | Status |
65
+ |-------|----------|--------|
66
+ {{#each issues}}
67
+ | {{this.description}} | {{this.severity}} | {{this.status}} |
68
+ {{/each}}
69
+ {{else}}
70
+ No issues found during testing.
71
+ {{/if}}
72
+
73
+ ## Ready for Completion
74
+
75
+ - [{{#if all_tests_pass}}x{{else}} {{/if}}] All tests passing
76
+ - [{{#if coverage_met}}x{{else}} {{/if}}] Coverage target met ({{coverage_target}}%)
77
+ - [{{#if acceptance_met}}x{{else}} {{/if}}] All acceptance criteria validated
78
+ - [{{#if no_critical_issues}}x{{else}} {{/if}}] No critical issues open
79
+
80
+ ---
81
+ *Generated by FIRE Run {{run_id}}*