@brainforge/core 3.1.12 → 3.1.14
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/dist/index.d.ts +14 -6
- package/dist/index.js +153 -45
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
|
|
|
3
3
|
type ProjectType = 'student' | 'junior' | 'solo' | 'personal' | 'pro';
|
|
4
4
|
type StudentLevel = 'beginner' | 'intermediate' | 'advanced' | 'professional';
|
|
5
5
|
type Language = 'typescript' | 'javascript' | 'python' | 'java' | 'go' | 'php' | 'rust' | 'csharp' | 'ruby';
|
|
6
|
-
type PhaseStatus = 'pending' | 'active' | 'completed' | 'blocked' | 'draft' | 'discussed' | 'planned' | 'checked' | 'executing' | 'executed' | 'verified' | 'failed' | 'fixed' | 'shipped';
|
|
6
|
+
type PhaseStatus = 'pending' | 'active' | 'completed' | 'blocked' | 'draft' | 'discussed' | 'planned' | 'checked' | 'executing' | 'executed' | 'verified' | 'failed' | 'fixed' | 'shipped' | 'needs-manual-verification';
|
|
7
7
|
type TaskStatus = 'todo' | 'in-progress' | 'done' | 'skipped';
|
|
8
8
|
type TaskPriority = 'low' | 'medium' | 'high' | 'critical';
|
|
9
9
|
type TaskType = 'backend' | 'frontend' | 'database' | 'devops' | 'security' | 'test' | 'docs' | 'general';
|
|
@@ -152,10 +152,7 @@ declare class StateManager {
|
|
|
152
152
|
private statePath;
|
|
153
153
|
private state;
|
|
154
154
|
constructor(workingDir: string);
|
|
155
|
-
|
|
156
|
-
ensurePhaseDir(phaseId: string): Promise<void>;
|
|
157
|
-
writePhaseFile(phaseId: string, filename: string, content: string): Promise<void>;
|
|
158
|
-
readPhaseFile(phaseId: string, filename: string): Promise<string>;
|
|
155
|
+
updatePhaseStatus(phaseId: string, newStatus: PhaseStatus, force?: boolean): Promise<void>;
|
|
159
156
|
load(): Promise<BrainforgeState>;
|
|
160
157
|
save(newState?: BrainforgeState): Promise<void>;
|
|
161
158
|
init(project: ProjectConfig): Promise<BrainforgeState>;
|
|
@@ -750,6 +747,14 @@ interface BugEntry {
|
|
|
750
747
|
taskId?: string;
|
|
751
748
|
description?: string;
|
|
752
749
|
}
|
|
750
|
+
interface CommandResult {
|
|
751
|
+
command: string;
|
|
752
|
+
status: 'passed' | 'failed' | 'timeout';
|
|
753
|
+
exitCode: number | null;
|
|
754
|
+
stdout: string;
|
|
755
|
+
stderr: string;
|
|
756
|
+
duration: number;
|
|
757
|
+
}
|
|
753
758
|
interface VerifyResult {
|
|
754
759
|
phaseId: string;
|
|
755
760
|
phaseName: string;
|
|
@@ -758,11 +763,14 @@ interface VerifyResult {
|
|
|
758
763
|
passed: number;
|
|
759
764
|
bugs: BugEntry[];
|
|
760
765
|
blocked: boolean;
|
|
766
|
+
commandResults?: CommandResult[];
|
|
761
767
|
}
|
|
762
768
|
declare class VerificationEngine {
|
|
769
|
+
private timeoutMs;
|
|
763
770
|
detectAvailableCommands(cwd: string): Promise<string[]>;
|
|
771
|
+
runCommands(commands: string[], cwd: string): Promise<CommandResult[]>;
|
|
764
772
|
buildVerifyPrompt(phase: Phase, stack?: StackInfo, detectedCommands?: string[]): string;
|
|
765
|
-
buildVerifyMd(phase: Phase, bugs?: BugEntry[]): string;
|
|
773
|
+
buildVerifyMd(phase: Phase, commandResults?: CommandResult[], bugs?: BugEntry[]): string;
|
|
766
774
|
buildBugsMd(bugs: BugEntry[]): string;
|
|
767
775
|
}
|
|
768
776
|
|
package/dist/index.js
CHANGED
|
@@ -166,7 +166,25 @@ async function fileExists(p) {
|
|
|
166
166
|
// src/state/manager.ts
|
|
167
167
|
var STATE_DIR = ".brainforge";
|
|
168
168
|
var STATE_FILE = "core.json";
|
|
169
|
-
var
|
|
169
|
+
var VALID_TRANSITIONS = {
|
|
170
|
+
pending: ["active", "draft"],
|
|
171
|
+
draft: ["discussed", "active"],
|
|
172
|
+
discussed: ["planned", "active"],
|
|
173
|
+
active: ["discussed", "planned", "blocked", "executing"],
|
|
174
|
+
planned: ["executing", "checked", "active"],
|
|
175
|
+
checked: ["executing", "active"],
|
|
176
|
+
executing: ["executed", "failed", "blocked"],
|
|
177
|
+
executed: ["verified", "failed", "needs-manual-verification"],
|
|
178
|
+
"needs-manual-verification": ["verified", "failed"],
|
|
179
|
+
verified: ["shipped", "failed"],
|
|
180
|
+
failed: ["fixed", "active", "planned"],
|
|
181
|
+
fixed: ["verified", "executing"],
|
|
182
|
+
shipped: [],
|
|
183
|
+
// Terminal state
|
|
184
|
+
completed: [],
|
|
185
|
+
// Terminal state
|
|
186
|
+
blocked: ["active", "failed"]
|
|
187
|
+
};
|
|
170
188
|
var StateManager = class {
|
|
171
189
|
constructor(workingDir) {
|
|
172
190
|
this.workingDir = workingDir;
|
|
@@ -175,20 +193,23 @@ var StateManager = class {
|
|
|
175
193
|
workingDir;
|
|
176
194
|
statePath;
|
|
177
195
|
state = null;
|
|
178
|
-
getPhaseDir
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
196
|
+
// ... (getPhaseDir, ensurePhaseDir, writePhaseFile, readPhaseFile unchanged)
|
|
197
|
+
async updatePhaseStatus(phaseId, newStatus, force = false) {
|
|
198
|
+
const phase = this.get().phases.find((p) => p.id === phaseId);
|
|
199
|
+
if (!phase) throw new Error(`Phase "${phaseId}" not found.`);
|
|
200
|
+
if (!force) {
|
|
201
|
+
const allowed = VALID_TRANSITIONS[phase.status] || [];
|
|
202
|
+
if (!allowed.includes(newStatus) && phase.status !== newStatus) {
|
|
203
|
+
throw new Error(`Invalid transition: cannot move phase from "${phase.status}" to "${newStatus}".`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
phase.status = newStatus;
|
|
207
|
+
if (newStatus === "shipped" || newStatus === "completed") {
|
|
208
|
+
phase.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
209
|
+
}
|
|
210
|
+
await this.save();
|
|
191
211
|
}
|
|
212
|
+
// ... (rest of the methods)
|
|
192
213
|
async load() {
|
|
193
214
|
const raw = await fs3.readFile(this.statePath, "utf-8");
|
|
194
215
|
this.state = JSON.parse(raw);
|
|
@@ -604,16 +625,20 @@ Identify:
|
|
|
604
625
|
Ensure every acceptance criterion is covered.`,
|
|
605
626
|
fix_phase: `You are BrainForge AI. Generate a fix plan for phase "{{phase.name}}".
|
|
606
627
|
|
|
607
|
-
|
|
608
|
-
|
|
628
|
+
# Verification Results
|
|
629
|
+
{{failures}}
|
|
630
|
+
|
|
631
|
+
# Original Plan Context
|
|
632
|
+
{{plan}}
|
|
609
633
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
-
|
|
634
|
+
# Analysis Requirements
|
|
635
|
+
1. Identify the failed commands and their error outputs.
|
|
636
|
+
2. Formulate root cause hypotheses for each failure.
|
|
637
|
+
3. List specific files to inspect and potential logical bugs.
|
|
638
|
+
4. Propose step-by-step correction instructions for an AI agent.
|
|
639
|
+
5. Identify the exact verification commands to re-run after fixes.
|
|
615
640
|
|
|
616
|
-
Produce a FIX_PLAN.md.`,
|
|
641
|
+
Produce a FIX_PLAN.md that is honest: it guides the fix, it doesn't pretend to have fixed it automatically.`,
|
|
617
642
|
ship_phase: `You are BrainForge AI. Generate a final ship report for phase "{{phase.name}}".
|
|
618
643
|
|
|
619
644
|
Completed Tasks: {{tasks}}
|
|
@@ -7362,7 +7387,12 @@ ${cmd}
|
|
|
7362
7387
|
// src/workflow/verification-engine.ts
|
|
7363
7388
|
import fs23 from "fs/promises";
|
|
7364
7389
|
import path25 from "path";
|
|
7390
|
+
import { exec as exec2 } from "child_process";
|
|
7391
|
+
import { promisify as promisify2 } from "util";
|
|
7392
|
+
var execAsync2 = promisify2(exec2);
|
|
7365
7393
|
var VerificationEngine = class {
|
|
7394
|
+
timeoutMs = 5 * 60 * 1e3;
|
|
7395
|
+
// 5 minutes default
|
|
7366
7396
|
async detectAvailableCommands(cwd) {
|
|
7367
7397
|
const commands = [];
|
|
7368
7398
|
try {
|
|
@@ -7370,10 +7400,15 @@ var VerificationEngine = class {
|
|
|
7370
7400
|
const raw = await fs23.readFile(pkgPath, "utf-8");
|
|
7371
7401
|
const pkg = JSON.parse(raw);
|
|
7372
7402
|
const scripts = pkg.scripts || {};
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7403
|
+
const hasYarn = await fs23.access(path25.join(cwd, "yarn.lock")).then(() => true).catch(() => false);
|
|
7404
|
+
const hasPnpm = await fs23.access(path25.join(cwd, "pnpm-lock.yaml")).then(() => true).catch(() => false);
|
|
7405
|
+
const hasBun = await fs23.access(path25.join(cwd, "bun.lockb")).then(() => true).catch(() => false);
|
|
7406
|
+
const manager = hasBun ? "bun" : hasPnpm ? "pnpm" : hasYarn ? "yarn" : "npm";
|
|
7407
|
+
const run = manager === "npm" || manager === "bun" ? `${manager} run` : manager;
|
|
7408
|
+
if (scripts.build) commands.push(`${run} build`);
|
|
7409
|
+
if (scripts.test) commands.push(`${manager} test`);
|
|
7410
|
+
if (scripts.lint) commands.push(`${run} lint`);
|
|
7411
|
+
if (scripts.typecheck) commands.push(`${run} typecheck`);
|
|
7377
7412
|
} catch {
|
|
7378
7413
|
}
|
|
7379
7414
|
try {
|
|
@@ -7393,11 +7428,49 @@ var VerificationEngine = class {
|
|
|
7393
7428
|
}
|
|
7394
7429
|
try {
|
|
7395
7430
|
await fs23.access(path25.join(cwd, "build.gradle"));
|
|
7396
|
-
commands.push("
|
|
7431
|
+
commands.push("./gradlew test");
|
|
7432
|
+
} catch {
|
|
7433
|
+
}
|
|
7434
|
+
try {
|
|
7435
|
+
await fs23.access(path25.join(cwd, "go.mod"));
|
|
7436
|
+
commands.push("go test ./...");
|
|
7437
|
+
} catch {
|
|
7438
|
+
}
|
|
7439
|
+
try {
|
|
7440
|
+
await fs23.access(path25.join(cwd, "Cargo.toml"));
|
|
7441
|
+
commands.push("cargo test");
|
|
7397
7442
|
} catch {
|
|
7398
7443
|
}
|
|
7399
7444
|
return commands;
|
|
7400
7445
|
}
|
|
7446
|
+
async runCommands(commands, cwd) {
|
|
7447
|
+
const results = [];
|
|
7448
|
+
for (const command of commands) {
|
|
7449
|
+
const start = Date.now();
|
|
7450
|
+
try {
|
|
7451
|
+
const { stdout, stderr } = await execAsync2(command, { cwd, timeout: this.timeoutMs });
|
|
7452
|
+
results.push({
|
|
7453
|
+
command,
|
|
7454
|
+
status: "passed",
|
|
7455
|
+
exitCode: 0,
|
|
7456
|
+
stdout,
|
|
7457
|
+
stderr,
|
|
7458
|
+
duration: Date.now() - start
|
|
7459
|
+
});
|
|
7460
|
+
} catch (err) {
|
|
7461
|
+
const isTimeout = err.signal === "SIGTERM" || err.killed;
|
|
7462
|
+
results.push({
|
|
7463
|
+
command,
|
|
7464
|
+
status: isTimeout ? "timeout" : "failed",
|
|
7465
|
+
exitCode: err.code ?? null,
|
|
7466
|
+
stdout: err.stdout || "",
|
|
7467
|
+
stderr: err.stderr || err.message,
|
|
7468
|
+
duration: Date.now() - start
|
|
7469
|
+
});
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7472
|
+
return results;
|
|
7473
|
+
}
|
|
7401
7474
|
buildVerifyPrompt(phase, stack, detectedCommands = []) {
|
|
7402
7475
|
const allCriteria = [];
|
|
7403
7476
|
for (const t of phase.tasks) {
|
|
@@ -7447,23 +7520,54 @@ ${cmd}
|
|
|
7447
7520
|
lines.push("*Generated by BrainForge AI \u2014 paste into your AI tool to verify the phase.*");
|
|
7448
7521
|
return lines.join("\n");
|
|
7449
7522
|
}
|
|
7450
|
-
buildVerifyMd(phase, bugs = []) {
|
|
7523
|
+
buildVerifyMd(phase, commandResults = [], bugs = []) {
|
|
7451
7524
|
const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", " ");
|
|
7452
|
-
|
|
7453
|
-
|
|
7525
|
+
let status = "passed";
|
|
7526
|
+
if (commandResults.length === 0) {
|
|
7527
|
+
status = "manual-required";
|
|
7528
|
+
} else if (commandResults.some((r) => r.status !== "passed") || bugs.some((b) => b.severity === "critical")) {
|
|
7529
|
+
status = "failed";
|
|
7530
|
+
}
|
|
7531
|
+
const passedCount = commandResults.filter((r) => r.status === "passed").length;
|
|
7532
|
+
const totalDuration = commandResults.reduce((acc, r) => acc + r.duration, 0);
|
|
7454
7533
|
const lines = [
|
|
7455
|
-
`#
|
|
7456
|
-
`> Generated: ${now} UTC`,
|
|
7457
|
-
`> Status: ${blocked ? "\u{1F534} BLOCKED" : bugs.length > 0 ? "\u{1F7E1} ISSUES FOUND" : "\u{1F7E2} VERIFIED"}`,
|
|
7534
|
+
`# Verify Report: ${phase.name}`,
|
|
7458
7535
|
"",
|
|
7459
|
-
"##
|
|
7536
|
+
"## Summary",
|
|
7537
|
+
"",
|
|
7538
|
+
`- **Status:** ${status.toUpperCase()}`,
|
|
7539
|
+
`- **Date:** ${now} UTC`,
|
|
7540
|
+
`- **Duration:** ${(totalDuration / 1e3).toFixed(2)}s`,
|
|
7541
|
+
`- **Commands run:** ${commandResults.length}`,
|
|
7542
|
+
`- **Commands passed:** ${passedCount}`,
|
|
7543
|
+
`- **Commands failed:** ${commandResults.length - passedCount}`,
|
|
7544
|
+
"",
|
|
7545
|
+
"## Commands",
|
|
7546
|
+
""
|
|
7460
7547
|
];
|
|
7461
|
-
for (const
|
|
7462
|
-
const
|
|
7463
|
-
lines.push(
|
|
7548
|
+
for (const r of commandResults) {
|
|
7549
|
+
const statusIcon = r.status === "passed" ? "\u{1F7E2} PASSED" : r.status === "timeout" ? "\u23F3 TIMEOUT" : "\u{1F534} FAILED";
|
|
7550
|
+
lines.push(
|
|
7551
|
+
`### \`${r.command}\``,
|
|
7552
|
+
"",
|
|
7553
|
+
`- **Status:** ${statusIcon}`,
|
|
7554
|
+
`- **Exit code:** ${r.exitCode ?? "N/A"}`,
|
|
7555
|
+
`- **Duration:** ${(r.duration / 1e3).toFixed(2)}s`,
|
|
7556
|
+
"",
|
|
7557
|
+
"#### stdout",
|
|
7558
|
+
"```txt",
|
|
7559
|
+
r.stdout || "(empty)",
|
|
7560
|
+
"```",
|
|
7561
|
+
"",
|
|
7562
|
+
"#### stderr",
|
|
7563
|
+
"```txt",
|
|
7564
|
+
r.stderr || "(empty)",
|
|
7565
|
+
"```",
|
|
7566
|
+
""
|
|
7567
|
+
);
|
|
7464
7568
|
}
|
|
7465
7569
|
if (bugs.length > 0) {
|
|
7466
|
-
lines.push("", "
|
|
7570
|
+
lines.push("## Findings", "");
|
|
7467
7571
|
for (const bug of bugs) {
|
|
7468
7572
|
lines.push(`### ${bug.id}: ${bug.title}`);
|
|
7469
7573
|
lines.push(`**Severity:** ${bug.severity}`);
|
|
@@ -7472,13 +7576,17 @@ ${cmd}
|
|
|
7472
7576
|
lines.push("");
|
|
7473
7577
|
}
|
|
7474
7578
|
}
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7579
|
+
lines.push(
|
|
7580
|
+
"## Diagnosis",
|
|
7581
|
+
"",
|
|
7582
|
+
status === "passed" ? "* All automated checks passed. Ready to ship." : status === "manual-required" ? "* No automated commands detected. Manual verification is required." : "* Automated checks failed. Review stderr above for root causes.",
|
|
7583
|
+
"* Files likely involved: " + (phase.tasks.flatMap((t) => t.files || []).join(", ") || "N/A"),
|
|
7584
|
+
"",
|
|
7585
|
+
"## Final decision",
|
|
7586
|
+
"",
|
|
7587
|
+
`Phase status changed to: **${status === "passed" ? "verified" : status === "failed" ? "failed" : "needs-manual-verification"}**`,
|
|
7588
|
+
""
|
|
7589
|
+
);
|
|
7482
7590
|
return lines.join("\n");
|
|
7483
7591
|
}
|
|
7484
7592
|
buildBugsMd(bugs) {
|