@beastmode-develeap/beastmode 0.1.156 → 0.1.157

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.js CHANGED
@@ -3366,7 +3366,7 @@ async function createEpicsOnBoard(factoryDir, productName, boardUrl = "http://12
3366
3366
  });
3367
3367
  let itemId = "";
3368
3368
  try {
3369
- const itemResult = await new Promise((resolve21, reject) => {
3369
+ const itemResult = await new Promise((resolve22, reject) => {
3370
3370
  const url = new URL("/api/items", boardUrl);
3371
3371
  const req = http4.default.request(url, {
3372
3372
  method: "POST",
@@ -3378,7 +3378,7 @@ async function createEpicsOnBoard(factoryDir, productName, boardUrl = "http://12
3378
3378
  });
3379
3379
  res.on("end", () => {
3380
3380
  try {
3381
- resolve21(JSON.parse(data));
3381
+ resolve22(JSON.parse(data));
3382
3382
  } catch {
3383
3383
  reject(new Error(data));
3384
3384
  }
@@ -3394,13 +3394,13 @@ async function createEpicsOnBoard(factoryDir, productName, boardUrl = "http://12
3394
3394
  if (itemId) {
3395
3395
  createdIds.push(itemId);
3396
3396
  const updatePayload = JSON.stringify({ body: detailContent, creator_name: "beastmode-inception" });
3397
- await new Promise((resolve21) => {
3397
+ await new Promise((resolve22) => {
3398
3398
  const url = new URL(`/api/items/${itemId}/updates`, boardUrl);
3399
3399
  const req = http4.default.request(url, {
3400
3400
  method: "POST",
3401
3401
  headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(updatePayload).toString() }
3402
- }, () => resolve21());
3403
- req.on("error", () => resolve21());
3402
+ }, () => resolve22());
3403
+ req.on("error", () => resolve22());
3404
3404
  req.end(updatePayload);
3405
3405
  });
3406
3406
  }
@@ -3620,19 +3620,19 @@ function getRecentTokenUsage() {
3620
3620
  return recentTokenUsage.slice();
3621
3621
  }
3622
3622
  function boardGet(path, boardUrl) {
3623
- return new Promise((resolve21) => {
3623
+ return new Promise((resolve22) => {
3624
3624
  const url = new URL(path, boardUrl);
3625
3625
  const req = http.request(url, { method: "GET", timeout: 5e3 }, (res) => {
3626
3626
  let data = "";
3627
3627
  res.on("data", (chunk) => {
3628
3628
  data += chunk.toString();
3629
3629
  });
3630
- res.on("end", () => resolve21(data));
3630
+ res.on("end", () => resolve22(data));
3631
3631
  });
3632
- req.on("error", (err) => resolve21(`Board API error: ${err.message}`));
3632
+ req.on("error", (err) => resolve22(`Board API error: ${err.message}`));
3633
3633
  req.on("timeout", () => {
3634
3634
  req.destroy();
3635
- resolve21("Board API timeout");
3635
+ resolve22("Board API timeout");
3636
3636
  });
3637
3637
  req.end();
3638
3638
  });
@@ -4582,7 +4582,7 @@ function getBoardUrl2(factoryDir) {
4582
4582
  return "http://127.0.0.1:8080";
4583
4583
  }
4584
4584
  function proxyToBoard(boardUrl, method, path, body, query) {
4585
- return new Promise((resolve21, reject) => {
4585
+ return new Promise((resolve22, reject) => {
4586
4586
  const url = new URL(path, boardUrl);
4587
4587
  if (query) {
4588
4588
  for (const [k, v] of Object.entries(query)) {
@@ -4614,7 +4614,7 @@ function proxyToBoard(boardUrl, method, path, body, query) {
4614
4614
  if (statusCode >= 400) {
4615
4615
  reject(new HttpError(statusCode, parsed));
4616
4616
  } else {
4617
- resolve21(parsed);
4617
+ resolve22(parsed);
4618
4618
  }
4619
4619
  });
4620
4620
  });
@@ -4624,7 +4624,7 @@ function proxyToBoard(boardUrl, method, path, body, query) {
4624
4624
  });
4625
4625
  }
4626
4626
  function proxyBinaryToBoard(boardUrl, path, query) {
4627
- return new Promise((resolve21, reject) => {
4627
+ return new Promise((resolve22, reject) => {
4628
4628
  const url = new URL(path, boardUrl);
4629
4629
  if (query) {
4630
4630
  for (const [k, v] of Object.entries(query)) {
@@ -4653,7 +4653,7 @@ function proxyBinaryToBoard(boardUrl, path, query) {
4653
4653
  const cd = res.headers["content-disposition"] || "";
4654
4654
  const match = /filename="([^"]+)"/.exec(cd);
4655
4655
  const filename = match ? match[1] : void 0;
4656
- resolve21(new BinaryResponse(body, contentType, filename));
4656
+ resolve22(new BinaryResponse(body, contentType, filename));
4657
4657
  });
4658
4658
  }
4659
4659
  );
@@ -4665,7 +4665,7 @@ function proxyBinaryToBoard(boardUrl, path, query) {
4665
4665
  });
4666
4666
  }
4667
4667
  function transparentProxyToBoard(boardUrl, method, path, body, query) {
4668
- return new Promise((resolve21, reject) => {
4668
+ return new Promise((resolve22, reject) => {
4669
4669
  const url = new URL(path, boardUrl);
4670
4670
  if (query) {
4671
4671
  for (const [k, v] of Object.entries(query)) {
@@ -4688,7 +4688,7 @@ function transparentProxyToBoard(boardUrl, method, path, body, query) {
4688
4688
  const chunks = [];
4689
4689
  res.on("data", (chunk) => chunks.push(chunk));
4690
4690
  res.on("end", () => {
4691
- resolve21({
4691
+ resolve22({
4692
4692
  statusCode: res.statusCode || 502,
4693
4693
  contentType: res.headers["content-type"] || "application/json",
4694
4694
  body: Buffer.concat(chunks)
@@ -6420,17 +6420,17 @@ function resolveStaticDir() {
6420
6420
  );
6421
6421
  }
6422
6422
  function parseBody(req) {
6423
- return new Promise((resolve21, reject) => {
6423
+ return new Promise((resolve22, reject) => {
6424
6424
  const chunks = [];
6425
6425
  req.on("data", (chunk) => chunks.push(chunk));
6426
6426
  req.on("end", () => {
6427
6427
  const raw = Buffer.concat(chunks).toString("utf-8");
6428
6428
  if (!raw) {
6429
- resolve21({});
6429
+ resolve22({});
6430
6430
  return;
6431
6431
  }
6432
6432
  try {
6433
- resolve21(JSON.parse(raw));
6433
+ resolve22(JSON.parse(raw));
6434
6434
  } catch {
6435
6435
  reject(new Error("Invalid JSON body"));
6436
6436
  }
@@ -6492,11 +6492,11 @@ function clearSessionCookie(res) {
6492
6492
  }
6493
6493
  async function findAvailablePort(start) {
6494
6494
  for (let port = start; port < start + 100; port++) {
6495
- const available = await new Promise((resolve21) => {
6495
+ const available = await new Promise((resolve22) => {
6496
6496
  const testServer = createServer();
6497
- testServer.once("error", () => resolve21(false));
6497
+ testServer.once("error", () => resolve22(false));
6498
6498
  testServer.listen(port, "127.0.0.1", () => {
6499
- testServer.close(() => resolve21(true));
6499
+ testServer.close(() => resolve22(true));
6500
6500
  });
6501
6501
  });
6502
6502
  if (available) return port;
@@ -6817,23 +6817,23 @@ async function startServer(options = {}) {
6817
6817
  if (chatManager) {
6818
6818
  chatManager.shutdown();
6819
6819
  }
6820
- return new Promise((resolve21) => {
6820
+ return new Promise((resolve22) => {
6821
6821
  server.close(() => {
6822
6822
  if (shutdownResolve) shutdownResolve();
6823
- resolve21();
6823
+ resolve22();
6824
6824
  });
6825
6825
  setTimeout(() => {
6826
6826
  if (shutdownResolve) shutdownResolve();
6827
- resolve21();
6827
+ resolve22();
6828
6828
  }, 2e3);
6829
6829
  });
6830
6830
  }
6831
- return new Promise((resolve21, reject) => {
6831
+ return new Promise((resolve22, reject) => {
6832
6832
  server.once("error", reject);
6833
6833
  const host = options.host || "127.0.0.1";
6834
6834
  server.listen(port, host, () => {
6835
6835
  resetInactivityTimer();
6836
- resolve21({
6836
+ resolve22({
6837
6837
  server,
6838
6838
  port,
6839
6839
  url: `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`,
@@ -8141,7 +8141,7 @@ var init_mcp = __esm({
8141
8141
  });
8142
8142
 
8143
8143
  // src/index.ts
8144
- import { Command as Command24 } from "commander";
8144
+ import { Command as Command25 } from "commander";
8145
8145
 
8146
8146
  // src/cli/commands/init.ts
8147
8147
  init_engine();
@@ -8425,12 +8425,12 @@ async function runInit(name, opts) {
8425
8425
  } catch {
8426
8426
  info(`Open ${uiServer.url} in your browser to continue.`);
8427
8427
  }
8428
- await new Promise((resolve21) => {
8429
- uiServer.server.on("close", resolve21);
8428
+ await new Promise((resolve22) => {
8429
+ uiServer.server.on("close", resolve22);
8430
8430
  process.on("SIGINT", async () => {
8431
8431
  info("\nShutting down wizard server...");
8432
8432
  await uiServer.shutdown();
8433
- resolve21();
8433
+ resolve22();
8434
8434
  });
8435
8435
  });
8436
8436
  return;
@@ -8447,8 +8447,8 @@ async function runInit(name, opts) {
8447
8447
  throw new Error(`Factory already exists at ./${factoryName}. Use 'beastmode config' to modify.`);
8448
8448
  }
8449
8449
  if (opts.from) {
8450
- const { readFileSync: readFileSync32 } = await import("fs");
8451
- const templateContent = readFileSync32(resolve6(opts.from), "utf-8");
8450
+ const { readFileSync: readFileSync33 } = await import("fs");
8451
+ const templateContent = readFileSync33(resolve6(opts.from), "utf-8");
8452
8452
  const { parseTemplate: parseTemplate2 } = await Promise.resolve().then(() => (init_template_importer(), template_importer_exports));
8453
8453
  const template = parseTemplate2(templateContent);
8454
8454
  info(`Importing from template: ${opts.from}`);
@@ -9660,16 +9660,16 @@ function tryExec(cmd, timeout = 8e3) {
9660
9660
  }
9661
9661
  }
9662
9662
  function httpGet(url, timeoutMs = 5e3) {
9663
- return new Promise((resolve21) => {
9663
+ return new Promise((resolve22) => {
9664
9664
  const lib = url.startsWith("https") ? https : http3;
9665
9665
  const req = lib.get(url, { timeout: timeoutMs }, (res) => {
9666
9666
  res.resume();
9667
- resolve21(res.statusCode ?? null);
9667
+ resolve22(res.statusCode ?? null);
9668
9668
  });
9669
- req.on("error", () => resolve21(null));
9669
+ req.on("error", () => resolve22(null));
9670
9670
  req.on("timeout", () => {
9671
9671
  req.destroy();
9672
- resolve21(null);
9672
+ resolve22(null);
9673
9673
  });
9674
9674
  });
9675
9675
  }
@@ -9717,7 +9717,7 @@ async function checkClaudeAuth(key) {
9717
9717
  max_tokens: 1,
9718
9718
  messages: [{ role: "user", content: "hi" }]
9719
9719
  });
9720
- const statusCode = await new Promise((resolve21) => {
9720
+ const statusCode = await new Promise((resolve22) => {
9721
9721
  const req = https.request(
9722
9722
  {
9723
9723
  hostname: "api.anthropic.com",
@@ -9733,13 +9733,13 @@ async function checkClaudeAuth(key) {
9733
9733
  },
9734
9734
  (res) => {
9735
9735
  res.resume();
9736
- resolve21(res.statusCode ?? null);
9736
+ resolve22(res.statusCode ?? null);
9737
9737
  }
9738
9738
  );
9739
- req.on("error", () => resolve21(null));
9739
+ req.on("error", () => resolve22(null));
9740
9740
  req.on("timeout", () => {
9741
9741
  req.destroy();
9742
- resolve21(null);
9742
+ resolve22(null);
9743
9743
  });
9744
9744
  req.write(payload);
9745
9745
  req.end();
@@ -11380,10 +11380,10 @@ async function runMigrate(opts) {
11380
11380
  let runDirs = [];
11381
11381
  const checkpoints = /* @__PURE__ */ new Map();
11382
11382
  if (existsSync28(runsDir)) {
11383
- const { readdirSync: readdirSync12 } = await import("fs");
11384
- runDirs = readdirSync12(runsDir).filter((d) => {
11383
+ const { readdirSync: readdirSync13 } = await import("fs");
11384
+ runDirs = readdirSync13(runsDir).filter((d) => {
11385
11385
  try {
11386
- return readdirSync12(join26(runsDir, d)).length > 0;
11386
+ return readdirSync13(join26(runsDir, d)).length > 0;
11387
11387
  } catch {
11388
11388
  return false;
11389
11389
  }
@@ -11401,8 +11401,8 @@ async function runMigrate(opts) {
11401
11401
  }
11402
11402
  let worktreeOutput = "";
11403
11403
  try {
11404
- const { execSync: execSync12 } = await import("child_process");
11405
- worktreeOutput = execSync12("git worktree list", {
11404
+ const { execSync: execSync13 } = await import("child_process");
11405
+ worktreeOutput = execSync13("git worktree list", {
11406
11406
  cwd,
11407
11407
  encoding: "utf-8",
11408
11408
  timeout: 5e3
@@ -11525,8 +11525,8 @@ async function runPipeline(projectName, opts) {
11525
11525
  let projectConfig = null;
11526
11526
  const projectsDir = join27(bmDir, "projects");
11527
11527
  if (existsSync29(projectsDir)) {
11528
- const { readdirSync: readdirSync12 } = await import("fs");
11529
- const projectFiles = readdirSync12(projectsDir).filter(
11528
+ const { readdirSync: readdirSync13 } = await import("fs");
11529
+ const projectFiles = readdirSync13(projectsDir).filter(
11530
11530
  (f) => f.endsWith(".json")
11531
11531
  );
11532
11532
  if (projectName) {
@@ -11575,14 +11575,14 @@ async function runPipeline(projectName, opts) {
11575
11575
  const daemonConfig = generateDaemonConfig(factoryConfig, projectConfig, factoryDir);
11576
11576
  writeFileSync22(daemonConfigPath, JSON.stringify(daemonConfig, null, 2), "utf-8");
11577
11577
  info(`Generated daemon config at: ${daemonConfigPath}`);
11578
- const { execSync: execSync12 } = await import("child_process");
11578
+ const { execSync: execSync13 } = await import("child_process");
11579
11579
  let pythonAvailable = false;
11580
11580
  try {
11581
- execSync12("python --version", { timeout: 5e3, encoding: "utf-8" });
11581
+ execSync13("python --version", { timeout: 5e3, encoding: "utf-8" });
11582
11582
  pythonAvailable = true;
11583
11583
  } catch {
11584
11584
  try {
11585
- execSync12("python3 --version", { timeout: 5e3, encoding: "utf-8" });
11585
+ execSync13("python3 --version", { timeout: 5e3, encoding: "utf-8" });
11586
11586
  pythonAvailable = true;
11587
11587
  } catch {
11588
11588
  }
@@ -11703,8 +11703,8 @@ async function runDaemon(opts) {
11703
11703
  let projectConfig = null;
11704
11704
  const projectsDir = join28(bmDir, "projects");
11705
11705
  if (existsSync30(projectsDir)) {
11706
- const { readdirSync: readdirSync12 } = await import("fs");
11707
- const projectFiles = readdirSync12(projectsDir).filter(
11706
+ const { readdirSync: readdirSync13 } = await import("fs");
11707
+ const projectFiles = readdirSync13(projectsDir).filter(
11708
11708
  (f) => f.endsWith(".json")
11709
11709
  );
11710
11710
  if (projectFiles.length > 0) {
@@ -11735,16 +11735,16 @@ async function runDaemon(opts) {
11735
11735
  console.log(JSON.stringify(daemonConfig, null, 2));
11736
11736
  return;
11737
11737
  }
11738
- const { execSync: execSync12 } = await import("child_process");
11738
+ const { execSync: execSync13 } = await import("child_process");
11739
11739
  let pythonCmd = "python";
11740
11740
  let pythonAvailable = false;
11741
11741
  try {
11742
- execSync12("python --version", { timeout: 5e3, encoding: "utf-8" });
11742
+ execSync13("python --version", { timeout: 5e3, encoding: "utf-8" });
11743
11743
  pythonAvailable = true;
11744
11744
  pythonCmd = "python";
11745
11745
  } catch {
11746
11746
  try {
11747
- execSync12("python3 --version", { timeout: 5e3, encoding: "utf-8" });
11747
+ execSync13("python3 --version", { timeout: 5e3, encoding: "utf-8" });
11748
11748
  pythonAvailable = true;
11749
11749
  pythonCmd = "python3";
11750
11750
  } catch {
@@ -12534,7 +12534,7 @@ async function pullImageIfNeeded(image) {
12534
12534
  );
12535
12535
  }
12536
12536
  if (inspect.status === 0) return;
12537
- await new Promise((resolve21, reject) => {
12537
+ await new Promise((resolve22, reject) => {
12538
12538
  const child = spawn("docker", ["pull", image], {
12539
12539
  stdio: ["ignore", "inherit", "inherit"]
12540
12540
  });
@@ -12551,7 +12551,7 @@ async function pullImageIfNeeded(image) {
12551
12551
  }
12552
12552
  });
12553
12553
  child.on("exit", (code) => {
12554
- if (code === 0) resolve21();
12554
+ if (code === 0) resolve22();
12555
12555
  else reject(new Error(`docker pull ${image} exited with code ${code}`));
12556
12556
  });
12557
12557
  });
@@ -13166,7 +13166,7 @@ async function runnerSetupAction(opts) {
13166
13166
  success(`Runner '${opts.name}' registered and online.`);
13167
13167
  }
13168
13168
  async function composeUpRunner() {
13169
- await new Promise((resolve21, reject) => {
13169
+ await new Promise((resolve22, reject) => {
13170
13170
  const child = spawn3(
13171
13171
  "docker",
13172
13172
  ["compose", "--profile", "runner", "up", "-d", "runner"],
@@ -13174,7 +13174,7 @@ async function composeUpRunner() {
13174
13174
  );
13175
13175
  child.on("error", (err) => reject(err));
13176
13176
  child.on("exit", (code) => {
13177
- if (code === 0) resolve21();
13177
+ if (code === 0) resolve22();
13178
13178
  else
13179
13179
  reject(
13180
13180
  new Error(
@@ -13376,9 +13376,194 @@ runnerCommand.command("restore-workflows").description("Restore workflows to ori
13376
13376
  }
13377
13377
  });
13378
13378
 
13379
+ // src/cli/commands/project-cmd.ts
13380
+ init_engine();
13381
+ import { Command as Command24 } from "commander";
13382
+ import { existsSync as existsSync35, mkdirSync as mkdirSync22, writeFileSync as writeFileSync29, readFileSync as readFileSync32, readdirSync as readdirSync12, renameSync as renameSync2 } from "fs";
13383
+ import { join as join34, resolve as resolve21, basename as basename6 } from "path";
13384
+ import { execSync as execSync12 } from "child_process";
13385
+ var DEFAULT_MAX_PROJECTS = 5;
13386
+ var MIN_DISK_WARNING_GB = 10;
13387
+ function countExistingProjects(factoryDir) {
13388
+ const projectsDir = join34(factoryDir, ".beastmode", "projects");
13389
+ if (!existsSync35(projectsDir)) return 0;
13390
+ let count = 0;
13391
+ for (const entry of readdirSync12(projectsDir)) {
13392
+ if (entry.startsWith(".")) continue;
13393
+ if (existsSync35(join34(projectsDir, entry, "project.json"))) count++;
13394
+ else if (entry.endsWith(".json")) count++;
13395
+ }
13396
+ return count;
13397
+ }
13398
+ function getMaxProjects(factoryDir) {
13399
+ const configPath = join34(factoryDir, ".beastmode", "config.json");
13400
+ if (existsSync35(configPath)) {
13401
+ try {
13402
+ const config = JSON.parse(readFileSync32(configPath, "utf-8"));
13403
+ if (typeof config.max_projects === "number") return config.max_projects;
13404
+ } catch {
13405
+ }
13406
+ }
13407
+ return DEFAULT_MAX_PROJECTS;
13408
+ }
13409
+ function getFreeDiskGB() {
13410
+ try {
13411
+ const output = execSync12("df -k / | tail -1", { encoding: "utf-8", timeout: 3e3 });
13412
+ const parts = output.trim().split(/\s+/);
13413
+ const availKB = parseInt(parts[3], 10);
13414
+ if (!isNaN(availKB)) return Math.floor(availKB / (1024 * 1024));
13415
+ } catch {
13416
+ }
13417
+ return null;
13418
+ }
13419
+ function projectAddAction(factoryDir, projectPath, opts) {
13420
+ const resolvedPath = resolve21(projectPath);
13421
+ if (!existsSync35(resolvedPath)) throw new Error(`Directory not found: ${resolvedPath}`);
13422
+ const projectName = opts.name || basename6(resolvedPath);
13423
+ const projectsDir = join34(factoryDir, ".beastmode", "projects", projectName);
13424
+ if (existsSync35(projectsDir)) throw new Error(`Project already exists: ${projectName}`);
13425
+ const currentCount = countExistingProjects(factoryDir);
13426
+ const maxProjects = getMaxProjects(factoryDir);
13427
+ if (currentCount >= maxProjects) {
13428
+ throw new Error(
13429
+ `Factory has ${currentCount}/${maxProjects} projects. Increase "max_projects" in .beastmode/config.json or remove a project with "beastmode project remove <name>".`
13430
+ );
13431
+ }
13432
+ const freeGB = getFreeDiskGB();
13433
+ if (freeGB !== null && freeGB < MIN_DISK_WARNING_GB) {
13434
+ console.warn(
13435
+ `Warning: only ${freeGB}GB free disk space. Each project with worktrees may use 1-2GB. Consider freeing space.`
13436
+ );
13437
+ }
13438
+ mkdirSync22(projectsDir, { recursive: true });
13439
+ let stack = {};
13440
+ try {
13441
+ stack = detectStack(resolvedPath);
13442
+ } catch {
13443
+ }
13444
+ let verifyPort = 3001;
13445
+ const allProjectsDir = join34(factoryDir, ".beastmode", "projects");
13446
+ if (existsSync35(allProjectsDir)) {
13447
+ for (const d of readdirSync12(allProjectsDir)) {
13448
+ if (d.startsWith(".")) continue;
13449
+ if (d === projectName) continue;
13450
+ const pf = join34(allProjectsDir, d, "project.json");
13451
+ if (existsSync35(pf)) {
13452
+ try {
13453
+ const pc = JSON.parse(readFileSync32(pf, "utf-8"));
13454
+ const port = pc?.deploy?.verify_port;
13455
+ if (typeof port === "number" && port >= verifyPort) verifyPort = port + 1;
13456
+ } catch {
13457
+ }
13458
+ }
13459
+ }
13460
+ }
13461
+ const projectConfig = {
13462
+ name: projectName,
13463
+ path: resolvedPath,
13464
+ github: {
13465
+ repo: stack.git_remote || "",
13466
+ default_branch: "main"
13467
+ },
13468
+ board: {
13469
+ id: opts.boardId ? parseInt(opts.boardId, 10) : null,
13470
+ url: "http://127.0.0.1:8080",
13471
+ auto_created: !opts.boardId
13472
+ },
13473
+ deploy: { verify_port: verifyPort },
13474
+ pipeline: {},
13475
+ models: {},
13476
+ slots: { max: null },
13477
+ registered_at: (/* @__PURE__ */ new Date()).toISOString()
13478
+ };
13479
+ writeFileSync29(join34(projectsDir, "project.json"), JSON.stringify(projectConfig, null, 2) + "\n");
13480
+ writeFileSync29(join34(projectsDir, "extensions.json"), JSON.stringify({
13481
+ plugins: { add: [], remove: [] },
13482
+ mcps: { add: {}, remove: [] },
13483
+ skills: { add: [], remove: [] }
13484
+ }, null, 2) + "\n");
13485
+ const runsDir = join34(factoryDir, "runs", projectName);
13486
+ if (!existsSync35(runsDir)) mkdirSync22(runsDir, { recursive: true });
13487
+ if (!opts.boardId) {
13488
+ void (async () => {
13489
+ try {
13490
+ const boardUrl = projectConfig.board.url;
13491
+ const http4 = await import("http");
13492
+ const postData = JSON.stringify({ project_name: projectName });
13493
+ await new Promise((resolve22, reject) => {
13494
+ const url = new URL("/api/boards", boardUrl);
13495
+ const req = http4.request(url, {
13496
+ method: "POST",
13497
+ headers: {
13498
+ "Content-Type": "application/json",
13499
+ "Content-Length": Buffer.byteLength(postData).toString()
13500
+ }
13501
+ }, (res) => {
13502
+ let data = "";
13503
+ res.on("data", (chunk) => {
13504
+ data += chunk.toString();
13505
+ });
13506
+ res.on("end", () => {
13507
+ try {
13508
+ const result = JSON.parse(data);
13509
+ if (result.created) {
13510
+ projectConfig.board.auto_created = true;
13511
+ }
13512
+ } catch {
13513
+ }
13514
+ resolve22();
13515
+ });
13516
+ });
13517
+ req.on("error", () => resolve22());
13518
+ req.end(postData);
13519
+ });
13520
+ } catch {
13521
+ }
13522
+ })();
13523
+ }
13524
+ }
13525
+ function projectListAction(factoryDir) {
13526
+ const projectsDir = join34(factoryDir, ".beastmode", "projects");
13527
+ if (!existsSync35(projectsDir)) return [];
13528
+ return readdirSync12(projectsDir).filter((d) => !d.startsWith(".") && existsSync35(join34(projectsDir, d, "project.json"))).map((d) => {
13529
+ try {
13530
+ return JSON.parse(readFileSync32(join34(projectsDir, d, "project.json"), "utf-8"));
13531
+ } catch {
13532
+ return null;
13533
+ }
13534
+ }).filter(Boolean);
13535
+ }
13536
+ function projectRemoveAction(factoryDir, name) {
13537
+ const projectDir = join34(factoryDir, ".beastmode", "projects", name);
13538
+ if (!existsSync35(projectDir)) throw new Error(`Project not found: ${name}`);
13539
+ const archiveDir = join34(factoryDir, ".beastmode", "projects", ".archived", name);
13540
+ mkdirSync22(join34(factoryDir, ".beastmode", "projects", ".archived"), { recursive: true });
13541
+ renameSync2(projectDir, archiveDir);
13542
+ }
13543
+ var projectCommand = new Command24("project").description("Manage projects in this factory");
13544
+ projectCommand.command("add <path>").description("Register a project").option("--name <name>", "Override project name").option("--board-id <id>", "Link to existing board ID").action((path, opts) => {
13545
+ const factoryDir = resolve21(".");
13546
+ projectAddAction(factoryDir, path, opts);
13547
+ console.log(`Project registered: ${opts.name || basename6(resolve21(path))}`);
13548
+ });
13549
+ projectCommand.command("list").description("List registered projects").action(() => {
13550
+ const projects = projectListAction(resolve21("."));
13551
+ if (projects.length === 0) {
13552
+ console.log("No projects registered.");
13553
+ return;
13554
+ }
13555
+ for (const p of projects) {
13556
+ console.log(` ${p.name} \u2014 ${p.path}`);
13557
+ }
13558
+ });
13559
+ projectCommand.command("remove <name>").description("Archive a project").action((name) => {
13560
+ projectRemoveAction(resolve21("."), name);
13561
+ console.log(`Project archived: ${name}`);
13562
+ });
13563
+
13379
13564
  // src/index.ts
13380
13565
  init_version();
13381
- var program = new Command24();
13566
+ var program = new Command25();
13382
13567
  program.name("beastmode").description("BeastMode Dark Factory \u2014 turn intent into verified software").version(ENGINE_VERSION);
13383
13568
  program.addCommand(initCommand);
13384
13569
  program.addCommand(boardCommand);
@@ -13403,5 +13588,6 @@ program.addCommand(downCommand);
13403
13588
  program.addCommand(logsCommand);
13404
13589
  program.addCommand(updateCommand);
13405
13590
  program.addCommand(runnerCommand);
13591
+ program.addCommand(projectCommand);
13406
13592
  program.parse();
13407
13593
  //# sourceMappingURL=index.js.map