specsmd 0.0.0-dev.9 → 0.0.0-dev.90

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 (146) hide show
  1. package/README.md +190 -224
  2. package/bin/cli.js +28 -1
  3. package/flows/aidlc/commands/construction-agent.md +5 -1
  4. package/flows/aidlc/commands/inception-agent.md +4 -0
  5. package/flows/aidlc/commands/master-agent.md +4 -0
  6. package/flows/aidlc/commands/operations-agent.md +4 -0
  7. package/flows/aidlc/memory-bank.yaml +2 -1
  8. package/{scripts/artifact-validator.js → flows/aidlc/scripts/artifact-validator.cjs} +3 -3
  9. package/{scripts/bolt-complete.js → flows/aidlc/scripts/bolt-complete.cjs} +36 -5
  10. package/{scripts/status-integrity.js → flows/aidlc/scripts/status-integrity.cjs} +5 -5
  11. package/flows/aidlc/skills/construction/bolt-list.md +1 -1
  12. package/flows/aidlc/skills/construction/bolt-start.md +3 -3
  13. package/flows/aidlc/skills/construction/bolt-status.md +1 -1
  14. package/flows/aidlc/skills/construction/prototype-apply.md +311 -0
  15. package/flows/aidlc/skills/inception/vibe-to-spec.md +410 -0
  16. package/flows/aidlc/skills/master/analyze-context.md +1 -1
  17. package/flows/aidlc/templates/construction/bolt-template.md +2 -2
  18. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +73 -11
  19. package/flows/aidlc/templates/standards/decision-index-template.md +32 -0
  20. package/flows/fire/README.md +19 -0
  21. package/flows/fire/agents/builder/agent.md +260 -0
  22. package/flows/fire/agents/builder/skills/code-review/SKILL.md +257 -0
  23. package/flows/fire/agents/builder/skills/code-review/references/auto-fix-rules.md +218 -0
  24. package/flows/fire/agents/builder/skills/code-review/references/review-categories.md +154 -0
  25. package/flows/fire/agents/builder/skills/code-review/templates/review-report.md.hbs +120 -0
  26. package/flows/fire/agents/builder/skills/run-execute/SKILL.md +714 -0
  27. package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.cjs +800 -0
  28. package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.cjs +500 -0
  29. package/flows/fire/agents/builder/skills/run-execute/scripts/update-checkpoint.cjs +254 -0
  30. package/flows/fire/agents/builder/skills/run-execute/scripts/update-phase.cjs +250 -0
  31. package/flows/fire/agents/builder/skills/run-execute/templates/plan.md.hbs +61 -0
  32. package/flows/fire/agents/builder/skills/run-execute/templates/test-report.md.hbs +81 -0
  33. package/flows/fire/agents/builder/skills/run-plan/SKILL.md +378 -0
  34. package/flows/fire/agents/builder/skills/run-status/SKILL.md +96 -0
  35. package/flows/fire/agents/builder/skills/walkthrough-generate/SKILL.md +267 -0
  36. package/flows/fire/agents/builder/skills/walkthrough-generate/templates/walkthrough.md.hbs +176 -0
  37. package/flows/fire/agents/orchestrator/agent.md +144 -0
  38. package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +226 -0
  39. package/flows/fire/agents/orchestrator/skills/project-init/templates/coding-standards.md.hbs +149 -0
  40. package/flows/fire/agents/orchestrator/skills/project-init/templates/constitution.md.hbs +43 -0
  41. package/flows/fire/agents/orchestrator/skills/project-init/templates/system-architecture.md.hbs +101 -0
  42. package/flows/fire/agents/orchestrator/skills/project-init/templates/tech-stack.md.hbs +136 -0
  43. package/flows/fire/agents/orchestrator/skills/project-init/templates/testing-standards.md.hbs +94 -0
  44. package/flows/fire/agents/orchestrator/skills/route/SKILL.md +146 -0
  45. package/flows/fire/agents/orchestrator/skills/status/SKILL.md +696 -0
  46. package/flows/fire/agents/planner/agent.md +143 -0
  47. package/flows/fire/agents/planner/skills/design-doc-generate/SKILL.md +156 -0
  48. package/flows/fire/agents/planner/skills/design-doc-generate/templates/design.md.hbs +124 -0
  49. package/flows/fire/agents/planner/skills/intent-capture/SKILL.md +125 -0
  50. package/flows/fire/agents/planner/skills/intent-capture/templates/brief.md.hbs +40 -0
  51. package/flows/fire/agents/planner/skills/work-item-decompose/SKILL.md +166 -0
  52. package/flows/fire/agents/planner/skills/work-item-decompose/templates/work-item.md.hbs +40 -0
  53. package/flows/fire/commands/fire-builder.md +56 -0
  54. package/flows/fire/commands/fire-planner.md +48 -0
  55. package/flows/fire/commands/fire.md +46 -0
  56. package/flows/fire/memory-bank.yaml +240 -0
  57. package/flows/fire/quick-start.md +146 -0
  58. package/flows/ideation/README.md +35 -0
  59. package/flows/ideation/agents/orchestrator/agent.md +103 -0
  60. package/flows/ideation/agents/orchestrator/skills/flame/SKILL.md +132 -0
  61. package/flows/ideation/agents/orchestrator/skills/flame/references/evaluation-criteria.md +81 -0
  62. package/flows/ideation/agents/orchestrator/skills/flame/references/six-hats-method.md +87 -0
  63. package/flows/ideation/agents/orchestrator/skills/flame/templates/flame-report.md.hbs +81 -0
  64. package/flows/ideation/agents/orchestrator/skills/forge/SKILL.md +153 -0
  65. package/flows/ideation/agents/orchestrator/skills/forge/references/disney-method.md +94 -0
  66. package/flows/ideation/agents/orchestrator/skills/forge/references/pitch-framework.md +87 -0
  67. package/flows/ideation/agents/orchestrator/skills/forge/templates/concept-brief.md.hbs +83 -0
  68. package/flows/ideation/agents/orchestrator/skills/spark/SKILL.md +152 -0
  69. package/flows/ideation/agents/orchestrator/skills/spark/references/anti-bias.md +43 -0
  70. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/analogy.md +60 -0
  71. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/first-principles.md +56 -0
  72. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/index.yaml +76 -0
  73. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/inversion.md +52 -0
  74. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/questorming.md +57 -0
  75. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/random-word.md +35 -0
  76. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/scamper.md +67 -0
  77. package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/what-if.md +55 -0
  78. package/flows/ideation/agents/orchestrator/skills/spark/templates/spark-bank.md.hbs +72 -0
  79. package/flows/ideation/commands/flame.md +43 -0
  80. package/flows/ideation/commands/forge.md +43 -0
  81. package/flows/ideation/commands/ideation.md +51 -0
  82. package/flows/ideation/commands/spark.md +43 -0
  83. package/flows/ideation/memory-bank.yaml +177 -0
  84. package/flows/ideation/quick-start.md +84 -0
  85. package/flows/ideation/shared/protocols/anti-bias.md +79 -0
  86. package/flows/ideation/shared/protocols/deep-thinking.md +92 -0
  87. package/flows/ideation/shared/protocols/diverge-converge.md +72 -0
  88. package/flows/ideation/shared/protocols/interaction-adaptation.md +88 -0
  89. package/flows/simple/README.md +190 -0
  90. package/flows/simple/agents/agent.md +404 -0
  91. package/flows/simple/commands/agent.md +60 -0
  92. package/flows/simple/context-config.yaml +34 -0
  93. package/flows/simple/memory-bank.yaml +66 -0
  94. package/flows/simple/quick-start.md +231 -0
  95. package/flows/simple/skills/design.md +96 -0
  96. package/flows/simple/skills/execute.md +190 -0
  97. package/flows/simple/skills/requirements.md +94 -0
  98. package/flows/simple/skills/tasks.md +136 -0
  99. package/flows/simple/templates/design-template.md +138 -0
  100. package/flows/simple/templates/requirements-template.md +85 -0
  101. package/flows/simple/templates/tasks-template.md +104 -0
  102. package/lib/analytics/tracker.js +6 -2
  103. package/lib/constants.js +25 -8
  104. package/lib/dashboard/aidlc/parser.js +581 -0
  105. package/lib/dashboard/fire/model.js +382 -0
  106. package/lib/dashboard/fire/parser.js +470 -0
  107. package/lib/dashboard/flow-detect.js +86 -0
  108. package/lib/dashboard/git/changes.js +362 -0
  109. package/lib/dashboard/git/worktrees.js +248 -0
  110. package/lib/dashboard/index.js +709 -0
  111. package/lib/dashboard/runtime/watch-runtime.js +122 -0
  112. package/lib/dashboard/simple/parser.js +293 -0
  113. package/lib/dashboard/tui/app.js +1675 -0
  114. package/lib/dashboard/tui/components/error-banner.js +35 -0
  115. package/lib/dashboard/tui/components/header.js +60 -0
  116. package/lib/dashboard/tui/components/help-footer.js +15 -0
  117. package/lib/dashboard/tui/components/stats-strip.js +35 -0
  118. package/lib/dashboard/tui/file-entries.js +383 -0
  119. package/lib/dashboard/tui/flow-builders.js +991 -0
  120. package/lib/dashboard/tui/git-builders.js +218 -0
  121. package/lib/dashboard/tui/helpers.js +236 -0
  122. package/lib/dashboard/tui/overlays.js +242 -0
  123. package/lib/dashboard/tui/preview.js +220 -0
  124. package/lib/dashboard/tui/renderer.js +76 -0
  125. package/lib/dashboard/tui/row-builders.js +797 -0
  126. package/lib/dashboard/tui/sections.js +45 -0
  127. package/lib/dashboard/tui/store.js +44 -0
  128. package/lib/dashboard/tui/views/overview-view.js +61 -0
  129. package/lib/dashboard/tui/views/runs-view.js +93 -0
  130. package/lib/dashboard/tui/worktree-builders.js +229 -0
  131. package/lib/dashboard/web/extension-adapter.js +726 -0
  132. package/lib/dashboard/web/public/app.js +9 -0
  133. package/lib/dashboard/web/public/index.html +14 -0
  134. package/lib/dashboard/web/public/styles.css +36 -0
  135. package/lib/dashboard/web/public/webview-bundle.js +7596 -0
  136. package/lib/dashboard/web/server.js +376 -0
  137. package/lib/dashboard/web/snapshot.js +299 -0
  138. package/lib/installer.js +19 -15
  139. package/lib/installers/CodexInstaller.js +72 -1
  140. package/lib/installers/KiroInstaller.js +55 -0
  141. package/lib/installers/OpenCodeInstaller.js +9 -1
  142. package/lib/installers/ToolInstaller.js +4 -1
  143. package/lib/installers/WindsurfInstaller.js +0 -54
  144. package/package.json +15 -55
  145. package/scripts/check-webview-bundle-sync.cjs +38 -0
  146. package/scripts/sync-webview-bundle.cjs +19 -0
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FIRE Checkpoint State Update Script
5
+ *
6
+ * Tracks explicit approval-gate state for the active work item in a run.
7
+ *
8
+ * Usage:
9
+ * node update-checkpoint.cjs <rootPath> <runId> <checkpointState> [--item=<workItemId>] [--checkpoint=<name>]
10
+ *
11
+ * Examples:
12
+ * node update-checkpoint.cjs /project run-fabriqa-2026-001 awaiting_approval --checkpoint=plan
13
+ * node update-checkpoint.cjs /project run-fabriqa-2026-001 approved
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const yaml = require('yaml');
19
+
20
+ const VALID_STATES = ['awaiting_approval', 'approved', 'none', 'not_required'];
21
+
22
+ function fireError(message, code, suggestion) {
23
+ const err = new Error(`FIRE Error [${code}]: ${message} ${suggestion}`);
24
+ err.code = code;
25
+ err.suggestion = suggestion;
26
+ return err;
27
+ }
28
+
29
+ function normalizeCheckpointState(value) {
30
+ const normalized = String(value || '').toLowerCase().trim().replace(/[\s-]+/g, '_');
31
+ const map = {
32
+ waiting: 'awaiting_approval',
33
+ awaiting: 'awaiting_approval',
34
+ awaiting_approval: 'awaiting_approval',
35
+ pending_approval: 'awaiting_approval',
36
+ approval_needed: 'awaiting_approval',
37
+ approval_required: 'awaiting_approval',
38
+ approved: 'approved',
39
+ confirmed: 'approved',
40
+ accepted: 'approved',
41
+ resumed: 'approved',
42
+ none: 'none',
43
+ clear: 'none',
44
+ cleared: 'none',
45
+ reset: 'none',
46
+ not_required: 'not_required',
47
+ n_a: 'not_required',
48
+ na: 'not_required',
49
+ skipped: 'not_required'
50
+ };
51
+
52
+ return map[normalized] || null;
53
+ }
54
+
55
+ function validateInputs(rootPath, runId, checkpointState) {
56
+ if (!rootPath || typeof rootPath !== 'string' || rootPath.trim() === '') {
57
+ throw fireError('rootPath is required.', 'CHECKPOINT_001', 'Provide a valid project root path.');
58
+ }
59
+
60
+ if (!runId || typeof runId !== 'string' || runId.trim() === '') {
61
+ throw fireError('runId is required.', 'CHECKPOINT_002', 'Provide the run ID.');
62
+ }
63
+
64
+ const normalizedState = normalizeCheckpointState(checkpointState);
65
+ if (!normalizedState || !VALID_STATES.includes(normalizedState)) {
66
+ throw fireError(
67
+ `Invalid checkpointState: "${checkpointState}".`,
68
+ 'CHECKPOINT_003',
69
+ `Valid states are: ${VALID_STATES.join(', ')}`
70
+ );
71
+ }
72
+
73
+ if (!fs.existsSync(rootPath)) {
74
+ throw fireError(
75
+ `Project root not found: "${rootPath}".`,
76
+ 'CHECKPOINT_004',
77
+ 'Ensure the path exists and is accessible.'
78
+ );
79
+ }
80
+
81
+ return normalizedState;
82
+ }
83
+
84
+ function validateFireProject(rootPath) {
85
+ const fireDir = path.join(rootPath, '.specs-fire');
86
+ const statePath = path.join(fireDir, 'state.yaml');
87
+
88
+ if (!fs.existsSync(fireDir)) {
89
+ throw fireError(
90
+ `FIRE project not initialized at: "${rootPath}".`,
91
+ 'CHECKPOINT_010',
92
+ 'Run fire-init first to initialize the project.'
93
+ );
94
+ }
95
+
96
+ if (!fs.existsSync(statePath)) {
97
+ throw fireError(
98
+ `State file not found at: "${statePath}".`,
99
+ 'CHECKPOINT_011',
100
+ 'The project may be corrupted. Try re-initializing.'
101
+ );
102
+ }
103
+
104
+ return { statePath };
105
+ }
106
+
107
+ function readState(statePath) {
108
+ try {
109
+ const content = fs.readFileSync(statePath, 'utf8');
110
+ const state = yaml.parse(content);
111
+ if (!state || typeof state !== 'object') {
112
+ throw fireError('State file is empty or invalid.', 'CHECKPOINT_020', 'Check state.yaml format.');
113
+ }
114
+ return state;
115
+ } catch (err) {
116
+ if (err.code && err.code.startsWith('CHECKPOINT_')) throw err;
117
+ throw fireError(
118
+ `Failed to read state file: ${err.message}`,
119
+ 'CHECKPOINT_021',
120
+ 'Check file permissions and YAML syntax.'
121
+ );
122
+ }
123
+ }
124
+
125
+ function writeState(statePath, state) {
126
+ try {
127
+ fs.writeFileSync(statePath, yaml.stringify(state));
128
+ } catch (err) {
129
+ throw fireError(
130
+ `Failed to write state file: ${err.message}`,
131
+ 'CHECKPOINT_022',
132
+ 'Check file permissions and disk space.'
133
+ );
134
+ }
135
+ }
136
+
137
+ function updateCheckpoint(rootPath, runId, checkpointState, options = {}) {
138
+ const normalizedState = validateInputs(rootPath, runId, checkpointState);
139
+ const { statePath } = validateFireProject(rootPath);
140
+ const state = readState(statePath);
141
+
142
+ const activeRuns = state.runs?.active || [];
143
+ const runIndex = activeRuns.findIndex((run) => run.id === runId);
144
+ if (runIndex === -1) {
145
+ throw fireError(
146
+ `Run "${runId}" not found in active runs.`,
147
+ 'CHECKPOINT_030',
148
+ 'The run may have already been completed or was never started.'
149
+ );
150
+ }
151
+
152
+ const activeRun = activeRuns[runIndex];
153
+ const workItems = Array.isArray(activeRun.work_items) ? activeRun.work_items : [];
154
+ const targetItemId = options.itemId || activeRun.current_item;
155
+ if (!targetItemId) {
156
+ throw fireError(
157
+ `Run "${runId}" has no current item.`,
158
+ 'CHECKPOINT_031',
159
+ 'Specify --item=<workItemId> explicitly.'
160
+ );
161
+ }
162
+
163
+ const itemIndex = workItems.findIndex((item) => item.id === targetItemId);
164
+ if (itemIndex === -1) {
165
+ throw fireError(
166
+ `Work item "${targetItemId}" not found in run "${runId}".`,
167
+ 'CHECKPOINT_032',
168
+ 'Check the work item ID or run state.'
169
+ );
170
+ }
171
+
172
+ const item = workItems[itemIndex];
173
+ const previousState = item.checkpoint_state || null;
174
+ item.checkpoint_state = normalizedState;
175
+
176
+ if (typeof options.checkpoint === 'string' && options.checkpoint.trim() !== '') {
177
+ item.current_checkpoint = options.checkpoint.trim();
178
+ } else if (!item.current_checkpoint && (normalizedState === 'awaiting_approval' || normalizedState === 'approved')) {
179
+ item.current_checkpoint = 'plan';
180
+ }
181
+
182
+ if (!item.current_phase && normalizedState === 'awaiting_approval') {
183
+ item.current_phase = 'plan';
184
+ }
185
+
186
+ activeRun.work_items = workItems;
187
+ state.runs.active[runIndex] = activeRun;
188
+ writeState(statePath, state);
189
+
190
+ return {
191
+ success: true,
192
+ runId,
193
+ workItemId: targetItemId,
194
+ checkpointState: normalizedState,
195
+ previousCheckpointState: previousState,
196
+ currentCheckpoint: item.current_checkpoint || null
197
+ };
198
+ }
199
+
200
+ function parseOptions(argv) {
201
+ const options = {};
202
+ for (const arg of argv) {
203
+ if (arg.startsWith('--item=')) {
204
+ options.itemId = arg.slice('--item='.length);
205
+ } else if (arg.startsWith('--checkpoint=')) {
206
+ options.checkpoint = arg.slice('--checkpoint='.length);
207
+ } else {
208
+ throw fireError(
209
+ `Unknown option: ${arg}`,
210
+ 'CHECKPOINT_033',
211
+ 'Use --item=<workItemId> or --checkpoint=<name>.'
212
+ );
213
+ }
214
+ }
215
+ return options;
216
+ }
217
+
218
+ function printUsage() {
219
+ console.error('Usage:');
220
+ console.error(' node update-checkpoint.cjs <rootPath> <runId> <checkpointState> [--item=<workItemId>] [--checkpoint=<name>]');
221
+ console.error('');
222
+ console.error('checkpointState:');
223
+ console.error(` ${VALID_STATES.join(', ')}`);
224
+ console.error('');
225
+ console.error('Examples:');
226
+ console.error(' node update-checkpoint.cjs /project run-fabriqa-2026-001 awaiting_approval --checkpoint=plan');
227
+ console.error(' node update-checkpoint.cjs /project run-fabriqa-2026-001 approved');
228
+ }
229
+
230
+ if (require.main === module) {
231
+ const args = process.argv.slice(2);
232
+ if (args.length < 3) {
233
+ printUsage();
234
+ process.exit(1);
235
+ }
236
+
237
+ const [rootPath, runId, checkpointState, ...optionArgs] = args;
238
+
239
+ try {
240
+ const options = parseOptions(optionArgs);
241
+ const result = updateCheckpoint(rootPath, runId, checkpointState, options);
242
+ console.log(JSON.stringify(result, null, 2));
243
+ process.exit(0);
244
+ } catch (err) {
245
+ console.error(err.message);
246
+ process.exit(1);
247
+ }
248
+ }
249
+
250
+ module.exports = {
251
+ VALID_STATES,
252
+ normalizeCheckpointState,
253
+ updateCheckpoint
254
+ };
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FIRE Phase Update Script
5
+ *
6
+ * Updates the current phase for a work item in an active run.
7
+ * Phases: plan → execute → test → review
8
+ *
9
+ * Usage:
10
+ * node update-phase.cjs <rootPath> <runId> <phase>
11
+ *
12
+ * Examples:
13
+ * node update-phase.cjs /project run-fabriqa-2026-001 execute
14
+ * node update-phase.cjs /project run-fabriqa-2026-001 test
15
+ * node update-phase.cjs /project run-fabriqa-2026-001 review
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const yaml = require('yaml');
21
+
22
+ // =============================================================================
23
+ // Constants
24
+ // =============================================================================
25
+
26
+ const VALID_PHASES = ['plan', 'execute', 'test', 'review'];
27
+
28
+ // =============================================================================
29
+ // Error Helper
30
+ // =============================================================================
31
+
32
+ function fireError(message, code, suggestion) {
33
+ const err = new Error(`FIRE Error [${code}]: ${message} ${suggestion}`);
34
+ err.code = code;
35
+ err.suggestion = suggestion;
36
+ return err;
37
+ }
38
+
39
+ // =============================================================================
40
+ // Validation
41
+ // =============================================================================
42
+
43
+ function validateInputs(rootPath, runId, phase) {
44
+ if (!rootPath || typeof rootPath !== 'string' || rootPath.trim() === '') {
45
+ throw fireError('rootPath is required.', 'PHASE_001', 'Provide a valid project root path.');
46
+ }
47
+
48
+ if (!runId || typeof runId !== 'string' || runId.trim() === '') {
49
+ throw fireError('runId is required.', 'PHASE_002', 'Provide the run ID.');
50
+ }
51
+
52
+ if (!phase || !VALID_PHASES.includes(phase)) {
53
+ throw fireError(
54
+ `Invalid phase: "${phase}".`,
55
+ 'PHASE_003',
56
+ `Valid phases are: ${VALID_PHASES.join(', ')}`
57
+ );
58
+ }
59
+
60
+ if (!fs.existsSync(rootPath)) {
61
+ throw fireError(
62
+ `Project root not found: "${rootPath}".`,
63
+ 'PHASE_004',
64
+ 'Ensure the path exists and is accessible.'
65
+ );
66
+ }
67
+ }
68
+
69
+ function validateFireProject(rootPath, runId) {
70
+ const fireDir = path.join(rootPath, '.specs-fire');
71
+ const statePath = path.join(fireDir, 'state.yaml');
72
+
73
+ if (!fs.existsSync(fireDir)) {
74
+ throw fireError(
75
+ `FIRE project not initialized at: "${rootPath}".`,
76
+ 'PHASE_010',
77
+ 'Run fire-init first to initialize the project.'
78
+ );
79
+ }
80
+
81
+ if (!fs.existsSync(statePath)) {
82
+ throw fireError(
83
+ `State file not found at: "${statePath}".`,
84
+ 'PHASE_011',
85
+ 'The project may be corrupted. Try re-initializing.'
86
+ );
87
+ }
88
+
89
+ return { statePath };
90
+ }
91
+
92
+ // =============================================================================
93
+ // State Operations
94
+ // =============================================================================
95
+
96
+ function readState(statePath) {
97
+ try {
98
+ const content = fs.readFileSync(statePath, 'utf8');
99
+ const state = yaml.parse(content);
100
+ if (!state || typeof state !== 'object') {
101
+ throw fireError('State file is empty or invalid.', 'PHASE_020', 'Check state.yaml format.');
102
+ }
103
+ return state;
104
+ } catch (err) {
105
+ if (err.code && err.code.startsWith('PHASE_')) throw err;
106
+ throw fireError(
107
+ `Failed to read state file: ${err.message}`,
108
+ 'PHASE_021',
109
+ 'Check file permissions and YAML syntax.'
110
+ );
111
+ }
112
+ }
113
+
114
+ function writeState(statePath, state) {
115
+ try {
116
+ fs.writeFileSync(statePath, yaml.stringify(state));
117
+ } catch (err) {
118
+ throw fireError(
119
+ `Failed to write state file: ${err.message}`,
120
+ 'PHASE_022',
121
+ 'Check file permissions and disk space.'
122
+ );
123
+ }
124
+ }
125
+
126
+ // =============================================================================
127
+ // Main Function
128
+ // =============================================================================
129
+
130
+ /**
131
+ * Update the current phase for the active work item in a run.
132
+ *
133
+ * @param {string} rootPath - Project root directory
134
+ * @param {string} runId - Run ID
135
+ * @param {string} phase - New phase (plan, execute, test, review)
136
+ * @returns {object} Result with updated phase info
137
+ */
138
+ function updatePhase(rootPath, runId, phase) {
139
+ validateInputs(rootPath, runId, phase);
140
+ const { statePath } = validateFireProject(rootPath, runId);
141
+ const state = readState(statePath);
142
+
143
+ // Find run in active runs list
144
+ const activeRuns = state.runs?.active || [];
145
+ const runIndex = activeRuns.findIndex(r => r.id === runId);
146
+
147
+ if (runIndex === -1) {
148
+ throw fireError(
149
+ `Run "${runId}" not found in active runs.`,
150
+ 'PHASE_030',
151
+ 'The run may have been completed or was never started.'
152
+ );
153
+ }
154
+
155
+ const activeRun = activeRuns[runIndex];
156
+ const workItems = activeRun.work_items || [];
157
+ const currentItemId = activeRun.current_item;
158
+
159
+ if (!currentItemId) {
160
+ throw fireError(
161
+ `No current item in run "${runId}".`,
162
+ 'PHASE_031',
163
+ 'The run may have completed all work items.'
164
+ );
165
+ }
166
+
167
+ // Find and update current item's phase
168
+ let updated = false;
169
+ let previousPhase = null;
170
+ for (const item of workItems) {
171
+ if (item.id === currentItemId) {
172
+ previousPhase = item.current_phase || 'plan';
173
+ item.current_phase = phase;
174
+ const mode = String(item.mode || '').toLowerCase();
175
+ const isApprovalMode = mode === 'confirm' || mode === 'validate';
176
+
177
+ if (isApprovalMode && phase !== 'plan') {
178
+ item.checkpoint_state = 'approved';
179
+ if (!item.current_checkpoint) {
180
+ item.current_checkpoint = 'plan';
181
+ }
182
+ } else if (!item.checkpoint_state) {
183
+ item.checkpoint_state = 'none';
184
+ }
185
+ updated = true;
186
+ break;
187
+ }
188
+ }
189
+
190
+ if (!updated) {
191
+ throw fireError(
192
+ `Current item "${currentItemId}" not found in work items.`,
193
+ 'PHASE_032',
194
+ 'The run state may be corrupted.'
195
+ );
196
+ }
197
+
198
+ // Update state
199
+ activeRun.work_items = workItems;
200
+ state.runs.active[runIndex] = activeRun;
201
+ writeState(statePath, state);
202
+
203
+ return {
204
+ success: true,
205
+ runId: runId,
206
+ workItemId: currentItemId,
207
+ previousPhase: previousPhase,
208
+ currentPhase: phase,
209
+ };
210
+ }
211
+
212
+ // =============================================================================
213
+ // CLI Interface
214
+ // =============================================================================
215
+
216
+ function printUsage() {
217
+ console.error('Usage:');
218
+ console.error(' node update-phase.cjs <rootPath> <runId> <phase>');
219
+ console.error('');
220
+ console.error('Arguments:');
221
+ console.error(' rootPath - Project root directory');
222
+ console.error(' runId - Run ID (e.g., run-fabriqa-2026-001)');
223
+ console.error(' phase - New phase: plan, execute, test, review');
224
+ console.error('');
225
+ console.error('Examples:');
226
+ console.error(' node update-phase.cjs /project run-fabriqa-2026-001 execute');
227
+ console.error(' node update-phase.cjs /project run-fabriqa-2026-001 test');
228
+ }
229
+
230
+ if (require.main === module) {
231
+ const args = process.argv.slice(2);
232
+
233
+ if (args.length < 3) {
234
+ printUsage();
235
+ process.exit(1);
236
+ }
237
+
238
+ const [rootPath, runId, phase] = args;
239
+
240
+ try {
241
+ const result = updatePhase(rootPath, runId, phase);
242
+ console.log(JSON.stringify(result, null, 2));
243
+ process.exit(0);
244
+ } catch (err) {
245
+ console.error(err.message);
246
+ process.exit(1);
247
+ }
248
+ }
249
+
250
+ module.exports = { updatePhase, VALID_PHASES };
@@ -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 specs.md - fabriqa.ai FIRE Flow Run {{run_id}}*