@beastmode-develeap/beastmode 0.1.333 → 0.1.335

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
@@ -1637,11 +1637,36 @@ var init_secret_validator = __esm({
1637
1637
  });
1638
1638
 
1639
1639
  // src/engine/version.ts
1640
- var ENGINE_VERSION, SCHEMA_VERSION;
1640
+ import { readFileSync as readFileSync2 } from "fs";
1641
+ import { fileURLToPath } from "url";
1642
+ import { dirname, join as join2 } from "path";
1643
+ function resolveEngineVersion() {
1644
+ try {
1645
+ let dir = dirname(fileURLToPath(import.meta.url));
1646
+ for (let i = 0; i < 6; i++) {
1647
+ const candidate = join2(dir, "package.json");
1648
+ try {
1649
+ const pkg = JSON.parse(readFileSync2(candidate, "utf-8"));
1650
+ if (pkg.name === PACKAGE_NAME && pkg.version) {
1651
+ return pkg.version;
1652
+ }
1653
+ } catch {
1654
+ }
1655
+ const parent = dirname(dir);
1656
+ if (parent === dir) break;
1657
+ dir = parent;
1658
+ }
1659
+ } catch {
1660
+ }
1661
+ return FALLBACK_VERSION;
1662
+ }
1663
+ var FALLBACK_VERSION, PACKAGE_NAME, ENGINE_VERSION, SCHEMA_VERSION;
1641
1664
  var init_version = __esm({
1642
1665
  "src/engine/version.ts"() {
1643
1666
  "use strict";
1644
- ENGINE_VERSION = "0.1.0";
1667
+ FALLBACK_VERSION = "0.0.0-unknown";
1668
+ PACKAGE_NAME = "@beastmode-develeap/beastmode";
1669
+ ENGINE_VERSION = resolveEngineVersion();
1645
1670
  SCHEMA_VERSION = 1;
1646
1671
  }
1647
1672
  });
@@ -1839,21 +1864,21 @@ var init_template_importer = __esm({
1839
1864
  });
1840
1865
 
1841
1866
  // src/engine/config-validator.ts
1842
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1843
- import { join as join2 } from "path";
1867
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
1868
+ import { join as join3 } from "path";
1844
1869
  function validateFactory(factoryDir, env) {
1845
1870
  const errors = [];
1846
1871
  const warnings = [];
1847
- const bmDir = join2(factoryDir, ".beastmode");
1872
+ const bmDir = join3(factoryDir, ".beastmode");
1848
1873
  if (!existsSync2(bmDir)) {
1849
1874
  return { valid: false, errors: [`No factory found at ${factoryDir} (.beastmode directory missing)`], warnings };
1850
1875
  }
1851
- const factoryJsonPath = join2(bmDir, "factory.json");
1876
+ const factoryJsonPath = join3(bmDir, "factory.json");
1852
1877
  if (!existsSync2(factoryJsonPath)) {
1853
1878
  errors.push("Missing .beastmode/factory.json");
1854
1879
  } else {
1855
1880
  try {
1856
- const raw = JSON.parse(readFileSync2(factoryJsonPath, "utf-8"));
1881
+ const raw = JSON.parse(readFileSync3(factoryJsonPath, "utf-8"));
1857
1882
  const result = FactoryIdentitySchema.safeParse(raw);
1858
1883
  if (!result.success) {
1859
1884
  errors.push(`Invalid factory.json: ${result.error.issues.map((i) => i.message).join(", ")}`);
@@ -1862,12 +1887,12 @@ function validateFactory(factoryDir, env) {
1862
1887
  errors.push("factory.json is not valid JSON");
1863
1888
  }
1864
1889
  }
1865
- const configPath = join2(bmDir, "config.json");
1890
+ const configPath = join3(bmDir, "config.json");
1866
1891
  if (!existsSync2(configPath)) {
1867
1892
  errors.push("Missing .beastmode/config.json");
1868
1893
  } else {
1869
1894
  try {
1870
- const raw = JSON.parse(readFileSync2(configPath, "utf-8"));
1895
+ const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
1871
1896
  const result = FactoryConfigSchema.safeParse(raw);
1872
1897
  if (!result.success) {
1873
1898
  for (const issue of result.error.issues) {
@@ -2267,8 +2292,8 @@ var init_plugin_resolver = __esm({
2267
2292
  });
2268
2293
 
2269
2294
  // src/engine/plugin-registry.ts
2270
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
2271
- import { join as join3, resolve } from "path";
2295
+ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
2296
+ import { join as join4, resolve } from "path";
2272
2297
  function getRegistryUrl() {
2273
2298
  return process.env.BEASTMODE_REGISTRY_URL || DEFAULT_REGISTRY_URL;
2274
2299
  }
@@ -2280,24 +2305,24 @@ function readLocalRegistry(filePath) {
2280
2305
  if (!existsSync3(resolved)) {
2281
2306
  throw new Error(`Local registry file not found: ${resolved}`);
2282
2307
  }
2283
- const raw = JSON.parse(readFileSync3(resolved, "utf-8"));
2308
+ const raw = JSON.parse(readFileSync4(resolved, "utf-8"));
2284
2309
  return RegistryIndexSchema.parse(raw);
2285
2310
  }
2286
2311
  function getCachePath(factoryDir) {
2287
- return join3(factoryDir, ".beastmode", ".cache", "registry-index.json");
2312
+ return join4(factoryDir, ".beastmode", ".cache", "registry-index.json");
2288
2313
  }
2289
2314
  function readCache(factoryDir) {
2290
2315
  const cachePath = getCachePath(factoryDir);
2291
2316
  if (!existsSync3(cachePath)) return null;
2292
2317
  try {
2293
- return JSON.parse(readFileSync3(cachePath, "utf-8"));
2318
+ return JSON.parse(readFileSync4(cachePath, "utf-8"));
2294
2319
  } catch {
2295
2320
  return null;
2296
2321
  }
2297
2322
  }
2298
2323
  function writeCache(factoryDir, data) {
2299
2324
  const cachePath = getCachePath(factoryDir);
2300
- const cacheDir = join3(factoryDir, ".beastmode", ".cache");
2325
+ const cacheDir = join4(factoryDir, ".beastmode", ".cache");
2301
2326
  mkdirSync(cacheDir, { recursive: true });
2302
2327
  writeFileSync(
2303
2328
  cachePath,
@@ -2368,20 +2393,20 @@ var init_plugin_registry = __esm({
2368
2393
  });
2369
2394
 
2370
2395
  // src/engine/mcp-manager.ts
2371
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
2372
- import { dirname, join as join4 } from "path";
2396
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
2397
+ import { dirname as dirname2, join as join5 } from "path";
2373
2398
  function getMcpServersPath(factoryDir) {
2374
- return join4(factoryDir, ".beastmode", "mcp-servers.json");
2399
+ return join5(factoryDir, ".beastmode", "mcp-servers.json");
2375
2400
  }
2376
2401
  function readMcpServers(factoryDir) {
2377
2402
  const filePath = getMcpServersPath(factoryDir);
2378
2403
  if (!existsSync4(filePath)) return {};
2379
- const raw = JSON.parse(readFileSync4(filePath, "utf-8"));
2404
+ const raw = JSON.parse(readFileSync5(filePath, "utf-8"));
2380
2405
  return raw.servers || {};
2381
2406
  }
2382
2407
  function writeMcpServers(factoryDir, servers) {
2383
2408
  const filePath = getMcpServersPath(factoryDir);
2384
- const dir = dirname(filePath);
2409
+ const dir = dirname2(filePath);
2385
2410
  if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
2386
2411
  writeFileSync2(filePath, JSON.stringify({ servers }, null, 2) + "\n", "utf-8");
2387
2412
  }
@@ -2413,10 +2438,10 @@ var init_mcp_manager = __esm({
2413
2438
  });
2414
2439
 
2415
2440
  // src/engine/hook-manager.ts
2416
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
2417
- import { dirname as dirname2, join as join5 } from "path";
2441
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
2442
+ import { dirname as dirname3, join as join6 } from "path";
2418
2443
  function getHooksPath(factoryDir) {
2419
- return join5(factoryDir, ".beastmode", "hooks.json");
2444
+ return join6(factoryDir, ".beastmode", "hooks.json");
2420
2445
  }
2421
2446
  function validateEvent(event) {
2422
2447
  if (!HOOK_EVENTS.includes(event)) {
@@ -2433,12 +2458,12 @@ function emptyHooks() {
2433
2458
  function readHooks(factoryDir) {
2434
2459
  const filePath = getHooksPath(factoryDir);
2435
2460
  if (!existsSync5(filePath)) return emptyHooks();
2436
- const raw = JSON.parse(readFileSync5(filePath, "utf-8"));
2461
+ const raw = JSON.parse(readFileSync6(filePath, "utf-8"));
2437
2462
  return raw.hooks || emptyHooks();
2438
2463
  }
2439
2464
  function writeHooks(factoryDir, hooks) {
2440
2465
  const filePath = getHooksPath(factoryDir);
2441
- const dir = dirname2(filePath);
2466
+ const dir = dirname3(filePath);
2442
2467
  if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
2443
2468
  writeFileSync3(
2444
2469
  filePath,
@@ -2487,40 +2512,40 @@ var init_hook_manager = __esm({
2487
2512
  // src/engine/plugin-installer.ts
2488
2513
  import {
2489
2514
  existsSync as existsSync6,
2490
- readFileSync as readFileSync6,
2515
+ readFileSync as readFileSync7,
2491
2516
  writeFileSync as writeFileSync4,
2492
2517
  cpSync,
2493
2518
  rmSync,
2494
2519
  mkdirSync as mkdirSync4
2495
2520
  } from "fs";
2496
- import { join as join6, resolve as resolve2 } from "path";
2521
+ import { join as join7, resolve as resolve2 } from "path";
2497
2522
  function readLock(factoryDir) {
2498
- const lockPath = join6(factoryDir, ".beastmode", "extensions.lock");
2523
+ const lockPath = join7(factoryDir, ".beastmode", "extensions.lock");
2499
2524
  if (!existsSync6(lockPath)) {
2500
2525
  return { plugins: {}, locked_at: (/* @__PURE__ */ new Date()).toISOString() };
2501
2526
  }
2502
- return JSON.parse(readFileSync6(lockPath, "utf-8"));
2527
+ return JSON.parse(readFileSync7(lockPath, "utf-8"));
2503
2528
  }
2504
2529
  function writeLock(factoryDir, lock) {
2505
- const lockPath = join6(factoryDir, ".beastmode", "extensions.lock");
2530
+ const lockPath = join7(factoryDir, ".beastmode", "extensions.lock");
2506
2531
  lock.locked_at = (/* @__PURE__ */ new Date()).toISOString();
2507
2532
  writeFileSync4(lockPath, JSON.stringify(lock, null, 2) + "\n", "utf-8");
2508
2533
  }
2509
2534
  function readConfig(factoryDir) {
2510
- const configPath = join6(factoryDir, ".beastmode", "config.json");
2535
+ const configPath = join7(factoryDir, ".beastmode", "config.json");
2511
2536
  if (!existsSync6(configPath)) return {};
2512
- return JSON.parse(readFileSync6(configPath, "utf-8"));
2537
+ return JSON.parse(readFileSync7(configPath, "utf-8"));
2513
2538
  }
2514
2539
  function writeConfig(factoryDir, config) {
2515
- const configPath = join6(factoryDir, ".beastmode", "config.json");
2540
+ const configPath = join7(factoryDir, ".beastmode", "config.json");
2516
2541
  writeFileSync4(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
2517
2542
  }
2518
2543
  function registerPluginMcps(factoryDir, pluginName, pluginDir, manifest) {
2519
2544
  const mcpNames = manifest.provides?.mcp_servers || [];
2520
2545
  for (const mcpName of mcpNames) {
2521
- const mcpDefPath = join6(pluginDir, "mcp-servers", `${mcpName}.json`);
2546
+ const mcpDefPath = join7(pluginDir, "mcp-servers", `${mcpName}.json`);
2522
2547
  if (!existsSync6(mcpDefPath)) continue;
2523
- const mcpDef = JSON.parse(readFileSync6(mcpDefPath, "utf-8"));
2548
+ const mcpDef = JSON.parse(readFileSync7(mcpDefPath, "utf-8"));
2524
2549
  addMcp(factoryDir, mcpName, {
2525
2550
  command: mcpDef.command,
2526
2551
  args: mcpDef.args || [],
@@ -2533,9 +2558,9 @@ function registerPluginMcps(factoryDir, pluginName, pluginDir, manifest) {
2533
2558
  function registerPluginHooks(factoryDir, pluginName, pluginDir, manifest) {
2534
2559
  const hookNames = manifest.provides?.hooks || [];
2535
2560
  for (const hookName of hookNames) {
2536
- const hookDefPath = join6(pluginDir, "hooks", `${hookName}.json`);
2561
+ const hookDefPath = join7(pluginDir, "hooks", `${hookName}.json`);
2537
2562
  if (!existsSync6(hookDefPath)) continue;
2538
- const hookDef = JSON.parse(readFileSync6(hookDefPath, "utf-8"));
2563
+ const hookDef = JSON.parse(readFileSync7(hookDefPath, "utf-8"));
2539
2564
  const event = hookDef.event;
2540
2565
  if (!HOOK_EVENTS.includes(event)) continue;
2541
2566
  addHook(factoryDir, event, {
@@ -2569,13 +2594,13 @@ function unregisterPluginHooks(factoryDir, pluginName) {
2569
2594
  }
2570
2595
  async function installPlugin(factoryDir, sourcePath) {
2571
2596
  const resolvedPath = resolve2(sourcePath);
2572
- const manifestPath = join6(resolvedPath, "plugin.json");
2597
+ const manifestPath = join7(resolvedPath, "plugin.json");
2573
2598
  if (!existsSync6(manifestPath)) {
2574
2599
  throw new Error(
2575
2600
  `plugin.json not found at ${manifestPath}. Is this a BeastMode plugin directory?`
2576
2601
  );
2577
2602
  }
2578
- const rawManifest = JSON.parse(readFileSync6(manifestPath, "utf-8"));
2603
+ const rawManifest = JSON.parse(readFileSync7(manifestPath, "utf-8"));
2579
2604
  const manifest = parseManifest(rawManifest);
2580
2605
  const compat = checkEngineCompat(manifest.engine_version, ENGINE_VERSION);
2581
2606
  if (!compat.compatible) {
@@ -2589,15 +2614,15 @@ async function installPlugin(factoryDir, sourcePath) {
2589
2614
  `Plugin '${manifest.name}' is already installed. Remove it first with 'beastmode remove plugin ${manifest.name}'.`
2590
2615
  );
2591
2616
  }
2592
- const destDir = join6(factoryDir, ".beastmode", "plugins", manifest.name);
2617
+ const destDir = join7(factoryDir, ".beastmode", "plugins", manifest.name);
2593
2618
  mkdirSync4(destDir, { recursive: true });
2594
2619
  cpSync(resolvedPath, destDir, { recursive: true });
2595
2620
  registerPluginMcps(factoryDir, manifest.name, destDir, manifest);
2596
2621
  registerPluginHooks(factoryDir, manifest.name, destDir, manifest);
2597
2622
  if (manifest.config_defaults) {
2598
- const defaultsPath = join6(destDir, manifest.config_defaults);
2623
+ const defaultsPath = join7(destDir, manifest.config_defaults);
2599
2624
  if (existsSync6(defaultsPath)) {
2600
- const defaults = JSON.parse(readFileSync6(defaultsPath, "utf-8"));
2625
+ const defaults = JSON.parse(readFileSync7(defaultsPath, "utf-8"));
2601
2626
  const currentConfig = readConfig(factoryDir);
2602
2627
  const mergedConfig = mergePluginDefaults(currentConfig, defaults);
2603
2628
  writeConfig(factoryDir, mergedConfig);
@@ -2620,13 +2645,13 @@ function removePlugin(factoryDir, name, options) {
2620
2645
  }
2621
2646
  if (!force) {
2622
2647
  const dependents = [];
2623
- const pluginsDir = join6(factoryDir, ".beastmode", "plugins");
2648
+ const pluginsDir = join7(factoryDir, ".beastmode", "plugins");
2624
2649
  for (const [pluginName, _entry] of Object.entries(lock.plugins)) {
2625
2650
  if (pluginName === name) continue;
2626
- const pluginManifestPath = join6(pluginsDir, pluginName, "plugin.json");
2651
+ const pluginManifestPath = join7(pluginsDir, pluginName, "plugin.json");
2627
2652
  if (!existsSync6(pluginManifestPath)) continue;
2628
2653
  try {
2629
- const pluginManifest = JSON.parse(readFileSync6(pluginManifestPath, "utf-8"));
2654
+ const pluginManifest = JSON.parse(readFileSync7(pluginManifestPath, "utf-8"));
2630
2655
  if (Array.isArray(pluginManifest.dependencies) && pluginManifest.dependencies.includes(name)) {
2631
2656
  dependents.push(pluginName);
2632
2657
  }
@@ -2641,7 +2666,7 @@ function removePlugin(factoryDir, name, options) {
2641
2666
  }
2642
2667
  unregisterPluginMcps(factoryDir, name);
2643
2668
  unregisterPluginHooks(factoryDir, name);
2644
- const pluginDir = join6(factoryDir, ".beastmode", "plugins", name);
2669
+ const pluginDir = join7(factoryDir, ".beastmode", "plugins", name);
2645
2670
  if (existsSync6(pluginDir)) {
2646
2671
  rmSync(pluginDir, { recursive: true, force: true });
2647
2672
  }
@@ -2665,12 +2690,12 @@ import {
2665
2690
  readdirSync as readdirSync2,
2666
2691
  cpSync as cpSync2,
2667
2692
  rmSync as rmSync2,
2668
- readFileSync as readFileSync7,
2693
+ readFileSync as readFileSync8,
2669
2694
  writeFileSync as writeFileSync5,
2670
2695
  mkdirSync as mkdirSync5,
2671
2696
  statSync as statSync2
2672
2697
  } from "fs";
2673
- import { join as join7, basename as basename2 } from "path";
2698
+ import { join as join8, basename as basename2 } from "path";
2674
2699
  function parseSkillFrontmatter(content) {
2675
2700
  const match = content.match(/^---\n([\s\S]*?)\n---/);
2676
2701
  if (!match) return {};
@@ -2693,14 +2718,14 @@ function addSkill(factoryDir, sourcePath) {
2693
2718
  if (!stat.isDirectory()) {
2694
2719
  throw new Error(`Skill source must be a directory: ${sourcePath}`);
2695
2720
  }
2696
- const skillMdPath = join7(sourcePath, "SKILL.md");
2721
+ const skillMdPath = join8(sourcePath, "SKILL.md");
2697
2722
  if (!existsSync7(skillMdPath)) {
2698
2723
  throw new Error(
2699
2724
  `SKILL.md not found in ${sourcePath}. Every skill must have a SKILL.md with frontmatter.`
2700
2725
  );
2701
2726
  }
2702
2727
  const skillName = basename2(sourcePath);
2703
- const destPath = join7(factoryDir, ".beastmode", "skills", skillName);
2728
+ const destPath = join8(factoryDir, ".beastmode", "skills", skillName);
2704
2729
  if (existsSync7(destPath)) {
2705
2730
  throw new Error(
2706
2731
  `Skill '${skillName}' already exists at ${destPath}. Remove it first.`
@@ -2714,7 +2739,7 @@ function createSkill(factoryDir, name, description, body) {
2714
2739
  if (!body.trim()) throw new Error("Skill body is required");
2715
2740
  const slug = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2716
2741
  if (!slug) throw new Error("Skill name must contain alphanumeric characters");
2717
- const skillDir = join7(factoryDir, ".beastmode", "skills", slug);
2742
+ const skillDir = join8(factoryDir, ".beastmode", "skills", slug);
2718
2743
  if (existsSync7(skillDir)) {
2719
2744
  throw new Error(
2720
2745
  `Skill '${slug}' already exists. Remove it first or pick a different name.`
@@ -2731,11 +2756,11 @@ description: ${q(description.trim())}
2731
2756
 
2732
2757
  ${body.trim()}
2733
2758
  `;
2734
- writeFileSync5(join7(skillDir, "SKILL.md"), content, "utf-8");
2759
+ writeFileSync5(join8(skillDir, "SKILL.md"), content, "utf-8");
2735
2760
  return { slug, path: skillDir };
2736
2761
  }
2737
2762
  function removeSkill(factoryDir, name) {
2738
- const skillPath = join7(factoryDir, ".beastmode", "skills", name);
2763
+ const skillPath = join8(factoryDir, ".beastmode", "skills", name);
2739
2764
  if (!existsSync7(skillPath)) {
2740
2765
  throw new Error(`Skill '${name}' not found at ${skillPath}.`);
2741
2766
  }
@@ -2743,32 +2768,32 @@ function removeSkill(factoryDir, name) {
2743
2768
  }
2744
2769
  function listSkills(factoryDir) {
2745
2770
  const skills = [];
2746
- const bmDir = join7(factoryDir, ".beastmode");
2747
- const customSkillsDir = join7(bmDir, "skills");
2771
+ const bmDir = join8(factoryDir, ".beastmode");
2772
+ const customSkillsDir = join8(bmDir, "skills");
2748
2773
  if (existsSync7(customSkillsDir)) {
2749
2774
  for (const entry of readdirSync2(customSkillsDir, { withFileTypes: true })) {
2750
2775
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
2751
- const skillMdPath = join7(customSkillsDir, entry.name, "SKILL.md");
2776
+ const skillMdPath = join8(customSkillsDir, entry.name, "SKILL.md");
2752
2777
  if (!existsSync7(skillMdPath)) continue;
2753
2778
  const frontmatter = parseSkillFrontmatter(
2754
- readFileSync7(skillMdPath, "utf-8")
2779
+ readFileSync8(skillMdPath, "utf-8")
2755
2780
  );
2756
2781
  skills.push({
2757
2782
  name: frontmatter.name || entry.name,
2758
2783
  description: frontmatter.description || "",
2759
2784
  source: "custom",
2760
- path: join7(customSkillsDir, entry.name)
2785
+ path: join8(customSkillsDir, entry.name)
2761
2786
  });
2762
2787
  }
2763
2788
  }
2764
- const pluginsDir = join7(bmDir, "plugins");
2789
+ const pluginsDir = join8(bmDir, "plugins");
2765
2790
  if (existsSync7(pluginsDir)) {
2766
2791
  for (const pluginEntry of readdirSync2(pluginsDir, {
2767
2792
  withFileTypes: true
2768
2793
  })) {
2769
2794
  if (!pluginEntry.isDirectory() || pluginEntry.name.startsWith("."))
2770
2795
  continue;
2771
- const pluginSkillsDir = join7(
2796
+ const pluginSkillsDir = join8(
2772
2797
  pluginsDir,
2773
2798
  pluginEntry.name,
2774
2799
  "skills"
@@ -2779,20 +2804,20 @@ function listSkills(factoryDir) {
2779
2804
  })) {
2780
2805
  if (!skillEntry.isDirectory() || skillEntry.name.startsWith("."))
2781
2806
  continue;
2782
- const skillMdPath = join7(
2807
+ const skillMdPath = join8(
2783
2808
  pluginSkillsDir,
2784
2809
  skillEntry.name,
2785
2810
  "SKILL.md"
2786
2811
  );
2787
2812
  if (!existsSync7(skillMdPath)) continue;
2788
2813
  const frontmatter = parseSkillFrontmatter(
2789
- readFileSync7(skillMdPath, "utf-8")
2814
+ readFileSync8(skillMdPath, "utf-8")
2790
2815
  );
2791
2816
  skills.push({
2792
2817
  name: frontmatter.name || skillEntry.name,
2793
2818
  description: frontmatter.description || "",
2794
2819
  source: `plugin:${pluginEntry.name}`,
2795
- path: join7(pluginSkillsDir, skillEntry.name)
2820
+ path: join8(pluginSkillsDir, skillEntry.name)
2796
2821
  });
2797
2822
  }
2798
2823
  }
@@ -3223,8 +3248,8 @@ var init_bridge = __esm({
3223
3248
  });
3224
3249
 
3225
3250
  // src/engine/project-record.ts
3226
- import { existsSync as existsSync8, writeFileSync as writeFileSync6, renameSync, readFileSync as readFileSync8, readdirSync as readdirSync3, mkdirSync as mkdirSync6, unlinkSync } from "fs";
3227
- import { join as join8, basename as basename3 } from "path";
3251
+ import { existsSync as existsSync8, writeFileSync as writeFileSync6, renameSync, readFileSync as readFileSync9, readdirSync as readdirSync3, mkdirSync as mkdirSync6, unlinkSync } from "fs";
3252
+ import { join as join9, basename as basename3 } from "path";
3228
3253
  function computeVerifyPort(existingProjects) {
3229
3254
  let port = 3001;
3230
3255
  if (existingProjects) {
@@ -3386,13 +3411,13 @@ function createProjectRecord(input) {
3386
3411
  };
3387
3412
  }
3388
3413
  function writeProjectRecord(projectsDir, name, record) {
3389
- const dir = join8(projectsDir, name);
3414
+ const dir = join9(projectsDir, name);
3390
3415
  mkdirSync6(dir, { recursive: true });
3391
- const filePath = join8(dir, "project.json");
3416
+ const filePath = join9(dir, "project.json");
3392
3417
  const tmpPath = `${filePath}.tmp`;
3393
3418
  writeFileSync6(tmpPath, JSON.stringify(record, null, 2) + "\n");
3394
3419
  renameSync(tmpPath, filePath);
3395
- const extPath = join8(dir, "extensions.json");
3420
+ const extPath = join9(dir, "extensions.json");
3396
3421
  if (!existsSync8(extPath)) {
3397
3422
  writeFileSync6(
3398
3423
  extPath,
@@ -3409,17 +3434,17 @@ function writeProjectRecord(projectsDir, name, record) {
3409
3434
  }
3410
3435
  }
3411
3436
  function readProjectRecord(projectsDir, name) {
3412
- const subdirPath = join8(projectsDir, name, "project.json");
3413
- const flatPath = join8(projectsDir, `${name}.json`);
3437
+ const subdirPath = join9(projectsDir, name, "project.json");
3438
+ const flatPath = join9(projectsDir, `${name}.json`);
3414
3439
  let filePath;
3415
3440
  let isFlat = false;
3416
3441
  if (existsSync8(subdirPath)) {
3417
3442
  filePath = subdirPath;
3418
3443
  if (existsSync8(flatPath)) {
3419
3444
  try {
3420
- const flatParsed = JSON.parse(readFileSync8(flatPath, "utf-8"));
3445
+ const flatParsed = JSON.parse(readFileSync9(flatPath, "utf-8"));
3421
3446
  const flatStack = flatParsed.stack;
3422
- const subdirParsed = JSON.parse(readFileSync8(filePath, "utf-8"));
3447
+ const subdirParsed = JSON.parse(readFileSync9(filePath, "utf-8"));
3423
3448
  const subdirStack = subdirParsed.stack;
3424
3449
  if (flatStack && flatStack.detected && flatStack.detected !== "unknown" && (!subdirStack || subdirStack.detected === "unknown")) {
3425
3450
  subdirParsed.stack = flatStack;
@@ -3438,7 +3463,7 @@ function readProjectRecord(projectsDir, name) {
3438
3463
  }
3439
3464
  let parsed;
3440
3465
  try {
3441
- parsed = JSON.parse(readFileSync8(filePath, "utf-8"));
3466
+ parsed = JSON.parse(readFileSync9(filePath, "utf-8"));
3442
3467
  } catch {
3443
3468
  return null;
3444
3469
  }
@@ -3463,7 +3488,7 @@ function listProjectRecords(projectsDir) {
3463
3488
  const records = [];
3464
3489
  for (const entry of readdirSync3(projectsDir)) {
3465
3490
  if (entry.startsWith(".")) continue;
3466
- if (existsSync8(join8(projectsDir, entry, "project.json"))) {
3491
+ if (existsSync8(join9(projectsDir, entry, "project.json"))) {
3467
3492
  if (!seen.has(entry)) {
3468
3493
  seen.add(entry);
3469
3494
  const record = readProjectRecord(projectsDir, entry);
@@ -3629,10 +3654,10 @@ var init_engine = __esm({
3629
3654
 
3630
3655
  // src/cli/utils/file-writer.ts
3631
3656
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7, appendFileSync, existsSync as existsSync9 } from "fs";
3632
- import { dirname as dirname3 } from "path";
3657
+ import { dirname as dirname4 } from "path";
3633
3658
  function executeFileActions(actions) {
3634
3659
  for (const action of actions) {
3635
- const dir = dirname3(action.path);
3660
+ const dir = dirname4(action.path);
3636
3661
  mkdirSync7(dir, { recursive: true });
3637
3662
  switch (action.action) {
3638
3663
  case "create":
@@ -3817,21 +3842,21 @@ var init_api_routes = __esm({
3817
3842
  });
3818
3843
 
3819
3844
  // src/cli/ui/archival.ts
3820
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync4, renameSync as renameSync2, readFileSync as readFileSync9, statSync as statSync3, writeFileSync as writeFileSync9, unlinkSync as unlinkSync2 } from "fs";
3821
- import { join as join10 } from "path";
3845
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync4, renameSync as renameSync2, readFileSync as readFileSync10, statSync as statSync3, writeFileSync as writeFileSync9, unlinkSync as unlinkSync2 } from "fs";
3846
+ import { join as join11 } from "path";
3822
3847
  function archiveOldRuns(runsDir, archiveAfterDays) {
3823
3848
  if (archiveAfterDays <= 0 || !existsSync12(runsDir)) return { archived: 0 };
3824
- const archiveDir = join10(runsDir, ".archive");
3849
+ const archiveDir = join11(runsDir, ".archive");
3825
3850
  const cutoff = Date.now() - archiveAfterDays * 24 * 60 * 60 * 1e3;
3826
3851
  let archived = 0;
3827
3852
  for (const entry of readdirSync4(runsDir)) {
3828
3853
  if (entry.startsWith(".") || !entry.startsWith("run-")) continue;
3829
- const runDir = join10(runsDir, entry);
3830
- if (existsSync12(join10(runDir, "pinned"))) continue;
3831
- const cpPath = join10(runDir, "checkpoint.json");
3854
+ const runDir = join11(runsDir, entry);
3855
+ if (existsSync12(join11(runDir, "pinned"))) continue;
3856
+ const cpPath = join11(runDir, "checkpoint.json");
3832
3857
  if (!existsSync12(cpPath)) continue;
3833
3858
  try {
3834
- const cp = JSON.parse(readFileSync9(cpPath, "utf-8"));
3859
+ const cp = JSON.parse(readFileSync10(cpPath, "utf-8"));
3835
3860
  const stage = (cp.current_stage || "").toLowerCase();
3836
3861
  if (stage !== "done" && stage !== "ship") continue;
3837
3862
  } catch {
@@ -3844,25 +3869,25 @@ function archiveOldRuns(runsDir, archiveAfterDays) {
3844
3869
  continue;
3845
3870
  }
3846
3871
  mkdirSync8(archiveDir, { recursive: true });
3847
- renameSync2(runDir, join10(archiveDir, entry));
3872
+ renameSync2(runDir, join11(archiveDir, entry));
3848
3873
  archived++;
3849
3874
  }
3850
3875
  return { archived };
3851
3876
  }
3852
3877
  function pinRun(runsDir, runId) {
3853
- const runDir = join10(runsDir, runId);
3878
+ const runDir = join11(runsDir, runId);
3854
3879
  if (!existsSync12(runDir)) return false;
3855
- writeFileSync9(join10(runDir, "pinned"), (/* @__PURE__ */ new Date()).toISOString());
3880
+ writeFileSync9(join11(runDir, "pinned"), (/* @__PURE__ */ new Date()).toISOString());
3856
3881
  return true;
3857
3882
  }
3858
3883
  function unpinRun(runsDir, runId) {
3859
- const pinFile = join10(runsDir, runId, "pinned");
3884
+ const pinFile = join11(runsDir, runId, "pinned");
3860
3885
  if (!existsSync12(pinFile)) return false;
3861
3886
  unlinkSync2(pinFile);
3862
3887
  return true;
3863
3888
  }
3864
3889
  function isRunPinned(runsDir, runId) {
3865
- return existsSync12(join10(runsDir, runId, "pinned"));
3890
+ return existsSync12(join11(runsDir, runId, "pinned"));
3866
3891
  }
3867
3892
  var init_archival = __esm({
3868
3893
  "src/cli/ui/archival.ts"() {
@@ -3887,17 +3912,17 @@ __export(inception_exports, {
3887
3912
  saveArtifact: () => saveArtifact,
3888
3913
  saveInceptionState: () => saveInceptionState
3889
3914
  });
3890
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, readFileSync as readFileSync10, readdirSync as readdirSync5, unlinkSync as unlinkSync3 } from "fs";
3891
- import { join as join11, dirname as dirname4 } from "path";
3892
- import { fileURLToPath } from "url";
3915
+ import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, readFileSync as readFileSync11, readdirSync as readdirSync5, unlinkSync as unlinkSync3 } from "fs";
3916
+ import { join as join12, dirname as dirname5 } from "path";
3917
+ import { fileURLToPath as fileURLToPath2 } from "url";
3893
3918
  function getMethodologiesDir() {
3894
3919
  const candidates = [
3895
- join11(dirname4(fileURLToPath(import.meta.url)), "methodologies"),
3896
- join11(dirname4(fileURLToPath(import.meta.url)), "..", "methodologies"),
3897
- join11(process.cwd(), "src", "cli", "ui", "methodologies"),
3898
- join11(process.cwd(), "dist", "methodologies"),
3899
- join11(process.cwd(), "cli", "src", "cli", "ui", "methodologies"),
3900
- join11(process.cwd(), "cli", "dist", "methodologies")
3920
+ join12(dirname5(fileURLToPath2(import.meta.url)), "methodologies"),
3921
+ join12(dirname5(fileURLToPath2(import.meta.url)), "..", "methodologies"),
3922
+ join12(process.cwd(), "src", "cli", "ui", "methodologies"),
3923
+ join12(process.cwd(), "dist", "methodologies"),
3924
+ join12(process.cwd(), "cli", "src", "cli", "ui", "methodologies"),
3925
+ join12(process.cwd(), "cli", "dist", "methodologies")
3901
3926
  ];
3902
3927
  for (const dir of candidates) {
3903
3928
  if (existsSync13(dir)) return dir;
@@ -3906,18 +3931,18 @@ function getMethodologiesDir() {
3906
3931
  }
3907
3932
  function getMethodology(id) {
3908
3933
  const dir = getMethodologiesDir();
3909
- const filePath = join11(dir, `${id.replace(/_/g, "-")}.json`);
3934
+ const filePath = join12(dir, `${id.replace(/_/g, "-")}.json`);
3910
3935
  if (!existsSync13(filePath)) {
3911
3936
  throw new Error(`Methodology not found: ${id}`);
3912
3937
  }
3913
- return JSON.parse(readFileSync10(filePath, "utf-8"));
3938
+ return JSON.parse(readFileSync11(filePath, "utf-8"));
3914
3939
  }
3915
3940
  function listMethodologies() {
3916
3941
  const dir = getMethodologiesDir();
3917
3942
  if (!existsSync13(dir)) return [];
3918
3943
  return readdirSync5(dir).filter((f) => f.endsWith(".json")).map((f) => {
3919
3944
  try {
3920
- const m = JSON.parse(readFileSync10(join11(dir, f), "utf-8"));
3945
+ const m = JSON.parse(readFileSync11(join12(dir, f), "utf-8"));
3921
3946
  return { id: m.id, name: m.name, description: m.description };
3922
3947
  } catch {
3923
3948
  return null;
@@ -3925,7 +3950,7 @@ function listMethodologies() {
3925
3950
  }).filter(Boolean);
3926
3951
  }
3927
3952
  function getProductDir(factoryDir, productName) {
3928
- return join11(factoryDir, ".beastmode", "products", productName);
3953
+ return join12(factoryDir, ".beastmode", "products", productName);
3929
3954
  }
3930
3955
  function createInceptionState(factoryDir, opts) {
3931
3956
  const methodology = getMethodology(opts.methodology);
@@ -3947,21 +3972,21 @@ function createInceptionState(factoryDir, opts) {
3947
3972
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3948
3973
  projectName: null
3949
3974
  };
3950
- writeFileSync10(join11(productDir, "inception.json"), JSON.stringify(state, null, 2) + "\n");
3975
+ writeFileSync10(join12(productDir, "inception.json"), JSON.stringify(state, null, 2) + "\n");
3951
3976
  return state;
3952
3977
  }
3953
3978
  function loadInception(factoryDir, productName) {
3954
- const filePath = join11(getProductDir(factoryDir, productName), "inception.json");
3979
+ const filePath = join12(getProductDir(factoryDir, productName), "inception.json");
3955
3980
  if (!existsSync13(filePath)) return null;
3956
3981
  try {
3957
- return JSON.parse(readFileSync10(filePath, "utf-8"));
3982
+ return JSON.parse(readFileSync11(filePath, "utf-8"));
3958
3983
  } catch {
3959
3984
  return null;
3960
3985
  }
3961
3986
  }
3962
3987
  function saveInceptionState(factoryDir, state) {
3963
3988
  const productDir = getProductDir(factoryDir, state.productName);
3964
- writeFileSync10(join11(productDir, "inception.json"), JSON.stringify(state, null, 2) + "\n");
3989
+ writeFileSync10(join12(productDir, "inception.json"), JSON.stringify(state, null, 2) + "\n");
3965
3990
  }
3966
3991
  function advancePhase(factoryDir, productName) {
3967
3992
  const state = loadInception(factoryDir, productName);
@@ -3982,27 +4007,27 @@ function advancePhase(factoryDir, productName) {
3982
4007
  function saveArtifact(factoryDir, productName, filename, content) {
3983
4008
  const productDir = getProductDir(factoryDir, productName);
3984
4009
  mkdirSync9(productDir, { recursive: true });
3985
- const filePath = join11(productDir, filename);
3986
- mkdirSync9(dirname4(filePath), { recursive: true });
4010
+ const filePath = join12(productDir, filename);
4011
+ mkdirSync9(dirname5(filePath), { recursive: true });
3987
4012
  writeFileSync10(filePath, content);
3988
4013
  }
3989
4014
  function loadArtifact(factoryDir, productName, filename) {
3990
- const filePath = join11(getProductDir(factoryDir, productName), filename);
4015
+ const filePath = join12(getProductDir(factoryDir, productName), filename);
3991
4016
  if (!existsSync13(filePath)) return null;
3992
- return readFileSync10(filePath, "utf-8");
4017
+ return readFileSync11(filePath, "utf-8");
3993
4018
  }
3994
4019
  function migrateInceptionToSession(factoryDir, productName) {
3995
- const productDir = join11(factoryDir, ".beastmode", "products", productName);
3996
- const oldInception = join11(productDir, "inception.json");
4020
+ const productDir = join12(factoryDir, ".beastmode", "products", productName);
4021
+ const oldInception = join12(productDir, "inception.json");
3997
4022
  if (!existsSync13(oldInception)) return false;
3998
- const sessionsDir = join11(productDir, "sessions");
4023
+ const sessionsDir = join12(productDir, "sessions");
3999
4024
  if (existsSync13(sessionsDir) && readdirSync5(sessionsDir).some((d) => d.startsWith("session-"))) {
4000
4025
  return false;
4001
4026
  }
4002
- const sessionDir = join11(sessionsDir, "session-001");
4027
+ const sessionDir = join12(sessionsDir, "session-001");
4003
4028
  mkdirSync9(sessionDir, { recursive: true });
4004
- const content = readFileSync10(oldInception, "utf-8");
4005
- writeFileSync10(join11(sessionDir, "inception.json"), content);
4029
+ const content = readFileSync11(oldInception, "utf-8");
4030
+ writeFileSync10(join12(sessionDir, "inception.json"), content);
4006
4031
  const artifactFiles = [
4007
4032
  "prd.md",
4008
4033
  "architecture.md",
@@ -4015,9 +4040,9 @@ function migrateInceptionToSession(factoryDir, productName) {
4015
4040
  "project-plan.md"
4016
4041
  ];
4017
4042
  for (const file of artifactFiles) {
4018
- const src = join11(productDir, file);
4043
+ const src = join12(productDir, file);
4019
4044
  if (existsSync13(src)) {
4020
- writeFileSync10(join11(sessionDir, file), readFileSync10(src, "utf-8"));
4045
+ writeFileSync10(join12(sessionDir, file), readFileSync11(src, "utf-8"));
4021
4046
  unlinkSync3(src);
4022
4047
  }
4023
4048
  }
@@ -4025,11 +4050,11 @@ function migrateInceptionToSession(factoryDir, productName) {
4025
4050
  return true;
4026
4051
  }
4027
4052
  function listProducts(factoryDir) {
4028
- const productsDir = join11(factoryDir, ".beastmode", "products");
4053
+ const productsDir = join12(factoryDir, ".beastmode", "products");
4029
4054
  if (!existsSync13(productsDir)) return [];
4030
- return readdirSync5(productsDir).filter((d) => existsSync13(join11(productsDir, d, "inception.json"))).map((d) => {
4055
+ return readdirSync5(productsDir).filter((d) => existsSync13(join12(productsDir, d, "inception.json"))).map((d) => {
4031
4056
  try {
4032
- const state = JSON.parse(readFileSync10(join11(productsDir, d, "inception.json"), "utf-8"));
4057
+ const state = JSON.parse(readFileSync11(join12(productsDir, d, "inception.json"), "utf-8"));
4033
4058
  return {
4034
4059
  name: state.productName || d,
4035
4060
  methodology: state.methodology || "unknown",
@@ -4152,13 +4177,13 @@ __export(strategy_exports, {
4152
4177
  saveSessionArtifact: () => saveSessionArtifact,
4153
4178
  saveStrategySession: () => saveStrategySession
4154
4179
  });
4155
- import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, readFileSync as readFileSync11, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
4156
- import { join as join12 } from "path";
4180
+ import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, readFileSync as readFileSync12, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
4181
+ import { join as join13 } from "path";
4157
4182
  function getProductDir2(factoryDir, projectName) {
4158
- return join12(factoryDir, ".beastmode", "products", projectName);
4183
+ return join13(factoryDir, ".beastmode", "products", projectName);
4159
4184
  }
4160
4185
  function getSessionsDir(factoryDir, projectName) {
4161
- return join12(getProductDir2(factoryDir, projectName), "sessions");
4186
+ return join13(getProductDir2(factoryDir, projectName), "sessions");
4162
4187
  }
4163
4188
  function nextSessionId(factoryDir, projectName) {
4164
4189
  const sessionsDir = getSessionsDir(factoryDir, projectName);
@@ -4171,7 +4196,7 @@ function nextSessionId(factoryDir, projectName) {
4171
4196
  function createStrategySession(factoryDir, projectName, opts) {
4172
4197
  const methodology = getMethodology(opts.methodology);
4173
4198
  const sessionId = nextSessionId(factoryDir, projectName);
4174
- const sessionDir = join12(getSessionsDir(factoryDir, projectName), sessionId);
4199
+ const sessionDir = join13(getSessionsDir(factoryDir, projectName), sessionId);
4175
4200
  mkdirSync10(sessionDir, { recursive: true });
4176
4201
  const session = {
4177
4202
  sessionId,
@@ -4193,52 +4218,52 @@ function createStrategySession(factoryDir, projectName, opts) {
4193
4218
  sessionType: opts.sessionType || "free-form",
4194
4219
  approach: opts.approach || "auto"
4195
4220
  };
4196
- writeFileSync11(join12(sessionDir, "inception.json"), JSON.stringify(session, null, 2) + "\n");
4221
+ writeFileSync11(join13(sessionDir, "inception.json"), JSON.stringify(session, null, 2) + "\n");
4197
4222
  return session;
4198
4223
  }
4199
4224
  function listStrategySessions(factoryDir, projectName) {
4200
4225
  const sessionsDir = getSessionsDir(factoryDir, projectName);
4201
4226
  if (!existsSync14(sessionsDir)) return [];
4202
4227
  return readdirSync6(sessionsDir).filter((d) => d.startsWith("session-")).sort().map((d) => {
4203
- const file = join12(sessionsDir, d, "inception.json");
4228
+ const file = join13(sessionsDir, d, "inception.json");
4204
4229
  if (!existsSync14(file)) return null;
4205
4230
  try {
4206
- return JSON.parse(readFileSync11(file, "utf-8"));
4231
+ return JSON.parse(readFileSync12(file, "utf-8"));
4207
4232
  } catch {
4208
4233
  return null;
4209
4234
  }
4210
4235
  }).filter(Boolean);
4211
4236
  }
4212
4237
  function loadStrategySession(factoryDir, projectName, sessionId) {
4213
- const file = join12(getSessionsDir(factoryDir, projectName), sessionId, "inception.json");
4238
+ const file = join13(getSessionsDir(factoryDir, projectName), sessionId, "inception.json");
4214
4239
  if (!existsSync14(file)) return null;
4215
4240
  try {
4216
- return JSON.parse(readFileSync11(file, "utf-8"));
4241
+ return JSON.parse(readFileSync12(file, "utf-8"));
4217
4242
  } catch {
4218
4243
  return null;
4219
4244
  }
4220
4245
  }
4221
4246
  function saveStrategySession(factoryDir, projectName, sessionId, session) {
4222
- const dir = join12(getSessionsDir(factoryDir, projectName), sessionId);
4247
+ const dir = join13(getSessionsDir(factoryDir, projectName), sessionId);
4223
4248
  mkdirSync10(dir, { recursive: true });
4224
4249
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
4225
- writeFileSync11(join12(dir, "inception.json"), JSON.stringify(session, null, 2) + "\n");
4250
+ writeFileSync11(join13(dir, "inception.json"), JSON.stringify(session, null, 2) + "\n");
4226
4251
  }
4227
4252
  function saveSessionArtifact(factoryDir, projectName, sessionId, filename, content) {
4228
- const dir = join12(getSessionsDir(factoryDir, projectName), sessionId);
4253
+ const dir = join13(getSessionsDir(factoryDir, projectName), sessionId);
4229
4254
  mkdirSync10(dir, { recursive: true });
4230
- writeFileSync11(join12(dir, filename), content);
4255
+ writeFileSync11(join13(dir, filename), content);
4231
4256
  }
4232
4257
  function loadSessionArtifact(factoryDir, projectName, sessionId, filename) {
4233
- const file = join12(getSessionsDir(factoryDir, projectName), sessionId, filename);
4258
+ const file = join13(getSessionsDir(factoryDir, projectName), sessionId, filename);
4234
4259
  if (!existsSync14(file)) return null;
4235
- return readFileSync11(file, "utf-8");
4260
+ return readFileSync12(file, "utf-8");
4236
4261
  }
4237
4262
  function buildArtifactIndex(factoryDir, projectName) {
4238
4263
  const artifacts = [];
4239
- const brownfieldPath = join12(factoryDir, ".beastmode", "projects", projectName, "brownfield.md");
4264
+ const brownfieldPath = join13(factoryDir, ".beastmode", "projects", projectName, "brownfield.md");
4240
4265
  if (existsSync14(brownfieldPath)) {
4241
- const content = readFileSync11(brownfieldPath, "utf-8");
4266
+ const content = readFileSync12(brownfieldPath, "utf-8");
4242
4267
  const firstLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#")) || "";
4243
4268
  artifacts.push({
4244
4269
  type: "brownfield",
@@ -4251,19 +4276,19 @@ function buildArtifactIndex(factoryDir, projectName) {
4251
4276
  for (const session of sessions) {
4252
4277
  artifacts.push({
4253
4278
  type: "strategy_session",
4254
- path: join12(getSessionsDir(factoryDir, projectName), session.sessionId),
4279
+ path: join13(getSessionsDir(factoryDir, projectName), session.sessionId),
4255
4280
  summary: `${session.name} (${session.methodology}) \u2014 ${session.status}`,
4256
4281
  date: session.createdAt,
4257
4282
  sessionId: session.sessionId
4258
4283
  });
4259
4284
  }
4260
- const runsDir = join12(factoryDir, "runs", projectName);
4285
+ const runsDir = join13(factoryDir, "runs", projectName);
4261
4286
  if (existsSync14(runsDir)) {
4262
4287
  const runDirs = readdirSync6(runsDir).filter((d) => d.startsWith("run-")).sort().reverse().slice(0, 10);
4263
4288
  for (const runId of runDirs) {
4264
- const nlspecPath = join12(runsDir, runId, "nlspec.md");
4289
+ const nlspecPath = join13(runsDir, runId, "nlspec.md");
4265
4290
  if (existsSync14(nlspecPath)) {
4266
- const content = readFileSync11(nlspecPath, "utf-8");
4291
+ const content = readFileSync12(nlspecPath, "utf-8");
4267
4292
  const title = content.split("\n").find((l) => l.startsWith("# ")) || runId;
4268
4293
  artifacts.push({
4269
4294
  type: "nlspec",
@@ -4275,12 +4300,12 @@ function buildArtifactIndex(factoryDir, projectName) {
4275
4300
  }
4276
4301
  }
4277
4302
  }
4278
- const learningsDir = join12(getProductDir2(factoryDir, projectName), "learnings");
4303
+ const learningsDir = join13(getProductDir2(factoryDir, projectName), "learnings");
4279
4304
  if (existsSync14(learningsDir)) {
4280
4305
  const learningFiles = readdirSync6(learningsDir).filter((f) => f.endsWith(".md") && !f.startsWith("."));
4281
4306
  for (const file of learningFiles) {
4282
- const filePath = join12(learningsDir, file);
4283
- const content = readFileSync11(filePath, "utf-8");
4307
+ const filePath = join13(learningsDir, file);
4308
+ const content = readFileSync12(filePath, "utf-8");
4284
4309
  const firstHeading = content.split("\n").find((l) => l.startsWith("## ")) || file;
4285
4310
  artifacts.push({
4286
4311
  type: "learning",
@@ -4311,8 +4336,8 @@ var init_strategy = __esm({
4311
4336
  // src/cli/ui/chat-handler.ts
4312
4337
  import { randomUUID } from "crypto";
4313
4338
  import { execSync as execSync2 } from "child_process";
4314
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
4315
- import { join as join13 } from "path";
4339
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
4340
+ import { join as join14 } from "path";
4316
4341
  import http from "http";
4317
4342
  import { WebSocketServer, WebSocket as WsWebSocket } from "ws";
4318
4343
  function getRecentTokenUsage() {
@@ -4424,17 +4449,17 @@ function getToolDefinitions() {
4424
4449
  function readJsonSafe(filePath) {
4425
4450
  if (!existsSync15(filePath)) return null;
4426
4451
  try {
4427
- return JSON.parse(readFileSync12(filePath, "utf-8"));
4452
+ return JSON.parse(readFileSync13(filePath, "utf-8"));
4428
4453
  } catch {
4429
4454
  return null;
4430
4455
  }
4431
4456
  }
4432
4457
  function getBoardUrl(factoryPath) {
4433
4458
  if (process.env.BEASTMODE_BOARD_URL) return process.env.BEASTMODE_BOARD_URL;
4434
- const configPath = join13(factoryPath, ".beastmode", "config.json");
4459
+ const configPath = join14(factoryPath, ".beastmode", "config.json");
4435
4460
  if (existsSync15(configPath)) {
4436
4461
  try {
4437
- const config = JSON.parse(readFileSync12(configPath, "utf-8"));
4462
+ const config = JSON.parse(readFileSync13(configPath, "utf-8"));
4438
4463
  if (config.task_backend?.config?.url) return config.task_backend.config.url;
4439
4464
  } catch {
4440
4465
  }
@@ -4445,19 +4470,19 @@ async function executeTool(toolName, toolInput, factoryPath) {
4445
4470
  try {
4446
4471
  switch (toolName) {
4447
4472
  case "factory_status": {
4448
- const bmDir = join13(factoryPath, ".beastmode");
4449
- const factory = readJsonSafe(join13(bmDir, "factory.json"));
4450
- const projectsDir = join13(bmDir, "projects");
4473
+ const bmDir = join14(factoryPath, ".beastmode");
4474
+ const factory = readJsonSafe(join14(bmDir, "factory.json"));
4475
+ const projectsDir = join14(bmDir, "projects");
4451
4476
  let projects = [];
4452
4477
  if (existsSync15(projectsDir)) {
4453
4478
  projects = readdirSync7(projectsDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
4454
4479
  }
4455
- const runsDir = join13(factoryPath, "runs");
4480
+ const runsDir = join14(factoryPath, "runs");
4456
4481
  let runs = [];
4457
4482
  if (existsSync15(runsDir)) {
4458
4483
  runs = readdirSync7(runsDir).filter((d) => {
4459
4484
  try {
4460
- return statSync5(join13(runsDir, d)).isDirectory();
4485
+ return statSync5(join14(runsDir, d)).isDirectory();
4461
4486
  } catch {
4462
4487
  return false;
4463
4488
  }
@@ -4465,10 +4490,10 @@ async function executeTool(toolName, toolInput, factoryPath) {
4465
4490
  }
4466
4491
  let daemonStatus = "stopped";
4467
4492
  let daemonPid = null;
4468
- const pidFile = join13(bmDir, "daemon.pid");
4493
+ const pidFile = join14(bmDir, "daemon.pid");
4469
4494
  if (existsSync15(pidFile)) {
4470
4495
  try {
4471
- daemonPid = parseInt(readFileSync12(pidFile, "utf-8").trim(), 10);
4496
+ daemonPid = parseInt(readFileSync13(pidFile, "utf-8").trim(), 10);
4472
4497
  process.kill(daemonPid, 0);
4473
4498
  daemonStatus = "running";
4474
4499
  } catch {
@@ -4516,7 +4541,7 @@ async function executeTool(toolName, toolInput, factoryPath) {
4516
4541
  }, null, 2);
4517
4542
  }
4518
4543
  case "factory_config": {
4519
- const configPath = join13(factoryPath, ".beastmode", "config.json");
4544
+ const configPath = join14(factoryPath, ".beastmode", "config.json");
4520
4545
  const config = readJsonSafe(configPath);
4521
4546
  return config ? JSON.stringify(config, null, 2) : "No config.json found.";
4522
4547
  }
@@ -4571,12 +4596,12 @@ async function executeTool(toolName, toolInput, factoryPath) {
4571
4596
  }
4572
4597
  }
4573
4598
  case "list_runs": {
4574
- const runsDir = join13(factoryPath, "runs");
4599
+ const runsDir = join14(factoryPath, "runs");
4575
4600
  if (!existsSync15(runsDir)) return "No runs directory.";
4576
4601
  const runDirs = readdirSync7(runsDir).sort().reverse();
4577
4602
  const results = [];
4578
4603
  for (const id of runDirs.slice(0, 15)) {
4579
- const cp = readJsonSafe(join13(runsDir, id, "checkpoint.json"));
4604
+ const cp = readJsonSafe(join14(runsDir, id, "checkpoint.json"));
4580
4605
  if (cp) {
4581
4606
  const hist = Array.isArray(cp.satisfaction_history) ? cp.satisfaction_history : [];
4582
4607
  results.push({
@@ -4592,15 +4617,15 @@ async function executeTool(toolName, toolInput, factoryPath) {
4592
4617
  case "run_detail": {
4593
4618
  const runId = toolInput.run_id;
4594
4619
  if (runId.includes("..")) return "Invalid run_id.";
4595
- const runDir = join13(factoryPath, "runs", runId);
4620
+ const runDir = join14(factoryPath, "runs", runId);
4596
4621
  if (!existsSync15(runDir)) return `Run not found: ${runId}`;
4597
- const manifest = readJsonSafe(join13(runDir, "manifest.json"));
4598
- const checkpoint = readJsonSafe(join13(runDir, "checkpoint.json"));
4599
- const iterDir = join13(runDir, "iterations");
4622
+ const manifest = readJsonSafe(join14(runDir, "manifest.json"));
4623
+ const checkpoint = readJsonSafe(join14(runDir, "checkpoint.json"));
4624
+ const iterDir = join14(runDir, "iterations");
4600
4625
  const iterations = [];
4601
4626
  if (existsSync15(iterDir)) {
4602
4627
  for (const d of readdirSync7(iterDir).sort()) {
4603
- const sat = readJsonSafe(join13(iterDir, d, "satisfaction.json"));
4628
+ const sat = readJsonSafe(join14(iterDir, d, "satisfaction.json"));
4604
4629
  iterations.push({ number: parseInt(d, 10), satisfaction: sat });
4605
4630
  }
4606
4631
  }
@@ -4611,17 +4636,17 @@ async function executeTool(toolName, toolInput, factoryPath) {
4611
4636
  const runId = toolInput.run_id;
4612
4637
  const relPath = toolInput.file_path;
4613
4638
  if (runId.includes("..") || relPath.includes("..")) return "Invalid path.";
4614
- const fullPath = join13(factoryPath, "runs", runId, relPath);
4639
+ const fullPath = join14(factoryPath, "runs", runId, relPath);
4615
4640
  if (!existsSync15(fullPath)) return `File not found: runs/${runId}/${relPath}`;
4616
- const content = readFileSync12(fullPath, "utf-8");
4641
+ const content = readFileSync13(fullPath, "utf-8");
4617
4642
  return content.length > 1e4 ? content.slice(0, 1e4) + "\n\n... (truncated at 10KB)" : content;
4618
4643
  }
4619
4644
  case "list_projects": {
4620
- const projDir = join13(factoryPath, ".beastmode", "projects");
4645
+ const projDir = join14(factoryPath, ".beastmode", "projects");
4621
4646
  if (!existsSync15(projDir)) return "No projects registered.";
4622
4647
  const projects = readdirSync7(projDir).filter((f) => f.endsWith(".json")).map((f) => {
4623
4648
  try {
4624
- return JSON.parse(readFileSync12(join13(projDir, f), "utf-8"));
4649
+ return JSON.parse(readFileSync13(join14(projDir, f), "utf-8"));
4625
4650
  } catch {
4626
4651
  return null;
4627
4652
  }
@@ -4631,7 +4656,7 @@ async function executeTool(toolName, toolInput, factoryPath) {
4631
4656
  case "read_file": {
4632
4657
  const relPath = toolInput.path;
4633
4658
  if (relPath.includes("..")) return "Invalid path.";
4634
- const fullPath = join13(factoryPath, relPath);
4659
+ const fullPath = join14(factoryPath, relPath);
4635
4660
  if (!fullPath.startsWith(factoryPath)) return "Path traversal not allowed.";
4636
4661
  if (!existsSync15(fullPath)) return `File not found: ${relPath}`;
4637
4662
  try {
@@ -4639,13 +4664,13 @@ async function executeTool(toolName, toolInput, factoryPath) {
4639
4664
  return `"${relPath}" is a directory with ${entries.length} entries: ${entries.slice(0, 30).join(", ")}`;
4640
4665
  } catch {
4641
4666
  }
4642
- const content = readFileSync12(fullPath, "utf-8");
4667
+ const content = readFileSync13(fullPath, "utf-8");
4643
4668
  return content.length > 1e4 ? content.slice(0, 1e4) + "\n\n... (truncated at 10KB)" : content;
4644
4669
  }
4645
4670
  case "list_directory": {
4646
4671
  const relPath = toolInput.path || "";
4647
4672
  if (relPath.includes("..")) return "Invalid path.";
4648
- const fullPath = join13(factoryPath, relPath);
4673
+ const fullPath = join14(factoryPath, relPath);
4649
4674
  if (!fullPath.startsWith(factoryPath)) return "Path traversal not allowed.";
4650
4675
  if (!existsSync15(fullPath)) return `Directory not found: ${relPath || "/"}`;
4651
4676
  try {
@@ -4662,7 +4687,7 @@ async function executeTool(toolName, toolInput, factoryPath) {
4662
4687
  }
4663
4688
  }
4664
4689
  function getChatHistoryDir(factoryPath) {
4665
- const dir = join13(factoryPath, ".beastmode", "chat-history");
4690
+ const dir = join14(factoryPath, ".beastmode", "chat-history");
4666
4691
  if (!existsSync15(dir)) mkdirSync11(dir, { recursive: true });
4667
4692
  return dir;
4668
4693
  }
@@ -4674,13 +4699,13 @@ function saveConversation(factoryPath, sessionId, messages, scope) {
4674
4699
  updated_at: (/* @__PURE__ */ new Date()).toISOString(),
4675
4700
  messages
4676
4701
  };
4677
- writeFileSync12(join13(dir, `${sessionId}.json`), JSON.stringify(data, null, 2));
4702
+ writeFileSync12(join14(dir, `${sessionId}.json`), JSON.stringify(data, null, 2));
4678
4703
  }
4679
4704
  function listConversations(factoryPath) {
4680
4705
  const dir = getChatHistoryDir(factoryPath);
4681
4706
  return readdirSync7(dir).filter((f) => f.endsWith(".json")).map((f) => {
4682
4707
  try {
4683
- const data = JSON.parse(readFileSync12(join13(dir, f), "utf-8"));
4708
+ const data = JSON.parse(readFileSync13(join14(dir, f), "utf-8"));
4684
4709
  const msgs = data.messages || [];
4685
4710
  const firstUser = msgs.find((m) => m.role === "user");
4686
4711
  const preview = typeof firstUser?.content === "string" ? firstUser.content.slice(0, 80) : "";
@@ -4698,10 +4723,10 @@ function listConversations(factoryPath) {
4698
4723
  }
4699
4724
  function loadConversation(factoryPath, sessionId) {
4700
4725
  if (sessionId.includes("..")) return [];
4701
- const file = join13(getChatHistoryDir(factoryPath), `${sessionId}.json`);
4726
+ const file = join14(getChatHistoryDir(factoryPath), `${sessionId}.json`);
4702
4727
  if (!existsSync15(file)) return [];
4703
4728
  try {
4704
- const data = JSON.parse(readFileSync12(file, "utf-8"));
4729
+ const data = JSON.parse(readFileSync13(file, "utf-8"));
4705
4730
  return data.messages || [];
4706
4731
  } catch {
4707
4732
  return [];
@@ -4709,7 +4734,7 @@ function loadConversation(factoryPath, sessionId) {
4709
4734
  }
4710
4735
  function buildSystemPrompt(factoryPath) {
4711
4736
  let factoryName = "BeastMode Factory";
4712
- const factoryJson = readJsonSafe(join13(factoryPath, ".beastmode", "factory.json"));
4737
+ const factoryJson = readJsonSafe(join14(factoryPath, ".beastmode", "factory.json"));
4713
4738
  if (factoryJson?.factory_name) factoryName = factoryJson.factory_name;
4714
4739
  else if (factoryJson?.name) factoryName = factoryJson.name;
4715
4740
  return `You are BeastMode Assistant \u2014 a concise, helpful assistant for the "${factoryName}" factory at ${factoryPath}.
@@ -5256,8 +5281,8 @@ var init_chat_handler = __esm({
5256
5281
  });
5257
5282
 
5258
5283
  // src/cli/ui/board-api-routes.ts
5259
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync13, existsSync as existsSync16, readdirSync as readdirSync8, unlinkSync as unlinkSync4, mkdirSync as mkdirSync12, statSync as statSync6 } from "fs";
5260
- import { join as join14, basename as basename5, resolve as resolve4, dirname as dirname5, sep } from "path";
5284
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync13, existsSync as existsSync16, readdirSync as readdirSync8, unlinkSync as unlinkSync4, mkdirSync as mkdirSync12, statSync as statSync6 } from "fs";
5285
+ import { join as join15, basename as basename5, resolve as resolve4, dirname as dirname6, sep } from "path";
5261
5286
  import { homedir } from "os";
5262
5287
  import { randomUUID as randomUUID2 } from "crypto";
5263
5288
  import { execSync as execSync3, spawnSync } from "child_process";
@@ -5301,10 +5326,10 @@ function assertSafeName(name) {
5301
5326
  }
5302
5327
  function getBoardUrl2(factoryDir) {
5303
5328
  if (process.env.BEASTMODE_BOARD_URL) return process.env.BEASTMODE_BOARD_URL;
5304
- const configPath = join14(factoryDir, ".beastmode", "config.json");
5329
+ const configPath = join15(factoryDir, ".beastmode", "config.json");
5305
5330
  if (existsSync16(configPath)) {
5306
5331
  try {
5307
- const config = JSON.parse(readFileSync13(configPath, "utf-8"));
5332
+ const config = JSON.parse(readFileSync14(configPath, "utf-8"));
5308
5333
  if (config.task_backend?.config?.url) {
5309
5334
  return config.task_backend.config.url;
5310
5335
  }
@@ -5445,7 +5470,7 @@ function scopedQuery(query) {
5445
5470
  function readJsonFile(filePath) {
5446
5471
  if (!existsSync16(filePath)) return null;
5447
5472
  try {
5448
- return JSON.parse(readFileSync13(filePath, "utf-8"));
5473
+ return JSON.parse(readFileSync14(filePath, "utf-8"));
5449
5474
  } catch {
5450
5475
  return null;
5451
5476
  }
@@ -5486,8 +5511,8 @@ function applyPresetOverrides(current, patch, presetName) {
5486
5511
  }
5487
5512
  function syncToDaemonConfigs(merged, factoryDir) {
5488
5513
  const daemonConfigPaths = [
5489
- join14(factoryDir, "config", "beastmode.daemon.json"),
5490
- join14(factoryDir, "config", "beastmode.docker.json")
5514
+ join15(factoryDir, "config", "beastmode.daemon.json"),
5515
+ join15(factoryDir, "config", "beastmode.docker.json")
5491
5516
  ].filter(existsSync16);
5492
5517
  const pipelineToDaemonMap = {
5493
5518
  satisfaction_threshold: ["verification", "satisfaction_threshold"],
@@ -5512,7 +5537,7 @@ function syncToDaemonConfigs(merged, factoryDir) {
5512
5537
  ];
5513
5538
  for (const daemonConfigPath of daemonConfigPaths) {
5514
5539
  try {
5515
- const daemonConfig = JSON.parse(readFileSync13(daemonConfigPath, "utf-8"));
5540
+ const daemonConfig = JSON.parse(readFileSync14(daemonConfigPath, "utf-8"));
5516
5541
  let changed = false;
5517
5542
  for (const field of daemonFields) {
5518
5543
  if (field in merged && merged[field] !== daemonConfig[field]) {
@@ -5575,25 +5600,25 @@ function syncToDaemonConfigs(merged, factoryDir) {
5575
5600
  }
5576
5601
  }
5577
5602
  function getRunsDir(factoryDir) {
5578
- const configPath = join14(factoryDir, ".beastmode", "config.json");
5603
+ const configPath = join15(factoryDir, ".beastmode", "config.json");
5579
5604
  if (existsSync16(configPath)) {
5580
5605
  try {
5581
- const config = JSON.parse(readFileSync13(configPath, "utf-8"));
5606
+ const config = JSON.parse(readFileSync14(configPath, "utf-8"));
5582
5607
  if (config.runs_path) return config.runs_path;
5583
5608
  } catch {
5584
5609
  }
5585
5610
  }
5586
- return join14(factoryDir, "runs");
5611
+ return join15(factoryDir, "runs");
5587
5612
  }
5588
5613
  function scanStrandedRuns(runsDir, newThreshold) {
5589
5614
  if (!existsSync16(runsDir)) return [];
5590
5615
  const stranded = [];
5591
5616
  const walkRun = (runPath, projectId) => {
5592
- const ckptPath = join14(runPath, "checkpoint.json");
5617
+ const ckptPath = join15(runPath, "checkpoint.json");
5593
5618
  if (!existsSync16(ckptPath)) return;
5594
5619
  let ckpt;
5595
5620
  try {
5596
- ckpt = JSON.parse(readFileSync13(ckptPath, "utf-8"));
5621
+ ckpt = JSON.parse(readFileSync14(ckptPath, "utf-8"));
5597
5622
  } catch {
5598
5623
  return;
5599
5624
  }
@@ -5613,7 +5638,7 @@ function scanStrandedRuns(runsDir, newThreshold) {
5613
5638
  try {
5614
5639
  const entries = readdirSync8(runsDir);
5615
5640
  for (const entry of entries) {
5616
- const entryPath = join14(runsDir, entry);
5641
+ const entryPath = join15(runsDir, entry);
5617
5642
  let stat;
5618
5643
  try {
5619
5644
  stat = statSync6(entryPath);
@@ -5632,7 +5657,7 @@ function scanStrandedRuns(runsDir, newThreshold) {
5632
5657
  }
5633
5658
  for (const sub of subEntries) {
5634
5659
  if (!sub.startsWith("run-")) continue;
5635
- const subPath = join14(entryPath, sub);
5660
+ const subPath = join15(entryPath, sub);
5636
5661
  try {
5637
5662
  if (statSync6(subPath).isDirectory()) {
5638
5663
  walkRun(subPath, entry);
@@ -5649,10 +5674,10 @@ function scanStrandedRuns(runsDir, newThreshold) {
5649
5674
  return stranded;
5650
5675
  }
5651
5676
  function _readAnalyzeMeta(projectsDir, name) {
5652
- const metaPath = join14(projectsDir, name, "codebase-guide.meta.json");
5677
+ const metaPath = join15(projectsDir, name, "codebase-guide.meta.json");
5653
5678
  if (!existsSync16(metaPath)) return null;
5654
5679
  try {
5655
- return JSON.parse(readFileSync13(metaPath, "utf-8"));
5680
+ return JSON.parse(readFileSync14(metaPath, "utf-8"));
5656
5681
  } catch {
5657
5682
  return null;
5658
5683
  }
@@ -5671,7 +5696,7 @@ function getBoardRoutes(factoryDir) {
5671
5696
  method: "GET",
5672
5697
  pattern: "/api/status",
5673
5698
  handler: async () => {
5674
- const bmDir = join14(factoryDir, ".beastmode");
5699
+ const bmDir = join15(factoryDir, ".beastmode");
5675
5700
  const boardUrl = getBoardUrl2(factoryDir);
5676
5701
  const factoryIdentity = await proxyToBoard(
5677
5702
  boardUrl,
@@ -5680,11 +5705,11 @@ function getBoardRoutes(factoryDir) {
5680
5705
  );
5681
5706
  const projectsResp = await proxyToBoard(boardUrl, "GET", "/api/projects");
5682
5707
  const projectCount = (projectsResp.projects || []).length;
5683
- const lockPath = join14(bmDir, "extensions.lock");
5708
+ const lockPath = join15(bmDir, "extensions.lock");
5684
5709
  let pluginNames = [];
5685
5710
  if (existsSync16(lockPath)) {
5686
5711
  try {
5687
- const lock = JSON.parse(readFileSync13(lockPath, "utf-8"));
5712
+ const lock = JSON.parse(readFileSync14(lockPath, "utf-8"));
5688
5713
  pluginNames = Object.keys(lock.plugins || {});
5689
5714
  } catch {
5690
5715
  }
@@ -5704,24 +5729,24 @@ function getBoardRoutes(factoryDir) {
5704
5729
  skillCount = listSkills(factoryDir).length;
5705
5730
  } catch {
5706
5731
  }
5707
- const runsDir = join14(factoryDir, "runs");
5732
+ const runsDir = join15(factoryDir, "runs");
5708
5733
  let runDirs = [];
5709
5734
  if (existsSync16(runsDir)) {
5710
5735
  runDirs = readdirSync8(runsDir).filter((d) => {
5711
5736
  if (d.startsWith(".")) return false;
5712
5737
  try {
5713
- return readdirSync8(join14(runsDir, d)).length > 0;
5738
+ return readdirSync8(join15(runsDir, d)).length > 0;
5714
5739
  } catch {
5715
5740
  return false;
5716
5741
  }
5717
5742
  }).sort();
5718
5743
  }
5719
- const pidFile = join14(factoryDir, ".beastmode", "daemon.pid");
5744
+ const pidFile = join15(factoryDir, ".beastmode", "daemon.pid");
5720
5745
  let daemonPid = null;
5721
5746
  let pidAlive = false;
5722
5747
  if (existsSync16(pidFile)) {
5723
5748
  try {
5724
- daemonPid = parseInt(readFileSync13(pidFile, "utf-8").trim(), 10);
5749
+ daemonPid = parseInt(readFileSync14(pidFile, "utf-8").trim(), 10);
5725
5750
  process.kill(daemonPid, 0);
5726
5751
  pidAlive = true;
5727
5752
  } catch {
@@ -5778,10 +5803,10 @@ function getBoardRoutes(factoryDir) {
5778
5803
  handler: async () => {
5779
5804
  const boardUrl = getBoardUrl2(factoryDir);
5780
5805
  let ageSecs = null;
5781
- const heartbeatPath = join14(factoryDir, "daemon", "logs", ".heartbeat");
5806
+ const heartbeatPath = join15(factoryDir, "daemon", "logs", ".heartbeat");
5782
5807
  if (existsSync16(heartbeatPath)) {
5783
5808
  try {
5784
- const ts = parseInt(readFileSync13(heartbeatPath, "utf-8").trim(), 10);
5809
+ const ts = parseInt(readFileSync14(heartbeatPath, "utf-8").trim(), 10);
5785
5810
  if (!isNaN(ts)) {
5786
5811
  ageSecs = Math.floor(Date.now() / 1e3) - ts;
5787
5812
  }
@@ -5823,11 +5848,11 @@ function getBoardRoutes(factoryDir) {
5823
5848
  let totalSlots = 3;
5824
5849
  let shortPhaseReserve;
5825
5850
  let slotsTimestamp;
5826
- const slotsPath = join14(factoryDir, "daemon", "logs", ".slots.json");
5851
+ const slotsPath = join15(factoryDir, "daemon", "logs", ".slots.json");
5827
5852
  let usedDaemonSlotsFile = false;
5828
5853
  if (existsSync16(slotsPath)) {
5829
5854
  try {
5830
- const slots = JSON.parse(readFileSync13(slotsPath, "utf-8"));
5855
+ const slots = JSON.parse(readFileSync14(slotsPath, "utf-8"));
5831
5856
  if (typeof slots.busy === "number" && typeof slots.total === "number") {
5832
5857
  busySlots = slots.busy;
5833
5858
  totalSlots = slots.total;
@@ -5852,10 +5877,10 @@ function getBoardRoutes(factoryDir) {
5852
5877
  } catch {
5853
5878
  }
5854
5879
  for (const name of ["beastmode.docker.json", "beastmode.daemon.json"]) {
5855
- const configPath = join14(factoryDir, "config", name);
5880
+ const configPath = join15(factoryDir, "config", name);
5856
5881
  if (!existsSync16(configPath)) continue;
5857
5882
  try {
5858
- const config = JSON.parse(readFileSync13(configPath, "utf-8"));
5883
+ const config = JSON.parse(readFileSync14(configPath, "utf-8"));
5859
5884
  if (typeof config.max_slots === "number") {
5860
5885
  totalSlots = config.max_slots;
5861
5886
  }
@@ -5872,11 +5897,11 @@ function getBoardRoutes(factoryDir) {
5872
5897
  if (shortPhaseReserve === void 0) {
5873
5898
  shortPhaseReserve = Math.max(0, Math.min(1, totalSlots - 1));
5874
5899
  }
5875
- const alertsPath = join14(factoryDir, "daemon", "logs", ".alerts.json");
5900
+ const alertsPath = join15(factoryDir, "daemon", "logs", ".alerts.json");
5876
5901
  let alerts = [];
5877
5902
  if (existsSync16(alertsPath)) {
5878
5903
  try {
5879
- const parsed = JSON.parse(readFileSync13(alertsPath, "utf-8"));
5904
+ const parsed = JSON.parse(readFileSync14(alertsPath, "utf-8"));
5880
5905
  if (Array.isArray(parsed)) alerts = parsed;
5881
5906
  } catch {
5882
5907
  }
@@ -6103,12 +6128,12 @@ function getBoardRoutes(factoryDir) {
6103
6128
  method: "GET",
6104
6129
  pattern: "/api/extensions/plugins",
6105
6130
  handler: () => {
6106
- const lockPath = join14(factoryDir, ".beastmode", "extensions.lock");
6131
+ const lockPath = join15(factoryDir, ".beastmode", "extensions.lock");
6107
6132
  if (!existsSync16(lockPath)) {
6108
6133
  return { plugins: {} };
6109
6134
  }
6110
6135
  try {
6111
- const lock = JSON.parse(readFileSync13(lockPath, "utf-8"));
6136
+ const lock = JSON.parse(readFileSync14(lockPath, "utf-8"));
6112
6137
  return { plugins: lock.plugins || {} };
6113
6138
  } catch {
6114
6139
  return { plugins: {} };
@@ -6354,18 +6379,18 @@ function getBoardRoutes(factoryDir) {
6354
6379
  let entries = [];
6355
6380
  try {
6356
6381
  entries = readdirSync8(target, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => {
6357
- const full = join14(target, d.name);
6382
+ const full = join15(target, d.name);
6358
6383
  return {
6359
6384
  name: d.name,
6360
6385
  path: full,
6361
6386
  is_dir: true,
6362
- has_git: existsSync16(join14(full, ".git"))
6387
+ has_git: existsSync16(join15(full, ".git"))
6363
6388
  };
6364
6389
  }).sort((a, b) => a.name.localeCompare(b.name));
6365
6390
  } catch {
6366
6391
  entries = [];
6367
6392
  }
6368
- const parent = dirname5(target);
6393
+ const parent = dirname6(target);
6369
6394
  return {
6370
6395
  path: target,
6371
6396
  parent: parent !== target ? parent : null,
@@ -6387,7 +6412,7 @@ function getBoardRoutes(factoryDir) {
6387
6412
  }
6388
6413
  const repoName = url.replace(/\.git$/, "").split(/[/:]/).filter(Boolean).pop() || "";
6389
6414
  if (!repoName) throw new Error("Could not derive repo name from URL");
6390
- const baseDir = clone_target ? resolve4(clone_target) : process.env.BEASTMODE_CLONE_DIR ? resolve4(process.env.BEASTMODE_CLONE_DIR) : join14(homedir(), "repos");
6415
+ const baseDir = clone_target ? resolve4(clone_target) : process.env.BEASTMODE_CLONE_DIR ? resolve4(process.env.BEASTMODE_CLONE_DIR) : join15(homedir(), "repos");
6391
6416
  const allowedRoots = _getAllowedRoots();
6392
6417
  if (!_isPathAllowed(baseDir, allowedRoots)) {
6393
6418
  throw new Error(
@@ -6395,7 +6420,7 @@ function getBoardRoutes(factoryDir) {
6395
6420
  );
6396
6421
  }
6397
6422
  if (!existsSync16(baseDir)) mkdirSync12(baseDir, { recursive: true });
6398
- resolvedPath = join14(baseDir, repoName);
6423
+ resolvedPath = join15(baseDir, repoName);
6399
6424
  if (!_isPathAllowed(resolvedPath, allowedRoots)) {
6400
6425
  throw new Error(
6401
6426
  `clone_target not allowed: ${resolvedPath} is outside the configured allowlist`
@@ -6502,16 +6527,16 @@ function getBoardRoutes(factoryDir) {
6502
6527
  handler: async (body, params, query) => {
6503
6528
  const { name } = params;
6504
6529
  assertSafeName(name);
6505
- const projectsDir = join14(factoryDir, ".beastmode", "projects");
6530
+ const projectsDir = join15(factoryDir, ".beastmode", "projects");
6506
6531
  const projConfig = readProjectRecord(projectsDir, name);
6507
6532
  if (!projConfig) throw new Error(`Project not found: ${name}`);
6508
6533
  const projectPath = projConfig.path;
6509
- const subDir = join14(projectsDir, name);
6534
+ const subDir = join15(projectsDir, name);
6510
6535
  if (!existsSync16(subDir)) mkdirSync12(subDir, { recursive: true });
6511
6536
  const force = query?.force === "true" || query?.force === "1";
6512
6537
  const tier = body?.tier;
6513
6538
  if (tier === "quick") {
6514
- const brownfieldPath = join14(subDir, "brownfield.md");
6539
+ const brownfieldPath = join15(subDir, "brownfield.md");
6515
6540
  if (!existsSync16(brownfieldPath)) {
6516
6541
  const { detectStack: detectStackFn } = await Promise.resolve().then(() => (init_engine(), engine_exports));
6517
6542
  let stackInfo = "Unknown stack";
@@ -6545,12 +6570,12 @@ Path: ${projectPath}
6545
6570
  if (existingJob && existingJob.status === "running") {
6546
6571
  return { analyzing: true, project: name, job_id: existingJob.job_id, status: "running" };
6547
6572
  }
6548
- const triggerPath = join14(subDir, ".analyze-request.json");
6549
- const inProgressPath = join14(subDir, ".analyze-in-progress.json");
6573
+ const triggerPath = join15(subDir, ".analyze-request.json");
6574
+ const inProgressPath = join15(subDir, ".analyze-in-progress.json");
6550
6575
  if (existsSync16(triggerPath) || existsSync16(inProgressPath)) {
6551
6576
  const existingTrigger = existsSync16(triggerPath) ? triggerPath : inProgressPath;
6552
6577
  try {
6553
- const t = JSON.parse(readFileSync13(existingTrigger, "utf-8"));
6578
+ const t = JSON.parse(readFileSync14(existingTrigger, "utf-8"));
6554
6579
  const job2 = {
6555
6580
  job_id: t.job_id,
6556
6581
  project: name,
@@ -6604,15 +6629,15 @@ Path: ${projectPath}
6604
6629
  handler: (_body, params) => {
6605
6630
  const { name } = params;
6606
6631
  assertSafeName(name);
6607
- const projectsDir = join14(factoryDir, ".beastmode", "projects");
6608
- const subDir = join14(projectsDir, name);
6609
- const triggerPath = join14(subDir, ".analyze-request.json");
6610
- const inProgressPath = join14(subDir, ".analyze-in-progress.json");
6632
+ const projectsDir = join15(factoryDir, ".beastmode", "projects");
6633
+ const subDir = join15(projectsDir, name);
6634
+ const triggerPath = join15(subDir, ".analyze-request.json");
6635
+ const inProgressPath = join15(subDir, ".analyze-in-progress.json");
6611
6636
  if (existsSync16(triggerPath) || existsSync16(inProgressPath)) {
6612
6637
  const job2 = _analyzeJobs.get(name);
6613
6638
  try {
6614
6639
  const p = existsSync16(triggerPath) ? triggerPath : inProgressPath;
6615
- const raw = readFileSync13(p, "utf-8");
6640
+ const raw = readFileSync14(p, "utf-8");
6616
6641
  const t = JSON.parse(raw);
6617
6642
  const now = Date.now();
6618
6643
  const startedMs = new Date(t.requested_at).getTime();
@@ -6698,7 +6723,7 @@ Path: ${projectPath}
6698
6723
  handler: (_body, params, query) => {
6699
6724
  const { name } = params;
6700
6725
  assertSafeName(name);
6701
- const projectsDir = join14(factoryDir, ".beastmode", "projects");
6726
+ const projectsDir = join15(factoryDir, ".beastmode", "projects");
6702
6727
  const meta = _readAnalyzeMeta(projectsDir, name);
6703
6728
  if (!meta || meta.status !== "complete") {
6704
6729
  throw new HttpError(404, {
@@ -6709,11 +6734,11 @@ Path: ${projectPath}
6709
6734
  const levelRaw = query?.level || "l1";
6710
6735
  const level = levelRaw === "l0" || levelRaw === "l2" ? levelRaw : "l1";
6711
6736
  const filename = level === "l0" ? "codebase-guide.l0.txt" : level === "l2" ? "codebase-guide.md" : "codebase-guide.l1.md";
6712
- const filePath = join14(projectsDir, name, filename);
6737
+ const filePath = join15(projectsDir, name, filename);
6713
6738
  if (!existsSync16(filePath)) {
6714
6739
  throw new HttpError(404, { error: "Guide file not found" });
6715
6740
  }
6716
- const content = readFileSync13(filePath, "utf-8");
6741
+ const content = readFileSync14(filePath, "utf-8");
6717
6742
  return { level, content, meta };
6718
6743
  }
6719
6744
  },
@@ -6727,7 +6752,7 @@ Path: ${projectPath}
6727
6752
  const runEntries = [];
6728
6753
  for (const entry of readdirSync8(runsDir)) {
6729
6754
  if (entry.startsWith(".")) continue;
6730
- const entryPath = join14(runsDir, entry);
6755
+ const entryPath = join15(runsDir, entry);
6731
6756
  try {
6732
6757
  const children = readdirSync8(entryPath);
6733
6758
  if (entry.startsWith("run-")) {
@@ -6737,7 +6762,7 @@ Path: ${projectPath}
6737
6762
  } else {
6738
6763
  for (const child of children) {
6739
6764
  if (!child.startsWith("run-") || child.startsWith(".")) continue;
6740
- const childPath = join14(entryPath, child);
6765
+ const childPath = join15(entryPath, child);
6741
6766
  try {
6742
6767
  const grandchildren = readdirSync8(childPath);
6743
6768
  if (grandchildren.length > 0) {
@@ -6759,9 +6784,9 @@ Path: ${projectPath}
6759
6784
  }
6760
6785
  }
6761
6786
  const runs = Array.from(deduped.values()).sort((a, b) => b.id.localeCompare(a.id)).map(({ id, dir, projectId }) => {
6762
- const manifest = readJsonFile(join14(dir, "manifest.json"));
6763
- const checkpoint = readJsonFile(join14(dir, "checkpoint.json"));
6764
- const prodVerif = readJsonFile(join14(dir, "prod-verification.json"));
6787
+ const manifest = readJsonFile(join15(dir, "manifest.json"));
6788
+ const checkpoint = readJsonFile(join15(dir, "checkpoint.json"));
6789
+ const prodVerif = readJsonFile(join15(dir, "prod-verification.json"));
6765
6790
  const manifestData = manifest;
6766
6791
  const cpData = checkpoint;
6767
6792
  const prodAcceptFloor = 0.65;
@@ -6789,11 +6814,11 @@ Path: ${projectPath}
6789
6814
  handler: (_body, params, query) => {
6790
6815
  const { id } = params;
6791
6816
  const runsDir = getRunsDir(factoryDir);
6792
- let runDir = join14(runsDir, id);
6817
+ let runDir = join15(runsDir, id);
6793
6818
  if (!existsSync16(runDir)) {
6794
6819
  for (const proj of readdirSync8(runsDir)) {
6795
6820
  if (proj.startsWith(".") || proj.startsWith("run-")) continue;
6796
- const candidate = join14(runsDir, proj, id);
6821
+ const candidate = join15(runsDir, proj, id);
6797
6822
  if (existsSync16(candidate)) {
6798
6823
  runDir = candidate;
6799
6824
  break;
@@ -6801,15 +6826,15 @@ Path: ${projectPath}
6801
6826
  }
6802
6827
  }
6803
6828
  if (!existsSync16(runDir)) throw new Error(`Run not found: ${id}`);
6804
- const manifest = readJsonFile(join14(runDir, "manifest.json"));
6805
- const checkpoint = readJsonFile(join14(runDir, "checkpoint.json"));
6806
- const iterationsDir = join14(runDir, "iterations");
6829
+ const manifest = readJsonFile(join15(runDir, "manifest.json"));
6830
+ const checkpoint = readJsonFile(join15(runDir, "checkpoint.json"));
6831
+ const iterationsDir = join15(runDir, "iterations");
6807
6832
  const iterations = [];
6808
6833
  if (existsSync16(iterationsDir)) {
6809
6834
  const iterDirs = readdirSync8(iterationsDir).sort();
6810
6835
  for (const iterDir of iterDirs) {
6811
6836
  const satisfaction = readJsonFile(
6812
- join14(iterationsDir, iterDir, "satisfaction.json")
6837
+ join15(iterationsDir, iterDir, "satisfaction.json")
6813
6838
  );
6814
6839
  iterations.push({
6815
6840
  number: parseInt(iterDir, 10),
@@ -6837,11 +6862,11 @@ Path: ${projectPath}
6837
6862
  pattern: "/api/runs/archive",
6838
6863
  handler: () => {
6839
6864
  const runsDir = getRunsDir(factoryDir);
6840
- const configPath = join14(factoryDir, ".beastmode", "config.json");
6865
+ const configPath = join15(factoryDir, ".beastmode", "config.json");
6841
6866
  let days = 7;
6842
6867
  if (existsSync16(configPath)) {
6843
6868
  try {
6844
- days = JSON.parse(readFileSync13(configPath, "utf-8")).archive_after_days || 7;
6869
+ days = JSON.parse(readFileSync14(configPath, "utf-8")).archive_after_days || 7;
6845
6870
  } catch {
6846
6871
  }
6847
6872
  }
@@ -6867,8 +6892,8 @@ Path: ${projectPath}
6867
6892
  method: "GET",
6868
6893
  pattern: "/api/config",
6869
6894
  handler: () => {
6870
- const configPath = join14(factoryDir, ".beastmode", "config.json");
6871
- const raw = existsSync16(configPath) ? JSON.parse(readFileSync13(configPath, "utf-8")) : generateDefaults();
6895
+ const configPath = join15(factoryDir, ".beastmode", "config.json");
6896
+ const raw = existsSync16(configPath) ? JSON.parse(readFileSync14(configPath, "utf-8")) : generateDefaults();
6872
6897
  syncToDaemonConfigs(raw, factoryDir);
6873
6898
  delete raw.archive_s3_bucket;
6874
6899
  return raw;
@@ -6888,8 +6913,8 @@ Path: ${projectPath}
6888
6913
  const updatesClean = { ...updates };
6889
6914
  delete updatesClean.force;
6890
6915
  delete updatesClean._force;
6891
- const configPath = join14(factoryDir, ".beastmode", "config.json");
6892
- const current = existsSync16(configPath) ? JSON.parse(readFileSync13(configPath, "utf-8")) : generateDefaults();
6916
+ const configPath = join15(factoryDir, ".beastmode", "config.json");
6917
+ const current = existsSync16(configPath) ? JSON.parse(readFileSync14(configPath, "utf-8")) : generateDefaults();
6893
6918
  if (!force) {
6894
6919
  const oldPipeline = current.pipeline || {};
6895
6920
  const newPipeline = updatesClean.pipeline || {};
@@ -6936,8 +6961,8 @@ Path: ${projectPath}
6936
6961
  pattern: "/api/config/reset",
6937
6962
  handler: (body) => {
6938
6963
  const { keyPath } = body || {};
6939
- const configPath = join14(factoryDir, ".beastmode", "config.json");
6940
- const current = existsSync16(configPath) ? JSON.parse(readFileSync13(configPath, "utf-8")) : {};
6964
+ const configPath = join15(factoryDir, ".beastmode", "config.json");
6965
+ const current = existsSync16(configPath) ? JSON.parse(readFileSync14(configPath, "utf-8")) : {};
6941
6966
  const result = configReset(current, keyPath);
6942
6967
  writeFileSync13(configPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
6943
6968
  return result;
@@ -7029,12 +7054,12 @@ Path: ${projectPath}
7029
7054
  handler: () => {
7030
7055
  const runsDir = getRunsDir(factoryDir);
7031
7056
  const retroDirs = [];
7032
- const rootRetroDir = join14(runsDir, ".retrospectives");
7057
+ const rootRetroDir = join15(runsDir, ".retrospectives");
7033
7058
  if (existsSync16(rootRetroDir)) retroDirs.push(rootRetroDir);
7034
7059
  if (existsSync16(runsDir)) {
7035
7060
  for (const entry of readdirSync8(runsDir)) {
7036
7061
  if (entry === ".retrospectives" || entry.startsWith(".")) continue;
7037
- const projectRetroDir = join14(runsDir, entry, ".retrospectives");
7062
+ const projectRetroDir = join15(runsDir, entry, ".retrospectives");
7038
7063
  if (existsSync16(projectRetroDir)) retroDirs.push(projectRetroDir);
7039
7064
  }
7040
7065
  }
@@ -7046,7 +7071,7 @@ Path: ${projectPath}
7046
7071
  for (const retroDir of retroDirs) {
7047
7072
  const retroFiles = readdirSync8(retroDir).filter((f) => f.startsWith("run-") && f.endsWith(".json")).sort().reverse();
7048
7073
  for (const file of retroFiles) {
7049
- const data = readJsonFile(join14(retroDir, file));
7074
+ const data = readJsonFile(join15(retroDir, file));
7050
7075
  if (!data) continue;
7051
7076
  const retro = data;
7052
7077
  retrospectives.push(retro);
@@ -7068,10 +7093,10 @@ Path: ${projectPath}
7068
7093
  let wisdom = "";
7069
7094
  let wisdomMeta = {};
7070
7095
  for (const retroDir of retroDirs) {
7071
- const wisdomPath = join14(retroDir, "wisdom.md");
7096
+ const wisdomPath = join15(retroDir, "wisdom.md");
7072
7097
  if (existsSync16(wisdomPath)) {
7073
- wisdom = readFileSync13(wisdomPath, "utf-8");
7074
- wisdomMeta = readJsonFile(join14(retroDir, "wisdom-meta.json")) || {};
7098
+ wisdom = readFileSync14(wisdomPath, "utf-8");
7099
+ wisdomMeta = readJsonFile(join15(retroDir, "wisdom-meta.json")) || {};
7075
7100
  break;
7076
7101
  }
7077
7102
  }
@@ -7083,11 +7108,11 @@ Path: ${projectPath}
7083
7108
  pattern: "/api/learnings/wisdom/refresh",
7084
7109
  handler: () => {
7085
7110
  const runsDir = getRunsDir(factoryDir);
7086
- const retroDir = join14(runsDir, ".retrospectives");
7111
+ const retroDir = join15(runsDir, ".retrospectives");
7087
7112
  if (!existsSync16(retroDir)) {
7088
7113
  mkdirSync12(retroDir, { recursive: true });
7089
7114
  }
7090
- writeFileSync13(join14(retroDir, ".refresh-requested"), (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
7115
+ writeFileSync13(join15(retroDir, ".refresh-requested"), (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
7091
7116
  return { success: true };
7092
7117
  }
7093
7118
  },
@@ -7096,23 +7121,23 @@ Path: ${projectPath}
7096
7121
  pattern: "/api/learnings/:runId/:index/dismiss",
7097
7122
  handler: (_body, params) => {
7098
7123
  const runsDir = getRunsDir(factoryDir);
7099
- const candidateDirs = [join14(runsDir, ".retrospectives")];
7124
+ const candidateDirs = [join15(runsDir, ".retrospectives")];
7100
7125
  if (existsSync16(runsDir)) {
7101
7126
  for (const entry of readdirSync8(runsDir)) {
7102
7127
  if (entry === ".retrospectives" || entry.startsWith(".")) continue;
7103
- candidateDirs.push(join14(runsDir, entry, ".retrospectives"));
7128
+ candidateDirs.push(join15(runsDir, entry, ".retrospectives"));
7104
7129
  }
7105
7130
  }
7106
7131
  let filePath = null;
7107
7132
  for (const dir of candidateDirs) {
7108
- const candidate = join14(dir, `${params.runId}.json`);
7133
+ const candidate = join15(dir, `${params.runId}.json`);
7109
7134
  if (existsSync16(candidate)) {
7110
7135
  filePath = candidate;
7111
7136
  break;
7112
7137
  }
7113
7138
  }
7114
7139
  if (!filePath) throw new Error(`Retrospective not found: ${params.runId}`);
7115
- const data = JSON.parse(readFileSync13(filePath, "utf-8"));
7140
+ const data = JSON.parse(readFileSync14(filePath, "utf-8"));
7116
7141
  const idx = parseInt(params.index, 10);
7117
7142
  if (!Array.isArray(data.learnings) || idx < 0 || idx >= data.learnings.length) {
7118
7143
  throw new Error(`Learning index out of range: ${idx}`);
@@ -7127,23 +7152,23 @@ Path: ${projectPath}
7127
7152
  pattern: "/api/learnings/:runId/:index/restore",
7128
7153
  handler: (_body, params) => {
7129
7154
  const runsDir = getRunsDir(factoryDir);
7130
- const candidateDirs = [join14(runsDir, ".retrospectives")];
7155
+ const candidateDirs = [join15(runsDir, ".retrospectives")];
7131
7156
  if (existsSync16(runsDir)) {
7132
7157
  for (const entry of readdirSync8(runsDir)) {
7133
7158
  if (entry === ".retrospectives" || entry.startsWith(".")) continue;
7134
- candidateDirs.push(join14(runsDir, entry, ".retrospectives"));
7159
+ candidateDirs.push(join15(runsDir, entry, ".retrospectives"));
7135
7160
  }
7136
7161
  }
7137
7162
  let filePath = null;
7138
7163
  for (const dir of candidateDirs) {
7139
- const candidate = join14(dir, `${params.runId}.json`);
7164
+ const candidate = join15(dir, `${params.runId}.json`);
7140
7165
  if (existsSync16(candidate)) {
7141
7166
  filePath = candidate;
7142
7167
  break;
7143
7168
  }
7144
7169
  }
7145
7170
  if (!filePath) throw new Error(`Retrospective not found: ${params.runId}`);
7146
- const data = JSON.parse(readFileSync13(filePath, "utf-8"));
7171
+ const data = JSON.parse(readFileSync14(filePath, "utf-8"));
7147
7172
  const idx = parseInt(params.index, 10);
7148
7173
  if (!Array.isArray(data.learnings) || idx < 0 || idx >= data.learnings.length) {
7149
7174
  throw new Error(`Learning index out of range: ${idx}`);
@@ -7292,22 +7317,22 @@ Path: ${projectPath}
7292
7317
  } catch {
7293
7318
  }
7294
7319
  try {
7295
- const runsDir = join14(factoryDir, "runs", project);
7320
+ const runsDir = join15(factoryDir, "runs", project);
7296
7321
  if (existsSync16(runsDir)) {
7297
7322
  const runDirs = readdirSync8(runsDir).filter((d) => d.startsWith("run-"));
7298
7323
  for (const runId of runDirs) {
7299
- const ckptPath = join14(runsDir, runId, "checkpoint.json");
7324
+ const ckptPath = join15(runsDir, runId, "checkpoint.json");
7300
7325
  let subtitle = "";
7301
7326
  let timestamp = "";
7302
7327
  try {
7303
- const st = statSync6(join14(runsDir, runId));
7328
+ const st = statSync6(join15(runsDir, runId));
7304
7329
  timestamp = st.mtime.toISOString();
7305
7330
  } catch {
7306
7331
  timestamp = (/* @__PURE__ */ new Date()).toISOString();
7307
7332
  }
7308
7333
  if (existsSync16(ckptPath)) {
7309
7334
  try {
7310
- const ckpt = JSON.parse(readFileSync13(ckptPath, "utf-8"));
7335
+ const ckpt = JSON.parse(readFileSync14(ckptPath, "utf-8"));
7311
7336
  const history = ckpt.satisfaction_history;
7312
7337
  if (history && history.length > 0) {
7313
7338
  const latest = history[history.length - 1];
@@ -7432,9 +7457,9 @@ __export(server_exports, {
7432
7457
  });
7433
7458
  import { createServer } from "http";
7434
7459
  import { createHmac } from "crypto";
7435
- import { readFileSync as readFileSync14, existsSync as existsSync17 } from "fs";
7436
- import { join as join15, dirname as dirname6 } from "path";
7437
- import { fileURLToPath as fileURLToPath2 } from "url";
7460
+ import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
7461
+ import { join as join16, dirname as dirname7 } from "path";
7462
+ import { fileURLToPath as fileURLToPath3 } from "url";
7438
7463
  import { WebSocketServer as WebSocketServer2, WebSocket as WsClient } from "ws";
7439
7464
  function proxyBoardWebSocket(req, socket, head, boardWsUrl) {
7440
7465
  _boardWsProxy.handleUpgrade(req, socket, head, (clientWs) => {
@@ -7475,16 +7500,16 @@ function proxyBoardWebSocket(req, socket, head, boardWsUrl) {
7475
7500
  });
7476
7501
  }
7477
7502
  function resolveStaticDir() {
7478
- const devPath = join15(__dirname, "static");
7479
- if (existsSync17(join15(devPath, "index.html"))) {
7503
+ const devPath = join16(__dirname, "static");
7504
+ if (existsSync17(join16(devPath, "index.html"))) {
7480
7505
  return devPath;
7481
7506
  }
7482
- const distWebPath = join15(__dirname, "web");
7483
- if (existsSync17(join15(distWebPath, "index.html"))) {
7507
+ const distWebPath = join16(__dirname, "web");
7508
+ if (existsSync17(join16(distWebPath, "index.html"))) {
7484
7509
  return distWebPath;
7485
7510
  }
7486
- const siblingPath = join15(__dirname, "..", "web");
7487
- if (existsSync17(join15(siblingPath, "index.html"))) {
7511
+ const siblingPath = join16(__dirname, "..", "web");
7512
+ if (existsSync17(join16(siblingPath, "index.html"))) {
7488
7513
  return siblingPath;
7489
7514
  }
7490
7515
  throw new Error(
@@ -7593,9 +7618,9 @@ async function startServer(options = {}) {
7593
7618
  const fallback = "<html><body><h1>BeastMode Init Wizard</h1><p>Static files not found.</p></body></html>";
7594
7619
  if (!staticDir) return fallback;
7595
7620
  try {
7596
- const indexPath = join15(staticDir, "index.html");
7621
+ const indexPath = join16(staticDir, "index.html");
7597
7622
  if (!existsSync17(indexPath)) return fallback;
7598
- return readFileSync14(indexPath, "utf-8");
7623
+ return readFileSync15(indexPath, "utf-8");
7599
7624
  } catch {
7600
7625
  return fallback;
7601
7626
  }
@@ -7603,9 +7628,9 @@ async function startServer(options = {}) {
7603
7628
  function loadBoardHtml() {
7604
7629
  try {
7605
7630
  const dir = staticDir || resolveStaticDir();
7606
- const boardPath = join15(dir, "board.html");
7631
+ const boardPath = join16(dir, "board.html");
7607
7632
  if (!existsSync17(boardPath)) return null;
7608
- return readFileSync14(boardPath, "utf-8");
7633
+ return readFileSync15(boardPath, "utf-8");
7609
7634
  } catch {
7610
7635
  return null;
7611
7636
  }
@@ -7700,13 +7725,13 @@ async function startServer(options = {}) {
7700
7725
  let commit_sha = null;
7701
7726
  try {
7702
7727
  const dir = staticDir || resolveStaticDir();
7703
- const stampPath = join15(dir, "build-stamp.txt");
7728
+ const stampPath = join16(dir, "build-stamp.txt");
7704
7729
  if (existsSync17(stampPath)) {
7705
- stamp = readFileSync14(stampPath, "utf-8").trim();
7730
+ stamp = readFileSync15(stampPath, "utf-8").trim();
7706
7731
  }
7707
- const commitPath = join15(dir, "build-commit.txt");
7732
+ const commitPath = join16(dir, "build-commit.txt");
7708
7733
  if (existsSync17(commitPath)) {
7709
- commit_sha = readFileSync14(commitPath, "utf-8").trim() || null;
7734
+ commit_sha = readFileSync15(commitPath, "utf-8").trim() || null;
7710
7735
  }
7711
7736
  } catch {
7712
7737
  }
@@ -7784,7 +7809,7 @@ async function startServer(options = {}) {
7784
7809
  }
7785
7810
  if (method === "GET" && url.startsWith("/static/")) {
7786
7811
  const filename = url.slice("/static/".length);
7787
- const filePath = staticDir ? join15(staticDir, filename) : "";
7812
+ const filePath = staticDir ? join16(staticDir, filename) : "";
7788
7813
  if (filePath && existsSync17(filePath)) {
7789
7814
  const ext = filename.split(".").pop()?.toLowerCase();
7790
7815
  const mimeTypes = {
@@ -7796,7 +7821,7 @@ async function startServer(options = {}) {
7796
7821
  ico: "image/x-icon"
7797
7822
  };
7798
7823
  const contentType = mimeTypes[ext || ""] || "application/octet-stream";
7799
- const content = readFileSync14(filePath);
7824
+ const content = readFileSync15(filePath);
7800
7825
  res.writeHead(200, {
7801
7826
  "Content-Type": contentType,
7802
7827
  "Content-Length": content.length.toString(),
@@ -7934,8 +7959,8 @@ var init_server = __esm({
7934
7959
  init_board_api_routes();
7935
7960
  init_chat_handler();
7936
7961
  _boardWsProxy = new WebSocketServer2({ noServer: true });
7937
- __filename = fileURLToPath2(import.meta.url);
7938
- __dirname = dirname6(__filename);
7962
+ __filename = fileURLToPath3(import.meta.url);
7963
+ __dirname = dirname7(__filename);
7939
7964
  INACTIVITY_TIMEOUT_MS = 30 * 60 * 1e3;
7940
7965
  UI_PASSWORD = process.env.BEASTMODE_UI_PASSWORD || "";
7941
7966
  ALLOW_NO_AUTH = /^(1|true|yes)$/i.test(process.env.BEASTMODE_ALLOW_NO_AUTH || "");
@@ -8046,27 +8071,27 @@ var init_server = __esm({
8046
8071
 
8047
8072
  // src/cli/commands/board.ts
8048
8073
  import { Command } from "commander";
8049
- import { resolve as resolve5, join as join16 } from "path";
8050
- import { existsSync as existsSync18, readFileSync as readFileSync15, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14 } from "fs";
8074
+ import { resolve as resolve5, join as join17 } from "path";
8075
+ import { existsSync as existsSync18, readFileSync as readFileSync16, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14 } from "fs";
8051
8076
  import { execSync as execSync4 } from "child_process";
8052
8077
  function findFactoryDir(startDir) {
8053
8078
  let dir = startDir || process.cwd();
8054
8079
  const root = resolve5("/");
8055
8080
  while (dir !== root) {
8056
- if (existsSync18(join16(dir, ".beastmode", "factory.json"))) {
8081
+ if (existsSync18(join17(dir, ".beastmode", "factory.json"))) {
8057
8082
  return dir;
8058
8083
  }
8059
8084
  const parent = resolve5(dir, "..");
8060
8085
  if (parent === dir) break;
8061
8086
  dir = parent;
8062
8087
  }
8063
- if (existsSync18(join16(dir, ".beastmode", "factory.json"))) {
8088
+ if (existsSync18(join17(dir, ".beastmode", "factory.json"))) {
8064
8089
  return dir;
8065
8090
  }
8066
8091
  return null;
8067
8092
  }
8068
8093
  function inferProjectName(factoryDir) {
8069
- const projectsDir = join16(factoryDir, ".beastmode", "projects");
8094
+ const projectsDir = join17(factoryDir, ".beastmode", "projects");
8070
8095
  const records = listProjectRecords(projectsDir);
8071
8096
  if (records.length === 1) return records[0].name;
8072
8097
  return null;
@@ -8145,11 +8170,11 @@ async function runBoard(opts) {
8145
8170
  let factoryDir = findFactoryDir();
8146
8171
  if (!factoryDir) {
8147
8172
  factoryDir = process.cwd();
8148
- const bmDir = join16(factoryDir, ".beastmode");
8173
+ const bmDir = join17(factoryDir, ".beastmode");
8149
8174
  if (!existsSync18(bmDir)) {
8150
8175
  mkdirSync13(bmDir, { recursive: true });
8151
8176
  }
8152
- const factoryJsonPath2 = join16(bmDir, "factory.json");
8177
+ const factoryJsonPath2 = join17(bmDir, "factory.json");
8153
8178
  if (!existsSync18(factoryJsonPath2)) {
8154
8179
  writeFileSync14(
8155
8180
  factoryJsonPath2,
@@ -8159,8 +8184,8 @@ async function runBoard(opts) {
8159
8184
  info("No factory found \u2014 created minimal stub at .beastmode/factory.json");
8160
8185
  }
8161
8186
  }
8162
- const factoryJsonPath = join16(factoryDir, ".beastmode", "factory.json");
8163
- const factoryJson = JSON.parse(readFileSync15(factoryJsonPath, "utf-8"));
8187
+ const factoryJsonPath = join17(factoryDir, ".beastmode", "factory.json");
8188
+ const factoryJson = JSON.parse(readFileSync16(factoryJsonPath, "utf-8"));
8164
8189
  const factoryName = factoryJson.factory_name || "BeastMode Factory";
8165
8190
  header(`BeastMode Board \u2014 ${factoryName}`);
8166
8191
  const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
@@ -8233,26 +8258,26 @@ __export(sync_claude_creds_exports, {
8233
8258
  });
8234
8259
  import { Command as Command2 } from "commander";
8235
8260
  import { execSync as execSync5, spawnSync as spawnSync2 } from "child_process";
8236
- import { writeFileSync as writeFileSync15, readFileSync as readFileSync16, chmodSync, mkdirSync as mkdirSync14, existsSync as existsSync19, unlinkSync as unlinkSync5 } from "fs";
8237
- import { join as join17 } from "path";
8261
+ import { writeFileSync as writeFileSync15, readFileSync as readFileSync17, chmodSync, mkdirSync as mkdirSync14, existsSync as existsSync19, unlinkSync as unlinkSync5 } from "fs";
8262
+ import { join as join18 } from "path";
8238
8263
  import { homedir as homedir2, platform } from "os";
8239
8264
  function systemdUserDir() {
8240
- return join17(homedir2(), ".config", "systemd", "user");
8265
+ return join18(homedir2(), ".config", "systemd", "user");
8241
8266
  }
8242
8267
  function systemdServicePath() {
8243
- return join17(systemdUserDir(), `${SYSTEMD_UNIT_NAME}.service`);
8268
+ return join18(systemdUserDir(), `${SYSTEMD_UNIT_NAME}.service`);
8244
8269
  }
8245
8270
  function systemdTimerPath() {
8246
- return join17(systemdUserDir(), `${SYSTEMD_UNIT_NAME}.timer`);
8271
+ return join18(systemdUserDir(), `${SYSTEMD_UNIT_NAME}.timer`);
8247
8272
  }
8248
8273
  function linuxCredsPath() {
8249
- return join17(homedir2(), ".claude", ".credentials.json");
8274
+ return join18(homedir2(), ".claude", ".credentials.json");
8250
8275
  }
8251
8276
  function plistPath() {
8252
- return join17(homedir2(), "Library", "LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`);
8277
+ return join18(homedir2(), "Library", "LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`);
8253
8278
  }
8254
8279
  function agentLogPath() {
8255
- return join17(homedir2(), ".beastmode", "logs", "sync-claude-creds.log");
8280
+ return join18(homedir2(), ".beastmode", "logs", "sync-claude-creds.log");
8256
8281
  }
8257
8282
  function readKeychainTokenSafe() {
8258
8283
  try {
@@ -8284,9 +8309,9 @@ function writeCredentialsFile(rawJson) {
8284
8309
  if (!oauth?.accessToken) {
8285
8310
  throw new Error("Keychain entry missing claudeAiOauth.accessToken. Fix: run `claude login` again to reset the credential.");
8286
8311
  }
8287
- const claudeDir = join17(homedir2(), ".claude");
8312
+ const claudeDir = join18(homedir2(), ".claude");
8288
8313
  if (!existsSync19(claudeDir)) mkdirSync14(claudeDir, { recursive: true });
8289
- const credsPath = join17(claudeDir, ".credentials.json");
8314
+ const credsPath = join18(claudeDir, ".credentials.json");
8290
8315
  writeFileSync15(credsPath, rawJson + "\n", "utf-8");
8291
8316
  chmodSync(credsPath, 384);
8292
8317
  if (oauth.expiresAt) {
@@ -8300,7 +8325,7 @@ function writeCredentialsFile(rawJson) {
8300
8325
  return credsPath;
8301
8326
  }
8302
8327
  function urgencyMarkerHostPath(factoryDir) {
8303
- return join17(factoryDir, "daemon", "logs", ".cred-refresh-needed");
8328
+ return join18(factoryDir, "daemon", "logs", ".cred-refresh-needed");
8304
8329
  }
8305
8330
  function removeUrgencyMarker(factoryDir) {
8306
8331
  if (!factoryDir) return;
@@ -8314,7 +8339,7 @@ function buildPlist(intervalSeconds, factoryDir) {
8314
8339
  const nodePath = process.execPath;
8315
8340
  const cliEntry = process.argv[1];
8316
8341
  const logPath = agentLogPath();
8317
- const keychainPath = join17(homedir2(), "Library", "Keychains", "login.keychain-db");
8342
+ const keychainPath = join18(homedir2(), "Library", "Keychains", "login.keychain-db");
8318
8343
  const watchPaths = [keychainPath];
8319
8344
  if (factoryDir) {
8320
8345
  watchPaths.push(urgencyMarkerHostPath(factoryDir));
@@ -8354,8 +8379,8 @@ function installAgent(intervalSeconds, {
8354
8379
  } = {}) {
8355
8380
  const plist = plistPath();
8356
8381
  const logPath = agentLogPath();
8357
- mkdirSync14(join17(homedir2(), "Library", "LaunchAgents"), { recursive: true });
8358
- mkdirSync14(join17(homedir2(), ".beastmode", "logs"), { recursive: true });
8382
+ mkdirSync14(join18(homedir2(), "Library", "LaunchAgents"), { recursive: true });
8383
+ mkdirSync14(join18(homedir2(), ".beastmode", "logs"), { recursive: true });
8359
8384
  const uid = process.getuid?.();
8360
8385
  if (existsSync19(plist)) {
8361
8386
  spawnSync2("launchctl", ["bootout", `gui/${uid}`, plist], { stdio: "pipe" });
@@ -8451,7 +8476,7 @@ function syncClaudeCredsLinux() {
8451
8476
  }
8452
8477
  let parsed;
8453
8478
  try {
8454
- const raw = readFileSync16(credsPath, "utf-8");
8479
+ const raw = readFileSync17(credsPath, "utf-8");
8455
8480
  parsed = JSON.parse(raw);
8456
8481
  } catch {
8457
8482
  return {
@@ -8480,7 +8505,7 @@ function syncClaudeCredsLinux() {
8480
8505
  );
8481
8506
  }
8482
8507
  try {
8483
- const raw2 = readFileSync16(credsPath, "utf-8");
8508
+ const raw2 = readFileSync17(credsPath, "utf-8");
8484
8509
  const parsed2 = JSON.parse(raw2);
8485
8510
  if (parsed2.claudeAiOauth?.expiresAt) {
8486
8511
  const newMinutes = (parsed2.claudeAiOauth.expiresAt - Date.now()) / 6e4;
@@ -8534,10 +8559,10 @@ function installAgentLinux(intervalSeconds, { throwOnError = false } = {}) {
8534
8559
  process.exit(1);
8535
8560
  }
8536
8561
  const unitDir = systemdUserDir();
8537
- const logDir = join17(homedir2(), ".beastmode", "logs");
8562
+ const logDir = join18(homedir2(), ".beastmode", "logs");
8538
8563
  mkdirSync14(unitDir, { recursive: true });
8539
8564
  mkdirSync14(logDir, { recursive: true });
8540
- const logPath = join17(logDir, "sync-claude-creds.log");
8565
+ const logPath = join18(logDir, "sync-claude-creds.log");
8541
8566
  const nodePath = process.execPath;
8542
8567
  const cliEntry = process.argv[1];
8543
8568
  writeFileSync15(
@@ -8782,26 +8807,26 @@ var init_sync_claude_creds = __esm({
8782
8807
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8783
8808
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8784
8809
  import { z as z2 } from "zod";
8785
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync25, existsSync as existsSync32, readdirSync as readdirSync12 } from "fs";
8786
- import { join as join30, resolve as resolve18, basename as basename7 } from "path";
8810
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync25, existsSync as existsSync32, readdirSync as readdirSync12 } from "fs";
8811
+ import { join as join31, resolve as resolve18, basename as basename7 } from "path";
8787
8812
  import { randomUUID as randomUUID4 } from "crypto";
8788
8813
  function readJsonFile2(filePath) {
8789
8814
  if (!existsSync32(filePath)) return null;
8790
8815
  try {
8791
- return JSON.parse(readFileSync29(filePath, "utf-8"));
8816
+ return JSON.parse(readFileSync30(filePath, "utf-8"));
8792
8817
  } catch {
8793
8818
  return null;
8794
8819
  }
8795
8820
  }
8796
8821
  function getFactoryPath() {
8797
8822
  const envPath = process.env.BEASTMODE_FACTORY_PATH;
8798
- if (envPath && existsSync32(join30(envPath, ".beastmode", "factory.json"))) {
8823
+ if (envPath && existsSync32(join31(envPath, ".beastmode", "factory.json"))) {
8799
8824
  return envPath;
8800
8825
  }
8801
8826
  let dir = process.cwd();
8802
8827
  const root = resolve18("/");
8803
8828
  while (dir !== root) {
8804
- if (existsSync32(join30(dir, ".beastmode", "factory.json"))) {
8829
+ if (existsSync32(join31(dir, ".beastmode", "factory.json"))) {
8805
8830
  return dir;
8806
8831
  }
8807
8832
  const parent = resolve18(dir, "..");
@@ -8813,17 +8838,17 @@ function getFactoryPath() {
8813
8838
  );
8814
8839
  }
8815
8840
  function readFactoryStatus(factoryDir) {
8816
- const bmDir = join30(factoryDir, ".beastmode");
8841
+ const bmDir = join31(factoryDir, ".beastmode");
8817
8842
  const factoryIdentity = FactoryIdentitySchema.parse(
8818
- JSON.parse(readFileSync29(join30(bmDir, "factory.json"), "utf-8"))
8843
+ JSON.parse(readFileSync30(join31(bmDir, "factory.json"), "utf-8"))
8819
8844
  );
8820
- const projectsDir = join30(bmDir, "projects");
8845
+ const projectsDir = join31(bmDir, "projects");
8821
8846
  const projectCount = listProjectRecords(projectsDir).length;
8822
- const lockPath = join30(bmDir, "extensions.lock");
8847
+ const lockPath = join31(bmDir, "extensions.lock");
8823
8848
  let pluginNames = [];
8824
8849
  if (existsSync32(lockPath)) {
8825
8850
  try {
8826
- const lock = JSON.parse(readFileSync29(lockPath, "utf-8"));
8851
+ const lock = JSON.parse(readFileSync30(lockPath, "utf-8"));
8827
8852
  pluginNames = Object.keys(lock.plugins || {});
8828
8853
  } catch {
8829
8854
  }
@@ -8843,23 +8868,23 @@ function readFactoryStatus(factoryDir) {
8843
8868
  skillCount = listSkills(factoryDir).length;
8844
8869
  } catch {
8845
8870
  }
8846
- const runsDir = join30(factoryDir, "runs");
8871
+ const runsDir = join31(factoryDir, "runs");
8847
8872
  let runDirs = [];
8848
8873
  if (existsSync32(runsDir)) {
8849
8874
  runDirs = readdirSync12(runsDir).filter((d) => {
8850
8875
  try {
8851
- return readdirSync12(join30(runsDir, d)).length > 0;
8876
+ return readdirSync12(join31(runsDir, d)).length > 0;
8852
8877
  } catch {
8853
8878
  return false;
8854
8879
  }
8855
8880
  }).sort();
8856
8881
  }
8857
- const pidFile = join30(bmDir, "daemon.pid");
8882
+ const pidFile = join31(bmDir, "daemon.pid");
8858
8883
  let daemonPid = null;
8859
8884
  let pidAlive = false;
8860
8885
  if (existsSync32(pidFile)) {
8861
8886
  try {
8862
- daemonPid = parseInt(readFileSync29(pidFile, "utf-8").trim(), 10);
8887
+ daemonPid = parseInt(readFileSync30(pidFile, "utf-8").trim(), 10);
8863
8888
  process.kill(daemonPid, 0);
8864
8889
  pidAlive = true;
8865
8890
  } catch {
@@ -8880,17 +8905,17 @@ function readFactoryStatus(factoryDir) {
8880
8905
  return collectStatus(input);
8881
8906
  }
8882
8907
  function readBoardItems(factoryDir) {
8883
- const filePath = join30(factoryDir, ".beastmode", "board.json");
8908
+ const filePath = join31(factoryDir, ".beastmode", "board.json");
8884
8909
  if (!existsSync32(filePath)) return [];
8885
8910
  try {
8886
- const raw = JSON.parse(readFileSync29(filePath, "utf-8"));
8911
+ const raw = JSON.parse(readFileSync30(filePath, "utf-8"));
8887
8912
  return Array.isArray(raw.items) ? raw.items : [];
8888
8913
  } catch {
8889
8914
  return [];
8890
8915
  }
8891
8916
  }
8892
8917
  function writeBoardItems(factoryDir, items) {
8893
- const filePath = join30(factoryDir, ".beastmode", "board.json");
8918
+ const filePath = join31(factoryDir, ".beastmode", "board.json");
8894
8919
  writeFileSync25(filePath, JSON.stringify({ items }, null, 2) + "\n", "utf-8");
8895
8920
  }
8896
8921
  function createMcpServer() {
@@ -8914,8 +8939,8 @@ function createMcpServer() {
8914
8939
  { key_path: z2.string().describe("Dot-notation key path") },
8915
8940
  async ({ key_path }) => {
8916
8941
  const factoryDir = getFactoryPath();
8917
- const configPath = join30(factoryDir, ".beastmode", "config.json");
8918
- const config = existsSync32(configPath) ? JSON.parse(readFileSync29(configPath, "utf-8")) : generateDefaults();
8942
+ const configPath = join31(factoryDir, ".beastmode", "config.json");
8943
+ const config = existsSync32(configPath) ? JSON.parse(readFileSync30(configPath, "utf-8")) : generateDefaults();
8919
8944
  try {
8920
8945
  const value = configGet(config, key_path);
8921
8946
  return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
@@ -8933,8 +8958,8 @@ function createMcpServer() {
8933
8958
  },
8934
8959
  async ({ key_path, value }) => {
8935
8960
  const factoryDir = getFactoryPath();
8936
- const configPath = join30(factoryDir, ".beastmode", "config.json");
8937
- const config = existsSync32(configPath) ? JSON.parse(readFileSync29(configPath, "utf-8")) : generateDefaults();
8961
+ const configPath = join31(factoryDir, ".beastmode", "config.json");
8962
+ const config = existsSync32(configPath) ? JSON.parse(readFileSync30(configPath, "utf-8")) : generateDefaults();
8938
8963
  const coerced = coerceValue(value);
8939
8964
  const updated = configSet(config, key_path, coerced);
8940
8965
  writeFileSync25(configPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
@@ -8971,14 +8996,14 @@ function createMcpServer() {
8971
8996
  {},
8972
8997
  async () => {
8973
8998
  const factoryDir = getFactoryPath();
8974
- const runsDir = join30(factoryDir, "runs");
8999
+ const runsDir = join31(factoryDir, "runs");
8975
9000
  if (!existsSync32(runsDir)) {
8976
9001
  return { content: [{ type: "text", text: "No runs directory found." }] };
8977
9002
  }
8978
9003
  const runDirs = readdirSync12(runsDir).sort().reverse();
8979
9004
  const activeRuns = [];
8980
9005
  for (const id of runDirs.slice(0, 10)) {
8981
- const cp = readJsonFile2(join30(runsDir, id, "checkpoint.json"));
9006
+ const cp = readJsonFile2(join31(runsDir, id, "checkpoint.json"));
8982
9007
  if (cp) {
8983
9008
  activeRuns.push({ id, checkpoint: cp });
8984
9009
  }
@@ -8992,17 +9017,17 @@ function createMcpServer() {
8992
9017
  { run_id: z2.string().describe("Run ID (directory name)") },
8993
9018
  async ({ run_id }) => {
8994
9019
  const factoryDir = getFactoryPath();
8995
- const runDir = join30(factoryDir, "runs", run_id);
9020
+ const runDir = join31(factoryDir, "runs", run_id);
8996
9021
  if (!existsSync32(runDir)) {
8997
9022
  return { content: [{ type: "text", text: `Run not found: ${run_id}` }], isError: true };
8998
9023
  }
8999
- const manifest = readJsonFile2(join30(runDir, "manifest.json"));
9000
- const checkpoint = readJsonFile2(join30(runDir, "checkpoint.json"));
9001
- const iterationsDir = join30(runDir, "iterations");
9024
+ const manifest = readJsonFile2(join31(runDir, "manifest.json"));
9025
+ const checkpoint = readJsonFile2(join31(runDir, "checkpoint.json"));
9026
+ const iterationsDir = join31(runDir, "iterations");
9002
9027
  const iterations = [];
9003
9028
  if (existsSync32(iterationsDir)) {
9004
9029
  for (const dir of readdirSync12(iterationsDir).sort()) {
9005
- const satisfaction = readJsonFile2(join30(iterationsDir, dir, "satisfaction.json"));
9030
+ const satisfaction = readJsonFile2(join31(iterationsDir, dir, "satisfaction.json"));
9006
9031
  iterations.push({ number: parseInt(dir, 10), satisfaction });
9007
9032
  }
9008
9033
  }
@@ -9048,12 +9073,12 @@ function createMcpServer() {
9048
9073
  {},
9049
9074
  async () => {
9050
9075
  const factoryDir = getFactoryPath();
9051
- const bmDir = join30(factoryDir, ".beastmode");
9076
+ const bmDir = join31(factoryDir, ".beastmode");
9052
9077
  let plugins = {};
9053
- const lockPath = join30(bmDir, "extensions.lock");
9078
+ const lockPath = join31(bmDir, "extensions.lock");
9054
9079
  if (existsSync32(lockPath)) {
9055
9080
  try {
9056
- const lock = JSON.parse(readFileSync29(lockPath, "utf-8"));
9081
+ const lock = JSON.parse(readFileSync30(lockPath, "utf-8"));
9057
9082
  plugins = lock.plugins || {};
9058
9083
  } catch {
9059
9084
  }
@@ -9087,7 +9112,7 @@ function createMcpServer() {
9087
9112
  {},
9088
9113
  async () => {
9089
9114
  const factoryDir = getFactoryPath();
9090
- const projectsDir = join30(factoryDir, ".beastmode", "projects");
9115
+ const projectsDir = join31(factoryDir, ".beastmode", "projects");
9091
9116
  const projects = listProjectRecords(projectsDir);
9092
9117
  return { content: [{ type: "text", text: JSON.stringify(projects, null, 2) }] };
9093
9118
  }
@@ -9104,7 +9129,7 @@ function createMcpServer() {
9104
9129
  }
9105
9130
  const projectName = basename7(resolvedPath);
9106
9131
  const stack = detectStack(resolvedPath);
9107
- const projectsDir = join30(factoryDir, ".beastmode", "projects");
9132
+ const projectsDir = join31(factoryDir, ".beastmode", "projects");
9108
9133
  let verifyPort = 3001;
9109
9134
  for (const existing of listProjectRecords(projectsDir)) {
9110
9135
  const port = existing.deploy?.verify_port;
@@ -9220,16 +9245,16 @@ init_engine();
9220
9245
  init_file_writer();
9221
9246
  import { Command as Command3 } from "commander";
9222
9247
  import inquirer from "inquirer";
9223
- import { resolve as resolve6, basename as basename6, join as join18 } from "path";
9224
- import { existsSync as existsSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync17 } from "fs";
9248
+ import { resolve as resolve6, basename as basename6, join as join19 } from "path";
9249
+ import { existsSync as existsSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync18 } from "fs";
9225
9250
 
9226
9251
  // src/cli/utils/docker.ts
9227
9252
  import { existsSync as existsSync10 } from "fs";
9228
- import { join as join9 } from "path";
9253
+ import { join as join10 } from "path";
9229
9254
  import { execSync } from "child_process";
9230
9255
  var GHCR_IMAGE_PREFIX = "ghcr.io/develeap/beastmode";
9231
9256
  function findComposeFile(dir) {
9232
- const path = join9(dir, "docker-compose.yml");
9257
+ const path = join10(dir, "docker-compose.yml");
9233
9258
  return existsSync10(path) ? path : null;
9234
9259
  }
9235
9260
  function requireComposeFile(dir) {
@@ -9293,7 +9318,7 @@ function loginToGhcr(token) {
9293
9318
  }
9294
9319
  }
9295
9320
  function seedConfigFromImage(targetDir, tag) {
9296
- const configDir = join9(targetDir, "config");
9321
+ const configDir = join10(targetDir, "config");
9297
9322
  const containerName = "bm-config-seed";
9298
9323
  try {
9299
9324
  try {
@@ -9473,7 +9498,7 @@ function collect(val, acc) {
9473
9498
  return acc;
9474
9499
  }
9475
9500
  function readSecretFile(filePath) {
9476
- return readFileSync17(resolve6(filePath), "utf-8").trim();
9501
+ return readFileSync18(resolve6(filePath), "utf-8").trim();
9477
9502
  }
9478
9503
  function isSourceRepo(dir) {
9479
9504
  return existsSync20(resolve6(dir, "daemon")) && existsSync20(resolve6(dir, "board")) && existsSync20(resolve6(dir, "cli"));
@@ -9519,8 +9544,8 @@ async function runInit(name, opts) {
9519
9544
  throw new Error(`Factory already exists at ./${factoryName}. Use 'beastmode config' to modify.`);
9520
9545
  }
9521
9546
  if (opts.from) {
9522
- const { readFileSync: readFileSync36 } = await import("fs");
9523
- const templateContent = readFileSync36(resolve6(opts.from), "utf-8");
9547
+ const { readFileSync: readFileSync37 } = await import("fs");
9548
+ const templateContent = readFileSync37(resolve6(opts.from), "utf-8");
9524
9549
  const { parseTemplate: parseTemplate2 } = await Promise.resolve().then(() => (init_template_importer(), template_importer_exports));
9525
9550
  const template = parseTemplate2(templateContent);
9526
9551
  info(`Importing from template: ${opts.from}`);
@@ -9878,7 +9903,7 @@ async function runImageModeInit(name, opts) {
9878
9903
  success(`Project path: ${projectPath}`);
9879
9904
  } else if (opts.yes) {
9880
9905
  const cwd = resolve6(".");
9881
- if (!existsSync20(join18(cwd, ".git"))) {
9906
+ if (!existsSync20(join19(cwd, ".git"))) {
9882
9907
  error(
9883
9908
  "--project is required in non-interactive mode (--yes) unless you run from inside a git repository. Pass --project <path> or cd into your project clone first."
9884
9909
  );
@@ -9896,7 +9921,7 @@ async function runImageModeInit(name, opts) {
9896
9921
  validate: (input) => {
9897
9922
  const p = resolve6(input);
9898
9923
  if (!existsSync20(p)) return `Directory not found: ${p}`;
9899
- if (!existsSync20(join18(p, ".git"))) {
9924
+ if (!existsSync20(join19(p, ".git"))) {
9900
9925
  return `Not a git repo: ${p} (run 'git init' first, or pick a different path)`;
9901
9926
  }
9902
9927
  return true;
@@ -9937,7 +9962,7 @@ async function runImageModeInit(name, opts) {
9937
9962
  step(3, totalSteps, "Generate");
9938
9963
  mkdirSync15(targetDir, { recursive: true });
9939
9964
  const composeContent = generateComposeYaml("latest");
9940
- writeFileSync16(join18(targetDir, "docker-compose.yml"), composeContent, "utf-8");
9965
+ writeFileSync16(join19(targetDir, "docker-compose.yml"), composeContent, "utf-8");
9941
9966
  success("docker-compose.yml");
9942
9967
  const envLines = [
9943
9968
  "# BeastMode environment \u2014 DO NOT COMMIT",
@@ -9977,10 +10002,10 @@ async function runImageModeInit(name, opts) {
9977
10002
  "# PROJECT_REPO=owner/repo",
9978
10003
  ""
9979
10004
  ];
9980
- writeFileSync16(join18(targetDir, ".env"), envLines.join("\n"), "utf-8");
10005
+ writeFileSync16(join19(targetDir, ".env"), envLines.join("\n"), "utf-8");
9981
10006
  success(`.env (PROJECT_DIR=${projectPath}, two-PAT model)`);
9982
10007
  for (const dir of ["data", "runs", "daemon/logs", ".beastmode", "config"]) {
9983
- mkdirSync15(join18(targetDir, dir), { recursive: true });
10008
+ mkdirSync15(join19(targetDir, dir), { recursive: true });
9984
10009
  }
9985
10010
  step(4, totalSteps, "Pull Images");
9986
10011
  try {
@@ -10069,20 +10094,20 @@ async function runImageModeInit(name, opts) {
10069
10094
  init_export_adapter();
10070
10095
  init_display();
10071
10096
  import { Command as Command4 } from "commander";
10072
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync17, existsSync as existsSync21, readdirSync as readdirSync9 } from "fs";
10073
- import { resolve as resolve7, join as join19 } from "path";
10097
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync17, existsSync as existsSync21, readdirSync as readdirSync9 } from "fs";
10098
+ import { resolve as resolve7, join as join20 } from "path";
10074
10099
  function exportFactory(factoryDir, outputPath) {
10075
- const bmDir = join19(factoryDir, ".beastmode");
10076
- const configPath = join19(bmDir, "config.json");
10100
+ const bmDir = join20(factoryDir, ".beastmode");
10101
+ const configPath = join20(bmDir, "config.json");
10077
10102
  if (!existsSync21(configPath)) {
10078
10103
  throw new Error(`No factory found at ${factoryDir}`);
10079
10104
  }
10080
- const config = JSON.parse(readFileSync18(configPath, "utf-8"));
10081
- const identity = JSON.parse(readFileSync18(join19(bmDir, "factory.json"), "utf-8"));
10105
+ const config = JSON.parse(readFileSync19(configPath, "utf-8"));
10106
+ const identity = JSON.parse(readFileSync19(join20(bmDir, "factory.json"), "utf-8"));
10082
10107
  let plugins = [];
10083
- const lockPath = join19(bmDir, "extensions.lock");
10108
+ const lockPath = join20(bmDir, "extensions.lock");
10084
10109
  if (existsSync21(lockPath)) {
10085
- const lock = JSON.parse(readFileSync18(lockPath, "utf-8"));
10110
+ const lock = JSON.parse(readFileSync19(lockPath, "utf-8"));
10086
10111
  plugins = Object.keys(lock.plugins || {});
10087
10112
  }
10088
10113
  const template = {
@@ -10113,19 +10138,19 @@ function createArtifactExportCommand(artifact) {
10113
10138
  const runDir = findRunDir(opts.run);
10114
10139
  let sourceContent;
10115
10140
  if (artifact === "scenarios") {
10116
- const scenariosDir = join19(runDir, "scenarios");
10141
+ const scenariosDir = join20(runDir, "scenarios");
10117
10142
  if (!existsSync21(scenariosDir)) {
10118
10143
  throw new Error(`No scenarios directory found in run ${opts.run}`);
10119
10144
  }
10120
10145
  const files = readdirSync9(scenariosDir).filter((f) => f.endsWith(".md")).sort();
10121
- sourceContent = files.map((f) => readFileSync18(join19(scenariosDir, f), "utf-8")).join("\n\n---\n\n");
10146
+ sourceContent = files.map((f) => readFileSync19(join20(scenariosDir, f), "utf-8")).join("\n\n---\n\n");
10122
10147
  } else {
10123
10148
  const artifactFile = artifact === "nlspec" ? "nlspec.md" : "plan.md";
10124
- const artifactPath = join19(runDir, artifactFile);
10149
+ const artifactPath = join20(runDir, artifactFile);
10125
10150
  if (!existsSync21(artifactPath)) {
10126
10151
  throw new Error(`${artifactFile} not found in run ${opts.run}`);
10127
10152
  }
10128
- sourceContent = readFileSync18(artifactPath, "utf-8");
10153
+ sourceContent = readFileSync19(artifactPath, "utf-8");
10129
10154
  }
10130
10155
  const result = runExportAdapter(opts.adapter, sourceContent);
10131
10156
  if (opts.output) {
@@ -10353,15 +10378,15 @@ init_presets();
10353
10378
  init_display();
10354
10379
  import { Command as Command8 } from "commander";
10355
10380
  import { resolve as resolve11 } from "path";
10356
- import { readFileSync as readFileSync19, existsSync as existsSync22 } from "fs";
10357
- import { join as join20 } from "path";
10381
+ import { readFileSync as readFileSync20, existsSync as existsSync22 } from "fs";
10382
+ import { join as join21 } from "path";
10358
10383
  function listPluginsAction(factoryDir) {
10359
- const lockPath = join20(factoryDir, ".beastmode", "extensions.lock");
10384
+ const lockPath = join21(factoryDir, ".beastmode", "extensions.lock");
10360
10385
  if (!existsSync22(lockPath)) {
10361
10386
  console.log(" No plugins installed (extensions.lock not found).");
10362
10387
  return;
10363
10388
  }
10364
- const lock = JSON.parse(readFileSync19(lockPath, "utf-8"));
10389
+ const lock = JSON.parse(readFileSync20(lockPath, "utf-8"));
10365
10390
  const plugins = lock.plugins || {};
10366
10391
  const names = Object.keys(plugins);
10367
10392
  if (names.length === 0) {
@@ -10460,8 +10485,8 @@ var listCommand = new Command8("list").description("List installed extensions an
10460
10485
  init_import_adapter();
10461
10486
  init_display();
10462
10487
  import { Command as Command9 } from "commander";
10463
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16, existsSync as existsSync23 } from "fs";
10464
- import { resolve as resolve12, join as join21 } from "path";
10488
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16, existsSync as existsSync23 } from "fs";
10489
+ import { resolve as resolve12, join as join22 } from "path";
10465
10490
  var VALID_ARTIFACTS = ["nlspec", "plan", "scenarios"];
10466
10491
  function createArtifactCommand(artifact) {
10467
10492
  return new Command9(artifact).description(`Import external document as ${artifact}`).requiredOption("--from <path>", "Path to source file").requiredOption("--adapter <id>", "Adapter ID (e.g., generic:prd, bmad:brainstorm)").option("--output <path>", "Output path (default: stdout)").action((opts) => {
@@ -10470,7 +10495,7 @@ function createArtifactCommand(artifact) {
10470
10495
  if (!existsSync23(sourcePath)) {
10471
10496
  throw new Error(`Source file not found: ${sourcePath}`);
10472
10497
  }
10473
- const sourceContent = readFileSync20(sourcePath, "utf-8");
10498
+ const sourceContent = readFileSync21(sourcePath, "utf-8");
10474
10499
  const result = runImportAdapter(opts.adapter, sourceContent);
10475
10500
  if (opts.output) {
10476
10501
  const outputPath = resolve12(opts.output);
@@ -10478,7 +10503,7 @@ function createArtifactCommand(artifact) {
10478
10503
  const scenarios = JSON.parse(result);
10479
10504
  mkdirSync16(outputPath, { recursive: true });
10480
10505
  for (const scenario of scenarios) {
10481
- const filePath = join21(outputPath, `${scenario.name}.md`);
10506
+ const filePath = join22(outputPath, `${scenario.name}.md`);
10482
10507
  writeFileSync18(filePath, scenario.content, "utf-8");
10483
10508
  success(` ${scenario.name}.md`);
10484
10509
  }
@@ -10508,52 +10533,52 @@ init_engine();
10508
10533
  init_schemas();
10509
10534
  init_display();
10510
10535
  import { Command as Command10 } from "commander";
10511
- import { existsSync as existsSync24, readFileSync as readFileSync21, readdirSync as readdirSync10 } from "fs";
10536
+ import { existsSync as existsSync24, readFileSync as readFileSync22, readdirSync as readdirSync10 } from "fs";
10512
10537
  import { execSync as execSync6 } from "child_process";
10513
- import { join as join22, resolve as resolve13 } from "path";
10538
+ import { join as join23, resolve as resolve13 } from "path";
10514
10539
  function statusAction(factoryDir, opts) {
10515
- const bmDir = join22(factoryDir, ".beastmode");
10540
+ const bmDir = join23(factoryDir, ".beastmode");
10516
10541
  if (!existsSync24(bmDir)) {
10517
10542
  throw new Error(`No factory found at ${factoryDir}`);
10518
10543
  }
10519
- const factoryJsonPath = join22(bmDir, "factory.json");
10520
- const rawIdentity = JSON.parse(readFileSync21(factoryJsonPath, "utf-8"));
10544
+ const factoryJsonPath = join23(bmDir, "factory.json");
10545
+ const rawIdentity = JSON.parse(readFileSync22(factoryJsonPath, "utf-8"));
10521
10546
  const factoryIdentity = FactoryIdentitySchema.parse(rawIdentity);
10522
- const projectsDir = join22(bmDir, "projects");
10547
+ const projectsDir = join23(bmDir, "projects");
10523
10548
  const projectCount = listProjectRecords(projectsDir).length;
10524
- const pluginsDir = join22(bmDir, "plugins");
10549
+ const pluginsDir = join23(bmDir, "plugins");
10525
10550
  const pluginNames = existsSync24(pluginsDir) ? readdirSync10(pluginsDir) : [];
10526
- const mcpPath = join22(bmDir, "mcp-servers.json");
10551
+ const mcpPath = join23(bmDir, "mcp-servers.json");
10527
10552
  let mcpServers = {};
10528
10553
  if (existsSync24(mcpPath)) {
10529
10554
  try {
10530
- const raw = JSON.parse(readFileSync21(mcpPath, "utf-8"));
10555
+ const raw = JSON.parse(readFileSync22(mcpPath, "utf-8"));
10531
10556
  mcpServers = raw.servers || {};
10532
10557
  } catch {
10533
10558
  }
10534
10559
  }
10535
- const hooksPath = join22(bmDir, "hooks.json");
10560
+ const hooksPath = join23(bmDir, "hooks.json");
10536
10561
  let hooks = {};
10537
10562
  if (existsSync24(hooksPath)) {
10538
10563
  try {
10539
- const raw = JSON.parse(readFileSync21(hooksPath, "utf-8"));
10564
+ const raw = JSON.parse(readFileSync22(hooksPath, "utf-8"));
10540
10565
  hooks = raw.hooks || {};
10541
10566
  } catch {
10542
10567
  }
10543
10568
  }
10544
- const skillsDir = join22(bmDir, "skills");
10569
+ const skillsDir = join23(bmDir, "skills");
10545
10570
  const skillCount = existsSync24(skillsDir) ? readdirSync10(skillsDir).length : 0;
10546
- const runsDir = join22(factoryDir, "runs");
10571
+ const runsDir = join23(factoryDir, "runs");
10547
10572
  let runDirs = [];
10548
10573
  if (existsSync24(runsDir)) {
10549
10574
  runDirs = readdirSync10(runsDir).filter((d) => d.startsWith("run-")).sort();
10550
10575
  }
10551
- const pidPath = join22(bmDir, "daemon.pid");
10576
+ const pidPath = join23(bmDir, "daemon.pid");
10552
10577
  let daemonPid = null;
10553
10578
  let pidAlive = false;
10554
10579
  if (existsSync24(pidPath)) {
10555
10580
  try {
10556
- daemonPid = parseInt(readFileSync21(pidPath, "utf-8").trim(), 10);
10581
+ daemonPid = parseInt(readFileSync22(pidPath, "utf-8").trim(), 10);
10557
10582
  process.kill(daemonPid, 0);
10558
10583
  pidAlive = true;
10559
10584
  } catch {
@@ -10627,18 +10652,18 @@ var statusCommand = new Command10("status").description("Show factory status ove
10627
10652
  init_config_manager();
10628
10653
  init_display();
10629
10654
  import { Command as Command11 } from "commander";
10630
- import { existsSync as existsSync25, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
10631
- import { join as join23, resolve as resolve14 } from "path";
10655
+ import { existsSync as existsSync25, readFileSync as readFileSync23, writeFileSync as writeFileSync19 } from "fs";
10656
+ import { join as join24, resolve as resolve14 } from "path";
10632
10657
  import { execSync as execSync7 } from "child_process";
10633
10658
  function readConfig2(factoryDir) {
10634
- const configPath = join23(factoryDir, ".beastmode", "config.json");
10659
+ const configPath = join24(factoryDir, ".beastmode", "config.json");
10635
10660
  if (!existsSync25(configPath)) {
10636
10661
  throw new Error("No config.json found. Run beastmode init first.");
10637
10662
  }
10638
- return JSON.parse(readFileSync22(configPath, "utf-8"));
10663
+ return JSON.parse(readFileSync23(configPath, "utf-8"));
10639
10664
  }
10640
10665
  function writeConfig2(factoryDir, config) {
10641
- const configPath = join23(factoryDir, ".beastmode", "config.json");
10666
+ const configPath = join24(factoryDir, ".beastmode", "config.json");
10642
10667
  writeFileSync19(configPath, JSON.stringify(config, null, 2) + "\n");
10643
10668
  }
10644
10669
  function configGetAction(factoryDir, key) {
@@ -10682,7 +10707,7 @@ var configSetCmd = new Command11("set").description("Set a config value by dot-n
10682
10707
  });
10683
10708
  var configEditCmd = new Command11("edit").description("Open config.json in $EDITOR").action(() => {
10684
10709
  const factoryDir = resolve14(".");
10685
- const configPath = join23(factoryDir, ".beastmode", "config.json");
10710
+ const configPath = join24(factoryDir, ".beastmode", "config.json");
10686
10711
  if (!existsSync25(configPath)) {
10687
10712
  error("No config.json found. Run beastmode init first.");
10688
10713
  process.exit(1);
@@ -10719,8 +10744,8 @@ init_version();
10719
10744
  init_plugin_resolver();
10720
10745
  init_display();
10721
10746
  import { Command as Command12 } from "commander";
10722
- import { existsSync as existsSync26, readFileSync as readFileSync23, readdirSync as readdirSync11 } from "fs";
10723
- import { join as join24, resolve as resolve15 } from "path";
10747
+ import { existsSync as existsSync26, readFileSync as readFileSync24, readdirSync as readdirSync11 } from "fs";
10748
+ import { join as join25, resolve as resolve15 } from "path";
10724
10749
  import { execSync as execSync8, spawnSync as spawnSync3 } from "child_process";
10725
10750
  import { homedir as homedir3, platform as platform2 } from "os";
10726
10751
  import * as http3 from "http";
@@ -10761,7 +10786,7 @@ async function checkClaudeAuth(key) {
10761
10786
  });
10762
10787
  }
10763
10788
  if (claudeInstalled && platform2() === "darwin" && !key) {
10764
- const credsPath = join24(homedir3(), ".claude", ".credentials.json");
10789
+ const credsPath = join25(homedir3(), ".claude", ".credentials.json");
10765
10790
  if (!existsSync26(credsPath)) {
10766
10791
  results.push({
10767
10792
  label: "Claude creds (Docker)",
@@ -10864,16 +10889,16 @@ async function checkProjectGithubToken(env, factoryDir) {
10864
10889
  }
10865
10890
  let ownerRepo = null;
10866
10891
  if (factoryDir) {
10867
- const envPath = join24(factoryDir, ".env");
10892
+ const envPath = join25(factoryDir, ".env");
10868
10893
  if (existsSync26(envPath)) {
10869
10894
  try {
10870
- const content = readFileSync23(envPath, "utf-8");
10895
+ const content = readFileSync24(envPath, "utf-8");
10871
10896
  const dirLine = content.split("\n").find(
10872
10897
  (l) => l.trim().startsWith("PROJECT_DIR=") && !l.trim().startsWith("#")
10873
10898
  );
10874
10899
  if (dirLine) {
10875
10900
  const projectDir = dirLine.split("=").slice(1).join("=").trim();
10876
- if (projectDir && existsSync26(join24(projectDir, ".git"))) {
10901
+ if (projectDir && existsSync26(join25(projectDir, ".git"))) {
10877
10902
  const remote = tryExec(
10878
10903
  `git -C "${projectDir}" remote get-url origin 2>/dev/null`
10879
10904
  );
@@ -11089,7 +11114,7 @@ function checkProjectDirEnv(factoryDir) {
11089
11114
  detail: "no factory in scope"
11090
11115
  };
11091
11116
  }
11092
- const envPath = join24(factoryDir, ".env");
11117
+ const envPath = join25(factoryDir, ".env");
11093
11118
  if (!existsSync26(envPath)) {
11094
11119
  return {
11095
11120
  label: "PROJECT_DIR (.env)",
@@ -11100,7 +11125,7 @@ function checkProjectDirEnv(factoryDir) {
11100
11125
  }
11101
11126
  let content;
11102
11127
  try {
11103
- content = readFileSync23(envPath, "utf-8");
11128
+ content = readFileSync24(envPath, "utf-8");
11104
11129
  } catch {
11105
11130
  return {
11106
11131
  label: "PROJECT_DIR (.env)",
@@ -11137,7 +11162,7 @@ function checkProjectDirEnv(factoryDir) {
11137
11162
  fix: `Clone your project to ${value} or re-run 'beastmode init --project <path>'`
11138
11163
  };
11139
11164
  }
11140
- if (!existsSync26(join24(value, ".git"))) {
11165
+ if (!existsSync26(join25(value, ".git"))) {
11141
11166
  return {
11142
11167
  label: "PROJECT_DIR (.env)",
11143
11168
  status: "fail",
@@ -11168,7 +11193,7 @@ async function checkFactoryContainers(factoryDir) {
11168
11193
  detail: "no factory in scope"
11169
11194
  };
11170
11195
  }
11171
- const composePath = join24(factoryDir, "docker-compose.yml");
11196
+ const composePath = join25(factoryDir, "docker-compose.yml");
11172
11197
  if (!existsSync26(composePath)) {
11173
11198
  return {
11174
11199
  label: "Factory containers",
@@ -11451,8 +11476,8 @@ function checkProjectDirectory(factoryDir) {
11451
11476
  fix: "Run beastmode init to create a factory"
11452
11477
  };
11453
11478
  }
11454
- const bmDir = join24(factoryDir, ".beastmode");
11455
- const projectsDir = join24(bmDir, "projects");
11479
+ const bmDir = join25(factoryDir, ".beastmode");
11480
+ const projectsDir = join25(bmDir, "projects");
11456
11481
  if (!existsSync26(projectsDir)) {
11457
11482
  return {
11458
11483
  label: "Project directory",
@@ -11480,7 +11505,7 @@ function checkProjectDirectory(factoryDir) {
11480
11505
  anyFail = true;
11481
11506
  continue;
11482
11507
  }
11483
- const isGit = existsSync26(join24(projPath, ".git"));
11508
+ const isGit = existsSync26(join25(projPath, ".git"));
11484
11509
  const manifests = [
11485
11510
  "package.json",
11486
11511
  "Cargo.toml",
@@ -11491,7 +11516,7 @@ function checkProjectDirectory(factoryDir) {
11491
11516
  "build.gradle",
11492
11517
  "build.gradle.kts"
11493
11518
  ];
11494
- const manifest = manifests.find((m) => existsSync26(join24(projPath, m)));
11519
+ const manifest = manifests.find((m) => existsSync26(join25(projPath, m)));
11495
11520
  const framework = proj.stack?.detected || manifest?.replace(".json", "") || "unknown";
11496
11521
  results.push(
11497
11522
  `${projName}: ${projPath} (${framework})${isGit ? "" : " [no .git]"}`
@@ -11541,7 +11566,7 @@ function checkStack(factoryDir) {
11541
11566
  detail: "no factory \u2014 run beastmode init"
11542
11567
  };
11543
11568
  }
11544
- const projectsDir = join24(factoryDir, ".beastmode", "projects");
11569
+ const projectsDir = join25(factoryDir, ".beastmode", "projects");
11545
11570
  if (!existsSync26(projectsDir)) {
11546
11571
  return { label: "Stack", status: "warn", detail: "no projects configured" };
11547
11572
  }
@@ -11593,12 +11618,12 @@ function checkBoardPassword(env, factoryDir) {
11593
11618
  return { label: "Board password", status: "pass", detail: "set" };
11594
11619
  }
11595
11620
  if (factoryDir) {
11596
- const dotEnv = join24(factoryDir, ".env");
11597
- const secretsEnv = join24(factoryDir, ".beastmode", "secrets.env.local");
11621
+ const dotEnv = join25(factoryDir, ".env");
11622
+ const secretsEnv = join25(factoryDir, ".beastmode", "secrets.env.local");
11598
11623
  for (const filePath of [dotEnv, secretsEnv]) {
11599
11624
  if (existsSync26(filePath)) {
11600
11625
  try {
11601
- const content = readFileSync23(filePath, "utf-8");
11626
+ const content = readFileSync24(filePath, "utf-8");
11602
11627
  const lines = content.split("\n");
11603
11628
  const line = lines.find((l) => l.startsWith("BEASTMODE_UI_PASSWORD="));
11604
11629
  if (line) {
@@ -11620,12 +11645,12 @@ function checkBoardPassword(env, factoryDir) {
11620
11645
  };
11621
11646
  }
11622
11647
  function doctorAction(factoryDir, env) {
11623
- const bmDir = join24(factoryDir, ".beastmode");
11648
+ const bmDir = join25(factoryDir, ".beastmode");
11624
11649
  const factoryDirExists = existsSync26(bmDir);
11625
11650
  let factoryIdentity = null;
11626
11651
  if (factoryDirExists) {
11627
11652
  try {
11628
- const raw = JSON.parse(readFileSync23(join24(bmDir, "factory.json"), "utf-8"));
11653
+ const raw = JSON.parse(readFileSync24(join25(bmDir, "factory.json"), "utf-8"));
11629
11654
  factoryIdentity = FactoryIdentitySchema.parse(raw);
11630
11655
  } catch {
11631
11656
  }
@@ -11633,10 +11658,10 @@ function doctorAction(factoryDir, env) {
11633
11658
  let config = null;
11634
11659
  let configParseError = null;
11635
11660
  if (factoryDirExists) {
11636
- const configPath = join24(bmDir, "config.json");
11661
+ const configPath = join25(bmDir, "config.json");
11637
11662
  if (existsSync26(configPath)) {
11638
11663
  try {
11639
- const raw = JSON.parse(readFileSync23(configPath, "utf-8"));
11664
+ const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
11640
11665
  const result = FactoryConfigSchema.safeParse(raw);
11641
11666
  if (result.success) {
11642
11667
  config = raw;
@@ -11652,7 +11677,7 @@ function doctorAction(factoryDir, env) {
11652
11677
  }
11653
11678
  const projectPaths = [];
11654
11679
  if (factoryDirExists) {
11655
- const projectsDir = join24(bmDir, "projects");
11680
+ const projectsDir = join25(bmDir, "projects");
11656
11681
  for (const proj of listProjectRecords(projectsDir)) {
11657
11682
  if (proj.path) {
11658
11683
  projectPaths.push({
@@ -11665,13 +11690,13 @@ function doctorAction(factoryDir, env) {
11665
11690
  }
11666
11691
  const installedPlugins = [];
11667
11692
  if (factoryDirExists) {
11668
- const pluginsDir = join24(bmDir, "plugins");
11693
+ const pluginsDir = join25(bmDir, "plugins");
11669
11694
  if (existsSync26(pluginsDir)) {
11670
11695
  for (const pluginName of readdirSync11(pluginsDir)) {
11671
- const manifestPath = join24(pluginsDir, pluginName, "manifest.json");
11696
+ const manifestPath = join25(pluginsDir, pluginName, "manifest.json");
11672
11697
  if (existsSync26(manifestPath)) {
11673
11698
  try {
11674
- const manifest = JSON.parse(readFileSync23(manifestPath, "utf-8"));
11699
+ const manifest = JSON.parse(readFileSync24(manifestPath, "utf-8"));
11675
11700
  installedPlugins.push({
11676
11701
  name: pluginName,
11677
11702
  engine_version: manifest.engine_version || "*"
@@ -11699,7 +11724,7 @@ var SYSTEMD_CREDS_TIMER = "beastmode-claude-creds.timer";
11699
11724
  var LAUNCH_AGENT_LABEL2 = "com.develeap.beastmode.claude-creds";
11700
11725
  function checkCredentialSyncAgent() {
11701
11726
  const results = [];
11702
- const credsPath = join24(homedir3(), ".claude", ".credentials.json");
11727
+ const credsPath = join25(homedir3(), ".claude", ".credentials.json");
11703
11728
  if (!existsSync26(credsPath)) {
11704
11729
  results.push({
11705
11730
  label: "Claude credentials file",
@@ -11715,7 +11740,7 @@ function checkCredentialSyncAgent() {
11715
11740
  detail: credsPath
11716
11741
  });
11717
11742
  try {
11718
- const creds = JSON.parse(readFileSync23(credsPath, "utf-8"));
11743
+ const creds = JSON.parse(readFileSync24(credsPath, "utf-8"));
11719
11744
  const expiresAt = creds?.claudeAiOauth?.expiresAt;
11720
11745
  if (typeof expiresAt === "number") {
11721
11746
  const hoursLeft = Math.round((expiresAt - Date.now()) / 36e5);
@@ -12031,7 +12056,7 @@ var doctorCommand = new Command12("doctor").description("Health check \u2014 val
12031
12056
  console.log();
12032
12057
  const env = process.env;
12033
12058
  const factoryDir = findFactoryDir() ?? resolve15(".");
12034
- const hasFactory = existsSync26(join24(factoryDir, ".beastmode"));
12059
+ const hasFactory = existsSync26(join25(factoryDir, ".beastmode"));
12035
12060
  const checks = [];
12036
12061
  checks.push(...await checkClaudeAuth(env.ANTHROPIC_API_KEY));
12037
12062
  checks.push(...checkCredentialSyncAgent());
@@ -12107,12 +12132,12 @@ init_schemas();
12107
12132
  init_version();
12108
12133
  init_display();
12109
12134
  import { Command as Command13 } from "commander";
12110
- import { existsSync as existsSync28, readFileSync as readFileSync25, writeFileSync as writeFileSync21 } from "fs";
12111
- import { join as join26, resolve as resolve16 } from "path";
12135
+ import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync21 } from "fs";
12136
+ import { join as join27, resolve as resolve16 } from "path";
12112
12137
 
12113
12138
  // src/cli/utils/regenerate.ts
12114
- import { existsSync as existsSync27, readFileSync as readFileSync24, writeFileSync as writeFileSync20, copyFileSync } from "fs";
12115
- import { join as join25 } from "path";
12139
+ import { existsSync as existsSync27, readFileSync as readFileSync25, writeFileSync as writeFileSync20, copyFileSync } from "fs";
12140
+ import { join as join26 } from "path";
12116
12141
  var RECOGNIZED_KEYS = /* @__PURE__ */ new Set([
12117
12142
  "PROJECT_DIR",
12118
12143
  "PROJECT_GITHUB_TOKEN",
@@ -12226,8 +12251,8 @@ function buildNewEnv(values) {
12226
12251
  return lines.join("\n");
12227
12252
  }
12228
12253
  function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
12229
- const envPath = join25(factoryDir, ".env");
12230
- const composePath = join25(factoryDir, "docker-compose.yml");
12254
+ const envPath = join26(factoryDir, ".env");
12255
+ const composePath = join26(factoryDir, "docker-compose.yml");
12231
12256
  if (!existsSync27(envPath)) {
12232
12257
  throw new Error(
12233
12258
  `.env not found at ${envPath}. This does not look like a beastmode factory \u2014 run 'beastmode init' first.`
@@ -12238,7 +12263,7 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
12238
12263
  `docker-compose.yml not found at ${composePath}. This does not look like a beastmode factory \u2014 run 'beastmode init' first.`
12239
12264
  );
12240
12265
  }
12241
- const existingEnv = readFileSync24(envPath, "utf-8");
12266
+ const existingEnv = readFileSync25(envPath, "utf-8");
12242
12267
  const values = parseExistingEnv(existingEnv);
12243
12268
  const missing = [];
12244
12269
  if (!values.projectDir) missing.push("PROJECT_DIR");
@@ -12256,7 +12281,7 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
12256
12281
  }
12257
12282
  const newEnv = buildNewEnv(values);
12258
12283
  const newCompose = generateComposeYaml("latest");
12259
- const existingCompose = readFileSync24(composePath, "utf-8");
12284
+ const existingCompose = readFileSync25(composePath, "utf-8");
12260
12285
  const envChanged = newEnv !== existingEnv;
12261
12286
  const composeChanged = newCompose !== existingCompose;
12262
12287
  if (!envChanged && !composeChanged) {
@@ -12292,18 +12317,18 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
12292
12317
 
12293
12318
  // src/cli/commands/upgrade.ts
12294
12319
  function readIdentity(factoryDir) {
12295
- const path = join26(factoryDir, ".beastmode", "factory.json");
12320
+ const path = join27(factoryDir, ".beastmode", "factory.json");
12296
12321
  if (!existsSync28(path)) {
12297
12322
  throw new Error("No factory.json found. Run beastmode init first.");
12298
12323
  }
12299
- return FactoryIdentitySchema.parse(JSON.parse(readFileSync25(path, "utf-8")));
12324
+ return FactoryIdentitySchema.parse(JSON.parse(readFileSync26(path, "utf-8")));
12300
12325
  }
12301
12326
  function readConfig3(factoryDir) {
12302
- const path = join26(factoryDir, ".beastmode", "config.json");
12327
+ const path = join27(factoryDir, ".beastmode", "config.json");
12303
12328
  if (!existsSync28(path)) {
12304
12329
  return {};
12305
12330
  }
12306
- return JSON.parse(readFileSync25(path, "utf-8"));
12331
+ return JSON.parse(readFileSync26(path, "utf-8"));
12307
12332
  }
12308
12333
  function upgradeCheckAction(factoryDir) {
12309
12334
  const identity = readIdentity(factoryDir);
@@ -12316,11 +12341,11 @@ function upgradeAction(factoryDir, migrateOnly = false) {
12316
12341
  const result = performUpgrade(identity, config, targetVersion, SCHEMA_VERSION);
12317
12342
  if (result.changes.length > 0) {
12318
12343
  writeFileSync21(
12319
- join26(factoryDir, ".beastmode", "factory.json"),
12344
+ join27(factoryDir, ".beastmode", "factory.json"),
12320
12345
  JSON.stringify(result.updatedIdentity, null, 2) + "\n"
12321
12346
  );
12322
12347
  writeFileSync21(
12323
- join26(factoryDir, ".beastmode", "config.json"),
12348
+ join27(factoryDir, ".beastmode", "config.json"),
12324
12349
  JSON.stringify(result.updatedConfig, null, 2) + "\n"
12325
12350
  );
12326
12351
  }
@@ -12410,8 +12435,8 @@ init_board();
12410
12435
  init_display();
12411
12436
  init_migrator();
12412
12437
  import { Command as Command14 } from "commander";
12413
- import { resolve as resolve17, join as join27 } from "path";
12414
- import { existsSync as existsSync29, readFileSync as readFileSync26, mkdirSync as mkdirSync17, writeFileSync as writeFileSync22 } from "fs";
12438
+ import { resolve as resolve17, join as join28 } from "path";
12439
+ import { existsSync as existsSync29, readFileSync as readFileSync27, mkdirSync as mkdirSync17, writeFileSync as writeFileSync22 } from "fs";
12415
12440
  var migrateCommand = new Command14("migrate").description("Migrate a daemon config into a .beastmode/ factory").option("--config <path>", "Path to beastmode.daemon.json").option("--dry-run", "Show what would be created without writing files").action(async (opts) => {
12416
12441
  try {
12417
12442
  await runMigrate(opts);
@@ -12431,25 +12456,25 @@ async function runMigrate(opts) {
12431
12456
  }
12432
12457
  header("BeastMode Migrate");
12433
12458
  info(`Reading daemon config from: ${configPath}`);
12434
- const configContent = readFileSync26(configPath, "utf-8");
12459
+ const configContent = readFileSync27(configPath, "utf-8");
12435
12460
  const daemonConfig = parseDaemonConfig(configContent);
12436
- const runsDir = join27(cwd, "runs");
12461
+ const runsDir = join28(cwd, "runs");
12437
12462
  let runDirs = [];
12438
12463
  const checkpoints = /* @__PURE__ */ new Map();
12439
12464
  if (existsSync29(runsDir)) {
12440
12465
  const { readdirSync: readdirSync13 } = await import("fs");
12441
12466
  runDirs = readdirSync13(runsDir).filter((d) => {
12442
12467
  try {
12443
- return readdirSync13(join27(runsDir, d)).length > 0;
12468
+ return readdirSync13(join28(runsDir, d)).length > 0;
12444
12469
  } catch {
12445
12470
  return false;
12446
12471
  }
12447
12472
  });
12448
12473
  for (const dir of runDirs) {
12449
- const cpPath = join27(runsDir, dir, "checkpoint.json");
12474
+ const cpPath = join28(runsDir, dir, "checkpoint.json");
12450
12475
  if (existsSync29(cpPath)) {
12451
12476
  try {
12452
- const cp = JSON.parse(readFileSync26(cpPath, "utf-8"));
12477
+ const cp = JSON.parse(readFileSync27(cpPath, "utf-8"));
12453
12478
  checkpoints.set(dir, cp);
12454
12479
  } catch {
12455
12480
  }
@@ -12506,28 +12531,28 @@ async function runMigrate(opts) {
12506
12531
  warn("Dry run \u2014 no files written.");
12507
12532
  return;
12508
12533
  }
12509
- const bmDir = join27(cwd, ".beastmode");
12534
+ const bmDir = join28(cwd, ".beastmode");
12510
12535
  if (existsSync29(bmDir)) {
12511
12536
  throw new Error(
12512
12537
  "A .beastmode/ directory already exists. Remove it first to re-migrate."
12513
12538
  );
12514
12539
  }
12515
12540
  for (const file of files) {
12516
- const fullPath = join27(cwd, file.path);
12541
+ const fullPath = join28(cwd, file.path);
12517
12542
  const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
12518
12543
  mkdirSync17(dir, { recursive: true });
12519
12544
  writeFileSync22(fullPath, file.content, "utf-8");
12520
12545
  }
12521
- const runsSymlinkTarget = join27(cwd, "runs");
12522
- const bmRunsPath = join27(cwd, "runs");
12546
+ const runsSymlinkTarget = join28(cwd, "runs");
12547
+ const bmRunsPath = join28(cwd, "runs");
12523
12548
  if (existsSync29(runsSymlinkTarget)) {
12524
12549
  info("Existing runs/ directory preserved in-place.");
12525
12550
  }
12526
- const boardPath = join27(bmDir, "board.json");
12551
+ const boardPath = join28(bmDir, "board.json");
12527
12552
  if (!existsSync29(boardPath)) {
12528
12553
  writeFileSync22(boardPath, JSON.stringify({ items: [] }, null, 2), "utf-8");
12529
12554
  }
12530
- mkdirSync17(join27(bmDir, ".cache"), { recursive: true });
12555
+ mkdirSync17(join28(bmDir, ".cache"), { recursive: true });
12531
12556
  console.log();
12532
12557
  success("Migration complete!");
12533
12558
  info(`Factory created at: ${bmDir}`);
@@ -12550,8 +12575,8 @@ init_bridge();
12550
12575
  init_schemas();
12551
12576
  init_engine();
12552
12577
  import { Command as Command15 } from "commander";
12553
- import { join as join28 } from "path";
12554
- import { existsSync as existsSync30, readFileSync as readFileSync27, writeFileSync as writeFileSync23, mkdirSync as mkdirSync18 } from "fs";
12578
+ import { join as join29 } from "path";
12579
+ import { existsSync as existsSync30, readFileSync as readFileSync28, writeFileSync as writeFileSync23, mkdirSync as mkdirSync18 } from "fs";
12555
12580
  import { randomUUID as randomUUID3 } from "crypto";
12556
12581
  var runCommand = new Command15("run").description("Run a single pipeline task").argument("[project]", "Project name (defaults to first project)").option("--task <description>", "Task description").action(async (project, opts) => {
12557
12582
  try {
@@ -12571,17 +12596,17 @@ async function runPipeline(projectName, opts) {
12571
12596
  "No BeastMode factory found. Run 'beastmode init' first."
12572
12597
  );
12573
12598
  }
12574
- const bmDir = join28(factoryDir, ".beastmode");
12599
+ const bmDir = join29(factoryDir, ".beastmode");
12575
12600
  header("BeastMode Run");
12576
- const configPath = join28(bmDir, "config.json");
12601
+ const configPath = join29(bmDir, "config.json");
12577
12602
  if (!existsSync30(configPath)) {
12578
12603
  throw new Error("Factory config not found. Run 'beastmode init' first.");
12579
12604
  }
12580
12605
  const factoryConfig = FactoryConfigSchema.parse(
12581
- JSON.parse(readFileSync27(configPath, "utf-8"))
12606
+ JSON.parse(readFileSync28(configPath, "utf-8"))
12582
12607
  );
12583
12608
  let projectConfig = null;
12584
- const projectsDir = join28(bmDir, "projects");
12609
+ const projectsDir = join29(bmDir, "projects");
12585
12610
  if (projectName) {
12586
12611
  const record = readProjectRecord(projectsDir, projectName);
12587
12612
  if (!record) {
@@ -12595,11 +12620,11 @@ async function runPipeline(projectName, opts) {
12595
12620
  info(`Using project: ${projectConfig.name}`);
12596
12621
  }
12597
12622
  }
12598
- const boardPath = join28(bmDir, "board.json");
12623
+ const boardPath = join29(bmDir, "board.json");
12599
12624
  let boardItems = [];
12600
12625
  if (existsSync30(boardPath)) {
12601
12626
  try {
12602
- const raw = JSON.parse(readFileSync27(boardPath, "utf-8"));
12627
+ const raw = JSON.parse(readFileSync28(boardPath, "utf-8"));
12603
12628
  boardItems = Array.isArray(raw.items) ? raw.items : [];
12604
12629
  } catch {
12605
12630
  boardItems = [];
@@ -12618,9 +12643,9 @@ async function runPipeline(projectName, opts) {
12618
12643
  boardItems.push(task);
12619
12644
  writeFileSync23(boardPath, JSON.stringify({ items: boardItems }, null, 2), "utf-8");
12620
12645
  info(`Created task: ${task.title} (${taskId})`);
12621
- const cacheDir = join28(bmDir, ".cache");
12646
+ const cacheDir = join29(bmDir, ".cache");
12622
12647
  mkdirSync18(cacheDir, { recursive: true });
12623
- const daemonConfigPath = join28(cacheDir, "daemon.json");
12648
+ const daemonConfigPath = join29(cacheDir, "daemon.json");
12624
12649
  const daemonConfig = generateDaemonConfig(factoryConfig, projectConfig, factoryDir);
12625
12650
  writeFileSync23(daemonConfigPath, JSON.stringify(daemonConfig, null, 2), "utf-8");
12626
12651
  info(`Generated daemon config at: ${daemonConfigPath}`);
@@ -12673,7 +12698,7 @@ async function runPipeline(projectName, opts) {
12673
12698
  const startTime = Date.now();
12674
12699
  const pollInterval = setInterval(() => {
12675
12700
  try {
12676
- const board = JSON.parse(readFileSync27(boardPath, "utf-8"));
12701
+ const board = JSON.parse(readFileSync28(boardPath, "utf-8"));
12677
12702
  const items = Array.isArray(board.items) ? board.items : [];
12678
12703
  const taskItem = items.find((i) => i.id === taskId);
12679
12704
  if (taskItem) {
@@ -12720,8 +12745,8 @@ init_bridge();
12720
12745
  init_schemas();
12721
12746
  init_engine();
12722
12747
  import { Command as Command16 } from "commander";
12723
- import { join as join29 } from "path";
12724
- import { existsSync as existsSync31, readFileSync as readFileSync28, writeFileSync as writeFileSync24, mkdirSync as mkdirSync19 } from "fs";
12748
+ import { join as join30 } from "path";
12749
+ import { existsSync as existsSync31, readFileSync as readFileSync29, writeFileSync as writeFileSync24, mkdirSync as mkdirSync19 } from "fs";
12725
12750
  var daemonCommand = new Command16("daemon").description("Start the BeastMode daemon via bridge").option("--dry-run", "Generate config but don't start daemon").option(
12726
12751
  "--log-level <level>",
12727
12752
  "Log level (DEBUG, INFO, WARNING, ERROR)",
@@ -12741,25 +12766,25 @@ async function runDaemon(opts) {
12741
12766
  "No BeastMode factory found. Run 'beastmode init' first."
12742
12767
  );
12743
12768
  }
12744
- const bmDir = join29(factoryDir, ".beastmode");
12769
+ const bmDir = join30(factoryDir, ".beastmode");
12745
12770
  header("BeastMode Daemon");
12746
- const configPath = join29(bmDir, "config.json");
12771
+ const configPath = join30(bmDir, "config.json");
12747
12772
  if (!existsSync31(configPath)) {
12748
12773
  throw new Error("Factory config not found. Run 'beastmode init' first.");
12749
12774
  }
12750
12775
  const factoryConfig = FactoryConfigSchema.parse(
12751
- JSON.parse(readFileSync28(configPath, "utf-8"))
12776
+ JSON.parse(readFileSync29(configPath, "utf-8"))
12752
12777
  );
12753
12778
  let projectConfig = null;
12754
- const projectsDir = join29(bmDir, "projects");
12779
+ const projectsDir = join30(bmDir, "projects");
12755
12780
  const projects = listProjectRecords(projectsDir);
12756
12781
  if (projects.length > 0) {
12757
12782
  projectConfig = projects[0];
12758
12783
  info(`Using project: ${projectConfig.name}`);
12759
12784
  }
12760
- const cacheDir = join29(bmDir, ".cache");
12785
+ const cacheDir = join30(bmDir, ".cache");
12761
12786
  mkdirSync19(cacheDir, { recursive: true });
12762
- const daemonConfigPath = join29(cacheDir, "daemon.json");
12787
+ const daemonConfigPath = join30(cacheDir, "daemon.json");
12763
12788
  const daemonConfig = generateDaemonConfig(
12764
12789
  factoryConfig,
12765
12790
  projectConfig,
@@ -12804,7 +12829,7 @@ async function runDaemon(opts) {
12804
12829
  });
12805
12830
  info(`Starting daemon: ${pythonCmd} ${cmd.args.join(" ")}`);
12806
12831
  console.log();
12807
- const pidFile = join29(bmDir, "daemon.pid");
12832
+ const pidFile = join30(bmDir, "daemon.pid");
12808
12833
  const { spawn: spawn4 } = await import("child_process");
12809
12834
  const child = spawn4(pythonCmd, cmd.args, {
12810
12835
  stdio: "inherit",
@@ -12854,12 +12879,12 @@ var mcpCommand = new Command17("mcp").description("Start the BeastMode MCP serve
12854
12879
  // src/cli/commands/deploy.ts
12855
12880
  init_display();
12856
12881
  import { Command as Command18 } from "commander";
12857
- import { resolve as resolve19, join as join31 } from "path";
12858
- import { existsSync as existsSync33, writeFileSync as writeFileSync26, readFileSync as readFileSync30 } from "fs";
12882
+ import { resolve as resolve19, join as join32 } from "path";
12883
+ import { existsSync as existsSync33, writeFileSync as writeFileSync26, readFileSync as readFileSync31 } from "fs";
12859
12884
  import { execSync as execSync9 } from "child_process";
12860
12885
  import { randomBytes } from "crypto";
12861
- import { fileURLToPath as fileURLToPath3 } from "url";
12862
- import { dirname as dirname7 } from "path";
12886
+ import { fileURLToPath as fileURLToPath4 } from "url";
12887
+ import { dirname as dirname8 } from "path";
12863
12888
  var deployCommand = new Command18("deploy").description("Deploy BeastMode services as systemd services").option("--port <number>", "Port to serve the board UI on", "8420").option("--host <host>", "Host to bind the board UI to", "0.0.0.0").option("--dry-run", "Show what would be done without executing").option("--no-start", "Install services but don't start them").option("--stop", "Stop all 3 BeastMode services").option("--status", "Show status of all 3 BeastMode services").option("--cloud <provider>", "Deploy to cloud (aws)").option("--upgrade", "Update an existing cloud deployment").option("--destroy", "Destroy the cloud deployment (delete CloudFormation stack)").action(async (opts) => {
12864
12889
  try {
12865
12890
  await runDeploy(opts);
@@ -12910,7 +12935,7 @@ async function runDeploy(opts) {
12910
12935
  process.exit(1);
12911
12936
  }
12912
12937
  const factoryDir = resolve19(".");
12913
- const bmDir = join31(factoryDir, ".beastmode");
12938
+ const bmDir = join32(factoryDir, ".beastmode");
12914
12939
  if (!existsSync33(bmDir)) {
12915
12940
  error(
12916
12941
  "No .beastmode directory found. Run 'beastmode init' or 'beastmode migrate' first."
@@ -12935,18 +12960,18 @@ async function runDeploy(opts) {
12935
12960
  "../../index.js"
12936
12961
  );
12937
12962
  }
12938
- const boardVenvPython = join31(factoryDir, "board", ".venv", "bin", "python");
12939
- const daemonVenvPython = join31(factoryDir, "daemon", ".venv", "bin", "python");
12963
+ const boardVenvPython = join32(factoryDir, "board", ".venv", "bin", "python");
12964
+ const daemonVenvPython = join32(factoryDir, "daemon", ".venv", "bin", "python");
12940
12965
  const boardPython = existsSync33(boardVenvPython) ? boardVenvPython : "python3";
12941
12966
  const daemonPython = existsSync33(daemonVenvPython) ? daemonVenvPython : "python3";
12942
12967
  const user = execSync9("whoami", { encoding: "utf-8" }).trim();
12943
12968
  const home = process.env.HOME || `/home/${user}`;
12944
12969
  const port = opts.port;
12945
12970
  const host = opts.host;
12946
- const dotEnv = join31(factoryDir, ".env");
12947
- const secretsEnv = join31(bmDir, "secrets.env.local");
12948
- const envContent = existsSync33(dotEnv) ? readFileSync30(dotEnv, "utf-8") : "";
12949
- const secretsContent = existsSync33(secretsEnv) ? readFileSync30(secretsEnv, "utf-8") : "";
12971
+ const dotEnv = join32(factoryDir, ".env");
12972
+ const secretsEnv = join32(bmDir, "secrets.env.local");
12973
+ const envContent = existsSync33(dotEnv) ? readFileSync31(dotEnv, "utf-8") : "";
12974
+ const secretsContent = existsSync33(secretsEnv) ? readFileSync31(secretsEnv, "utf-8") : "";
12950
12975
  const hasPassword = envContent.includes("BEASTMODE_UI_PASSWORD=") && !envContent.includes("BEASTMODE_UI_PASSWORD=\n") || secretsContent.includes("BEASTMODE_UI_PASSWORD=") && !secretsContent.includes("BEASTMODE_UI_PASSWORD=\n") || !!process.env.BEASTMODE_UI_PASSWORD;
12951
12976
  if (!hasPassword && opts.host === "0.0.0.0") {
12952
12977
  const generated = randomBytes(18).toString("base64url");
@@ -12955,7 +12980,7 @@ async function runDeploy(opts) {
12955
12980
  # Auto-generated board UI password (deploy)
12956
12981
  BEASTMODE_UI_PASSWORD=${generated}
12957
12982
  `;
12958
- writeFileSync26(target, (existsSync33(target) ? readFileSync30(target, "utf-8") : "") + append, "utf-8");
12983
+ writeFileSync26(target, (existsSync33(target) ? readFileSync31(target, "utf-8") : "") + append, "utf-8");
12959
12984
  info(`Board UI password auto-generated and saved to ${target}`);
12960
12985
  success(`Password: ${generated}`);
12961
12986
  info("Save this password \u2014 you'll need it to access the board UI.");
@@ -13171,10 +13196,10 @@ async function deployToAWS(opts) {
13171
13196
  error("AWS credentials not configured. Run: aws configure");
13172
13197
  process.exit(1);
13173
13198
  }
13174
- const __filename2 = fileURLToPath3(import.meta.url);
13175
- const __dirname2 = dirname7(__filename2);
13176
- const templatePath = join31(__dirname2, "..", "..", "infra", "cloudformation", "beastmode.yaml");
13177
- const cwdTemplate = join31(process.cwd(), "infra", "cloudformation", "beastmode.yaml");
13199
+ const __filename2 = fileURLToPath4(import.meta.url);
13200
+ const __dirname2 = dirname8(__filename2);
13201
+ const templatePath = join32(__dirname2, "..", "..", "infra", "cloudformation", "beastmode.yaml");
13202
+ const cwdTemplate = join32(process.cwd(), "infra", "cloudformation", "beastmode.yaml");
13178
13203
  const template = existsSync33(templatePath) ? templatePath : existsSync33(cwdTemplate) ? cwdTemplate : null;
13179
13204
  if (!template) {
13180
13205
  error("CloudFormation template not found. Expected at infra/cloudformation/beastmode.yaml");
@@ -13429,13 +13454,13 @@ var logsCommand = new Command21("logs").description("Stream BeastMode service lo
13429
13454
 
13430
13455
  // src/cli/commands/update.ts
13431
13456
  import { Command as Command22 } from "commander";
13432
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync27 } from "fs";
13457
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync27 } from "fs";
13433
13458
  init_display();
13434
13459
  async function runUpdate(opts) {
13435
13460
  const cwd = opts.cwd ?? process.cwd();
13436
13461
  const composePath = requireComposeFile(cwd);
13437
13462
  if (opts.tag) {
13438
- let content = readFileSync31(composePath, "utf-8");
13463
+ let content = readFileSync32(composePath, "utf-8");
13439
13464
  const tagPattern = new RegExp(
13440
13465
  `(${GHCR_IMAGE_PREFIX.replace(/[/]/g, "\\/")}\\/(?:board|daemon|ui)):([\\w.\\-]+)`,
13441
13466
  "g"
@@ -13468,7 +13493,7 @@ init_display();
13468
13493
  import { Command as Command23 } from "commander";
13469
13494
  import { spawn as spawn3 } from "child_process";
13470
13495
  import { existsSync as existsSync38 } from "fs";
13471
- import { basename as basename8, join as join37, resolve as resolve20 } from "path";
13496
+ import { basename as basename8, join as join38, resolve as resolve20 } from "path";
13472
13497
 
13473
13498
  // src/cli/runner-image-builder.ts
13474
13499
  import { execSync as execSync10 } from "child_process";
@@ -13476,14 +13501,14 @@ import { createHash } from "crypto";
13476
13501
  import {
13477
13502
  existsSync as existsSync35,
13478
13503
  mkdirSync as mkdirSync21,
13479
- readFileSync as readFileSync33,
13504
+ readFileSync as readFileSync34,
13480
13505
  writeFileSync as writeFileSync28
13481
13506
  } from "fs";
13482
- import { join as join33 } from "path";
13507
+ import { join as join34 } from "path";
13483
13508
 
13484
13509
  // src/cli/stack-detect.ts
13485
- import { existsSync as existsSync34, readFileSync as readFileSync32 } from "fs";
13486
- import { join as join32 } from "path";
13510
+ import { existsSync as existsSync34, readFileSync as readFileSync33 } from "fs";
13511
+ import { join as join33 } from "path";
13487
13512
  var NODE_LOCKFILES = [
13488
13513
  "package-lock.json",
13489
13514
  "pnpm-lock.yaml",
@@ -13521,7 +13546,7 @@ var STACK_LOCKFILES = {
13521
13546
  };
13522
13547
  function readFileSafe2(path) {
13523
13548
  try {
13524
- return readFileSync32(path, "utf-8");
13549
+ return readFileSync33(path, "utf-8");
13525
13550
  } catch {
13526
13551
  return null;
13527
13552
  }
@@ -13534,7 +13559,7 @@ function parseJsonSafe2(content) {
13534
13559
  }
13535
13560
  }
13536
13561
  function detectRunnerStack(projectDir) {
13537
- const pkgContent = readFileSafe2(join32(projectDir, "package.json"));
13562
+ const pkgContent = readFileSafe2(join33(projectDir, "package.json"));
13538
13563
  if (pkgContent) {
13539
13564
  const pkg = parseJsonSafe2(pkgContent);
13540
13565
  if (pkg) {
@@ -13548,36 +13573,36 @@ function detectRunnerStack(projectDir) {
13548
13573
  }
13549
13574
  return { name: "node", language: "node" };
13550
13575
  }
13551
- if (existsSync34(join32(projectDir, "manage.py"))) {
13576
+ if (existsSync34(join33(projectDir, "manage.py"))) {
13552
13577
  return { name: "django", language: "python" };
13553
13578
  }
13554
- const pyproject = readFileSafe2(join32(projectDir, "pyproject.toml"));
13579
+ const pyproject = readFileSafe2(join33(projectDir, "pyproject.toml"));
13555
13580
  if (pyproject) {
13556
13581
  if (pyproject.toLowerCase().includes("fastapi")) {
13557
13582
  return { name: "fastapi", language: "python" };
13558
13583
  }
13559
13584
  return { name: "python", language: "python" };
13560
13585
  }
13561
- if (existsSync34(join32(projectDir, "requirements.txt"))) {
13586
+ if (existsSync34(join33(projectDir, "requirements.txt"))) {
13562
13587
  return { name: "python", language: "python" };
13563
13588
  }
13564
- if (existsSync34(join32(projectDir, "go.mod"))) {
13589
+ if (existsSync34(join33(projectDir, "go.mod"))) {
13565
13590
  return { name: "go", language: "go" };
13566
13591
  }
13567
- if (existsSync34(join32(projectDir, "Cargo.toml"))) {
13592
+ if (existsSync34(join33(projectDir, "Cargo.toml"))) {
13568
13593
  return { name: "rust", language: "rust" };
13569
13594
  }
13570
- if (existsSync34(join32(projectDir, "pom.xml"))) {
13595
+ if (existsSync34(join33(projectDir, "pom.xml"))) {
13571
13596
  return { name: "java-maven", language: "java" };
13572
13597
  }
13573
- if (existsSync34(join32(projectDir, "build.gradle")) || existsSync34(join32(projectDir, "build.gradle.kts"))) {
13598
+ if (existsSync34(join33(projectDir, "build.gradle")) || existsSync34(join33(projectDir, "build.gradle.kts"))) {
13574
13599
  return { name: "java-gradle", language: "java" };
13575
13600
  }
13576
13601
  return { name: "node", language: "node" };
13577
13602
  }
13578
13603
  function findLockfiles(projectDir, stackName) {
13579
13604
  const candidates = STACK_LOCKFILES[stackName] ?? [];
13580
- return candidates.filter((f) => existsSync34(join32(projectDir, f)));
13605
+ return candidates.filter((f) => existsSync34(join33(projectDir, f)));
13581
13606
  }
13582
13607
 
13583
13608
  // src/cli/runner-image-builder.ts
@@ -13679,12 +13704,12 @@ function generateDockerfile(stack, lockfiles) {
13679
13704
  function computeLockfileHash(projectDir, lockfiles) {
13680
13705
  const hash = createHash("sha256");
13681
13706
  for (const lf of lockfiles) {
13682
- const path = join33(projectDir, lf);
13707
+ const path = join34(projectDir, lf);
13683
13708
  hash.update(lf);
13684
13709
  hash.update("\0");
13685
13710
  if (existsSync35(path)) {
13686
13711
  try {
13687
- hash.update(readFileSync33(path));
13712
+ hash.update(readFileSync34(path));
13688
13713
  } catch {
13689
13714
  hash.update("<unreadable>");
13690
13715
  }
@@ -13696,10 +13721,10 @@ function computeLockfileHash(projectDir, lockfiles) {
13696
13721
  return hash.digest("hex").slice(0, 16);
13697
13722
  }
13698
13723
  function readLastBuild(projectDir) {
13699
- const path = join33(projectDir, RUNNER_STATE_DIR, LAST_BUILD_FILE);
13724
+ const path = join34(projectDir, RUNNER_STATE_DIR, LAST_BUILD_FILE);
13700
13725
  if (!existsSync35(path)) return null;
13701
13726
  try {
13702
- const data = JSON.parse(readFileSync33(path, "utf-8"));
13727
+ const data = JSON.parse(readFileSync34(path, "utf-8"));
13703
13728
  if (data && typeof data === "object" && typeof data.lockfileHash === "string" && typeof data.imageTag === "string" && typeof data.builtAt === "string") {
13704
13729
  return data;
13705
13730
  }
@@ -13718,10 +13743,10 @@ function imageTagFor(projectName, hash) {
13718
13743
  return `beastmode-runner-${safeName}:${hash}`;
13719
13744
  }
13720
13745
  function writeStateFile(projectDir, fileName, payload) {
13721
- const dir = join33(projectDir, RUNNER_STATE_DIR);
13746
+ const dir = join34(projectDir, RUNNER_STATE_DIR);
13722
13747
  mkdirSync21(dir, { recursive: true });
13723
13748
  writeFileSync28(
13724
- join33(dir, fileName),
13749
+ join34(dir, fileName),
13725
13750
  JSON.stringify(payload, null, 2) + "\n",
13726
13751
  "utf-8"
13727
13752
  );
@@ -13847,11 +13872,11 @@ function resolveGitHubConfig() {
13847
13872
  // src/cli/runner-helpers.ts
13848
13873
  import { execSync as execSync11, spawn, spawnSync as spawnSync5 } from "child_process";
13849
13874
  import { promises as fs } from "fs";
13850
- import { join as join34 } from "path";
13875
+ import { join as join35 } from "path";
13851
13876
  init_display();
13852
13877
  async function writeRunnerMeta(dir, meta) {
13853
13878
  await fs.mkdir(dir, { recursive: true });
13854
- const path = join34(dir, "runner-meta.json");
13879
+ const path = join35(dir, "runner-meta.json");
13855
13880
  await fs.writeFile(path, JSON.stringify(meta, null, 2) + "\n", "utf-8");
13856
13881
  }
13857
13882
  function resolveRepoSlug() {
@@ -14058,7 +14083,7 @@ import {
14058
14083
  writeFileSync as writeFileSync29
14059
14084
  } from "fs";
14060
14085
  import { homedir as homedir4 } from "os";
14061
- import { join as join35, dirname as dirname8 } from "path";
14086
+ import { join as join36, dirname as dirname9 } from "path";
14062
14087
  import { Readable } from "stream";
14063
14088
  import { pipeline } from "stream/promises";
14064
14089
  init_display();
@@ -14088,7 +14113,7 @@ function runnerDownloadUrl(os, arch) {
14088
14113
  return `https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-${ghOs}-${arch}-${RUNNER_VERSION}.tar.gz`;
14089
14114
  }
14090
14115
  async function downloadAndExtractRunner(installDir, os, arch) {
14091
- const runShPath = join35(installDir, "run.sh");
14116
+ const runShPath = join36(installDir, "run.sh");
14092
14117
  if (existsSync36(runShPath)) {
14093
14118
  info(
14094
14119
  `Runner binary already present at ${runShPath} \u2014 skipping download.`
@@ -14099,7 +14124,7 @@ async function downloadAndExtractRunner(installDir, os, arch) {
14099
14124
  return;
14100
14125
  }
14101
14126
  const url = runnerDownloadUrl(os, arch);
14102
- const tarball = join35(installDir, "runner.tar.gz");
14127
+ const tarball = join36(installDir, "runner.tar.gz");
14103
14128
  setupStep(
14104
14129
  `Downloading GitHub Actions runner v${RUNNER_VERSION} (${os}/${arch})...`
14105
14130
  );
@@ -14150,7 +14175,7 @@ async function configureRunner(installDir, repoUrl, token, name, labels) {
14150
14175
  "--unattended",
14151
14176
  "--replace"
14152
14177
  ];
14153
- const result = spawnSync6(join35(installDir, "config.sh"), args, {
14178
+ const result = spawnSync6(join36(installDir, "config.sh"), args, {
14154
14179
  cwd: installDir,
14155
14180
  stdio: ["ignore", "inherit", "pipe"],
14156
14181
  encoding: "utf-8"
@@ -14178,7 +14203,7 @@ function startRunnerForeground(installDir) {
14178
14203
  "Use --service to install as a launchd agent for persistent operation."
14179
14204
  );
14180
14205
  }
14181
- spawn2(join35(installDir, "run.sh"), [], {
14206
+ spawn2(join36(installDir, "run.sh"), [], {
14182
14207
  cwd: installDir,
14183
14208
  stdio: "inherit"
14184
14209
  });
@@ -14203,10 +14228,10 @@ WantedBy=default.target
14203
14228
  }
14204
14229
  async function installSystemdService(installDir, name) {
14205
14230
  const unitName = `beastmode-runner-${name}.service`;
14206
- const unitDir = join35(homedir4(), ".config", "systemd", "user");
14231
+ const unitDir = join36(homedir4(), ".config", "systemd", "user");
14207
14232
  mkdirSync22(unitDir, { recursive: true });
14208
14233
  writeFileSync29(
14209
- join35(unitDir, unitName),
14234
+ join36(unitDir, unitName),
14210
14235
  systemdUnitContent(installDir, name),
14211
14236
  "utf-8"
14212
14237
  );
@@ -14260,14 +14285,14 @@ function launchdPlistContent(installDir, name) {
14260
14285
  }
14261
14286
  async function installLaunchdService(installDir, name) {
14262
14287
  const label = `com.beastmode.runner.${name}`;
14263
- const plistPath2 = join35(
14288
+ const plistPath2 = join36(
14264
14289
  homedir4(),
14265
14290
  "Library",
14266
14291
  "LaunchAgents",
14267
14292
  `${label}.plist`
14268
14293
  );
14269
- mkdirSync22(dirname8(plistPath2), { recursive: true });
14270
- mkdirSync22(join35(installDir, "logs"), { recursive: true });
14294
+ mkdirSync22(dirname9(plistPath2), { recursive: true });
14295
+ mkdirSync22(join36(installDir, "logs"), { recursive: true });
14271
14296
  writeFileSync29(plistPath2, launchdPlistContent(installDir, name), "utf-8");
14272
14297
  try {
14273
14298
  execSync12(`launchctl load ${plistPath2}`, { stdio: "inherit" });
@@ -14309,7 +14334,7 @@ async function nativeRunnerSetup(opts) {
14309
14334
  }
14310
14335
  const ghConfig = resolveGitHubConfig();
14311
14336
  const repoSlug = opts.repo ?? resolveRepoSlug();
14312
- const installDir = join35(homedir4(), ".beastmode", "runners", opts.name);
14337
+ const installDir = join36(homedir4(), ".beastmode", "runners", opts.name);
14313
14338
  if (opts.dryRun) {
14314
14339
  info(
14315
14340
  `[dry-run] Would install native runner '${opts.name}' for repo ${repoSlug}`
@@ -14359,11 +14384,11 @@ async function nativeRunnerSetup(opts) {
14359
14384
  import {
14360
14385
  existsSync as existsSync37,
14361
14386
  mkdirSync as mkdirSync23,
14362
- readFileSync as readFileSync34,
14387
+ readFileSync as readFileSync35,
14363
14388
  unlinkSync as unlinkSync7,
14364
14389
  writeFileSync as writeFileSync30
14365
14390
  } from "fs";
14366
- import { dirname as dirname9, join as join36 } from "path";
14391
+ import { dirname as dirname10, join as join37 } from "path";
14367
14392
  var TARGET_LABEL = "[self-hosted, self-hosted-beastmode]";
14368
14393
  var TARGET_WORKFLOWS = [
14369
14394
  ".github/workflows/test.yml",
@@ -14417,7 +14442,7 @@ async function assertOnlineRunnerExists() {
14417
14442
  }
14418
14443
  }
14419
14444
  async function switchWorkflows(projectDir) {
14420
- const statePath = join36(projectDir, STATE_FILE);
14445
+ const statePath = join37(projectDir, STATE_FILE);
14421
14446
  if (existsSync37(statePath)) {
14422
14447
  return { alreadySwitched: true, files: [], skipped: [] };
14423
14448
  }
@@ -14430,12 +14455,12 @@ async function switchWorkflows(projectDir) {
14430
14455
  const resultFiles = [];
14431
14456
  const skipped = [];
14432
14457
  for (const relPath of TARGET_WORKFLOWS) {
14433
- const absPath = join36(projectDir, relPath);
14458
+ const absPath = join37(projectDir, relPath);
14434
14459
  if (!existsSync37(absPath)) {
14435
14460
  skipped.push({ relativePath: relPath, reason: "missing" });
14436
14461
  continue;
14437
14462
  }
14438
- const content = readFileSync34(absPath, "utf-8");
14463
+ const content = readFileSync35(absPath, "utf-8");
14439
14464
  const { newContent, originals } = replaceRunsOn(content, TARGET_LABEL);
14440
14465
  if (originals.length === 0) {
14441
14466
  skipped.push({ relativePath: relPath, reason: "no_runs_on" });
@@ -14451,25 +14476,25 @@ async function switchWorkflows(projectDir) {
14451
14476
  state.files.push({ relativePath: relPath, originals });
14452
14477
  resultFiles.push({ relativePath: relPath, jobCount: originals.length });
14453
14478
  }
14454
- mkdirSync23(dirname9(statePath), { recursive: true });
14479
+ mkdirSync23(dirname10(statePath), { recursive: true });
14455
14480
  writeFileSync30(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
14456
14481
  return { alreadySwitched: false, files: resultFiles, skipped };
14457
14482
  }
14458
14483
  async function restoreWorkflows(projectDir) {
14459
- const statePath = join36(projectDir, STATE_FILE);
14484
+ const statePath = join37(projectDir, STATE_FILE);
14460
14485
  if (!existsSync37(statePath)) {
14461
14486
  return { nothingToRestore: true, files: [] };
14462
14487
  }
14463
14488
  const state = JSON.parse(
14464
- readFileSync34(statePath, "utf-8")
14489
+ readFileSync35(statePath, "utf-8")
14465
14490
  );
14466
14491
  const resultFiles = [];
14467
14492
  for (const fileState of state.files) {
14468
- const absPath = join36(projectDir, fileState.relativePath);
14493
+ const absPath = join37(projectDir, fileState.relativePath);
14469
14494
  if (!existsSync37(absPath)) {
14470
14495
  throw new Error(`Workflow file not found: ${fileState.relativePath}`);
14471
14496
  }
14472
- const content = readFileSync34(absPath, "utf-8");
14497
+ const content = readFileSync35(absPath, "utf-8");
14473
14498
  const newContent = restoreRunsOn(content, fileState.originals);
14474
14499
  writeFileSync30(absPath, newContent, "utf-8");
14475
14500
  resultFiles.push({
@@ -14484,7 +14509,7 @@ async function restoreWorkflows(projectDir) {
14484
14509
  // src/cli/commands/runner-cmd.ts
14485
14510
  var runnerCommand = new Command23("runner").description("Manage self-hosted GitHub Actions runners");
14486
14511
  function resolveProjectName(projectDir) {
14487
- const projectsDir = join37(projectDir, ".beastmode", "projects");
14512
+ const projectsDir = join38(projectDir, ".beastmode", "projects");
14488
14513
  if (existsSync38(projectsDir)) {
14489
14514
  try {
14490
14515
  const records = listProjectRecords(projectsDir);
@@ -14874,16 +14899,16 @@ runnerCommand.command("build-image").description(
14874
14899
  // src/cli/commands/project-cmd.ts
14875
14900
  init_engine();
14876
14901
  import { Command as Command24 } from "commander";
14877
- import { existsSync as existsSync39, mkdirSync as mkdirSync24, readFileSync as readFileSync35, renameSync as renameSync3 } from "fs";
14878
- import { join as join38, resolve as resolve21, basename as basename9 } from "path";
14902
+ import { existsSync as existsSync39, mkdirSync as mkdirSync24, readFileSync as readFileSync36, renameSync as renameSync3 } from "fs";
14903
+ import { join as join39, resolve as resolve21, basename as basename9 } from "path";
14879
14904
  import { execSync as execSync13 } from "child_process";
14880
14905
  var DEFAULT_MAX_PROJECTS = 5;
14881
14906
  var MIN_DISK_WARNING_GB = 10;
14882
14907
  function getMaxProjects(factoryDir) {
14883
- const configPath = join38(factoryDir, ".beastmode", "config.json");
14908
+ const configPath = join39(factoryDir, ".beastmode", "config.json");
14884
14909
  if (existsSync39(configPath)) {
14885
14910
  try {
14886
- const config = JSON.parse(readFileSync35(configPath, "utf-8"));
14911
+ const config = JSON.parse(readFileSync36(configPath, "utf-8"));
14887
14912
  if (typeof config.max_projects === "number") return config.max_projects;
14888
14913
  } catch {
14889
14914
  }
@@ -14904,8 +14929,8 @@ function projectAddAction(factoryDir, projectPath, opts) {
14904
14929
  const resolvedPath = resolve21(projectPath);
14905
14930
  if (!existsSync39(resolvedPath)) throw new Error(`Directory not found: ${resolvedPath}`);
14906
14931
  const projectName = opts.name || basename9(resolvedPath);
14907
- const projectsDir = join38(factoryDir, ".beastmode", "projects");
14908
- if (existsSync39(join38(projectsDir, projectName, "project.json"))) {
14932
+ const projectsDir = join39(factoryDir, ".beastmode", "projects");
14933
+ if (existsSync39(join39(projectsDir, projectName, "project.json"))) {
14909
14934
  throw new Error(`Project already exists: ${projectName}`);
14910
14935
  }
14911
14936
  const currentCount = listProjectRecords(projectsDir).length;
@@ -14956,7 +14981,7 @@ function projectAddAction(factoryDir, projectPath, opts) {
14956
14981
  } : void 0
14957
14982
  });
14958
14983
  writeProjectRecord(projectsDir, projectName, record);
14959
- const runsDir = join38(factoryDir, "runs", projectName);
14984
+ const runsDir = join39(factoryDir, "runs", projectName);
14960
14985
  if (!existsSync39(runsDir)) mkdirSync24(runsDir, { recursive: true });
14961
14986
  if (!boardId) {
14962
14987
  void (async () => {
@@ -14986,15 +15011,15 @@ function projectAddAction(factoryDir, projectPath, opts) {
14986
15011
  }
14987
15012
  }
14988
15013
  function projectListAction(factoryDir) {
14989
- const projectsDir = join38(factoryDir, ".beastmode", "projects");
15014
+ const projectsDir = join39(factoryDir, ".beastmode", "projects");
14990
15015
  return listProjectRecords(projectsDir);
14991
15016
  }
14992
15017
  function projectRemoveAction(factoryDir, name) {
14993
- const projectDir = join38(factoryDir, ".beastmode", "projects", name);
15018
+ const projectDir = join39(factoryDir, ".beastmode", "projects", name);
14994
15019
  if (!existsSync39(projectDir)) throw new Error(`Project not found: ${name}`);
14995
- const archivedBase = join38(factoryDir, ".beastmode", "projects", ".archived");
15020
+ const archivedBase = join39(factoryDir, ".beastmode", "projects", ".archived");
14996
15021
  mkdirSync24(archivedBase, { recursive: true });
14997
- renameSync3(projectDir, join38(archivedBase, name));
15022
+ renameSync3(projectDir, join39(archivedBase, name));
14998
15023
  }
14999
15024
  var projectCommand = new Command24("project").description("Manage projects in this factory");
15000
15025
  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) => {
@@ -15021,7 +15046,7 @@ projectCommand.command("remove <name>").description("Archive a project").action(
15021
15046
  init_display();
15022
15047
  init_board();
15023
15048
  import { Command as Command25 } from "commander";
15024
- import { join as join39 } from "path";
15049
+ import { join as join40 } from "path";
15025
15050
  import { existsSync as existsSync40, writeFileSync as writeFileSync31, mkdirSync as mkdirSync25 } from "fs";
15026
15051
  var redeployCommand = new Command25("redeploy").description("Trigger a rebuild + restart of a BeastMode service").argument("<service>", "Service to redeploy (currently: daemon)").action(async (service) => {
15027
15052
  try {
@@ -15043,8 +15068,8 @@ async function runRedeploy(service) {
15043
15068
  "No BeastMode factory found. Run 'beastmode init' first."
15044
15069
  );
15045
15070
  }
15046
- const markerDir = join39(factoryDir, "daemon", "logs");
15047
- const markerPath = join39(markerDir, ".daemon-rebuild-requested");
15071
+ const markerDir = join40(factoryDir, "daemon", "logs");
15072
+ const markerPath = join40(markerDir, ".daemon-rebuild-requested");
15048
15073
  if (!existsSync40(markerDir)) {
15049
15074
  mkdirSync25(markerDir, { recursive: true });
15050
15075
  }