brain-dev 2.5.0 → 2.5.2
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/bin/lib/commands/complete.cjs +4 -4
- package/bin/lib/commands/discuss.cjs +3 -3
- package/bin/lib/commands/execute.cjs +3 -3
- package/bin/lib/commands/plan.cjs +3 -3
- package/bin/lib/commands/review.cjs +4 -4
- package/bin/lib/commands/story.cjs +3 -3
- package/bin/lib/commands/verify.cjs +3 -5
- package/bin/lib/recovery.cjs +4 -5
- package/bin/lib/state.cjs +22 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const { readState, writeState } = require('../state.cjs');
|
|
5
|
+
const { readState, writeState, syncPhaseStatus } = require('../state.cjs');
|
|
6
6
|
const { parseRoadmap, writeRoadmap } = require('../roadmap.cjs');
|
|
7
7
|
const { gitTag } = require('../git.cjs');
|
|
8
8
|
const { output, error, success } = require('../core.cjs');
|
|
@@ -163,7 +163,7 @@ function handlePhaseComplete(args, phaseIdx, brainDir, state) {
|
|
|
163
163
|
return { error: 'not-verified', nextAction: '/brain:verify' };
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
// Mark phase as complete in
|
|
166
|
+
// Mark current phase as complete in per-phase array
|
|
167
167
|
if (Array.isArray(state.phase.phases)) {
|
|
168
168
|
const idx = state.phase.phases.findIndex(p =>
|
|
169
169
|
typeof p === 'object' ? p.number === phaseNumber : false
|
|
@@ -179,9 +179,9 @@ function handlePhaseComplete(args, phaseIdx, brainDir, state) {
|
|
|
179
179
|
const hasNextPhase = totalPhases > 0 && nextPhase <= totalPhases;
|
|
180
180
|
if (hasNextPhase) {
|
|
181
181
|
state.phase.current = nextPhase;
|
|
182
|
-
state
|
|
182
|
+
syncPhaseStatus(state, 'pending');
|
|
183
183
|
} else {
|
|
184
|
-
state
|
|
184
|
+
syncPhaseStatus(state, 'complete');
|
|
185
185
|
}
|
|
186
186
|
writeState(brainDir, state);
|
|
187
187
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const { readState, writeState, atomicWriteSync } = require('../state.cjs');
|
|
5
|
+
const { readState, writeState, atomicWriteSync, syncPhaseStatus } = require('../state.cjs');
|
|
6
6
|
const { parseRoadmap } = require('../roadmap.cjs');
|
|
7
7
|
const { loadTemplate, interpolate } = require('../templates.cjs');
|
|
8
8
|
const { output, error, success, prefix, pipelineGate } = require('../core.cjs');
|
|
@@ -132,7 +132,7 @@ function handleAnalyze(args, brainDir, state) {
|
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
// Update status to discussing
|
|
135
|
-
state
|
|
135
|
+
syncPhaseStatus(state, 'discussing');
|
|
136
136
|
writeState(brainDir, state);
|
|
137
137
|
|
|
138
138
|
const result = {
|
|
@@ -220,7 +220,7 @@ function handleSave(args, brainDir, state) {
|
|
|
220
220
|
atomicWriteSync(contextPath, lines.join('\n'));
|
|
221
221
|
|
|
222
222
|
// Update state: set phase status to "discussed"
|
|
223
|
-
state
|
|
223
|
+
syncPhaseStatus(state, 'discussed');
|
|
224
224
|
writeState(brainDir, state);
|
|
225
225
|
|
|
226
226
|
const result = {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const { readState, writeState } = require('../state.cjs');
|
|
5
|
+
const { readState, writeState, syncPhaseStatus } = require('../state.cjs');
|
|
6
6
|
const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
|
|
7
7
|
const { getAgent, resolveModel } = require('../agents.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
@@ -304,7 +304,7 @@ async function run(args = [], opts = {}) {
|
|
|
304
304
|
|
|
305
305
|
// All plans executed
|
|
306
306
|
if (!targetPlan) {
|
|
307
|
-
state
|
|
307
|
+
syncPhaseStatus(state, 'executed');
|
|
308
308
|
writeState(brainDir, state);
|
|
309
309
|
const msg = "All plans executed. Run /brain:review before verify.";
|
|
310
310
|
output({ action: 'all-executed', message: msg, nextAction: '/brain:review' }, `[brain] ${msg}\n${pipelineGate('npx brain-dev review --phase ' + phaseNumber)}`);
|
|
@@ -385,7 +385,7 @@ async function run(args = [], opts = {}) {
|
|
|
385
385
|
} catch { /* cost tracking failure is non-fatal */ }
|
|
386
386
|
|
|
387
387
|
// Update state to executing
|
|
388
|
-
state
|
|
388
|
+
syncPhaseStatus(state, 'executing');
|
|
389
389
|
if (!state.phase.execution_started_at) {
|
|
390
390
|
state.phase.execution_started_at = new Date().toISOString();
|
|
391
391
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const { readState, writeState } = require('../state.cjs');
|
|
5
|
+
const { readState, writeState, syncPhaseStatus } = require('../state.cjs');
|
|
6
6
|
const { parseRoadmap } = require('../roadmap.cjs');
|
|
7
7
|
const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
|
|
8
8
|
const { getAgent, resolveModel } = require('../agents.cjs');
|
|
@@ -291,7 +291,7 @@ function handleSingle(args, brainDir, state) {
|
|
|
291
291
|
const fullPrompt = prompt + checkerInstruction;
|
|
292
292
|
|
|
293
293
|
// Update state: phase status = "planning"
|
|
294
|
-
state
|
|
294
|
+
syncPhaseStatus(state, 'planning');
|
|
295
295
|
writeState(brainDir, state);
|
|
296
296
|
|
|
297
297
|
const result = {
|
|
@@ -531,7 +531,7 @@ function handleAll(brainDir, state) {
|
|
|
531
531
|
});
|
|
532
532
|
|
|
533
533
|
// Update status to planning
|
|
534
|
-
state
|
|
534
|
+
syncPhaseStatus(state, 'planning');
|
|
535
535
|
writeState(brainDir, state);
|
|
536
536
|
|
|
537
537
|
const result = {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const { readState, writeState } = require('../state.cjs');
|
|
5
|
+
const { readState, writeState, syncPhaseStatus } = require('../state.cjs');
|
|
6
6
|
const { loadTemplateWithOverlay, interpolate } = require('../templates.cjs');
|
|
7
7
|
const { getAgent, resolveModel } = require('../agents.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
@@ -93,7 +93,7 @@ async function run(args = [], opts = {}) {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
// Update state
|
|
96
|
-
state
|
|
96
|
+
syncPhaseStatus(state, 'reviewing');
|
|
97
97
|
writeState(brainDir, state);
|
|
98
98
|
|
|
99
99
|
const result = {
|
|
@@ -145,14 +145,14 @@ function handleResult(brainDir, state, args) {
|
|
|
145
145
|
const reviewStatus = statusMatch ? statusMatch[1] : 'passed';
|
|
146
146
|
|
|
147
147
|
if (reviewStatus === 'critical') {
|
|
148
|
-
state
|
|
148
|
+
syncPhaseStatus(state, 'review-failed');
|
|
149
149
|
writeState(brainDir, state);
|
|
150
150
|
error('Review found critical issues. Fix them and re-run /brain:review.');
|
|
151
151
|
output({ action: 'review-failed', phase: phaseNumber, status: reviewStatus }, '');
|
|
152
152
|
return { action: 'review-failed', phase: phaseNumber };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
state
|
|
155
|
+
syncPhaseStatus(state, 'reviewed');
|
|
156
156
|
writeState(brainDir, state);
|
|
157
157
|
|
|
158
158
|
const result = {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { parseArgs } = require('node:util');
|
|
6
|
-
const { readState, writeState } = require('../state.cjs');
|
|
6
|
+
const { readState, writeState, syncPhaseStatus } = require('../state.cjs');
|
|
7
7
|
const { output, error, prefix } = require('../core.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
9
9
|
const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
|
|
@@ -913,7 +913,6 @@ function stepActivate(brainDir, state, storyDir, storyMeta) {
|
|
|
913
913
|
// Update state phases
|
|
914
914
|
state.phase = state.phase || { current: 0, status: 'initialized', total: 0, phases: [] };
|
|
915
915
|
state.phase.current = 1;
|
|
916
|
-
state.phase.status = 'ready';
|
|
917
916
|
state.phase.total = roadmapData.phases.length;
|
|
918
917
|
state.phase.phases = roadmapData.phases.map(p => ({
|
|
919
918
|
number: p.number,
|
|
@@ -921,7 +920,8 @@ function stepActivate(brainDir, state, storyDir, storyMeta) {
|
|
|
921
920
|
status: p.status === 'Pending' ? 'pending' : p.status.toLowerCase(),
|
|
922
921
|
goal: p.goal
|
|
923
922
|
}));
|
|
924
|
-
|
|
923
|
+
// Sync AFTER phases array is rebuilt so per-phase status is correctly updated
|
|
924
|
+
syncPhaseStatus(state, 'ready');
|
|
925
925
|
state.phase.stuck_count = 0;
|
|
926
926
|
state.phase.last_stuck_at = null;
|
|
927
927
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
-
const { readState, writeState, atomicWriteSync } = require('../state.cjs');
|
|
5
|
+
const { readState, writeState, atomicWriteSync, syncPhaseStatus } = require('../state.cjs');
|
|
6
6
|
const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
|
|
7
7
|
const { getAgent, resolveModel } = require('../agents.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
@@ -320,7 +320,7 @@ async function run(args = [], opts = {}) {
|
|
|
320
320
|
const fullPrompt = prompt + depthInstruction + summarySection;
|
|
321
321
|
|
|
322
322
|
// Update state to verifying
|
|
323
|
-
state
|
|
323
|
+
syncPhaseStatus(state, 'verifying');
|
|
324
324
|
writeState(brainDir, state);
|
|
325
325
|
|
|
326
326
|
const result = {
|
|
@@ -421,9 +421,7 @@ function handleSaveResults(args, saveIdx, brainDir, state) {
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
// Update state
|
|
424
|
-
state
|
|
425
|
-
// Clear execution timer after verification completes
|
|
426
|
-
state.phase.execution_started_at = null;
|
|
424
|
+
syncPhaseStatus(state, results.passed ? 'verified' : 'verification-failed');
|
|
427
425
|
writeState(brainDir, state);
|
|
428
426
|
|
|
429
427
|
const msg = results.passed
|
package/bin/lib/recovery.cjs
CHANGED
|
@@ -4,7 +4,7 @@ const fs = require('node:fs');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { readLock, isLockStale, clearStaleLock } = require('./lock.cjs');
|
|
6
6
|
const { readLog } = require('./logger.cjs');
|
|
7
|
-
const { readState, writeState, VALID_PHASE_STATUSES } = require('./state.cjs');
|
|
7
|
+
const { readState, writeState, VALID_PHASE_STATUSES, syncPhaseStatus } = require('./state.cjs');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Analyze the JSONL execution log for a crashed phase.
|
|
@@ -293,11 +293,11 @@ function autoResume(brainDir, logAnalysis, state) {
|
|
|
293
293
|
if (state.phase) {
|
|
294
294
|
if (state.phase.status === 'executing' && logAnalysis.inProgressTask) {
|
|
295
295
|
// There was work in progress - set to planned so execution can retry
|
|
296
|
-
state
|
|
296
|
+
syncPhaseStatus(state, 'planned');
|
|
297
297
|
changes.push(`Reset phase status from "executing" to "planned"`);
|
|
298
298
|
} else if (state.phase.status === 'executing' && !logAnalysis.inProgressTask) {
|
|
299
299
|
// All tasks were completed or none in progress
|
|
300
|
-
state
|
|
300
|
+
syncPhaseStatus(state, 'planned');
|
|
301
301
|
changes.push(`Reset phase status to "planned" for re-verification`);
|
|
302
302
|
}
|
|
303
303
|
|
|
@@ -337,8 +337,7 @@ function rollback(brainDir, staleLock, state) {
|
|
|
337
337
|
// Reset phase status
|
|
338
338
|
if (state.phase) {
|
|
339
339
|
const previousStatus = state.phase.status;
|
|
340
|
-
state
|
|
341
|
-
state.phase.execution_started_at = null;
|
|
340
|
+
syncPhaseStatus(state, 'planned');
|
|
342
341
|
changes.push(`Reset phase status from "${previousStatus}" to "planned"`);
|
|
343
342
|
}
|
|
344
343
|
|
package/bin/lib/state.cjs
CHANGED
|
@@ -517,6 +517,27 @@ const VALID_PHASE_STATUSES = [
|
|
|
517
517
|
'partial', 'failed', 'paused', 'complete'
|
|
518
518
|
];
|
|
519
519
|
|
|
520
|
+
/**
|
|
521
|
+
* Atomically sync phase status across both top-level and per-phase array.
|
|
522
|
+
* Also clears execution_started_at when regressing to pre-execute states.
|
|
523
|
+
* @param {object} state - brain.json state object
|
|
524
|
+
* @param {string} newStatus - New status value
|
|
525
|
+
*/
|
|
526
|
+
function syncPhaseStatus(state, newStatus) {
|
|
527
|
+
const preExecuteStatuses = ['pending', 'ready', 'discussing', 'discussed', 'planning', 'planned'];
|
|
528
|
+
if (preExecuteStatuses.includes(newStatus)) {
|
|
529
|
+
state.phase.execution_started_at = null;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
state.phase.status = newStatus;
|
|
533
|
+
if (Array.isArray(state.phase.phases) && state.phase.current > 0) {
|
|
534
|
+
const idx = state.phase.phases.findIndex(p => p.number === state.phase.current);
|
|
535
|
+
if (idx >= 0) {
|
|
536
|
+
state.phase.phases[idx].status = newStatus;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
520
541
|
module.exports = {
|
|
521
542
|
atomicWriteSync,
|
|
522
543
|
readState,
|
|
@@ -524,5 +545,6 @@ module.exports = {
|
|
|
524
545
|
generateStateMd,
|
|
525
546
|
createDefaultState,
|
|
526
547
|
migrateState,
|
|
548
|
+
syncPhaseStatus,
|
|
527
549
|
VALID_PHASE_STATUSES
|
|
528
550
|
};
|