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.
- package/README.md +190 -224
- package/bin/cli.js +28 -1
- package/flows/aidlc/commands/construction-agent.md +5 -1
- package/flows/aidlc/commands/inception-agent.md +4 -0
- package/flows/aidlc/commands/master-agent.md +4 -0
- package/flows/aidlc/commands/operations-agent.md +4 -0
- package/flows/aidlc/memory-bank.yaml +2 -1
- package/{scripts/artifact-validator.js → flows/aidlc/scripts/artifact-validator.cjs} +3 -3
- package/{scripts/bolt-complete.js → flows/aidlc/scripts/bolt-complete.cjs} +36 -5
- package/{scripts/status-integrity.js → flows/aidlc/scripts/status-integrity.cjs} +5 -5
- package/flows/aidlc/skills/construction/bolt-list.md +1 -1
- package/flows/aidlc/skills/construction/bolt-start.md +3 -3
- package/flows/aidlc/skills/construction/bolt-status.md +1 -1
- package/flows/aidlc/skills/construction/prototype-apply.md +311 -0
- package/flows/aidlc/skills/inception/vibe-to-spec.md +410 -0
- package/flows/aidlc/skills/master/analyze-context.md +1 -1
- package/flows/aidlc/templates/construction/bolt-template.md +2 -2
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +73 -11
- package/flows/aidlc/templates/standards/decision-index-template.md +32 -0
- package/flows/fire/README.md +19 -0
- package/flows/fire/agents/builder/agent.md +260 -0
- package/flows/fire/agents/builder/skills/code-review/SKILL.md +257 -0
- package/flows/fire/agents/builder/skills/code-review/references/auto-fix-rules.md +218 -0
- package/flows/fire/agents/builder/skills/code-review/references/review-categories.md +154 -0
- package/flows/fire/agents/builder/skills/code-review/templates/review-report.md.hbs +120 -0
- package/flows/fire/agents/builder/skills/run-execute/SKILL.md +714 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.cjs +800 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.cjs +500 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/update-checkpoint.cjs +254 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/update-phase.cjs +250 -0
- package/flows/fire/agents/builder/skills/run-execute/templates/plan.md.hbs +61 -0
- package/flows/fire/agents/builder/skills/run-execute/templates/test-report.md.hbs +81 -0
- package/flows/fire/agents/builder/skills/run-plan/SKILL.md +378 -0
- package/flows/fire/agents/builder/skills/run-status/SKILL.md +96 -0
- package/flows/fire/agents/builder/skills/walkthrough-generate/SKILL.md +267 -0
- package/flows/fire/agents/builder/skills/walkthrough-generate/templates/walkthrough.md.hbs +176 -0
- package/flows/fire/agents/orchestrator/agent.md +144 -0
- package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +226 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/coding-standards.md.hbs +149 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/constitution.md.hbs +43 -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/agents/orchestrator/skills/route/SKILL.md +146 -0
- package/flows/fire/agents/orchestrator/skills/status/SKILL.md +696 -0
- package/flows/fire/agents/planner/agent.md +143 -0
- package/flows/fire/agents/planner/skills/design-doc-generate/SKILL.md +156 -0
- package/flows/fire/agents/planner/skills/design-doc-generate/templates/design.md.hbs +124 -0
- package/flows/fire/agents/planner/skills/intent-capture/SKILL.md +125 -0
- package/flows/fire/agents/planner/skills/intent-capture/templates/brief.md.hbs +40 -0
- package/flows/fire/agents/planner/skills/work-item-decompose/SKILL.md +166 -0
- package/flows/fire/agents/planner/skills/work-item-decompose/templates/work-item.md.hbs +40 -0
- package/flows/fire/commands/fire-builder.md +56 -0
- package/flows/fire/commands/fire-planner.md +48 -0
- package/flows/fire/commands/fire.md +46 -0
- package/flows/fire/memory-bank.yaml +240 -0
- package/flows/fire/quick-start.md +146 -0
- package/flows/ideation/README.md +35 -0
- package/flows/ideation/agents/orchestrator/agent.md +103 -0
- package/flows/ideation/agents/orchestrator/skills/flame/SKILL.md +132 -0
- package/flows/ideation/agents/orchestrator/skills/flame/references/evaluation-criteria.md +81 -0
- package/flows/ideation/agents/orchestrator/skills/flame/references/six-hats-method.md +87 -0
- package/flows/ideation/agents/orchestrator/skills/flame/templates/flame-report.md.hbs +81 -0
- package/flows/ideation/agents/orchestrator/skills/forge/SKILL.md +153 -0
- package/flows/ideation/agents/orchestrator/skills/forge/references/disney-method.md +94 -0
- package/flows/ideation/agents/orchestrator/skills/forge/references/pitch-framework.md +87 -0
- package/flows/ideation/agents/orchestrator/skills/forge/templates/concept-brief.md.hbs +83 -0
- package/flows/ideation/agents/orchestrator/skills/spark/SKILL.md +152 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/anti-bias.md +43 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/analogy.md +60 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/first-principles.md +56 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/index.yaml +76 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/inversion.md +52 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/questorming.md +57 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/random-word.md +35 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/scamper.md +67 -0
- package/flows/ideation/agents/orchestrator/skills/spark/references/techniques/what-if.md +55 -0
- package/flows/ideation/agents/orchestrator/skills/spark/templates/spark-bank.md.hbs +72 -0
- package/flows/ideation/commands/flame.md +43 -0
- package/flows/ideation/commands/forge.md +43 -0
- package/flows/ideation/commands/ideation.md +51 -0
- package/flows/ideation/commands/spark.md +43 -0
- package/flows/ideation/memory-bank.yaml +177 -0
- package/flows/ideation/quick-start.md +84 -0
- package/flows/ideation/shared/protocols/anti-bias.md +79 -0
- package/flows/ideation/shared/protocols/deep-thinking.md +92 -0
- package/flows/ideation/shared/protocols/diverge-converge.md +72 -0
- package/flows/ideation/shared/protocols/interaction-adaptation.md +88 -0
- package/flows/simple/README.md +190 -0
- package/flows/simple/agents/agent.md +404 -0
- package/flows/simple/commands/agent.md +60 -0
- package/flows/simple/context-config.yaml +34 -0
- package/flows/simple/memory-bank.yaml +66 -0
- package/flows/simple/quick-start.md +231 -0
- package/flows/simple/skills/design.md +96 -0
- package/flows/simple/skills/execute.md +190 -0
- package/flows/simple/skills/requirements.md +94 -0
- package/flows/simple/skills/tasks.md +136 -0
- package/flows/simple/templates/design-template.md +138 -0
- package/flows/simple/templates/requirements-template.md +85 -0
- package/flows/simple/templates/tasks-template.md +104 -0
- package/lib/analytics/tracker.js +6 -2
- package/lib/constants.js +25 -8
- package/lib/dashboard/aidlc/parser.js +581 -0
- package/lib/dashboard/fire/model.js +382 -0
- package/lib/dashboard/fire/parser.js +470 -0
- package/lib/dashboard/flow-detect.js +86 -0
- package/lib/dashboard/git/changes.js +362 -0
- package/lib/dashboard/git/worktrees.js +248 -0
- package/lib/dashboard/index.js +709 -0
- package/lib/dashboard/runtime/watch-runtime.js +122 -0
- package/lib/dashboard/simple/parser.js +293 -0
- package/lib/dashboard/tui/app.js +1675 -0
- package/lib/dashboard/tui/components/error-banner.js +35 -0
- package/lib/dashboard/tui/components/header.js +60 -0
- package/lib/dashboard/tui/components/help-footer.js +15 -0
- package/lib/dashboard/tui/components/stats-strip.js +35 -0
- package/lib/dashboard/tui/file-entries.js +383 -0
- package/lib/dashboard/tui/flow-builders.js +991 -0
- package/lib/dashboard/tui/git-builders.js +218 -0
- package/lib/dashboard/tui/helpers.js +236 -0
- package/lib/dashboard/tui/overlays.js +242 -0
- package/lib/dashboard/tui/preview.js +220 -0
- package/lib/dashboard/tui/renderer.js +76 -0
- package/lib/dashboard/tui/row-builders.js +797 -0
- package/lib/dashboard/tui/sections.js +45 -0
- package/lib/dashboard/tui/store.js +44 -0
- package/lib/dashboard/tui/views/overview-view.js +61 -0
- package/lib/dashboard/tui/views/runs-view.js +93 -0
- package/lib/dashboard/tui/worktree-builders.js +229 -0
- package/lib/dashboard/web/extension-adapter.js +726 -0
- package/lib/dashboard/web/public/app.js +9 -0
- package/lib/dashboard/web/public/index.html +14 -0
- package/lib/dashboard/web/public/styles.css +36 -0
- package/lib/dashboard/web/public/webview-bundle.js +7596 -0
- package/lib/dashboard/web/server.js +376 -0
- package/lib/dashboard/web/snapshot.js +299 -0
- package/lib/installer.js +19 -15
- package/lib/installers/CodexInstaller.js +72 -1
- package/lib/installers/KiroInstaller.js +55 -0
- package/lib/installers/OpenCodeInstaller.js +9 -1
- package/lib/installers/ToolInstaller.js +4 -1
- package/lib/installers/WindsurfInstaller.js +0 -54
- package/package.json +15 -55
- package/scripts/check-webview-bundle-sync.cjs +38 -0
- 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}}*
|