@beastmode-develeap/beastmode 0.1.117 → 0.1.119

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
@@ -2957,6 +2957,35 @@ var init_file_writer = __esm({
2957
2957
  }
2958
2958
  });
2959
2959
 
2960
+ // src/cli/utils/display.ts
2961
+ import chalk from "chalk";
2962
+ function header(text) {
2963
+ console.log();
2964
+ console.log(chalk.bold.cyan(` ${text}`));
2965
+ console.log(chalk.dim(" " + "\u2500".repeat(text.length + 2)));
2966
+ }
2967
+ function success(text) {
2968
+ console.log(chalk.green(` \u2713 ${text}`));
2969
+ }
2970
+ function warn(text) {
2971
+ console.log(chalk.yellow(` \u26A0 ${text}`));
2972
+ }
2973
+ function error(text) {
2974
+ console.log(chalk.red(` \u2717 ${text}`));
2975
+ }
2976
+ function info(text) {
2977
+ console.log(chalk.dim(` ${text}`));
2978
+ }
2979
+ function step(n, total, text) {
2980
+ console.log();
2981
+ console.log(chalk.bold(` [${n}/${total}] ${text}`));
2982
+ }
2983
+ var init_display = __esm({
2984
+ "src/cli/utils/display.ts"() {
2985
+ "use strict";
2986
+ }
2987
+ });
2988
+
2960
2989
  // src/cli/ui/api-routes.ts
2961
2990
  import { resolve as resolve3 } from "path";
2962
2991
  import { existsSync as existsSync10, writeFileSync as writeFileSync7 } from "fs";
@@ -6805,15 +6834,261 @@ var init_server = __esm({
6805
6834
  }
6806
6835
  });
6807
6836
 
6837
+ // src/cli/commands/sync-claude-creds.ts
6838
+ var sync_claude_creds_exports = {};
6839
+ __export(sync_claude_creds_exports, {
6840
+ installAgent: () => installAgent,
6841
+ readKeychainTokenSafe: () => readKeychainTokenSafe,
6842
+ syncClaudeCredsCommand: () => syncClaudeCredsCommand,
6843
+ syncClaudeCredsOnce: () => syncClaudeCredsOnce
6844
+ });
6845
+ import { Command } from "commander";
6846
+ import { execSync as execSync4, spawnSync as spawnSync2 } from "child_process";
6847
+ import { writeFileSync as writeFileSync13, chmodSync, mkdirSync as mkdirSync12, existsSync as existsSync17, unlinkSync as unlinkSync4 } from "fs";
6848
+ import { join as join15 } from "path";
6849
+ import { homedir as homedir2, platform } from "os";
6850
+ function plistPath() {
6851
+ return join15(homedir2(), "Library", "LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`);
6852
+ }
6853
+ function agentLogPath() {
6854
+ return join15(homedir2(), ".beastmode", "logs", "sync-claude-creds.log");
6855
+ }
6856
+ function readKeychainTokenSafe() {
6857
+ try {
6858
+ return execSync4(
6859
+ `security find-generic-password -s "Claude Code-credentials" -w`,
6860
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6861
+ ).trim();
6862
+ } catch {
6863
+ return null;
6864
+ }
6865
+ }
6866
+ function readKeychainToken() {
6867
+ const token = readKeychainTokenSafe();
6868
+ if (token === null) {
6869
+ error("Could not read Claude Code credentials from Keychain.");
6870
+ info("Fix: run `claude login` first, then re-run this command.");
6871
+ process.exit(1);
6872
+ }
6873
+ return token;
6874
+ }
6875
+ function writeCredentialsFile(rawJson) {
6876
+ let parsed;
6877
+ try {
6878
+ parsed = JSON.parse(rawJson);
6879
+ } catch {
6880
+ throw new Error("Keychain entry is not valid JSON. Fix: run `claude login` again to reset the credential.");
6881
+ }
6882
+ const oauth = parsed.claudeAiOauth;
6883
+ if (!oauth?.accessToken) {
6884
+ throw new Error("Keychain entry missing claudeAiOauth.accessToken. Fix: run `claude login` again to reset the credential.");
6885
+ }
6886
+ const claudeDir = join15(homedir2(), ".claude");
6887
+ if (!existsSync17(claudeDir)) mkdirSync12(claudeDir, { recursive: true });
6888
+ const credsPath = join15(claudeDir, ".credentials.json");
6889
+ writeFileSync13(credsPath, rawJson + "\n", "utf-8");
6890
+ chmodSync(credsPath, 384);
6891
+ if (oauth.expiresAt) {
6892
+ const hoursLeft = Math.round((oauth.expiresAt - Date.now()) / 36e5);
6893
+ if (hoursLeft < 0) {
6894
+ warn(`Token already expired ${Math.abs(hoursLeft)}h ago \u2014 run \`claude login\` to refresh.`);
6895
+ } else if (hoursLeft < 2) {
6896
+ warn(`Token expires in ${hoursLeft}h \u2014 background agent will re-sync after host refreshes it.`);
6897
+ }
6898
+ }
6899
+ return credsPath;
6900
+ }
6901
+ function buildPlist(intervalSeconds) {
6902
+ const nodePath = process.execPath;
6903
+ const cliEntry = process.argv[1];
6904
+ const logPath = agentLogPath();
6905
+ return `<?xml version="1.0" encoding="UTF-8"?>
6906
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
6907
+ <plist version="1.0">
6908
+ <dict>
6909
+ <key>Label</key>
6910
+ <string>${LAUNCH_AGENT_LABEL}</string>
6911
+ <key>ProgramArguments</key>
6912
+ <array>
6913
+ <string>${nodePath}</string>
6914
+ <string>${cliEntry}</string>
6915
+ <string>sync-claude-creds</string>
6916
+ </array>
6917
+ <key>StartInterval</key>
6918
+ <integer>${intervalSeconds}</integer>
6919
+ <key>RunAtLoad</key>
6920
+ <true/>
6921
+ <key>StandardOutPath</key>
6922
+ <string>${logPath}</string>
6923
+ <key>StandardErrorPath</key>
6924
+ <string>${logPath}</string>
6925
+ </dict>
6926
+ </plist>
6927
+ `;
6928
+ }
6929
+ function installAgent(intervalSeconds, { throwOnError = false } = {}) {
6930
+ const plist = plistPath();
6931
+ const logPath = agentLogPath();
6932
+ mkdirSync12(join15(homedir2(), "Library", "LaunchAgents"), { recursive: true });
6933
+ mkdirSync12(join15(homedir2(), ".beastmode", "logs"), { recursive: true });
6934
+ const uid = process.getuid?.();
6935
+ if (existsSync17(plist)) {
6936
+ spawnSync2("launchctl", ["bootout", `gui/${uid}`, plist], { stdio: "pipe" });
6937
+ }
6938
+ writeFileSync13(plist, buildPlist(intervalSeconds), "utf-8");
6939
+ writeFileSync13(logPath, "", { flag: "a" });
6940
+ const result = spawnSync2("launchctl", ["bootstrap", `gui/${uid}`, plist], {
6941
+ stdio: "pipe",
6942
+ encoding: "utf-8"
6943
+ });
6944
+ if (result.status !== 0) {
6945
+ const msg = `launchctl bootstrap failed: ${result.stderr || result.stdout}`;
6946
+ if (throwOnError) {
6947
+ throw new Error(msg);
6948
+ }
6949
+ error(msg);
6950
+ info(`Plist written to: ${plist}`);
6951
+ info(`Try manually: launchctl bootstrap gui/${uid} ${plist}`);
6952
+ process.exit(1);
6953
+ }
6954
+ success(`LaunchAgent installed: ${LAUNCH_AGENT_LABEL}`);
6955
+ info(` Plist: ${plist}`);
6956
+ info(` Interval: every ${Math.round(intervalSeconds / 60)} minutes`);
6957
+ info(` Logs: ${logPath}`);
6958
+ info(` Runs at: load + every ${Math.round(intervalSeconds / 60)}min (while you're logged in)`);
6959
+ console.log();
6960
+ info("The agent also runs once right now (RunAtLoad=true) \u2014 credentials synced.");
6961
+ }
6962
+ function syncClaudeCredsOnce() {
6963
+ const token = readKeychainTokenSafe();
6964
+ if (token === null) {
6965
+ return { error: "Could not read Claude Code credentials from Keychain \u2014 run `claude login` first." };
6966
+ }
6967
+ try {
6968
+ const path = writeCredentialsFile(token);
6969
+ return { path };
6970
+ } catch (e) {
6971
+ return { error: `Failed to write credentials: ${e instanceof Error ? e.message : String(e)}` };
6972
+ }
6973
+ }
6974
+ function uninstallAgent() {
6975
+ const plist = plistPath();
6976
+ const uid = process.getuid?.();
6977
+ if (!existsSync17(plist)) {
6978
+ info("No LaunchAgent installed \u2014 nothing to remove.");
6979
+ return;
6980
+ }
6981
+ const result = spawnSync2("launchctl", ["bootout", `gui/${uid}`, plist], {
6982
+ stdio: "pipe",
6983
+ encoding: "utf-8"
6984
+ });
6985
+ if (result.status !== 0 && !(result.stderr || "").includes("Could not find")) {
6986
+ warn(`launchctl bootout returned: ${result.stderr || result.stdout}`);
6987
+ }
6988
+ try {
6989
+ unlinkSync4(plist);
6990
+ } catch {
6991
+ }
6992
+ success(`LaunchAgent removed: ${LAUNCH_AGENT_LABEL}`);
6993
+ }
6994
+ function showStatus() {
6995
+ const plist = plistPath();
6996
+ const uid = process.getuid?.();
6997
+ if (!existsSync17(plist)) {
6998
+ info("LaunchAgent not installed.");
6999
+ info("Install with: beastmode sync-claude-creds --install");
7000
+ return;
7001
+ }
7002
+ const result = spawnSync2(
7003
+ "launchctl",
7004
+ ["print", `gui/${uid}/${LAUNCH_AGENT_LABEL}`],
7005
+ { stdio: "pipe", encoding: "utf-8" }
7006
+ );
7007
+ if (result.status === 0) {
7008
+ success(`LaunchAgent loaded: ${LAUNCH_AGENT_LABEL}`);
7009
+ const stateMatch = result.stdout.match(/state = (\S+)/);
7010
+ const lastExitMatch = result.stdout.match(/last exit code = (\S+)/);
7011
+ if (stateMatch) info(` State: ${stateMatch[1]}`);
7012
+ if (lastExitMatch) info(` Last exit code: ${lastExitMatch[1]}`);
7013
+ info(` Plist: ${plist}`);
7014
+ info(` Logs: ${agentLogPath()}`);
7015
+ } else {
7016
+ warn(`Plist exists but agent not loaded. Re-install with: beastmode sync-claude-creds --install`);
7017
+ }
7018
+ }
7019
+ var LAUNCH_AGENT_LABEL, syncClaudeCredsCommand;
7020
+ var init_sync_claude_creds = __esm({
7021
+ "src/cli/commands/sync-claude-creds.ts"() {
7022
+ "use strict";
7023
+ init_display();
7024
+ LAUNCH_AGENT_LABEL = "com.develeap.beastmode.claude-creds";
7025
+ syncClaudeCredsCommand = new Command("sync-claude-creds").description(
7026
+ "Sync Claude Code credentials from macOS Keychain to ~/.claude/.credentials.json (for Docker)"
7027
+ ).option("--restart", "Restart the daemon container after syncing (one-shot only)").option("--install", "Install a launchd agent that re-syncs every 30 minutes").option("--uninstall", "Remove the launchd agent").option("--status", "Show launchd agent status").option("--interval <seconds>", "Sync interval for --install (default: 1800)", "1800").action(async (opts) => {
7028
+ if (platform() !== "darwin") {
7029
+ info("Not macOS \u2014 Docker mounts ~/.claude/.credentials.json directly. Nothing to sync.");
7030
+ return;
7031
+ }
7032
+ if (opts.install) {
7033
+ header("Install Claude Credential Sync Agent");
7034
+ console.log();
7035
+ const seconds = parseInt(opts.interval || "1800", 10);
7036
+ if (isNaN(seconds) || seconds < 60) {
7037
+ error(`Invalid --interval: ${opts.interval} (minimum 60 seconds)`);
7038
+ process.exit(1);
7039
+ }
7040
+ installAgent(seconds);
7041
+ return;
7042
+ }
7043
+ if (opts.uninstall) {
7044
+ header("Remove Claude Credential Sync Agent");
7045
+ console.log();
7046
+ uninstallAgent();
7047
+ return;
7048
+ }
7049
+ if (opts.status) {
7050
+ header("Claude Credential Sync Agent Status");
7051
+ console.log();
7052
+ showStatus();
7053
+ return;
7054
+ }
7055
+ header("Sync Claude Code Credentials");
7056
+ console.log();
7057
+ const rawJson = readKeychainToken();
7058
+ let credsPath;
7059
+ try {
7060
+ credsPath = writeCredentialsFile(rawJson);
7061
+ } catch (e) {
7062
+ error(e instanceof Error ? e.message : String(e));
7063
+ process.exit(1);
7064
+ }
7065
+ success(`Wrote ${credsPath}`);
7066
+ if (opts.restart) {
7067
+ console.log();
7068
+ info("Restarting daemon container...");
7069
+ const result = spawnSync2("docker", ["compose", "restart", "daemon"], { stdio: "inherit" });
7070
+ if (result.status !== 0) {
7071
+ warn("docker compose restart daemon failed \u2014 run it manually.");
7072
+ process.exit(result.status ?? 1);
7073
+ }
7074
+ success("Daemon restarted.");
7075
+ } else {
7076
+ console.log();
7077
+ info("Automate: beastmode sync-claude-creds --install");
7078
+ }
7079
+ });
7080
+ }
7081
+ });
7082
+
6808
7083
  // src/mcp/server.ts
6809
7084
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6810
7085
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6811
7086
  import { z as z2 } from "zod";
6812
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync23, existsSync as existsSync30, readdirSync as readdirSync11, mkdirSync as mkdirSync18 } from "fs";
6813
- import { join as join28, resolve as resolve18, basename as basename5 } from "path";
7087
+ import { readFileSync as readFileSync27, writeFileSync as writeFileSync24, existsSync as existsSync31, readdirSync as readdirSync11, mkdirSync as mkdirSync19 } from "fs";
7088
+ import { join as join29, resolve as resolve18, basename as basename5 } from "path";
6814
7089
  import { randomUUID as randomUUID3 } from "crypto";
6815
7090
  function readJsonFile2(filePath) {
6816
- if (!existsSync30(filePath)) return null;
7091
+ if (!existsSync31(filePath)) return null;
6817
7092
  try {
6818
7093
  return JSON.parse(readFileSync27(filePath, "utf-8"));
6819
7094
  } catch {
@@ -6822,13 +7097,13 @@ function readJsonFile2(filePath) {
6822
7097
  }
6823
7098
  function getFactoryPath() {
6824
7099
  const envPath = process.env.BEASTMODE_FACTORY_PATH;
6825
- if (envPath && existsSync30(join28(envPath, ".beastmode", "factory.json"))) {
7100
+ if (envPath && existsSync31(join29(envPath, ".beastmode", "factory.json"))) {
6826
7101
  return envPath;
6827
7102
  }
6828
7103
  let dir = process.cwd();
6829
7104
  const root = resolve18("/");
6830
7105
  while (dir !== root) {
6831
- if (existsSync30(join28(dir, ".beastmode", "factory.json"))) {
7106
+ if (existsSync31(join29(dir, ".beastmode", "factory.json"))) {
6832
7107
  return dir;
6833
7108
  }
6834
7109
  const parent = resolve18(dir, "..");
@@ -6840,15 +7115,15 @@ function getFactoryPath() {
6840
7115
  );
6841
7116
  }
6842
7117
  function readFactoryStatus(factoryDir) {
6843
- const bmDir = join28(factoryDir, ".beastmode");
7118
+ const bmDir = join29(factoryDir, ".beastmode");
6844
7119
  const factoryIdentity = FactoryIdentitySchema.parse(
6845
- JSON.parse(readFileSync27(join28(bmDir, "factory.json"), "utf-8"))
7120
+ JSON.parse(readFileSync27(join29(bmDir, "factory.json"), "utf-8"))
6846
7121
  );
6847
- const projectsDir = join28(bmDir, "projects");
6848
- const projectCount = existsSync30(projectsDir) ? readdirSync11(projectsDir).filter((f) => f.endsWith(".json")).length : 0;
6849
- const lockPath = join28(bmDir, "extensions.lock");
7122
+ const projectsDir = join29(bmDir, "projects");
7123
+ const projectCount = existsSync31(projectsDir) ? readdirSync11(projectsDir).filter((f) => f.endsWith(".json")).length : 0;
7124
+ const lockPath = join29(bmDir, "extensions.lock");
6850
7125
  let pluginNames = [];
6851
- if (existsSync30(lockPath)) {
7126
+ if (existsSync31(lockPath)) {
6852
7127
  try {
6853
7128
  const lock = JSON.parse(readFileSync27(lockPath, "utf-8"));
6854
7129
  pluginNames = Object.keys(lock.plugins || {});
@@ -6870,21 +7145,21 @@ function readFactoryStatus(factoryDir) {
6870
7145
  skillCount = listSkills(factoryDir).length;
6871
7146
  } catch {
6872
7147
  }
6873
- const runsDir = join28(factoryDir, "runs");
7148
+ const runsDir = join29(factoryDir, "runs");
6874
7149
  let runDirs = [];
6875
- if (existsSync30(runsDir)) {
7150
+ if (existsSync31(runsDir)) {
6876
7151
  runDirs = readdirSync11(runsDir).filter((d) => {
6877
7152
  try {
6878
- return readdirSync11(join28(runsDir, d)).length > 0;
7153
+ return readdirSync11(join29(runsDir, d)).length > 0;
6879
7154
  } catch {
6880
7155
  return false;
6881
7156
  }
6882
7157
  }).sort();
6883
7158
  }
6884
- const pidFile = join28(bmDir, "daemon.pid");
7159
+ const pidFile = join29(bmDir, "daemon.pid");
6885
7160
  let daemonPid = null;
6886
7161
  let pidAlive = false;
6887
- if (existsSync30(pidFile)) {
7162
+ if (existsSync31(pidFile)) {
6888
7163
  try {
6889
7164
  daemonPid = parseInt(readFileSync27(pidFile, "utf-8").trim(), 10);
6890
7165
  process.kill(daemonPid, 0);
@@ -6907,8 +7182,8 @@ function readFactoryStatus(factoryDir) {
6907
7182
  return collectStatus(input);
6908
7183
  }
6909
7184
  function readBoardItems(factoryDir) {
6910
- const filePath = join28(factoryDir, ".beastmode", "board.json");
6911
- if (!existsSync30(filePath)) return [];
7185
+ const filePath = join29(factoryDir, ".beastmode", "board.json");
7186
+ if (!existsSync31(filePath)) return [];
6912
7187
  try {
6913
7188
  const raw = JSON.parse(readFileSync27(filePath, "utf-8"));
6914
7189
  return Array.isArray(raw.items) ? raw.items : [];
@@ -6917,8 +7192,8 @@ function readBoardItems(factoryDir) {
6917
7192
  }
6918
7193
  }
6919
7194
  function writeBoardItems(factoryDir, items) {
6920
- const filePath = join28(factoryDir, ".beastmode", "board.json");
6921
- writeFileSync23(filePath, JSON.stringify({ items }, null, 2) + "\n", "utf-8");
7195
+ const filePath = join29(factoryDir, ".beastmode", "board.json");
7196
+ writeFileSync24(filePath, JSON.stringify({ items }, null, 2) + "\n", "utf-8");
6922
7197
  }
6923
7198
  function createMcpServer() {
6924
7199
  const server = new McpServer(
@@ -6941,8 +7216,8 @@ function createMcpServer() {
6941
7216
  { key_path: z2.string().describe("Dot-notation key path") },
6942
7217
  async ({ key_path }) => {
6943
7218
  const factoryDir = getFactoryPath();
6944
- const configPath = join28(factoryDir, ".beastmode", "config.json");
6945
- const config = existsSync30(configPath) ? JSON.parse(readFileSync27(configPath, "utf-8")) : generateDefaults();
7219
+ const configPath = join29(factoryDir, ".beastmode", "config.json");
7220
+ const config = existsSync31(configPath) ? JSON.parse(readFileSync27(configPath, "utf-8")) : generateDefaults();
6946
7221
  try {
6947
7222
  const value = configGet(config, key_path);
6948
7223
  return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
@@ -6960,11 +7235,11 @@ function createMcpServer() {
6960
7235
  },
6961
7236
  async ({ key_path, value }) => {
6962
7237
  const factoryDir = getFactoryPath();
6963
- const configPath = join28(factoryDir, ".beastmode", "config.json");
6964
- const config = existsSync30(configPath) ? JSON.parse(readFileSync27(configPath, "utf-8")) : generateDefaults();
7238
+ const configPath = join29(factoryDir, ".beastmode", "config.json");
7239
+ const config = existsSync31(configPath) ? JSON.parse(readFileSync27(configPath, "utf-8")) : generateDefaults();
6965
7240
  const coerced = coerceValue(value);
6966
7241
  const updated = configSet(config, key_path, coerced);
6967
- writeFileSync23(configPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
7242
+ writeFileSync24(configPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
6968
7243
  return { content: [{ type: "text", text: `Set ${key_path} = ${JSON.stringify(coerced)}` }] };
6969
7244
  }
6970
7245
  );
@@ -6998,14 +7273,14 @@ function createMcpServer() {
6998
7273
  {},
6999
7274
  async () => {
7000
7275
  const factoryDir = getFactoryPath();
7001
- const runsDir = join28(factoryDir, "runs");
7002
- if (!existsSync30(runsDir)) {
7276
+ const runsDir = join29(factoryDir, "runs");
7277
+ if (!existsSync31(runsDir)) {
7003
7278
  return { content: [{ type: "text", text: "No runs directory found." }] };
7004
7279
  }
7005
7280
  const runDirs = readdirSync11(runsDir).sort().reverse();
7006
7281
  const activeRuns = [];
7007
7282
  for (const id of runDirs.slice(0, 10)) {
7008
- const cp = readJsonFile2(join28(runsDir, id, "checkpoint.json"));
7283
+ const cp = readJsonFile2(join29(runsDir, id, "checkpoint.json"));
7009
7284
  if (cp) {
7010
7285
  activeRuns.push({ id, checkpoint: cp });
7011
7286
  }
@@ -7019,17 +7294,17 @@ function createMcpServer() {
7019
7294
  { run_id: z2.string().describe("Run ID (directory name)") },
7020
7295
  async ({ run_id }) => {
7021
7296
  const factoryDir = getFactoryPath();
7022
- const runDir = join28(factoryDir, "runs", run_id);
7023
- if (!existsSync30(runDir)) {
7297
+ const runDir = join29(factoryDir, "runs", run_id);
7298
+ if (!existsSync31(runDir)) {
7024
7299
  return { content: [{ type: "text", text: `Run not found: ${run_id}` }], isError: true };
7025
7300
  }
7026
- const manifest = readJsonFile2(join28(runDir, "manifest.json"));
7027
- const checkpoint = readJsonFile2(join28(runDir, "checkpoint.json"));
7028
- const iterationsDir = join28(runDir, "iterations");
7301
+ const manifest = readJsonFile2(join29(runDir, "manifest.json"));
7302
+ const checkpoint = readJsonFile2(join29(runDir, "checkpoint.json"));
7303
+ const iterationsDir = join29(runDir, "iterations");
7029
7304
  const iterations = [];
7030
- if (existsSync30(iterationsDir)) {
7305
+ if (existsSync31(iterationsDir)) {
7031
7306
  for (const dir of readdirSync11(iterationsDir).sort()) {
7032
- const satisfaction = readJsonFile2(join28(iterationsDir, dir, "satisfaction.json"));
7307
+ const satisfaction = readJsonFile2(join29(iterationsDir, dir, "satisfaction.json"));
7033
7308
  iterations.push({ number: parseInt(dir, 10), satisfaction });
7034
7309
  }
7035
7310
  }
@@ -7075,10 +7350,10 @@ function createMcpServer() {
7075
7350
  {},
7076
7351
  async () => {
7077
7352
  const factoryDir = getFactoryPath();
7078
- const bmDir = join28(factoryDir, ".beastmode");
7353
+ const bmDir = join29(factoryDir, ".beastmode");
7079
7354
  let plugins = {};
7080
- const lockPath = join28(bmDir, "extensions.lock");
7081
- if (existsSync30(lockPath)) {
7355
+ const lockPath = join29(bmDir, "extensions.lock");
7356
+ if (existsSync31(lockPath)) {
7082
7357
  try {
7083
7358
  const lock = JSON.parse(readFileSync27(lockPath, "utf-8"));
7084
7359
  plugins = lock.plugins || {};
@@ -7114,13 +7389,13 @@ function createMcpServer() {
7114
7389
  {},
7115
7390
  async () => {
7116
7391
  const factoryDir = getFactoryPath();
7117
- const projectsDir = join28(factoryDir, ".beastmode", "projects");
7118
- if (!existsSync30(projectsDir)) {
7392
+ const projectsDir = join29(factoryDir, ".beastmode", "projects");
7393
+ if (!existsSync31(projectsDir)) {
7119
7394
  return { content: [{ type: "text", text: "[]" }] };
7120
7395
  }
7121
7396
  const projects = readdirSync11(projectsDir).filter((f) => f.endsWith(".json")).map((f) => {
7122
7397
  try {
7123
- return JSON.parse(readFileSync27(join28(projectsDir, f), "utf-8"));
7398
+ return JSON.parse(readFileSync27(join29(projectsDir, f), "utf-8"));
7124
7399
  } catch {
7125
7400
  return null;
7126
7401
  }
@@ -7135,7 +7410,7 @@ function createMcpServer() {
7135
7410
  async ({ path: projectPath }) => {
7136
7411
  const factoryDir = getFactoryPath();
7137
7412
  const resolvedPath = resolve18(projectPath);
7138
- if (!existsSync30(resolvedPath)) {
7413
+ if (!existsSync31(resolvedPath)) {
7139
7414
  return { content: [{ type: "text", text: `Directory not found: ${resolvedPath}` }], isError: true };
7140
7415
  }
7141
7416
  const projectName = basename5(resolvedPath);
@@ -7155,10 +7430,10 @@ function createMcpServer() {
7155
7430
  deploy: { target: stack.suggested_deploy },
7156
7431
  plugins: stack.suggested_plugins
7157
7432
  };
7158
- const projectsDir = join28(factoryDir, ".beastmode", "projects");
7159
- mkdirSync18(projectsDir, { recursive: true });
7160
- writeFileSync23(
7161
- join28(projectsDir, `${projectName}.json`),
7433
+ const projectsDir = join29(factoryDir, ".beastmode", "projects");
7434
+ mkdirSync19(projectsDir, { recursive: true });
7435
+ writeFileSync24(
7436
+ join29(projectsDir, `${projectName}.json`),
7162
7437
  JSON.stringify(projectConfig, null, 2) + "\n",
7163
7438
  "utf-8"
7164
7439
  );
@@ -7263,10 +7538,10 @@ import { Command as Command23 } from "commander";
7263
7538
  // src/cli/commands/init.ts
7264
7539
  init_engine();
7265
7540
  init_file_writer();
7266
- import { Command } from "commander";
7541
+ import { Command as Command2 } from "commander";
7267
7542
  import inquirer from "inquirer";
7268
- import { resolve as resolve5, basename as basename4, join as join15 } from "path";
7269
- import { existsSync as existsSync17, writeFileSync as writeFileSync13, mkdirSync as mkdirSync12, readFileSync as readFileSync14 } from "fs";
7543
+ import { resolve as resolve5, basename as basename4, join as join16 } from "path";
7544
+ import { existsSync as existsSync18, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, readFileSync as readFileSync14 } from "fs";
7270
7545
 
7271
7546
  // src/cli/utils/docker.ts
7272
7547
  import { existsSync as existsSync9 } from "fs";
@@ -7415,12 +7690,18 @@ function generateComposeYaml(tag) {
7415
7690
  - ./config:/app/config
7416
7691
  - ./runs:/app/runs
7417
7692
  - ./daemon/logs:/app/daemon/logs:ro
7418
- # Claude credentials for the chat feature. Target /home/appuser/.claude
7419
- # matches the ui image's USER appuser (cli/Dockerfile sets HOME to
7420
- # /home/appuser); the entrypoint restores .claude.json from the
7421
- # newest backup in this dir at startup so chat works across container
7422
- # restarts. Pre-2026-04-15 this used /root/.claude, which silently
7423
- # broke chat because appuser couldn't read those files.
7693
+ # Claude credentials for the chat feature. Claude Code stores
7694
+ # auth in TWO places on the host: ~/.claude.json (sibling file,
7695
+ # auth + settings) and ~/.claude/ (directory with backups/,
7696
+ # projects/, ...). Both must be mounted \u2014 the daemon's
7697
+ # docker-entrypoint.sh reads $HOME/.claude.json directly, and
7698
+ # pre-2026-04-18 only the directory was mounted, so every
7699
+ # container recreate came up un-authenticated and every Phase 1
7700
+ # spec run aborted with "Claude CLI Not Logged In".
7701
+ - type: bind
7702
+ source: \${HOME}/.claude.json
7703
+ target: /home/appuser/.claude.json
7704
+ read_only: true
7424
7705
  - \${HOME}/.claude:/home/appuser/.claude:ro
7425
7706
  depends_on:
7426
7707
  board:
@@ -7463,6 +7744,14 @@ function generateComposeYaml(tag) {
7463
7744
  # beatmode runtime dir (which has no source code). See
7464
7745
  # docs/zero-to-productive-readiness.md Gap 1.
7465
7746
  - \${PROJECT_DIR:?PROJECT_DIR must be set in .env \u2014 run 'beastmode init' to regenerate}:/app/project
7747
+ # See the ui service for the two-mount rationale. Without the
7748
+ # sibling-file mount every daemon recreate comes up without
7749
+ # Claude auth and every Phase 1 spec run aborts with
7750
+ # "Claude CLI Not Logged In" (2026-04-18 incident).
7751
+ - type: bind
7752
+ source: \${HOME}/.claude.json
7753
+ target: /home/appuser/.claude.json
7754
+ read_only: true
7466
7755
  - \${HOME}/.claude:/home/appuser/.claude
7467
7756
  depends_on:
7468
7757
  board:
@@ -7471,32 +7760,9 @@ function generateComposeYaml(tag) {
7471
7760
  `;
7472
7761
  }
7473
7762
 
7474
- // src/cli/utils/display.ts
7475
- import chalk from "chalk";
7476
- function header(text) {
7477
- console.log();
7478
- console.log(chalk.bold.cyan(` ${text}`));
7479
- console.log(chalk.dim(" " + "\u2500".repeat(text.length + 2)));
7480
- }
7481
- function success(text) {
7482
- console.log(chalk.green(` \u2713 ${text}`));
7483
- }
7484
- function warn(text) {
7485
- console.log(chalk.yellow(` \u26A0 ${text}`));
7486
- }
7487
- function error(text) {
7488
- console.log(chalk.red(` \u2717 ${text}`));
7489
- }
7490
- function info(text) {
7491
- console.log(chalk.dim(` ${text}`));
7492
- }
7493
- function step(n, total, text) {
7494
- console.log();
7495
- console.log(chalk.bold(` [${n}/${total}] ${text}`));
7496
- }
7497
-
7498
7763
  // src/cli/commands/init.ts
7499
- var initCommand = new Command("init").description("Create a new BeastMode factory").argument("[name]", "Factory name (default: current directory name)").option("--project <path>", "Path to target project").option("--preset <preset>", "Pipeline preset (full, lean, prototype, infra, docs)").option("--backend <backend>", "Task backend (beastmode-board, github-issues)").option("--deploy <target>", "Deploy target (pr-only, vercel, aws-ecs, ...)").option("--methodology <name>", "Development methodology plugin").option("--plugin <name>", "Install plugin (repeatable)", collect, []).option("--from <template>", "Import config from template file").option("--ui", "Open web wizard instead of CLI").option("--yes", "Accept all defaults (non-interactive)").option(
7764
+ init_display();
7765
+ var initCommand = new Command2("init").description("Create a new BeastMode factory").argument("[name]", "Factory name (default: current directory name)").option("--project <path>", "Path to target project").option("--preset <preset>", "Pipeline preset (full, lean, prototype, infra, docs)").option("--backend <backend>", "Task backend (beastmode-board, github-issues)").option("--deploy <target>", "Deploy target (pr-only, vercel, aws-ecs, ...)").option("--methodology <name>", "Development methodology plugin").option("--plugin <name>", "Install plugin (repeatable)", collect, []).option("--from <template>", "Import config from template file").option("--ui", "Open web wizard instead of CLI").option("--yes", "Accept all defaults (non-interactive)").option(
7500
7766
  "--project-github-token <token>",
7501
7767
  "GitHub PAT for the daemon's project operations (commit/push/PR/merge against PROJECT_DIR's git remote \u2014 needs `repo` scope). Alias for the legacy --github-token flag."
7502
7768
  ).option(
@@ -7514,10 +7780,7 @@ var initCommand = new Command("init").description("Create a new BeastMode factor
7514
7780
  ).option(
7515
7781
  "--ghcr-pull-token-file <path>",
7516
7782
  "Read GHCR pull token from file (safer than --ghcr-pull-token)"
7517
- ).option("--board-password <password>", "Board UI password (avoids interactive prompt)").option("--board-password-file <path>", "Read board password from file").option(
7518
- "--telemetry",
7519
- "Enable opt-in anonymous telemetry non-interactively (default off, including with --yes). See docs/telemetry.md."
7520
- ).action(async (name, opts) => {
7783
+ ).option("--board-password <password>", "Board UI password (avoids interactive prompt)").option("--board-password-file <path>", "Read board password from file").action(async (name, opts) => {
7521
7784
  try {
7522
7785
  await runInit(name, opts);
7523
7786
  } catch (err) {
@@ -7533,7 +7796,7 @@ function readSecretFile(filePath) {
7533
7796
  return readFileSync14(resolve5(filePath), "utf-8").trim();
7534
7797
  }
7535
7798
  function isSourceRepo(dir) {
7536
- return existsSync17(resolve5(dir, "daemon")) && existsSync17(resolve5(dir, "board")) && existsSync17(resolve5(dir, "cli"));
7799
+ return existsSync18(resolve5(dir, "daemon")) && existsSync18(resolve5(dir, "board")) && existsSync18(resolve5(dir, "cli"));
7537
7800
  }
7538
7801
  async function runInit(name, opts) {
7539
7802
  if (opts.ui) {
@@ -7568,11 +7831,11 @@ async function runInit(name, opts) {
7568
7831
  await runImageModeInit(name, opts);
7569
7832
  return;
7570
7833
  }
7571
- const totalSteps = 6;
7834
+ const totalSteps = 5;
7572
7835
  header("BeastMode \u2014 Dark Factory Init");
7573
7836
  info("Creating your Custom Dark Factory\n");
7574
7837
  const factoryName = name || basename4(resolve5("."));
7575
- if (existsSync17(factoryName) && existsSync17(resolve5(factoryName, ".beastmode"))) {
7838
+ if (existsSync18(factoryName) && existsSync18(resolve5(factoryName, ".beastmode"))) {
7576
7839
  throw new Error(`Factory already exists at ./${factoryName}. Use 'beastmode config' to modify.`);
7577
7840
  }
7578
7841
  if (opts.from) {
@@ -7622,7 +7885,7 @@ async function runInit(name, opts) {
7622
7885
  name: "project",
7623
7886
  message: "Path to your project:",
7624
7887
  default: ".",
7625
- validate: (input) => existsSync17(resolve5(input)) || `Directory not found: ${input}`
7888
+ validate: (input) => existsSync18(resolve5(input)) || `Directory not found: ${input}`
7626
7889
  }
7627
7890
  ]);
7628
7891
  projectPath = resolve5(answer.project);
@@ -7781,40 +8044,7 @@ async function runInit(name, opts) {
7781
8044
  } else if (uiPassword) {
7782
8045
  success("Board UI password set");
7783
8046
  }
7784
- step(5, totalSteps, "Telemetry");
7785
- let telemetryEnabled = false;
7786
- let telemetrySentryDsn = "";
7787
- if (opts.telemetry) {
7788
- telemetryEnabled = true;
7789
- } else if (!opts.yes) {
7790
- const answer = await inquirer.prompt([
7791
- {
7792
- type: "confirm",
7793
- name: "enabled",
7794
- message: "Help improve BeastMode with anonymous telemetry? (pipeline metrics only \u2014 no code, no PII)",
7795
- default: false
7796
- }
7797
- ]);
7798
- telemetryEnabled = !!answer.enabled;
7799
- if (telemetryEnabled) {
7800
- const sentryAnswer = await inquirer.prompt([
7801
- {
7802
- type: "input",
7803
- name: "dsn",
7804
- message: "Sentry DSN for error reporting (leave empty to skip):",
7805
- default: ""
7806
- }
7807
- ]);
7808
- telemetrySentryDsn = (sentryAnswer.dsn || "").trim();
7809
- }
7810
- }
7811
- if (telemetryEnabled) {
7812
- success("Telemetry enabled \u2014 anonymous pipeline metrics will be collected");
7813
- info("You can disable anytime: set telemetry.enabled=false in config/beastmode.daemon.json");
7814
- } else {
7815
- info("Telemetry disabled \u2014 no data will be collected");
7816
- }
7817
- step(6, totalSteps, "Boot");
8047
+ step(5, totalSteps, "Boot");
7818
8048
  const projectName = basename4(projectPath);
7819
8049
  const actions = scaffoldFactory(factoryName, config, {
7820
8050
  name: projectName,
@@ -7839,18 +8069,32 @@ async function runInit(name, opts) {
7839
8069
  if (uiPassword) {
7840
8070
  collectedSecrets.push(`BEASTMODE_UI_PASSWORD=${uiPassword}`);
7841
8071
  }
7842
- if (telemetryEnabled) {
7843
- collectedSecrets.push(`BEASTMODE_TELEMETRY_ENABLED=true`);
7844
- if (telemetrySentryDsn) {
7845
- collectedSecrets.push(`BEASTMODE_TELEMETRY_SENTRY_DSN=${telemetrySentryDsn}`);
7846
- }
7847
- }
7848
8072
  if (collectedSecrets.length > 0) {
7849
8073
  const secretsPath = resolve5(factoryName, ".beastmode", "secrets.env.local");
7850
8074
  const secretsContent = "# BeastMode secrets \u2014 DO NOT COMMIT\n" + collectedSecrets.join("\n") + "\n";
7851
8075
  const { writeFileSync: writeSecrets } = await import("fs");
7852
8076
  writeSecrets(secretsPath, secretsContent, "utf-8");
7853
8077
  }
8078
+ if (process.platform === "darwin") {
8079
+ try {
8080
+ const { syncClaudeCredsOnce: syncClaudeCredsOnce2, installAgent: installAgent2 } = await Promise.resolve().then(() => (init_sync_claude_creds(), sync_claude_creds_exports));
8081
+ const sync = syncClaudeCredsOnce2();
8082
+ if ("error" in sync) {
8083
+ warn(`Claude creds sync skipped: ${sync.error}`);
8084
+ info(" Run `beastmode sync-claude-creds --install` after `claude login`.");
8085
+ } else {
8086
+ success(`Claude creds synced: ${sync.path}`);
8087
+ try {
8088
+ installAgent2(1800, { throwOnError: true });
8089
+ } catch (e) {
8090
+ warn(`Claude-creds LaunchAgent install failed: ${e instanceof Error ? e.message : String(e)}`);
8091
+ info(" You can retry with: beastmode sync-claude-creds --install");
8092
+ }
8093
+ }
8094
+ } catch (e) {
8095
+ warn(`Claude-creds setup skipped: ${e instanceof Error ? e.message : String(e)}`);
8096
+ }
8097
+ }
7854
8098
  console.log();
7855
8099
  header(`Factory "${factoryName}" is ready!`);
7856
8100
  console.log();
@@ -7865,7 +8109,7 @@ async function runInit(name, opts) {
7865
8109
  console.log();
7866
8110
  }
7867
8111
  async function runImageModeInit(name, opts) {
7868
- const totalSteps = 5;
8112
+ const totalSteps = 4;
7869
8113
  header("BeastMode \u2014 Factory Init (Image Mode)");
7870
8114
  info("Setting up BeastMode from pre-built Docker images\n");
7871
8115
  const factoryName = name || ".";
@@ -7928,14 +8172,14 @@ async function runImageModeInit(name, opts) {
7928
8172
  let projectPath;
7929
8173
  if (opts.project) {
7930
8174
  projectPath = resolve5(opts.project);
7931
- if (!existsSync17(projectPath)) {
8175
+ if (!existsSync18(projectPath)) {
7932
8176
  error(`--project path does not exist: ${projectPath}`);
7933
8177
  process.exit(1);
7934
8178
  }
7935
8179
  success(`Project path: ${projectPath}`);
7936
8180
  } else if (opts.yes) {
7937
8181
  const cwd = resolve5(".");
7938
- if (!existsSync17(join15(cwd, ".git"))) {
8182
+ if (!existsSync18(join16(cwd, ".git"))) {
7939
8183
  error(
7940
8184
  "--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."
7941
8185
  );
@@ -7952,8 +8196,8 @@ async function runImageModeInit(name, opts) {
7952
8196
  default: ".",
7953
8197
  validate: (input) => {
7954
8198
  const p = resolve5(input);
7955
- if (!existsSync17(p)) return `Directory not found: ${p}`;
7956
- if (!existsSync17(join15(p, ".git"))) {
8199
+ if (!existsSync18(p)) return `Directory not found: ${p}`;
8200
+ if (!existsSync18(join16(p, ".git"))) {
7957
8201
  return `Not a git repo: ${p} (run 'git init' first, or pick a different path)`;
7958
8202
  }
7959
8203
  return true;
@@ -7991,43 +8235,10 @@ async function runImageModeInit(name, opts) {
7991
8235
  } else {
7992
8236
  success("Authenticated to ghcr.io");
7993
8237
  }
7994
- step(3, totalSteps, "Telemetry");
7995
- let telemetryEnabled = false;
7996
- let telemetrySentryDsn = "";
7997
- if (opts.telemetry) {
7998
- telemetryEnabled = true;
7999
- } else if (!opts.yes) {
8000
- const answer = await inquirer.prompt([
8001
- {
8002
- type: "confirm",
8003
- name: "enabled",
8004
- message: "Help improve BeastMode with anonymous telemetry? (pipeline metrics only \u2014 no code, no PII)",
8005
- default: false
8006
- }
8007
- ]);
8008
- telemetryEnabled = !!answer.enabled;
8009
- if (telemetryEnabled) {
8010
- const sentryAnswer = await inquirer.prompt([
8011
- {
8012
- type: "input",
8013
- name: "dsn",
8014
- message: "Sentry DSN for error reporting (leave empty to skip):",
8015
- default: ""
8016
- }
8017
- ]);
8018
- telemetrySentryDsn = (sentryAnswer.dsn || "").trim();
8019
- }
8020
- }
8021
- if (telemetryEnabled) {
8022
- success("Telemetry enabled \u2014 anonymous pipeline metrics will be collected");
8023
- info("You can disable anytime: remove BEASTMODE_TELEMETRY_ENABLED from .env");
8024
- } else {
8025
- info("Telemetry disabled \u2014 no data will be collected");
8026
- }
8027
- step(4, totalSteps, "Generate");
8028
- mkdirSync12(targetDir, { recursive: true });
8238
+ step(3, totalSteps, "Generate");
8239
+ mkdirSync13(targetDir, { recursive: true });
8029
8240
  const composeContent = generateComposeYaml("latest");
8030
- writeFileSync13(join15(targetDir, "docker-compose.yml"), composeContent, "utf-8");
8241
+ writeFileSync14(join16(targetDir, "docker-compose.yml"), composeContent, "utf-8");
8031
8242
  success("docker-compose.yml");
8032
8243
  const envLines = [
8033
8244
  "# BeastMode environment \u2014 DO NOT COMMIT",
@@ -8065,22 +8276,14 @@ async function runImageModeInit(name, opts) {
8065
8276
  "# (git remote get-url origin) doesn't work for your setup.",
8066
8277
  "# The daemon handles this automatically in >99% of cases.",
8067
8278
  "# PROJECT_REPO=owner/repo",
8068
- "",
8069
- "# \u2500\u2500 Telemetry (Gap 15) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
8070
- "# Opt-in anonymous pipeline metrics. Default: off. Collects",
8071
- "# durations, satisfaction scores, iteration counts, error classes.",
8072
- "# Never collects code, file paths, task descriptions, or PII.",
8073
- "# See docs/telemetry.md for the full privacy contract.",
8074
- `BEASTMODE_TELEMETRY_ENABLED=${telemetryEnabled ? "true" : "false"}`,
8075
- `BEASTMODE_TELEMETRY_SENTRY_DSN=${telemetrySentryDsn}`,
8076
8279
  ""
8077
8280
  ];
8078
- writeFileSync13(join15(targetDir, ".env"), envLines.join("\n"), "utf-8");
8281
+ writeFileSync14(join16(targetDir, ".env"), envLines.join("\n"), "utf-8");
8079
8282
  success(`.env (PROJECT_DIR=${projectPath}, two-PAT model)`);
8080
8283
  for (const dir of ["data", "runs", "daemon/logs", ".beastmode", "config"]) {
8081
- mkdirSync12(join15(targetDir, dir), { recursive: true });
8284
+ mkdirSync13(join16(targetDir, dir), { recursive: true });
8082
8285
  }
8083
- step(5, totalSteps, "Pull Images");
8286
+ step(4, totalSteps, "Pull Images");
8084
8287
  try {
8085
8288
  const { execSync: exec } = await import("child_process");
8086
8289
  exec("docker compose pull", {
@@ -8109,6 +8312,26 @@ async function runImageModeInit(name, opts) {
8109
8312
  console.log();
8110
8313
  warn("Files generated \u2014 fix auth above, then run: beastmode up --pull");
8111
8314
  }
8315
+ if (process.platform === "darwin") {
8316
+ try {
8317
+ const { syncClaudeCredsOnce: syncClaudeCredsOnce2, installAgent: installAgent2 } = await Promise.resolve().then(() => (init_sync_claude_creds(), sync_claude_creds_exports));
8318
+ const sync = syncClaudeCredsOnce2();
8319
+ if ("error" in sync) {
8320
+ warn(`Claude creds sync skipped: ${sync.error}`);
8321
+ info(" Run `beastmode sync-claude-creds --install` after `claude login`.");
8322
+ } else {
8323
+ success(`Claude creds synced: ${sync.path}`);
8324
+ try {
8325
+ installAgent2(1800, { throwOnError: true });
8326
+ } catch (e) {
8327
+ warn(`Claude-creds LaunchAgent install failed: ${e instanceof Error ? e.message : String(e)}`);
8328
+ info(" You can retry with: beastmode sync-claude-creds --install");
8329
+ }
8330
+ }
8331
+ } catch (e) {
8332
+ warn(`Claude-creds setup skipped: ${e instanceof Error ? e.message : String(e)}`);
8333
+ }
8334
+ }
8112
8335
  console.log();
8113
8336
  header("BeastMode is ready!");
8114
8337
  console.log();
@@ -8126,20 +8349,21 @@ async function runImageModeInit(name, opts) {
8126
8349
 
8127
8350
  // src/cli/commands/export-config.ts
8128
8351
  init_export_adapter();
8129
- import { Command as Command2 } from "commander";
8130
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync14, existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
8131
- import { resolve as resolve6, join as join16 } from "path";
8352
+ init_display();
8353
+ import { Command as Command3 } from "commander";
8354
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync15, existsSync as existsSync19, readdirSync as readdirSync7 } from "fs";
8355
+ import { resolve as resolve6, join as join17 } from "path";
8132
8356
  function exportFactory(factoryDir, outputPath) {
8133
- const bmDir = join16(factoryDir, ".beastmode");
8134
- const configPath = join16(bmDir, "config.json");
8135
- if (!existsSync18(configPath)) {
8357
+ const bmDir = join17(factoryDir, ".beastmode");
8358
+ const configPath = join17(bmDir, "config.json");
8359
+ if (!existsSync19(configPath)) {
8136
8360
  throw new Error(`No factory found at ${factoryDir}`);
8137
8361
  }
8138
8362
  const config = JSON.parse(readFileSync15(configPath, "utf-8"));
8139
- const identity = JSON.parse(readFileSync15(join16(bmDir, "factory.json"), "utf-8"));
8363
+ const identity = JSON.parse(readFileSync15(join17(bmDir, "factory.json"), "utf-8"));
8140
8364
  let plugins = [];
8141
- const lockPath = join16(bmDir, "extensions.lock");
8142
- if (existsSync18(lockPath)) {
8365
+ const lockPath = join17(bmDir, "extensions.lock");
8366
+ if (existsSync19(lockPath)) {
8143
8367
  const lock = JSON.parse(readFileSync15(lockPath, "utf-8"));
8144
8368
  plugins = Object.keys(lock.plugins || {});
8145
8369
  }
@@ -8149,7 +8373,7 @@ function exportFactory(factoryDir, outputPath) {
8149
8373
  config,
8150
8374
  plugins
8151
8375
  };
8152
- writeFileSync14(outputPath, JSON.stringify(template, null, 2) + "\n", "utf-8");
8376
+ writeFileSync15(outputPath, JSON.stringify(template, null, 2) + "\n", "utf-8");
8153
8377
  }
8154
8378
  function findRunDir(runId) {
8155
8379
  const candidates = [
@@ -8157,7 +8381,7 @@ function findRunDir(runId) {
8157
8381
  resolve6(".", runId)
8158
8382
  ];
8159
8383
  for (const candidate of candidates) {
8160
- if (existsSync18(candidate)) {
8384
+ if (existsSync19(candidate)) {
8161
8385
  return candidate;
8162
8386
  }
8163
8387
  }
@@ -8166,28 +8390,28 @@ function findRunDir(runId) {
8166
8390
  );
8167
8391
  }
8168
8392
  function createArtifactExportCommand(artifact) {
8169
- return new Command2(artifact).description(`Export ${artifact} from a pipeline run`).requiredOption("--run <id>", "Run ID to export from").option("--adapter <id>", "Export adapter (default: generic:markdown)", "generic:markdown").option("--output <path>", "Output file path (default: stdout)").action((opts) => {
8393
+ return new Command3(artifact).description(`Export ${artifact} from a pipeline run`).requiredOption("--run <id>", "Run ID to export from").option("--adapter <id>", "Export adapter (default: generic:markdown)", "generic:markdown").option("--output <path>", "Output file path (default: stdout)").action((opts) => {
8170
8394
  try {
8171
8395
  const runDir = findRunDir(opts.run);
8172
8396
  let sourceContent;
8173
8397
  if (artifact === "scenarios") {
8174
- const scenariosDir = join16(runDir, "scenarios");
8175
- if (!existsSync18(scenariosDir)) {
8398
+ const scenariosDir = join17(runDir, "scenarios");
8399
+ if (!existsSync19(scenariosDir)) {
8176
8400
  throw new Error(`No scenarios directory found in run ${opts.run}`);
8177
8401
  }
8178
8402
  const files = readdirSync7(scenariosDir).filter((f) => f.endsWith(".md")).sort();
8179
- sourceContent = files.map((f) => readFileSync15(join16(scenariosDir, f), "utf-8")).join("\n\n---\n\n");
8403
+ sourceContent = files.map((f) => readFileSync15(join17(scenariosDir, f), "utf-8")).join("\n\n---\n\n");
8180
8404
  } else {
8181
8405
  const artifactFile = artifact === "nlspec" ? "nlspec.md" : "plan.md";
8182
- const artifactPath = join16(runDir, artifactFile);
8183
- if (!existsSync18(artifactPath)) {
8406
+ const artifactPath = join17(runDir, artifactFile);
8407
+ if (!existsSync19(artifactPath)) {
8184
8408
  throw new Error(`${artifactFile} not found in run ${opts.run}`);
8185
8409
  }
8186
8410
  sourceContent = readFileSync15(artifactPath, "utf-8");
8187
8411
  }
8188
8412
  const result = runExportAdapter(opts.adapter, sourceContent);
8189
8413
  if (opts.output) {
8190
- writeFileSync14(resolve6(opts.output), result, "utf-8");
8414
+ writeFileSync15(resolve6(opts.output), result, "utf-8");
8191
8415
  header(`Exported ${artifact}`);
8192
8416
  success(`Written to: ${opts.output}`);
8193
8417
  } else {
@@ -8199,9 +8423,9 @@ function createArtifactExportCommand(artifact) {
8199
8423
  }
8200
8424
  });
8201
8425
  }
8202
- var exportCommand = new Command2("export").description("Export factory config or pipeline artifacts");
8426
+ var exportCommand = new Command3("export").description("Export factory config or pipeline artifacts");
8203
8427
  exportCommand.addCommand(
8204
- new Command2("config").description("Export factory configuration template (secrets excluded)").option("--output <path>", "Output file path", "beastmode-template.json").action((opts) => {
8428
+ new Command3("config").description("Export factory configuration template (secrets excluded)").option("--output <path>", "Output file path", "beastmode-template.json").action((opts) => {
8205
8429
  try {
8206
8430
  const factoryDir = resolve6(".");
8207
8431
  exportFactory(factoryDir, resolve6(opts.output));
@@ -8220,9 +8444,10 @@ exportCommand.addCommand(createArtifactExportCommand("scenarios"));
8220
8444
 
8221
8445
  // src/cli/commands/validate.ts
8222
8446
  init_config_validator();
8223
- import { Command as Command3 } from "commander";
8447
+ init_display();
8448
+ import { Command as Command4 } from "commander";
8224
8449
  import { resolve as resolve7 } from "path";
8225
- var validateCommand = new Command3("validate").description("Validate factory health").option("--verbose", "Show all checks").action((opts) => {
8450
+ var validateCommand = new Command4("validate").description("Validate factory health").option("--verbose", "Show all checks").action((opts) => {
8226
8451
  const factoryDir = resolve7(".");
8227
8452
  const result = validateFactory(factoryDir, process.env);
8228
8453
  header("Factory Validation");
@@ -8251,7 +8476,8 @@ init_plugin_installer();
8251
8476
  init_mcp_manager();
8252
8477
  init_hook_manager();
8253
8478
  init_skill_manager();
8254
- import { Command as Command4 } from "commander";
8479
+ init_display();
8480
+ import { Command as Command5 } from "commander";
8255
8481
  import { resolve as resolve8 } from "path";
8256
8482
  async function addPluginAction(factoryDir, source, options) {
8257
8483
  await installPlugin(factoryDir, source);
@@ -8278,8 +8504,8 @@ function addHookAction(factoryDir, event, name, options) {
8278
8504
  source: "user"
8279
8505
  });
8280
8506
  }
8281
- var addCommand = new Command4("add").description("Add extensions to the factory").addCommand(
8282
- new Command4("plugin").description("Install a plugin (from path, URL, or registry name)").argument("<source>", "Plugin source: local path, git URL, or registry name").option("--version <ver>", "Specific version to install").action(async (source, opts) => {
8507
+ var addCommand = new Command5("add").description("Add extensions to the factory").addCommand(
8508
+ new Command5("plugin").description("Install a plugin (from path, URL, or registry name)").argument("<source>", "Plugin source: local path, git URL, or registry name").option("--version <ver>", "Specific version to install").action(async (source, opts) => {
8283
8509
  try {
8284
8510
  const factoryDir = resolve8(".");
8285
8511
  await addPluginAction(factoryDir, source, opts);
@@ -8290,7 +8516,7 @@ var addCommand = new Command4("add").description("Add extensions to the factory"
8290
8516
  }
8291
8517
  })
8292
8518
  ).addCommand(
8293
- new Command4("mcp").description("Add an MCP server").argument("<name>", "Server name").requiredOption("--command <cmd>", "Command to run").option("--args <args...>", "Command arguments", []).option("--config <json>", "Configuration as JSON string").action(
8519
+ new Command5("mcp").description("Add an MCP server").argument("<name>", "Server name").requiredOption("--command <cmd>", "Command to run").option("--args <args...>", "Command arguments", []).option("--config <json>", "Configuration as JSON string").action(
8294
8520
  (name, opts) => {
8295
8521
  try {
8296
8522
  const factoryDir = resolve8(".");
@@ -8304,7 +8530,7 @@ var addCommand = new Command4("add").description("Add extensions to the factory"
8304
8530
  }
8305
8531
  )
8306
8532
  ).addCommand(
8307
- new Command4("skill").description("Add a custom skill from a directory").argument("<path>", "Path to skill directory (must contain SKILL.md)").action((path) => {
8533
+ new Command5("skill").description("Add a custom skill from a directory").argument("<path>", "Path to skill directory (must contain SKILL.md)").action((path) => {
8308
8534
  try {
8309
8535
  const factoryDir = resolve8(".");
8310
8536
  addSkillAction(factoryDir, path);
@@ -8315,7 +8541,7 @@ var addCommand = new Command4("add").description("Add extensions to the factory"
8315
8541
  }
8316
8542
  })
8317
8543
  ).addCommand(
8318
- new Command4("hook").description("Add a pipeline hook").argument("<event>", "Hook event (e.g., pre-build, post-verify)").argument("<name>", "Hook name").option("--command <cmd>", "Shell command to run").option("--skill <name>", "Skill to invoke").option("--webhook <url>", "Webhook URL to POST to").option("--config <json>", "Configuration as JSON string").action(
8544
+ new Command5("hook").description("Add a pipeline hook").argument("<event>", "Hook event (e.g., pre-build, post-verify)").argument("<name>", "Hook name").option("--command <cmd>", "Shell command to run").option("--skill <name>", "Skill to invoke").option("--webhook <url>", "Webhook URL to POST to").option("--config <json>", "Configuration as JSON string").action(
8319
8545
  (event, name, opts) => {
8320
8546
  try {
8321
8547
  const factoryDir = resolve8(".");
@@ -8340,7 +8566,8 @@ init_plugin_installer();
8340
8566
  init_mcp_manager();
8341
8567
  init_hook_manager();
8342
8568
  init_skill_manager();
8343
- import { Command as Command5 } from "commander";
8569
+ init_display();
8570
+ import { Command as Command6 } from "commander";
8344
8571
  import { resolve as resolve9 } from "path";
8345
8572
  function removePluginAction(factoryDir, name, options) {
8346
8573
  removePlugin(factoryDir, name, options);
@@ -8354,8 +8581,8 @@ function removeSkillAction(factoryDir, name) {
8354
8581
  function removeHookAction(factoryDir, event, name) {
8355
8582
  removeHook(factoryDir, event, name);
8356
8583
  }
8357
- var removeCommand = new Command5("remove").description("Remove extensions from the factory").addCommand(
8358
- new Command5("plugin").description("Remove an installed plugin").argument("<name>", "Plugin name").option("--force", "Force removal even if other plugins depend on it").action((name, opts) => {
8584
+ var removeCommand = new Command6("remove").description("Remove extensions from the factory").addCommand(
8585
+ new Command6("plugin").description("Remove an installed plugin").argument("<name>", "Plugin name").option("--force", "Force removal even if other plugins depend on it").action((name, opts) => {
8359
8586
  try {
8360
8587
  const factoryDir = resolve9(".");
8361
8588
  removePluginAction(factoryDir, name, opts);
@@ -8366,7 +8593,7 @@ var removeCommand = new Command5("remove").description("Remove extensions from t
8366
8593
  }
8367
8594
  })
8368
8595
  ).addCommand(
8369
- new Command5("mcp").description("Remove an MCP server").argument("<name>", "Server name").action((name) => {
8596
+ new Command6("mcp").description("Remove an MCP server").argument("<name>", "Server name").action((name) => {
8370
8597
  try {
8371
8598
  const factoryDir = resolve9(".");
8372
8599
  removeMcpAction(factoryDir, name);
@@ -8377,7 +8604,7 @@ var removeCommand = new Command5("remove").description("Remove extensions from t
8377
8604
  }
8378
8605
  })
8379
8606
  ).addCommand(
8380
- new Command5("skill").description("Remove a custom skill").argument("<name>", "Skill name").action((name) => {
8607
+ new Command6("skill").description("Remove a custom skill").argument("<name>", "Skill name").action((name) => {
8381
8608
  try {
8382
8609
  const factoryDir = resolve9(".");
8383
8610
  removeSkillAction(factoryDir, name);
@@ -8388,7 +8615,7 @@ var removeCommand = new Command5("remove").description("Remove extensions from t
8388
8615
  }
8389
8616
  })
8390
8617
  ).addCommand(
8391
- new Command5("hook").description("Remove a pipeline hook").argument("<event>", "Hook event (e.g., pre-build, post-verify)").argument("<name>", "Hook name").action((event, name) => {
8618
+ new Command6("hook").description("Remove a pipeline hook").argument("<event>", "Hook event (e.g., pre-build, post-verify)").argument("<name>", "Hook name").action((event, name) => {
8392
8619
  try {
8393
8620
  const factoryDir = resolve9(".");
8394
8621
  removeHookAction(factoryDir, event, name);
@@ -8405,13 +8632,14 @@ init_mcp_manager();
8405
8632
  init_hook_manager();
8406
8633
  init_skill_manager();
8407
8634
  init_presets();
8408
- import { Command as Command6 } from "commander";
8635
+ init_display();
8636
+ import { Command as Command7 } from "commander";
8409
8637
  import { resolve as resolve10 } from "path";
8410
- import { readFileSync as readFileSync16, existsSync as existsSync19 } from "fs";
8411
- import { join as join17 } from "path";
8638
+ import { readFileSync as readFileSync16, existsSync as existsSync20 } from "fs";
8639
+ import { join as join18 } from "path";
8412
8640
  function listPluginsAction(factoryDir) {
8413
- const lockPath = join17(factoryDir, ".beastmode", "extensions.lock");
8414
- if (!existsSync19(lockPath)) {
8641
+ const lockPath = join18(factoryDir, ".beastmode", "extensions.lock");
8642
+ if (!existsSync20(lockPath)) {
8415
8643
  console.log(" No plugins installed (extensions.lock not found).");
8416
8644
  return;
8417
8645
  }
@@ -8488,39 +8716,40 @@ function listPresetsAction() {
8488
8716
  console.log(` ${name} \u2014 ${preset.description}`);
8489
8717
  }
8490
8718
  }
8491
- var listCommand = new Command6("list").description("List installed extensions and resources").addCommand(
8492
- new Command6("plugins").description("List installed plugins").action(() => {
8719
+ var listCommand = new Command7("list").description("List installed extensions and resources").addCommand(
8720
+ new Command7("plugins").description("List installed plugins").action(() => {
8493
8721
  listPluginsAction(resolve10("."));
8494
8722
  })
8495
8723
  ).addCommand(
8496
- new Command6("mcps").description("List configured MCP servers").action(() => {
8724
+ new Command7("mcps").description("List configured MCP servers").action(() => {
8497
8725
  listMcpsAction(resolve10("."));
8498
8726
  })
8499
8727
  ).addCommand(
8500
- new Command6("skills").description("List available skills").action(() => {
8728
+ new Command7("skills").description("List available skills").action(() => {
8501
8729
  listSkillsAction(resolve10("."));
8502
8730
  })
8503
8731
  ).addCommand(
8504
- new Command6("hooks").description("List configured hooks").action(() => {
8732
+ new Command7("hooks").description("List configured hooks").action(() => {
8505
8733
  listHooksAction(resolve10("."));
8506
8734
  })
8507
8735
  ).addCommand(
8508
- new Command6("presets").description("List available pipeline presets").action(() => {
8736
+ new Command7("presets").description("List available pipeline presets").action(() => {
8509
8737
  listPresetsAction();
8510
8738
  })
8511
8739
  );
8512
8740
 
8513
8741
  // src/cli/commands/import-cmd.ts
8514
8742
  init_import_adapter();
8515
- import { Command as Command7 } from "commander";
8516
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync15, mkdirSync as mkdirSync13, existsSync as existsSync20 } from "fs";
8517
- import { resolve as resolve11, join as join18 } from "path";
8743
+ init_display();
8744
+ import { Command as Command8 } from "commander";
8745
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync16, mkdirSync as mkdirSync14, existsSync as existsSync21 } from "fs";
8746
+ import { resolve as resolve11, join as join19 } from "path";
8518
8747
  var VALID_ARTIFACTS = ["nlspec", "plan", "scenarios"];
8519
8748
  function createArtifactCommand(artifact) {
8520
- return new Command7(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) => {
8749
+ return new Command8(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) => {
8521
8750
  try {
8522
8751
  const sourcePath = resolve11(opts.from);
8523
- if (!existsSync20(sourcePath)) {
8752
+ if (!existsSync21(sourcePath)) {
8524
8753
  throw new Error(`Source file not found: ${sourcePath}`);
8525
8754
  }
8526
8755
  const sourceContent = readFileSync17(sourcePath, "utf-8");
@@ -8529,15 +8758,15 @@ function createArtifactCommand(artifact) {
8529
8758
  const outputPath = resolve11(opts.output);
8530
8759
  if (artifact === "scenarios" && opts.adapter.endsWith(":test-cases")) {
8531
8760
  const scenarios = JSON.parse(result);
8532
- mkdirSync13(outputPath, { recursive: true });
8761
+ mkdirSync14(outputPath, { recursive: true });
8533
8762
  for (const scenario of scenarios) {
8534
- const filePath = join18(outputPath, `${scenario.name}.md`);
8535
- writeFileSync15(filePath, scenario.content, "utf-8");
8763
+ const filePath = join19(outputPath, `${scenario.name}.md`);
8764
+ writeFileSync16(filePath, scenario.content, "utf-8");
8536
8765
  success(` ${scenario.name}.md`);
8537
8766
  }
8538
8767
  header(`Imported ${scenarios.length} scenarios to ${opts.output}`);
8539
8768
  } else {
8540
- writeFileSync15(outputPath, result, "utf-8");
8769
+ writeFileSync16(outputPath, result, "utf-8");
8541
8770
  header(`Imported ${artifact}`);
8542
8771
  success(`Written to: ${opts.output}`);
8543
8772
  }
@@ -8550,7 +8779,7 @@ function createArtifactCommand(artifact) {
8550
8779
  }
8551
8780
  });
8552
8781
  }
8553
- var importCommand = new Command7("import").description("Import external artifacts into BeastMode format");
8782
+ var importCommand = new Command8("import").description("Import external artifacts into BeastMode format");
8554
8783
  for (const artifact of VALID_ARTIFACTS) {
8555
8784
  importCommand.addCommand(createArtifactCommand(artifact));
8556
8785
  }
@@ -8558,51 +8787,52 @@ for (const artifact of VALID_ARTIFACTS) {
8558
8787
  // src/cli/commands/status.ts
8559
8788
  init_status_checker();
8560
8789
  init_schemas();
8561
- import { Command as Command8 } from "commander";
8562
- import { existsSync as existsSync21, readFileSync as readFileSync18, readdirSync as readdirSync8 } from "fs";
8563
- import { execSync as execSync4 } from "child_process";
8564
- import { join as join19, resolve as resolve12 } from "path";
8790
+ init_display();
8791
+ import { Command as Command9 } from "commander";
8792
+ import { existsSync as existsSync22, readFileSync as readFileSync18, readdirSync as readdirSync8 } from "fs";
8793
+ import { execSync as execSync5 } from "child_process";
8794
+ import { join as join20, resolve as resolve12 } from "path";
8565
8795
  function statusAction(factoryDir, opts) {
8566
- const bmDir = join19(factoryDir, ".beastmode");
8567
- if (!existsSync21(bmDir)) {
8796
+ const bmDir = join20(factoryDir, ".beastmode");
8797
+ if (!existsSync22(bmDir)) {
8568
8798
  throw new Error(`No factory found at ${factoryDir}`);
8569
8799
  }
8570
- const factoryJsonPath = join19(bmDir, "factory.json");
8800
+ const factoryJsonPath = join20(bmDir, "factory.json");
8571
8801
  const rawIdentity = JSON.parse(readFileSync18(factoryJsonPath, "utf-8"));
8572
8802
  const factoryIdentity = FactoryIdentitySchema.parse(rawIdentity);
8573
- const projectsDir = join19(bmDir, "projects");
8574
- const projectCount = existsSync21(projectsDir) ? readdirSync8(projectsDir).filter((f) => f.endsWith(".json")).length : 0;
8575
- const pluginsDir = join19(bmDir, "plugins");
8576
- const pluginNames = existsSync21(pluginsDir) ? readdirSync8(pluginsDir) : [];
8577
- const mcpPath = join19(bmDir, "mcp-servers.json");
8803
+ const projectsDir = join20(bmDir, "projects");
8804
+ const projectCount = existsSync22(projectsDir) ? readdirSync8(projectsDir).filter((f) => f.endsWith(".json")).length : 0;
8805
+ const pluginsDir = join20(bmDir, "plugins");
8806
+ const pluginNames = existsSync22(pluginsDir) ? readdirSync8(pluginsDir) : [];
8807
+ const mcpPath = join20(bmDir, "mcp-servers.json");
8578
8808
  let mcpServers = {};
8579
- if (existsSync21(mcpPath)) {
8809
+ if (existsSync22(mcpPath)) {
8580
8810
  try {
8581
8811
  const raw = JSON.parse(readFileSync18(mcpPath, "utf-8"));
8582
8812
  mcpServers = raw.servers || {};
8583
8813
  } catch {
8584
8814
  }
8585
8815
  }
8586
- const hooksPath = join19(bmDir, "hooks.json");
8816
+ const hooksPath = join20(bmDir, "hooks.json");
8587
8817
  let hooks = {};
8588
- if (existsSync21(hooksPath)) {
8818
+ if (existsSync22(hooksPath)) {
8589
8819
  try {
8590
8820
  const raw = JSON.parse(readFileSync18(hooksPath, "utf-8"));
8591
8821
  hooks = raw.hooks || {};
8592
8822
  } catch {
8593
8823
  }
8594
8824
  }
8595
- const skillsDir = join19(bmDir, "skills");
8596
- const skillCount = existsSync21(skillsDir) ? readdirSync8(skillsDir).length : 0;
8597
- const runsDir = join19(factoryDir, "runs");
8825
+ const skillsDir = join20(bmDir, "skills");
8826
+ const skillCount = existsSync22(skillsDir) ? readdirSync8(skillsDir).length : 0;
8827
+ const runsDir = join20(factoryDir, "runs");
8598
8828
  let runDirs = [];
8599
- if (existsSync21(runsDir)) {
8829
+ if (existsSync22(runsDir)) {
8600
8830
  runDirs = readdirSync8(runsDir).filter((d) => d.startsWith("run-")).sort();
8601
8831
  }
8602
- const pidPath = join19(bmDir, "daemon.pid");
8832
+ const pidPath = join20(bmDir, "daemon.pid");
8603
8833
  let daemonPid = null;
8604
8834
  let pidAlive = false;
8605
- if (existsSync21(pidPath)) {
8835
+ if (existsSync22(pidPath)) {
8606
8836
  try {
8607
8837
  daemonPid = parseInt(readFileSync18(pidPath, "utf-8").trim(), 10);
8608
8838
  process.kill(daemonPid, 0);
@@ -8613,7 +8843,7 @@ function statusAction(factoryDir, opts) {
8613
8843
  }
8614
8844
  if (!pidAlive) {
8615
8845
  try {
8616
- const out = execSync4("pgrep -f 'beastmode_daemon' 2>/dev/null || true", {
8846
+ const out = execSync5("pgrep -f 'beastmode_daemon' 2>/dev/null || true", {
8617
8847
  encoding: "utf-8",
8618
8848
  timeout: 3e3
8619
8849
  }).trim();
@@ -8640,7 +8870,7 @@ function statusAction(factoryDir, opts) {
8640
8870
  };
8641
8871
  return collectStatus(input);
8642
8872
  }
8643
- var statusCommand = new Command8("status").description("Show factory status overview").option("--json", "Output as JSON").option("--watch", "Enable watch mode (display layer)").action((opts) => {
8873
+ var statusCommand = new Command9("status").description("Show factory status overview").option("--json", "Output as JSON").option("--watch", "Enable watch mode (display layer)").action((opts) => {
8644
8874
  const factoryDir = resolve12(".");
8645
8875
  try {
8646
8876
  const status = statusAction(factoryDir, { json: !!opts.json });
@@ -8676,20 +8906,21 @@ var statusCommand = new Command8("status").description("Show factory status over
8676
8906
 
8677
8907
  // src/cli/commands/config-cmd.ts
8678
8908
  init_config_manager();
8679
- import { Command as Command9 } from "commander";
8680
- import { existsSync as existsSync22, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "fs";
8681
- import { join as join20, resolve as resolve13 } from "path";
8682
- import { execSync as execSync5 } from "child_process";
8909
+ init_display();
8910
+ import { Command as Command10 } from "commander";
8911
+ import { existsSync as existsSync23, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "fs";
8912
+ import { join as join21, resolve as resolve13 } from "path";
8913
+ import { execSync as execSync6 } from "child_process";
8683
8914
  function readConfig2(factoryDir) {
8684
- const configPath = join20(factoryDir, ".beastmode", "config.json");
8685
- if (!existsSync22(configPath)) {
8915
+ const configPath = join21(factoryDir, ".beastmode", "config.json");
8916
+ if (!existsSync23(configPath)) {
8686
8917
  throw new Error("No config.json found. Run beastmode init first.");
8687
8918
  }
8688
8919
  return JSON.parse(readFileSync19(configPath, "utf-8"));
8689
8920
  }
8690
8921
  function writeConfig2(factoryDir, config) {
8691
- const configPath = join20(factoryDir, ".beastmode", "config.json");
8692
- writeFileSync16(configPath, JSON.stringify(config, null, 2) + "\n");
8922
+ const configPath = join21(factoryDir, ".beastmode", "config.json");
8923
+ writeFileSync17(configPath, JSON.stringify(config, null, 2) + "\n");
8693
8924
  }
8694
8925
  function configGetAction(factoryDir, key) {
8695
8926
  const config = readConfig2(factoryDir);
@@ -8706,7 +8937,7 @@ function configResetAction(factoryDir, key) {
8706
8937
  const updated = configReset(config, key);
8707
8938
  writeConfig2(factoryDir, updated);
8708
8939
  }
8709
- var configGetCmd = new Command9("get").description("Get a config value by dot-notation key").argument("<key>", "Config key (e.g., pipeline.preset)").action((key) => {
8940
+ var configGetCmd = new Command10("get").description("Get a config value by dot-notation key").argument("<key>", "Config key (e.g., pipeline.preset)").action((key) => {
8710
8941
  const factoryDir = resolve13(".");
8711
8942
  try {
8712
8943
  const value = configGetAction(factoryDir, key);
@@ -8720,7 +8951,7 @@ var configGetCmd = new Command9("get").description("Get a config value by dot-no
8720
8951
  process.exit(1);
8721
8952
  }
8722
8953
  });
8723
- var configSetCmd = new Command9("set").description("Set a config value by dot-notation key").argument("<key>", "Config key (e.g., pipeline.preset)").argument("<value>", "Value to set (auto-coerces types)").action((key, value) => {
8954
+ var configSetCmd = new Command10("set").description("Set a config value by dot-notation key").argument("<key>", "Config key (e.g., pipeline.preset)").argument("<value>", "Value to set (auto-coerces types)").action((key, value) => {
8724
8955
  const factoryDir = resolve13(".");
8725
8956
  try {
8726
8957
  configSetAction(factoryDir, key, value);
@@ -8730,22 +8961,22 @@ var configSetCmd = new Command9("set").description("Set a config value by dot-no
8730
8961
  process.exit(1);
8731
8962
  }
8732
8963
  });
8733
- var configEditCmd = new Command9("edit").description("Open config.json in $EDITOR").action(() => {
8964
+ var configEditCmd = new Command10("edit").description("Open config.json in $EDITOR").action(() => {
8734
8965
  const factoryDir = resolve13(".");
8735
- const configPath = join20(factoryDir, ".beastmode", "config.json");
8736
- if (!existsSync22(configPath)) {
8966
+ const configPath = join21(factoryDir, ".beastmode", "config.json");
8967
+ if (!existsSync23(configPath)) {
8737
8968
  error("No config.json found. Run beastmode init first.");
8738
8969
  process.exit(1);
8739
8970
  }
8740
8971
  const editor = process.env.EDITOR || "vi";
8741
8972
  try {
8742
- execSync5(`${editor} ${configPath}`, { stdio: "inherit" });
8973
+ execSync6(`${editor} ${configPath}`, { stdio: "inherit" });
8743
8974
  } catch {
8744
8975
  error(`Failed to open editor: ${editor}`);
8745
8976
  process.exit(1);
8746
8977
  }
8747
8978
  });
8748
- var configResetCmd = new Command9("reset").description("Reset config to defaults (all or specific key)").argument("[key]", "Optional key to reset (resets all if omitted)").action((key) => {
8979
+ var configResetCmd = new Command10("reset").description("Reset config to defaults (all or specific key)").argument("[key]", "Optional key to reset (resets all if omitted)").action((key) => {
8749
8980
  const factoryDir = resolve13(".");
8750
8981
  try {
8751
8982
  configResetAction(factoryDir, key);
@@ -8759,27 +8990,29 @@ var configResetCmd = new Command9("reset").description("Reset config to defaults
8759
8990
  process.exit(1);
8760
8991
  }
8761
8992
  });
8762
- var configCommand = new Command9("config").description("Manage factory configuration").addCommand(configGetCmd).addCommand(configSetCmd).addCommand(configEditCmd).addCommand(configResetCmd);
8993
+ var configCommand = new Command10("config").description("Manage factory configuration").addCommand(configGetCmd).addCommand(configSetCmd).addCommand(configEditCmd).addCommand(configResetCmd);
8763
8994
 
8764
8995
  // src/cli/commands/doctor.ts
8765
8996
  init_doctor();
8766
8997
  init_schemas();
8767
8998
  init_version();
8768
8999
  init_plugin_resolver();
8769
- import { Command as Command11 } from "commander";
8770
- import { existsSync as existsSync24, readFileSync as readFileSync21, readdirSync as readdirSync10 } from "fs";
8771
- import { join as join22, resolve as resolve15 } from "path";
8772
- import { execSync as execSync7 } from "child_process";
8773
- import { homedir as homedir2, platform } from "os";
9000
+ init_display();
9001
+ import { Command as Command12 } from "commander";
9002
+ import { existsSync as existsSync25, readFileSync as readFileSync21, readdirSync as readdirSync10 } from "fs";
9003
+ import { join as join23, resolve as resolve15 } from "path";
9004
+ import { execSync as execSync8 } from "child_process";
9005
+ import { homedir as homedir3, platform as platform2 } from "os";
8774
9006
  import * as http3 from "http";
8775
9007
  import * as https from "https";
8776
9008
 
8777
9009
  // src/cli/commands/board.ts
8778
- import { Command as Command10 } from "commander";
8779
- import { resolve as resolve14, join as join21 } from "path";
8780
- import { existsSync as existsSync23, readFileSync as readFileSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync17, readdirSync as readdirSync9 } from "fs";
8781
- import { execSync as execSync6 } from "child_process";
8782
- var boardCommand = new Command10("board").description("Launch the BeastMode Board web UI").option("--port <number>", "Port to serve on", "7669").option("--host <host>", "Host to bind to (use 0.0.0.0 for external access)", "127.0.0.1").action(async (opts) => {
9010
+ init_display();
9011
+ import { Command as Command11 } from "commander";
9012
+ import { resolve as resolve14, join as join22 } from "path";
9013
+ import { existsSync as existsSync24, readFileSync as readFileSync20, mkdirSync as mkdirSync15, writeFileSync as writeFileSync18, readdirSync as readdirSync9 } from "fs";
9014
+ import { execSync as execSync7 } from "child_process";
9015
+ var boardCommand = new Command11("board").description("Launch the BeastMode Board web UI").option("--port <number>", "Port to serve on", "7669").option("--host <host>", "Host to bind to (use 0.0.0.0 for external access)", "127.0.0.1").action(async (opts) => {
8783
9016
  try {
8784
9017
  await runBoard(opts);
8785
9018
  } catch (err) {
@@ -8801,28 +9034,28 @@ function findFactoryDir(startDir) {
8801
9034
  let dir = startDir || process.cwd();
8802
9035
  const root = resolve14("/");
8803
9036
  while (dir !== root) {
8804
- if (existsSync23(join21(dir, ".beastmode", "factory.json"))) {
9037
+ if (existsSync24(join22(dir, ".beastmode", "factory.json"))) {
8805
9038
  return dir;
8806
9039
  }
8807
9040
  const parent = resolve14(dir, "..");
8808
9041
  if (parent === dir) break;
8809
9042
  dir = parent;
8810
9043
  }
8811
- if (existsSync23(join21(dir, ".beastmode", "factory.json"))) {
9044
+ if (existsSync24(join22(dir, ".beastmode", "factory.json"))) {
8812
9045
  return dir;
8813
9046
  }
8814
9047
  return null;
8815
9048
  }
8816
9049
  function inferProjectName(factoryDir) {
8817
- const projectsDir = join21(factoryDir, ".beastmode", "projects");
8818
- if (!existsSync23(projectsDir)) return null;
9050
+ const projectsDir = join22(factoryDir, ".beastmode", "projects");
9051
+ if (!existsSync24(projectsDir)) return null;
8819
9052
  const files = readdirSync9(projectsDir).filter((f) => f.endsWith(".json"));
8820
9053
  if (files.length === 1) return files[0].replace(/\.json$/, "");
8821
9054
  return null;
8822
9055
  }
8823
9056
  function tryExecSync(cmd, timeoutMs = 15e3) {
8824
9057
  try {
8825
- return execSync6(cmd, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: timeoutMs }).trim();
9058
+ return execSync7(cmd, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: timeoutMs }).trim();
8826
9059
  } catch {
8827
9060
  return null;
8828
9061
  }
@@ -8889,13 +9122,13 @@ async function runBoard(opts) {
8889
9122
  let factoryDir = findFactoryDir();
8890
9123
  if (!factoryDir) {
8891
9124
  factoryDir = process.cwd();
8892
- const bmDir = join21(factoryDir, ".beastmode");
8893
- if (!existsSync23(bmDir)) {
8894
- mkdirSync14(bmDir, { recursive: true });
9125
+ const bmDir = join22(factoryDir, ".beastmode");
9126
+ if (!existsSync24(bmDir)) {
9127
+ mkdirSync15(bmDir, { recursive: true });
8895
9128
  }
8896
- const factoryJsonPath2 = join21(bmDir, "factory.json");
8897
- if (!existsSync23(factoryJsonPath2)) {
8898
- writeFileSync17(
9129
+ const factoryJsonPath2 = join22(bmDir, "factory.json");
9130
+ if (!existsSync24(factoryJsonPath2)) {
9131
+ writeFileSync18(
8899
9132
  factoryJsonPath2,
8900
9133
  JSON.stringify({ factory_name: "BeastMode", created_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
8901
9134
  "utf-8"
@@ -8903,7 +9136,7 @@ async function runBoard(opts) {
8903
9136
  info("No factory found \u2014 created minimal stub at .beastmode/factory.json");
8904
9137
  }
8905
9138
  }
8906
- const factoryJsonPath = join21(factoryDir, ".beastmode", "factory.json");
9139
+ const factoryJsonPath = join22(factoryDir, ".beastmode", "factory.json");
8907
9140
  const factoryJson = JSON.parse(readFileSync20(factoryJsonPath, "utf-8"));
8908
9141
  const factoryName = factoryJson.factory_name || "BeastMode Factory";
8909
9142
  header(`BeastMode Board \u2014 ${factoryName}`);
@@ -8942,7 +9175,7 @@ async function runBoard(opts) {
8942
9175
  // src/cli/commands/doctor.ts
8943
9176
  function tryExec(cmd, timeout = 8e3) {
8944
9177
  try {
8945
- return execSync7(cmd, { encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"] }).trim();
9178
+ return execSync8(cmd, { encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"] }).trim();
8946
9179
  } catch {
8947
9180
  return null;
8948
9181
  }
@@ -8974,9 +9207,9 @@ async function checkClaudeAuth(key) {
8974
9207
  fix: "npm install -g @anthropic-ai/claude-code && claude login"
8975
9208
  });
8976
9209
  }
8977
- if (claudeInstalled && platform() === "darwin" && !key) {
8978
- const credsPath = join22(homedir2(), ".claude", ".credentials.json");
8979
- if (!existsSync24(credsPath)) {
9210
+ if (claudeInstalled && platform2() === "darwin" && !key) {
9211
+ const credsPath = join23(homedir3(), ".claude", ".credentials.json");
9212
+ if (!existsSync25(credsPath)) {
8980
9213
  results.push({
8981
9214
  label: "Claude creds (Docker)",
8982
9215
  status: "warn",
@@ -9078,8 +9311,8 @@ async function checkProjectGithubToken(env, factoryDir) {
9078
9311
  }
9079
9312
  let ownerRepo = null;
9080
9313
  if (factoryDir) {
9081
- const envPath = join22(factoryDir, ".env");
9082
- if (existsSync24(envPath)) {
9314
+ const envPath = join23(factoryDir, ".env");
9315
+ if (existsSync25(envPath)) {
9083
9316
  try {
9084
9317
  const content = readFileSync21(envPath, "utf-8");
9085
9318
  const dirLine = content.split("\n").find(
@@ -9087,7 +9320,7 @@ async function checkProjectGithubToken(env, factoryDir) {
9087
9320
  );
9088
9321
  if (dirLine) {
9089
9322
  const projectDir = dirLine.split("=").slice(1).join("=").trim();
9090
- if (projectDir && existsSync24(join22(projectDir, ".git"))) {
9323
+ if (projectDir && existsSync25(join23(projectDir, ".git"))) {
9091
9324
  const remote = tryExec(
9092
9325
  `git -C "${projectDir}" remote get-url origin 2>/dev/null`
9093
9326
  );
@@ -9303,8 +9536,8 @@ function checkProjectDirEnv(factoryDir) {
9303
9536
  detail: "no factory in scope"
9304
9537
  };
9305
9538
  }
9306
- const envPath = join22(factoryDir, ".env");
9307
- if (!existsSync24(envPath)) {
9539
+ const envPath = join23(factoryDir, ".env");
9540
+ if (!existsSync25(envPath)) {
9308
9541
  return {
9309
9542
  label: "PROJECT_DIR (.env)",
9310
9543
  status: "fail",
@@ -9343,7 +9576,7 @@ function checkProjectDirEnv(factoryDir) {
9343
9576
  fix: "Re-run `beastmode init --project <path>` to populate it"
9344
9577
  };
9345
9578
  }
9346
- if (!existsSync24(value)) {
9579
+ if (!existsSync25(value)) {
9347
9580
  return {
9348
9581
  label: "PROJECT_DIR (.env)",
9349
9582
  status: "fail",
@@ -9351,7 +9584,7 @@ function checkProjectDirEnv(factoryDir) {
9351
9584
  fix: `Clone your project to ${value} or re-run 'beastmode init --project <path>'`
9352
9585
  };
9353
9586
  }
9354
- if (!existsSync24(join22(value, ".git"))) {
9587
+ if (!existsSync25(join23(value, ".git"))) {
9355
9588
  return {
9356
9589
  label: "PROJECT_DIR (.env)",
9357
9590
  status: "fail",
@@ -9382,8 +9615,8 @@ async function checkFactoryContainers(factoryDir) {
9382
9615
  detail: "no factory in scope"
9383
9616
  };
9384
9617
  }
9385
- const composePath = join22(factoryDir, "docker-compose.yml");
9386
- if (!existsSync24(composePath)) {
9618
+ const composePath = join23(factoryDir, "docker-compose.yml");
9619
+ if (!existsSync25(composePath)) {
9387
9620
  return {
9388
9621
  label: "Factory containers",
9389
9622
  status: "warn",
@@ -9665,9 +9898,9 @@ function checkProjectDirectory(factoryDir) {
9665
9898
  fix: "Run beastmode init to create a factory"
9666
9899
  };
9667
9900
  }
9668
- const bmDir = join22(factoryDir, ".beastmode");
9669
- const projectsDir = join22(bmDir, "projects");
9670
- if (!existsSync24(projectsDir)) {
9901
+ const bmDir = join23(factoryDir, ".beastmode");
9902
+ const projectsDir = join23(bmDir, "projects");
9903
+ if (!existsSync25(projectsDir)) {
9671
9904
  return {
9672
9905
  label: "Project directory",
9673
9906
  status: "warn",
@@ -9675,7 +9908,7 @@ function checkProjectDirectory(factoryDir) {
9675
9908
  fix: "Run: beastmode add project <path>"
9676
9909
  };
9677
9910
  }
9678
- const projectFiles = existsSync24(projectsDir) ? readdirSync10(projectsDir).filter((f) => f.endsWith(".json")) : [];
9911
+ const projectFiles = existsSync25(projectsDir) ? readdirSync10(projectsDir).filter((f) => f.endsWith(".json")) : [];
9679
9912
  if (projectFiles.length === 0) {
9680
9913
  return {
9681
9914
  label: "Project directory",
@@ -9688,15 +9921,15 @@ function checkProjectDirectory(factoryDir) {
9688
9921
  let anyFail = false;
9689
9922
  for (const file of projectFiles) {
9690
9923
  try {
9691
- const proj = JSON.parse(readFileSync21(join22(projectsDir, file), "utf-8"));
9924
+ const proj = JSON.parse(readFileSync21(join23(projectsDir, file), "utf-8"));
9692
9925
  const projPath = proj.path || "";
9693
9926
  const projName = proj.name || file.replace(".json", "");
9694
- if (!projPath || !existsSync24(projPath)) {
9927
+ if (!projPath || !existsSync25(projPath)) {
9695
9928
  results.push(`${projName}: path not found`);
9696
9929
  anyFail = true;
9697
9930
  continue;
9698
9931
  }
9699
- const isGit = existsSync24(join22(projPath, ".git"));
9932
+ const isGit = existsSync25(join23(projPath, ".git"));
9700
9933
  const manifests = [
9701
9934
  "package.json",
9702
9935
  "Cargo.toml",
@@ -9707,7 +9940,7 @@ function checkProjectDirectory(factoryDir) {
9707
9940
  "build.gradle",
9708
9941
  "build.gradle.kts"
9709
9942
  ];
9710
- const manifest = manifests.find((m) => existsSync24(join22(projPath, m)));
9943
+ const manifest = manifests.find((m) => existsSync25(join23(projPath, m)));
9711
9944
  const framework = proj.stack?.detected || manifest?.replace(".json", "") || "unknown";
9712
9945
  results.push(
9713
9946
  `${projName}: ${projPath} (${framework})${isGit ? "" : " [no .git]"}`
@@ -9761,8 +9994,8 @@ function checkStack(factoryDir) {
9761
9994
  detail: "no factory \u2014 run beastmode init"
9762
9995
  };
9763
9996
  }
9764
- const projectsDir = join22(factoryDir, ".beastmode", "projects");
9765
- if (!existsSync24(projectsDir)) {
9997
+ const projectsDir = join23(factoryDir, ".beastmode", "projects");
9998
+ if (!existsSync25(projectsDir)) {
9766
9999
  return { label: "Stack", status: "warn", detail: "no projects configured" };
9767
10000
  }
9768
10001
  const projectFiles = readdirSync10(projectsDir).filter((f) => f.endsWith(".json"));
@@ -9772,7 +10005,7 @@ function checkStack(factoryDir) {
9772
10005
  const stacks = [];
9773
10006
  for (const file of projectFiles) {
9774
10007
  try {
9775
- const proj = JSON.parse(readFileSync21(join22(projectsDir, file), "utf-8"));
10008
+ const proj = JSON.parse(readFileSync21(join23(projectsDir, file), "utf-8"));
9776
10009
  const name = proj.name || file.replace(".json", "");
9777
10010
  const detected = proj.stack?.detected || "unknown";
9778
10011
  const build = proj.stack?.build_command || "";
@@ -9816,10 +10049,10 @@ function checkBoardPassword(env, factoryDir) {
9816
10049
  return { label: "Board password", status: "pass", detail: "set" };
9817
10050
  }
9818
10051
  if (factoryDir) {
9819
- const dotEnv = join22(factoryDir, ".env");
9820
- const secretsEnv = join22(factoryDir, ".beastmode", "secrets.env.local");
10052
+ const dotEnv = join23(factoryDir, ".env");
10053
+ const secretsEnv = join23(factoryDir, ".beastmode", "secrets.env.local");
9821
10054
  for (const filePath of [dotEnv, secretsEnv]) {
9822
- if (existsSync24(filePath)) {
10055
+ if (existsSync25(filePath)) {
9823
10056
  try {
9824
10057
  const content = readFileSync21(filePath, "utf-8");
9825
10058
  const lines = content.split("\n");
@@ -9843,12 +10076,12 @@ function checkBoardPassword(env, factoryDir) {
9843
10076
  };
9844
10077
  }
9845
10078
  function doctorAction(factoryDir, env) {
9846
- const bmDir = join22(factoryDir, ".beastmode");
9847
- const factoryDirExists = existsSync24(bmDir);
10079
+ const bmDir = join23(factoryDir, ".beastmode");
10080
+ const factoryDirExists = existsSync25(bmDir);
9848
10081
  let factoryIdentity = null;
9849
10082
  if (factoryDirExists) {
9850
10083
  try {
9851
- const raw = JSON.parse(readFileSync21(join22(bmDir, "factory.json"), "utf-8"));
10084
+ const raw = JSON.parse(readFileSync21(join23(bmDir, "factory.json"), "utf-8"));
9852
10085
  factoryIdentity = FactoryIdentitySchema.parse(raw);
9853
10086
  } catch {
9854
10087
  }
@@ -9856,8 +10089,8 @@ function doctorAction(factoryDir, env) {
9856
10089
  let config = null;
9857
10090
  let configParseError = null;
9858
10091
  if (factoryDirExists) {
9859
- const configPath = join22(bmDir, "config.json");
9860
- if (existsSync24(configPath)) {
10092
+ const configPath = join23(bmDir, "config.json");
10093
+ if (existsSync25(configPath)) {
9861
10094
  try {
9862
10095
  const raw = JSON.parse(readFileSync21(configPath, "utf-8"));
9863
10096
  const result = FactoryConfigSchema.safeParse(raw);
@@ -9875,17 +10108,17 @@ function doctorAction(factoryDir, env) {
9875
10108
  }
9876
10109
  const projectPaths = [];
9877
10110
  if (factoryDirExists) {
9878
- const projectsDir = join22(bmDir, "projects");
9879
- if (existsSync24(projectsDir)) {
10111
+ const projectsDir = join23(bmDir, "projects");
10112
+ if (existsSync25(projectsDir)) {
9880
10113
  for (const file of readdirSync10(projectsDir)) {
9881
10114
  if (file.endsWith(".json")) {
9882
10115
  try {
9883
- const proj = JSON.parse(readFileSync21(join22(projectsDir, file), "utf-8"));
10116
+ const proj = JSON.parse(readFileSync21(join23(projectsDir, file), "utf-8"));
9884
10117
  if (proj.path) {
9885
10118
  projectPaths.push({
9886
10119
  name: proj.name || file.replace(".json", ""),
9887
10120
  path: proj.path,
9888
- exists: existsSync24(proj.path)
10121
+ exists: existsSync25(proj.path)
9889
10122
  });
9890
10123
  }
9891
10124
  } catch {
@@ -9896,11 +10129,11 @@ function doctorAction(factoryDir, env) {
9896
10129
  }
9897
10130
  const installedPlugins = [];
9898
10131
  if (factoryDirExists) {
9899
- const pluginsDir = join22(bmDir, "plugins");
9900
- if (existsSync24(pluginsDir)) {
10132
+ const pluginsDir = join23(bmDir, "plugins");
10133
+ if (existsSync25(pluginsDir)) {
9901
10134
  for (const pluginName of readdirSync10(pluginsDir)) {
9902
- const manifestPath = join22(pluginsDir, pluginName, "manifest.json");
9903
- if (existsSync24(manifestPath)) {
10135
+ const manifestPath = join23(pluginsDir, pluginName, "manifest.json");
10136
+ if (existsSync25(manifestPath)) {
9904
10137
  try {
9905
10138
  const manifest = JSON.parse(readFileSync21(manifestPath, "utf-8"));
9906
10139
  installedPlugins.push({
@@ -9942,12 +10175,12 @@ function printCheck(check) {
9942
10175
  }
9943
10176
  }
9944
10177
  }
9945
- var doctorCommand = new Command11("doctor").description("Health check \u2014 validates the entire BeastMode setup").action(async () => {
10178
+ var doctorCommand = new Command12("doctor").description("Health check \u2014 validates the entire BeastMode setup").action(async () => {
9946
10179
  header("BeastMode Doctor");
9947
10180
  console.log();
9948
10181
  const env = process.env;
9949
10182
  const factoryDir = findFactoryDir() ?? resolve15(".");
9950
- const hasFactory = existsSync24(join22(factoryDir, ".beastmode"));
10183
+ const hasFactory = existsSync25(join23(factoryDir, ".beastmode"));
9951
10184
  const checks = [];
9952
10185
  checks.push(...await checkClaudeAuth(env.ANTHROPIC_API_KEY));
9953
10186
  checks.push(checkGithubToken(env));
@@ -10018,13 +10251,14 @@ var doctorCommand = new Command11("doctor").description("Health check \u2014 val
10018
10251
  init_upgrader();
10019
10252
  init_schemas();
10020
10253
  init_version();
10021
- import { Command as Command12 } from "commander";
10022
- import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync19 } from "fs";
10023
- import { join as join24, resolve as resolve16 } from "path";
10254
+ init_display();
10255
+ import { Command as Command13 } from "commander";
10256
+ import { existsSync as existsSync27, readFileSync as readFileSync23, writeFileSync as writeFileSync20 } from "fs";
10257
+ import { join as join25, resolve as resolve16 } from "path";
10024
10258
 
10025
10259
  // src/cli/utils/regenerate.ts
10026
- import { existsSync as existsSync25, readFileSync as readFileSync22, writeFileSync as writeFileSync18, copyFileSync } from "fs";
10027
- import { join as join23 } from "path";
10260
+ import { existsSync as existsSync26, readFileSync as readFileSync22, writeFileSync as writeFileSync19, copyFileSync } from "fs";
10261
+ import { join as join24 } from "path";
10028
10262
  var RECOGNIZED_KEYS = /* @__PURE__ */ new Set([
10029
10263
  "PROJECT_DIR",
10030
10264
  "PROJECT_GITHUB_TOKEN",
@@ -10138,14 +10372,14 @@ function buildNewEnv(values) {
10138
10372
  return lines.join("\n");
10139
10373
  }
10140
10374
  function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
10141
- const envPath = join23(factoryDir, ".env");
10142
- const composePath = join23(factoryDir, "docker-compose.yml");
10143
- if (!existsSync25(envPath)) {
10375
+ const envPath = join24(factoryDir, ".env");
10376
+ const composePath = join24(factoryDir, "docker-compose.yml");
10377
+ if (!existsSync26(envPath)) {
10144
10378
  throw new Error(
10145
10379
  `.env not found at ${envPath}. This does not look like a beastmode factory \u2014 run 'beastmode init' first.`
10146
10380
  );
10147
10381
  }
10148
- if (!existsSync25(composePath)) {
10382
+ if (!existsSync26(composePath)) {
10149
10383
  throw new Error(
10150
10384
  `docker-compose.yml not found at ${composePath}. This does not look like a beastmode factory \u2014 run 'beastmode init' first.`
10151
10385
  );
@@ -10186,12 +10420,12 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
10186
10420
  if (envChanged) {
10187
10421
  envBackupPath = `${envPath}.backup.${timestamp}`;
10188
10422
  copyFileSync(envPath, envBackupPath);
10189
- writeFileSync18(envPath, newEnv, "utf-8");
10423
+ writeFileSync19(envPath, newEnv, "utf-8");
10190
10424
  }
10191
10425
  if (composeChanged) {
10192
10426
  composeBackupPath = `${composePath}.backup.${timestamp}`;
10193
10427
  copyFileSync(composePath, composeBackupPath);
10194
- writeFileSync18(composePath, newCompose, "utf-8");
10428
+ writeFileSync19(composePath, newCompose, "utf-8");
10195
10429
  }
10196
10430
  return {
10197
10431
  envChanged,
@@ -10204,15 +10438,15 @@ function regenerateFactoryFiles(factoryDir, now = /* @__PURE__ */ new Date()) {
10204
10438
 
10205
10439
  // src/cli/commands/upgrade.ts
10206
10440
  function readIdentity(factoryDir) {
10207
- const path = join24(factoryDir, ".beastmode", "factory.json");
10208
- if (!existsSync26(path)) {
10441
+ const path = join25(factoryDir, ".beastmode", "factory.json");
10442
+ if (!existsSync27(path)) {
10209
10443
  throw new Error("No factory.json found. Run beastmode init first.");
10210
10444
  }
10211
10445
  return FactoryIdentitySchema.parse(JSON.parse(readFileSync23(path, "utf-8")));
10212
10446
  }
10213
10447
  function readConfig3(factoryDir) {
10214
- const path = join24(factoryDir, ".beastmode", "config.json");
10215
- if (!existsSync26(path)) {
10448
+ const path = join25(factoryDir, ".beastmode", "config.json");
10449
+ if (!existsSync27(path)) {
10216
10450
  return {};
10217
10451
  }
10218
10452
  return JSON.parse(readFileSync23(path, "utf-8"));
@@ -10227,18 +10461,18 @@ function upgradeAction(factoryDir, migrateOnly = false) {
10227
10461
  const targetVersion = migrateOnly ? identity.engine_version : ENGINE_VERSION;
10228
10462
  const result = performUpgrade(identity, config, targetVersion, SCHEMA_VERSION);
10229
10463
  if (result.changes.length > 0) {
10230
- writeFileSync19(
10231
- join24(factoryDir, ".beastmode", "factory.json"),
10464
+ writeFileSync20(
10465
+ join25(factoryDir, ".beastmode", "factory.json"),
10232
10466
  JSON.stringify(result.updatedIdentity, null, 2) + "\n"
10233
10467
  );
10234
- writeFileSync19(
10235
- join24(factoryDir, ".beastmode", "config.json"),
10468
+ writeFileSync20(
10469
+ join25(factoryDir, ".beastmode", "config.json"),
10236
10470
  JSON.stringify(result.updatedConfig, null, 2) + "\n"
10237
10471
  );
10238
10472
  }
10239
10473
  return result;
10240
10474
  }
10241
- var upgradeCommand = new Command12("upgrade").description("Upgrade engine version and migrate config").option("--check", "Check for updates without modifying").option("--migrate-only", "Migrate config without bumping engine version").option(
10475
+ var upgradeCommand = new Command13("upgrade").description("Upgrade engine version and migrate config").option("--check", "Check for updates without modifying").option("--migrate-only", "Migrate config without bumping engine version").option(
10242
10476
  "--files",
10243
10477
  "Regenerate .env and docker-compose.yml from current templates while preserving user values (Gap 4). Backs up existing files with a timestamped suffix before writing."
10244
10478
  ).action((opts) => {
@@ -10316,11 +10550,12 @@ var upgradeCommand = new Command12("upgrade").description("Upgrade engine versio
10316
10550
  });
10317
10551
 
10318
10552
  // src/cli/commands/migrate.ts
10319
- import { Command as Command13 } from "commander";
10320
- import { resolve as resolve17, join as join25 } from "path";
10321
- import { existsSync as existsSync27, readFileSync as readFileSync24, mkdirSync as mkdirSync15, writeFileSync as writeFileSync20 } from "fs";
10553
+ init_display();
10322
10554
  init_migrator();
10323
- var migrateCommand = new Command13("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) => {
10555
+ import { Command as Command14 } from "commander";
10556
+ import { resolve as resolve17, join as join26 } from "path";
10557
+ import { existsSync as existsSync28, readFileSync as readFileSync24, mkdirSync as mkdirSync16, writeFileSync as writeFileSync21 } from "fs";
10558
+ 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) => {
10324
10559
  try {
10325
10560
  await runMigrate(opts);
10326
10561
  } catch (err) {
@@ -10331,7 +10566,7 @@ var migrateCommand = new Command13("migrate").description("Migrate a daemon conf
10331
10566
  async function runMigrate(opts) {
10332
10567
  const cwd = process.cwd();
10333
10568
  const configPath = opts.config ? resolve17(opts.config) : resolve17(cwd, "config", "beastmode.daemon.json");
10334
- if (!existsSync27(configPath)) {
10569
+ if (!existsSync28(configPath)) {
10335
10570
  throw new Error(
10336
10571
  `Daemon config not found at ${configPath}
10337
10572
  Use --config <path> to specify a different location.`
@@ -10341,21 +10576,21 @@ async function runMigrate(opts) {
10341
10576
  info(`Reading daemon config from: ${configPath}`);
10342
10577
  const configContent = readFileSync24(configPath, "utf-8");
10343
10578
  const daemonConfig = parseDaemonConfig(configContent);
10344
- const runsDir = join25(cwd, "runs");
10579
+ const runsDir = join26(cwd, "runs");
10345
10580
  let runDirs = [];
10346
10581
  const checkpoints = /* @__PURE__ */ new Map();
10347
- if (existsSync27(runsDir)) {
10582
+ if (existsSync28(runsDir)) {
10348
10583
  const { readdirSync: readdirSync12 } = await import("fs");
10349
10584
  runDirs = readdirSync12(runsDir).filter((d) => {
10350
10585
  try {
10351
- return readdirSync12(join25(runsDir, d)).length > 0;
10586
+ return readdirSync12(join26(runsDir, d)).length > 0;
10352
10587
  } catch {
10353
10588
  return false;
10354
10589
  }
10355
10590
  });
10356
10591
  for (const dir of runDirs) {
10357
- const cpPath = join25(runsDir, dir, "checkpoint.json");
10358
- if (existsSync27(cpPath)) {
10592
+ const cpPath = join26(runsDir, dir, "checkpoint.json");
10593
+ if (existsSync28(cpPath)) {
10359
10594
  try {
10360
10595
  const cp = JSON.parse(readFileSync24(cpPath, "utf-8"));
10361
10596
  checkpoints.set(dir, cp);
@@ -10414,28 +10649,28 @@ async function runMigrate(opts) {
10414
10649
  warn("Dry run \u2014 no files written.");
10415
10650
  return;
10416
10651
  }
10417
- const bmDir = join25(cwd, ".beastmode");
10418
- if (existsSync27(bmDir)) {
10652
+ const bmDir = join26(cwd, ".beastmode");
10653
+ if (existsSync28(bmDir)) {
10419
10654
  throw new Error(
10420
10655
  "A .beastmode/ directory already exists. Remove it first to re-migrate."
10421
10656
  );
10422
10657
  }
10423
10658
  for (const file of files) {
10424
- const fullPath = join25(cwd, file.path);
10659
+ const fullPath = join26(cwd, file.path);
10425
10660
  const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
10426
- mkdirSync15(dir, { recursive: true });
10427
- writeFileSync20(fullPath, file.content, "utf-8");
10661
+ mkdirSync16(dir, { recursive: true });
10662
+ writeFileSync21(fullPath, file.content, "utf-8");
10428
10663
  }
10429
- const runsSymlinkTarget = join25(cwd, "runs");
10430
- const bmRunsPath = join25(cwd, "runs");
10431
- if (existsSync27(runsSymlinkTarget)) {
10664
+ const runsSymlinkTarget = join26(cwd, "runs");
10665
+ const bmRunsPath = join26(cwd, "runs");
10666
+ if (existsSync28(runsSymlinkTarget)) {
10432
10667
  info("Existing runs/ directory preserved in-place.");
10433
10668
  }
10434
- const boardPath = join25(bmDir, "board.json");
10435
- if (!existsSync27(boardPath)) {
10436
- writeFileSync20(boardPath, JSON.stringify({ items: [] }, null, 2), "utf-8");
10669
+ const boardPath = join26(bmDir, "board.json");
10670
+ if (!existsSync28(boardPath)) {
10671
+ writeFileSync21(boardPath, JSON.stringify({ items: [] }, null, 2), "utf-8");
10437
10672
  }
10438
- mkdirSync15(join25(bmDir, ".cache"), { recursive: true });
10673
+ mkdirSync16(join26(bmDir, ".cache"), { recursive: true });
10439
10674
  console.log();
10440
10675
  success("Migration complete!");
10441
10676
  info(`Factory created at: ${bmDir}`);
@@ -10452,13 +10687,14 @@ async function runMigrate(opts) {
10452
10687
  }
10453
10688
 
10454
10689
  // src/cli/commands/run.ts
10455
- import { Command as Command14 } from "commander";
10456
- import { join as join26 } from "path";
10457
- import { existsSync as existsSync28, readFileSync as readFileSync25, writeFileSync as writeFileSync21, mkdirSync as mkdirSync16 } from "fs";
10690
+ init_display();
10691
+ import { Command as Command15 } from "commander";
10692
+ import { join as join27 } from "path";
10693
+ import { existsSync as existsSync29, readFileSync as readFileSync25, writeFileSync as writeFileSync22, mkdirSync as mkdirSync17 } from "fs";
10458
10694
  import { randomUUID as randomUUID2 } from "crypto";
10459
10695
  init_bridge();
10460
10696
  init_schemas();
10461
- var runCommand = new Command14("run").description("Run a single pipeline task").argument("[project]", "Project name (defaults to first project)").option("--task <description>", "Task description").action(async (project, opts) => {
10697
+ 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) => {
10462
10698
  try {
10463
10699
  await runPipeline(project, opts);
10464
10700
  } catch (err) {
@@ -10476,18 +10712,18 @@ async function runPipeline(projectName, opts) {
10476
10712
  "No BeastMode factory found. Run 'beastmode init' first."
10477
10713
  );
10478
10714
  }
10479
- const bmDir = join26(factoryDir, ".beastmode");
10715
+ const bmDir = join27(factoryDir, ".beastmode");
10480
10716
  header("BeastMode Run");
10481
- const configPath = join26(bmDir, "config.json");
10482
- if (!existsSync28(configPath)) {
10717
+ const configPath = join27(bmDir, "config.json");
10718
+ if (!existsSync29(configPath)) {
10483
10719
  throw new Error("Factory config not found. Run 'beastmode init' first.");
10484
10720
  }
10485
10721
  const factoryConfig = FactoryConfigSchema.parse(
10486
10722
  JSON.parse(readFileSync25(configPath, "utf-8"))
10487
10723
  );
10488
10724
  let projectConfig = null;
10489
- const projectsDir = join26(bmDir, "projects");
10490
- if (existsSync28(projectsDir)) {
10725
+ const projectsDir = join27(bmDir, "projects");
10726
+ if (existsSync29(projectsDir)) {
10491
10727
  const { readdirSync: readdirSync12 } = await import("fs");
10492
10728
  const projectFiles = readdirSync12(projectsDir).filter(
10493
10729
  (f) => f.endsWith(".json")
@@ -10500,18 +10736,18 @@ async function runPipeline(projectName, opts) {
10500
10736
  throw new Error(`Project not found: ${projectName}`);
10501
10737
  }
10502
10738
  projectConfig = ProjectConfigSchema.parse(
10503
- JSON.parse(readFileSync25(join26(projectsDir, file), "utf-8"))
10739
+ JSON.parse(readFileSync25(join27(projectsDir, file), "utf-8"))
10504
10740
  );
10505
10741
  } else if (projectFiles.length > 0) {
10506
10742
  projectConfig = ProjectConfigSchema.parse(
10507
- JSON.parse(readFileSync25(join26(projectsDir, projectFiles[0]), "utf-8"))
10743
+ JSON.parse(readFileSync25(join27(projectsDir, projectFiles[0]), "utf-8"))
10508
10744
  );
10509
10745
  info(`Using project: ${projectConfig.name}`);
10510
10746
  }
10511
10747
  }
10512
- const boardPath = join26(bmDir, "board.json");
10748
+ const boardPath = join27(bmDir, "board.json");
10513
10749
  let boardItems = [];
10514
- if (existsSync28(boardPath)) {
10750
+ if (existsSync29(boardPath)) {
10515
10751
  try {
10516
10752
  const raw = JSON.parse(readFileSync25(boardPath, "utf-8"));
10517
10753
  boardItems = Array.isArray(raw.items) ? raw.items : [];
@@ -10530,13 +10766,13 @@ async function runPipeline(projectName, opts) {
10530
10766
  updated_at: now
10531
10767
  };
10532
10768
  boardItems.push(task);
10533
- writeFileSync21(boardPath, JSON.stringify({ items: boardItems }, null, 2), "utf-8");
10769
+ writeFileSync22(boardPath, JSON.stringify({ items: boardItems }, null, 2), "utf-8");
10534
10770
  info(`Created task: ${task.title} (${taskId})`);
10535
- const cacheDir = join26(bmDir, ".cache");
10536
- mkdirSync16(cacheDir, { recursive: true });
10537
- const daemonConfigPath = join26(cacheDir, "daemon.json");
10771
+ const cacheDir = join27(bmDir, ".cache");
10772
+ mkdirSync17(cacheDir, { recursive: true });
10773
+ const daemonConfigPath = join27(cacheDir, "daemon.json");
10538
10774
  const daemonConfig = generateDaemonConfig(factoryConfig, projectConfig, factoryDir);
10539
- writeFileSync21(daemonConfigPath, JSON.stringify(daemonConfig, null, 2), "utf-8");
10775
+ writeFileSync22(daemonConfigPath, JSON.stringify(daemonConfig, null, 2), "utf-8");
10540
10776
  info(`Generated daemon config at: ${daemonConfigPath}`);
10541
10777
  const { execSync: execSync10 } = await import("child_process");
10542
10778
  let pythonAvailable = false;
@@ -10560,7 +10796,7 @@ async function runPipeline(projectName, opts) {
10560
10796
  const daemonPaths = findPythonDaemonPaths(envPath, factoryDir);
10561
10797
  let daemonFound = false;
10562
10798
  for (const p of daemonPaths) {
10563
- if (existsSync28(p)) {
10799
+ if (existsSync29(p)) {
10564
10800
  daemonFound = true;
10565
10801
  break;
10566
10802
  }
@@ -10628,12 +10864,13 @@ async function runPipeline(projectName, opts) {
10628
10864
  }
10629
10865
 
10630
10866
  // src/cli/commands/daemon-cmd.ts
10631
- import { Command as Command15 } from "commander";
10632
- import { join as join27 } from "path";
10633
- import { existsSync as existsSync29, readFileSync as readFileSync26, writeFileSync as writeFileSync22, mkdirSync as mkdirSync17 } from "fs";
10867
+ init_display();
10868
+ import { Command as Command16 } from "commander";
10869
+ import { join as join28 } from "path";
10870
+ import { existsSync as existsSync30, readFileSync as readFileSync26, writeFileSync as writeFileSync23, mkdirSync as mkdirSync18 } from "fs";
10634
10871
  init_bridge();
10635
10872
  init_schemas();
10636
- var daemonCommand = new Command15("daemon").description("Start the BeastMode daemon via bridge").option("--dry-run", "Generate config but don't start daemon").option(
10873
+ var daemonCommand = new Command16("daemon").description("Start the BeastMode daemon via bridge").option("--dry-run", "Generate config but don't start daemon").option(
10637
10874
  "--log-level <level>",
10638
10875
  "Log level (DEBUG, INFO, WARNING, ERROR)",
10639
10876
  "INFO"
@@ -10652,38 +10889,38 @@ async function runDaemon(opts) {
10652
10889
  "No BeastMode factory found. Run 'beastmode init' first."
10653
10890
  );
10654
10891
  }
10655
- const bmDir = join27(factoryDir, ".beastmode");
10892
+ const bmDir = join28(factoryDir, ".beastmode");
10656
10893
  header("BeastMode Daemon");
10657
- const configPath = join27(bmDir, "config.json");
10658
- if (!existsSync29(configPath)) {
10894
+ const configPath = join28(bmDir, "config.json");
10895
+ if (!existsSync30(configPath)) {
10659
10896
  throw new Error("Factory config not found. Run 'beastmode init' first.");
10660
10897
  }
10661
10898
  const factoryConfig = FactoryConfigSchema.parse(
10662
10899
  JSON.parse(readFileSync26(configPath, "utf-8"))
10663
10900
  );
10664
10901
  let projectConfig = null;
10665
- const projectsDir = join27(bmDir, "projects");
10666
- if (existsSync29(projectsDir)) {
10902
+ const projectsDir = join28(bmDir, "projects");
10903
+ if (existsSync30(projectsDir)) {
10667
10904
  const { readdirSync: readdirSync12 } = await import("fs");
10668
10905
  const projectFiles = readdirSync12(projectsDir).filter(
10669
10906
  (f) => f.endsWith(".json")
10670
10907
  );
10671
10908
  if (projectFiles.length > 0) {
10672
10909
  projectConfig = ProjectConfigSchema.parse(
10673
- JSON.parse(readFileSync26(join27(projectsDir, projectFiles[0]), "utf-8"))
10910
+ JSON.parse(readFileSync26(join28(projectsDir, projectFiles[0]), "utf-8"))
10674
10911
  );
10675
10912
  info(`Using project: ${projectConfig.name}`);
10676
10913
  }
10677
10914
  }
10678
- const cacheDir = join27(bmDir, ".cache");
10679
- mkdirSync17(cacheDir, { recursive: true });
10680
- const daemonConfigPath = join27(cacheDir, "daemon.json");
10915
+ const cacheDir = join28(bmDir, ".cache");
10916
+ mkdirSync18(cacheDir, { recursive: true });
10917
+ const daemonConfigPath = join28(cacheDir, "daemon.json");
10681
10918
  const daemonConfig = generateDaemonConfig(
10682
10919
  factoryConfig,
10683
10920
  projectConfig,
10684
10921
  factoryDir
10685
10922
  );
10686
- writeFileSync22(
10923
+ writeFileSync23(
10687
10924
  daemonConfigPath,
10688
10925
  JSON.stringify(daemonConfig, null, 2),
10689
10926
  "utf-8"
@@ -10722,7 +10959,7 @@ async function runDaemon(opts) {
10722
10959
  });
10723
10960
  info(`Starting daemon: ${pythonCmd} ${cmd.args.join(" ")}`);
10724
10961
  console.log();
10725
- const pidFile = join27(bmDir, "daemon.pid");
10962
+ const pidFile = join28(bmDir, "daemon.pid");
10726
10963
  const { spawn } = await import("child_process");
10727
10964
  const child = spawn(pythonCmd, cmd.args, {
10728
10965
  stdio: "inherit",
@@ -10733,7 +10970,7 @@ async function runDaemon(opts) {
10733
10970
  }
10734
10971
  });
10735
10972
  if (child.pid) {
10736
- writeFileSync22(pidFile, String(child.pid), "utf-8");
10973
+ writeFileSync23(pidFile, String(child.pid), "utf-8");
10737
10974
  }
10738
10975
  const signalHandler = (signal) => {
10739
10976
  info(`Forwarding ${signal} to daemon...`);
@@ -10757,8 +10994,8 @@ async function runDaemon(opts) {
10757
10994
  }
10758
10995
 
10759
10996
  // src/cli/commands/mcp-cmd.ts
10760
- import { Command as Command16 } from "commander";
10761
- var mcpCommand = new Command16("mcp").description("Start the BeastMode MCP server (stdio transport)").action(async () => {
10997
+ import { Command as Command17 } from "commander";
10998
+ var mcpCommand = new Command17("mcp").description("Start the BeastMode MCP server (stdio transport)").action(async () => {
10762
10999
  try {
10763
11000
  const { startMcpServer: startMcpServer2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
10764
11001
  await startMcpServer2();
@@ -10770,14 +11007,15 @@ var mcpCommand = new Command16("mcp").description("Start the BeastMode MCP serve
10770
11007
  });
10771
11008
 
10772
11009
  // src/cli/commands/deploy.ts
10773
- import { Command as Command17 } from "commander";
10774
- import { resolve as resolve19, join as join29 } from "path";
10775
- import { existsSync as existsSync31, writeFileSync as writeFileSync24, readFileSync as readFileSync28 } from "fs";
10776
- import { execSync as execSync8 } from "child_process";
11010
+ init_display();
11011
+ import { Command as Command18 } from "commander";
11012
+ import { resolve as resolve19, join as join30 } from "path";
11013
+ import { existsSync as existsSync32, writeFileSync as writeFileSync25, readFileSync as readFileSync28 } from "fs";
11014
+ import { execSync as execSync9 } from "child_process";
10777
11015
  import { randomBytes } from "crypto";
10778
11016
  import { fileURLToPath as fileURLToPath3 } from "url";
10779
11017
  import { dirname as dirname7 } from "path";
10780
- var deployCommand = new Command17("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) => {
11018
+ 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) => {
10781
11019
  try {
10782
11020
  await runDeploy(opts);
10783
11021
  } catch (err) {
@@ -10795,7 +11033,7 @@ async function runDeploy(opts) {
10795
11033
  header("BeastMode Service Status");
10796
11034
  for (const name of SERVICE_NAMES) {
10797
11035
  try {
10798
- const out = execSync8(`systemctl status ${name} --no-pager 2>&1 || true`, {
11036
+ const out = execSync9(`systemctl status ${name} --no-pager 2>&1 || true`, {
10799
11037
  encoding: "utf-8",
10800
11038
  shell: "/bin/bash"
10801
11039
  });
@@ -10811,7 +11049,7 @@ async function runDeploy(opts) {
10811
11049
  for (const name of SERVICE_NAMES) {
10812
11050
  info(`Stopping ${name}...`);
10813
11051
  try {
10814
- execSync8(`sudo systemctl stop ${name}`, { stdio: "inherit" });
11052
+ execSync9(`sudo systemctl stop ${name}`, { stdio: "inherit" });
10815
11053
  success(`${name} stopped`);
10816
11054
  } catch {
10817
11055
  warn(`Could not stop ${name} (may not be running)`);
@@ -10827,8 +11065,8 @@ async function runDeploy(opts) {
10827
11065
  process.exit(1);
10828
11066
  }
10829
11067
  const factoryDir = resolve19(".");
10830
- const bmDir = join29(factoryDir, ".beastmode");
10831
- if (!existsSync31(bmDir)) {
11068
+ const bmDir = join30(factoryDir, ".beastmode");
11069
+ if (!existsSync32(bmDir)) {
10832
11070
  error(
10833
11071
  "No .beastmode directory found. Run 'beastmode init' or 'beastmode migrate' first."
10834
11072
  );
@@ -10837,12 +11075,12 @@ async function runDeploy(opts) {
10837
11075
  let nodePath;
10838
11076
  let cliPath;
10839
11077
  try {
10840
- nodePath = execSync8("which node", { encoding: "utf-8" }).trim();
11078
+ nodePath = execSync9("which node", { encoding: "utf-8" }).trim();
10841
11079
  } catch {
10842
11080
  nodePath = process.execPath;
10843
11081
  }
10844
11082
  try {
10845
- cliPath = execSync8(
11083
+ cliPath = execSync9(
10846
11084
  "readlink -f $(which beastmode) 2>/dev/null || which beastmode",
10847
11085
  { encoding: "utf-8", shell: "/bin/bash" }
10848
11086
  ).trim();
@@ -10852,33 +11090,33 @@ async function runDeploy(opts) {
10852
11090
  "../../index.js"
10853
11091
  );
10854
11092
  }
10855
- const boardVenvPython = join29(factoryDir, "board", ".venv", "bin", "python");
10856
- const daemonVenvPython = join29(factoryDir, "daemon", ".venv", "bin", "python");
10857
- const boardPython = existsSync31(boardVenvPython) ? boardVenvPython : "python3";
10858
- const daemonPython = existsSync31(daemonVenvPython) ? daemonVenvPython : "python3";
10859
- const user = execSync8("whoami", { encoding: "utf-8" }).trim();
11093
+ const boardVenvPython = join30(factoryDir, "board", ".venv", "bin", "python");
11094
+ const daemonVenvPython = join30(factoryDir, "daemon", ".venv", "bin", "python");
11095
+ const boardPython = existsSync32(boardVenvPython) ? boardVenvPython : "python3";
11096
+ const daemonPython = existsSync32(daemonVenvPython) ? daemonVenvPython : "python3";
11097
+ const user = execSync9("whoami", { encoding: "utf-8" }).trim();
10860
11098
  const home = process.env.HOME || `/home/${user}`;
10861
11099
  const port = opts.port;
10862
11100
  const host = opts.host;
10863
- const dotEnv = join29(factoryDir, ".env");
10864
- const secretsEnv = join29(bmDir, "secrets.env.local");
10865
- const envContent = existsSync31(dotEnv) ? readFileSync28(dotEnv, "utf-8") : "";
10866
- const secretsContent = existsSync31(secretsEnv) ? readFileSync28(secretsEnv, "utf-8") : "";
11101
+ const dotEnv = join30(factoryDir, ".env");
11102
+ const secretsEnv = join30(bmDir, "secrets.env.local");
11103
+ const envContent = existsSync32(dotEnv) ? readFileSync28(dotEnv, "utf-8") : "";
11104
+ const secretsContent = existsSync32(secretsEnv) ? readFileSync28(secretsEnv, "utf-8") : "";
10867
11105
  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;
10868
11106
  if (!hasPassword && opts.host === "0.0.0.0") {
10869
11107
  const generated = randomBytes(18).toString("base64url");
10870
- const target = existsSync31(secretsEnv) ? secretsEnv : dotEnv;
11108
+ const target = existsSync32(secretsEnv) ? secretsEnv : dotEnv;
10871
11109
  const append = `
10872
11110
  # Auto-generated board UI password (deploy)
10873
11111
  BEASTMODE_UI_PASSWORD=${generated}
10874
11112
  `;
10875
- writeFileSync24(target, (existsSync31(target) ? readFileSync28(target, "utf-8") : "") + append, "utf-8");
11113
+ writeFileSync25(target, (existsSync32(target) ? readFileSync28(target, "utf-8") : "") + append, "utf-8");
10876
11114
  info(`Board UI password auto-generated and saved to ${target}`);
10877
11115
  success(`Password: ${generated}`);
10878
11116
  info("Save this password \u2014 you'll need it to access the board UI.");
10879
11117
  }
10880
11118
  const envFileLines = [];
10881
- if (existsSync31(secretsEnv)) {
11119
+ if (existsSync32(secretsEnv)) {
10882
11120
  envFileLines.push(`EnvironmentFile=${secretsEnv}`);
10883
11121
  } else {
10884
11122
  envFileLines.push(`# No secrets.env.local found at time of deploy`);
@@ -10961,8 +11199,8 @@ BEASTMODE_UI_PASSWORD=${generated}
10961
11199
  info(`Writing service file to ${svc.path}...`);
10962
11200
  try {
10963
11201
  const tmpPath = `/tmp/${svc.name}.service`;
10964
- writeFileSync24(tmpPath, svc.content, "utf-8");
10965
- execSync8(`sudo cp ${tmpPath} ${svc.path}`, { stdio: "inherit" });
11202
+ writeFileSync25(tmpPath, svc.content, "utf-8");
11203
+ execSync9(`sudo cp ${tmpPath} ${svc.path}`, { stdio: "inherit" });
10966
11204
  success(`${svc.name} service file installed`);
10967
11205
  } catch {
10968
11206
  error(
@@ -10973,22 +11211,22 @@ ${svc.content}`
10973
11211
  }
10974
11212
  }
10975
11213
  info("Reloading systemd...");
10976
- execSync8("sudo systemctl daemon-reload", { stdio: "inherit" });
11214
+ execSync9("sudo systemctl daemon-reload", { stdio: "inherit" });
10977
11215
  success("systemd reloaded");
10978
11216
  for (const svc of services) {
10979
11217
  info(`Enabling ${svc.name}...`);
10980
- execSync8(`sudo systemctl enable ${svc.name}`, { stdio: "inherit" });
11218
+ execSync9(`sudo systemctl enable ${svc.name}`, { stdio: "inherit" });
10981
11219
  success(`${svc.name} enabled (will start on boot)`);
10982
11220
  if (opts.start !== false) {
10983
11221
  info(`Starting ${svc.name}...`);
10984
- execSync8(`sudo systemctl restart ${svc.name}`, { stdio: "inherit" });
11222
+ execSync9(`sudo systemctl restart ${svc.name}`, { stdio: "inherit" });
10985
11223
  }
10986
11224
  }
10987
11225
  if (opts.start !== false) {
10988
11226
  await new Promise((r) => setTimeout(r, 2e3));
10989
11227
  for (const svc of services) {
10990
11228
  try {
10991
- const status = execSync8(`systemctl is-active ${svc.name}`, {
11229
+ const status = execSync9(`systemctl is-active ${svc.name}`, {
10992
11230
  encoding: "utf-8"
10993
11231
  }).trim();
10994
11232
  if (status === "active") {
@@ -11001,7 +11239,7 @@ ${svc.content}`
11001
11239
  }
11002
11240
  }
11003
11241
  try {
11004
- const ip = execSync8("hostname -I | awk '{print $1}'", {
11242
+ const ip = execSync9("hostname -I | awk '{print $1}'", {
11005
11243
  encoding: "utf-8",
11006
11244
  shell: "/bin/bash"
11007
11245
  }).trim();
@@ -11037,7 +11275,7 @@ async function deployToAWS(opts) {
11037
11275
  info(" - SSM parameter (board password)");
11038
11276
  console.log();
11039
11277
  try {
11040
- execSync8(`aws cloudformation describe-stacks --stack-name ${stackName2}`, {
11278
+ execSync9(`aws cloudformation describe-stacks --stack-name ${stackName2}`, {
11041
11279
  encoding: "utf-8",
11042
11280
  stdio: "pipe"
11043
11281
  });
@@ -11047,13 +11285,13 @@ async function deployToAWS(opts) {
11047
11285
  }
11048
11286
  info("Deleting CloudFormation stack...");
11049
11287
  try {
11050
- execSync8(`aws cloudformation delete-stack --stack-name ${stackName2}`, {
11288
+ execSync9(`aws cloudformation delete-stack --stack-name ${stackName2}`, {
11051
11289
  encoding: "utf-8",
11052
11290
  stdio: "pipe"
11053
11291
  });
11054
11292
  success("Stack deletion initiated");
11055
11293
  info("Waiting for deletion to complete (2-5 minutes)...");
11056
- execSync8(`aws cloudformation wait stack-delete-complete --stack-name ${stackName2}`, {
11294
+ execSync9(`aws cloudformation wait stack-delete-complete --stack-name ${stackName2}`, {
11057
11295
  encoding: "utf-8",
11058
11296
  stdio: "pipe",
11059
11297
  timeout: 6e5
@@ -11076,13 +11314,13 @@ async function deployToAWS(opts) {
11076
11314
  process.exit(1);
11077
11315
  }
11078
11316
  try {
11079
- execSync8("aws --version", { encoding: "utf-8" });
11317
+ execSync9("aws --version", { encoding: "utf-8" });
11080
11318
  } catch {
11081
11319
  error("AWS CLI not found. Install: https://aws.amazon.com/cli/");
11082
11320
  process.exit(1);
11083
11321
  }
11084
11322
  try {
11085
- execSync8("aws sts get-caller-identity", { encoding: "utf-8", stdio: "pipe" });
11323
+ execSync9("aws sts get-caller-identity", { encoding: "utf-8", stdio: "pipe" });
11086
11324
  success("AWS credentials valid");
11087
11325
  } catch {
11088
11326
  error("AWS credentials not configured. Run: aws configure");
@@ -11090,9 +11328,9 @@ async function deployToAWS(opts) {
11090
11328
  }
11091
11329
  const __filename2 = fileURLToPath3(import.meta.url);
11092
11330
  const __dirname2 = dirname7(__filename2);
11093
- const templatePath = join29(__dirname2, "..", "..", "infra", "cloudformation", "beastmode.yaml");
11094
- const cwdTemplate = join29(process.cwd(), "infra", "cloudformation", "beastmode.yaml");
11095
- const template = existsSync31(templatePath) ? templatePath : existsSync31(cwdTemplate) ? cwdTemplate : null;
11331
+ const templatePath = join30(__dirname2, "..", "..", "infra", "cloudformation", "beastmode.yaml");
11332
+ const cwdTemplate = join30(process.cwd(), "infra", "cloudformation", "beastmode.yaml");
11333
+ const template = existsSync32(templatePath) ? templatePath : existsSync32(cwdTemplate) ? cwdTemplate : null;
11096
11334
  if (!template) {
11097
11335
  error("CloudFormation template not found. Expected at infra/cloudformation/beastmode.yaml");
11098
11336
  process.exit(1);
@@ -11120,7 +11358,7 @@ async function deployToAWS(opts) {
11120
11358
  }
11121
11359
  let isUpdate = false;
11122
11360
  try {
11123
- execSync8(createCmd, { encoding: "utf-8", stdio: "pipe" });
11361
+ execSync9(createCmd, { encoding: "utf-8", stdio: "pipe" });
11124
11362
  success("Stack creation initiated");
11125
11363
  } catch (e) {
11126
11364
  const msg = e.stderr || e.message || "";
@@ -11129,7 +11367,7 @@ async function deployToAWS(opts) {
11129
11367
  info("Stack exists \u2014 updating...");
11130
11368
  try {
11131
11369
  const updateCmd = createCmd.replace("create-stack", "update-stack");
11132
- execSync8(updateCmd, { encoding: "utf-8", stdio: "pipe" });
11370
+ execSync9(updateCmd, { encoding: "utf-8", stdio: "pipe" });
11133
11371
  success("Stack update initiated");
11134
11372
  isUpdate = true;
11135
11373
  } catch (ue) {
@@ -11153,7 +11391,7 @@ async function deployToAWS(opts) {
11153
11391
  const waitAction = isUpdate ? "update" : "create";
11154
11392
  info(`Waiting for deployment to complete (3-10 minutes)...`);
11155
11393
  try {
11156
- execSync8(`aws cloudformation wait stack-${waitAction}-complete --stack-name ${stackName}`, {
11394
+ execSync9(`aws cloudformation wait stack-${waitAction}-complete --stack-name ${stackName}`, {
11157
11395
  encoding: "utf-8",
11158
11396
  stdio: "pipe",
11159
11397
  timeout: 9e5
@@ -11165,7 +11403,7 @@ async function deployToAWS(opts) {
11165
11403
  process.exit(1);
11166
11404
  }
11167
11405
  try {
11168
- const outputsJson = execSync8(
11406
+ const outputsJson = execSync9(
11169
11407
  `aws cloudformation describe-stacks --stack-name ${stackName} --query 'Stacks[0].Outputs' --output json`,
11170
11408
  { encoding: "utf-8", stdio: "pipe" }
11171
11409
  );
@@ -11193,215 +11431,12 @@ async function deployToAWS(opts) {
11193
11431
  }
11194
11432
  }
11195
11433
 
11196
- // src/cli/commands/sync-claude-creds.ts
11197
- import { Command as Command18 } from "commander";
11198
- import { execSync as execSync9, spawnSync as spawnSync2 } from "child_process";
11199
- import { writeFileSync as writeFileSync25, chmodSync, mkdirSync as mkdirSync19, existsSync as existsSync32, unlinkSync as unlinkSync4 } from "fs";
11200
- import { join as join30 } from "path";
11201
- import { homedir as homedir3, platform as platform2 } from "os";
11202
- var LAUNCH_AGENT_LABEL = "com.develeap.beastmode.claude-creds";
11203
- function plistPath() {
11204
- return join30(homedir3(), "Library", "LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`);
11205
- }
11206
- function agentLogPath() {
11207
- return join30(homedir3(), ".beastmode", "logs", "sync-claude-creds.log");
11208
- }
11209
- function readKeychainToken() {
11210
- try {
11211
- return execSync9(
11212
- `security find-generic-password -s "Claude Code-credentials" -w`,
11213
- { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
11214
- ).trim();
11215
- } catch {
11216
- error("Could not read Claude Code credentials from Keychain.");
11217
- info("Fix: run `claude login` first, then re-run this command.");
11218
- process.exit(1);
11219
- }
11220
- }
11221
- function writeCredentialsFile(rawJson) {
11222
- let parsed;
11223
- try {
11224
- parsed = JSON.parse(rawJson);
11225
- } catch {
11226
- error("Keychain entry is not valid JSON.");
11227
- info("Fix: run `claude login` again to reset the credential.");
11228
- process.exit(1);
11229
- }
11230
- const oauth = parsed.claudeAiOauth;
11231
- if (!oauth?.accessToken) {
11232
- error("Keychain entry missing claudeAiOauth.accessToken.");
11233
- info("Fix: run `claude login` again to reset the credential.");
11234
- process.exit(1);
11235
- }
11236
- const claudeDir = join30(homedir3(), ".claude");
11237
- if (!existsSync32(claudeDir)) mkdirSync19(claudeDir, { recursive: true });
11238
- const credsPath = join30(claudeDir, ".credentials.json");
11239
- writeFileSync25(credsPath, rawJson + "\n", "utf-8");
11240
- chmodSync(credsPath, 384);
11241
- if (oauth.expiresAt) {
11242
- const hoursLeft = Math.round((oauth.expiresAt - Date.now()) / 36e5);
11243
- if (hoursLeft < 0) {
11244
- warn(`Token already expired ${Math.abs(hoursLeft)}h ago \u2014 run \`claude login\` to refresh.`);
11245
- } else if (hoursLeft < 2) {
11246
- warn(`Token expires in ${hoursLeft}h \u2014 background agent will re-sync after host refreshes it.`);
11247
- }
11248
- }
11249
- return credsPath;
11250
- }
11251
- function buildPlist(intervalSeconds) {
11252
- const nodePath = process.execPath;
11253
- const cliEntry = process.argv[1];
11254
- const logPath = agentLogPath();
11255
- return `<?xml version="1.0" encoding="UTF-8"?>
11256
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
11257
- <plist version="1.0">
11258
- <dict>
11259
- <key>Label</key>
11260
- <string>${LAUNCH_AGENT_LABEL}</string>
11261
- <key>ProgramArguments</key>
11262
- <array>
11263
- <string>${nodePath}</string>
11264
- <string>${cliEntry}</string>
11265
- <string>sync-claude-creds</string>
11266
- </array>
11267
- <key>StartInterval</key>
11268
- <integer>${intervalSeconds}</integer>
11269
- <key>RunAtLoad</key>
11270
- <true/>
11271
- <key>StandardOutPath</key>
11272
- <string>${logPath}</string>
11273
- <key>StandardErrorPath</key>
11274
- <string>${logPath}</string>
11275
- </dict>
11276
- </plist>
11277
- `;
11278
- }
11279
- function installAgent(intervalSeconds) {
11280
- const plist = plistPath();
11281
- const logPath = agentLogPath();
11282
- mkdirSync19(join30(homedir3(), "Library", "LaunchAgents"), { recursive: true });
11283
- mkdirSync19(join30(homedir3(), ".beastmode", "logs"), { recursive: true });
11284
- const uid = process.getuid?.();
11285
- if (existsSync32(plist)) {
11286
- spawnSync2("launchctl", ["bootout", `gui/${uid}`, plist], { stdio: "pipe" });
11287
- }
11288
- writeFileSync25(plist, buildPlist(intervalSeconds), "utf-8");
11289
- writeFileSync25(logPath, "", { flag: "a" });
11290
- const result = spawnSync2("launchctl", ["bootstrap", `gui/${uid}`, plist], {
11291
- stdio: "pipe",
11292
- encoding: "utf-8"
11293
- });
11294
- if (result.status !== 0) {
11295
- error(`launchctl bootstrap failed: ${result.stderr || result.stdout}`);
11296
- info(`Plist written to: ${plist}`);
11297
- info(`Try manually: launchctl bootstrap gui/${uid} ${plist}`);
11298
- process.exit(1);
11299
- }
11300
- success(`LaunchAgent installed: ${LAUNCH_AGENT_LABEL}`);
11301
- info(` Plist: ${plist}`);
11302
- info(` Interval: every ${Math.round(intervalSeconds / 60)} minutes`);
11303
- info(` Logs: ${logPath}`);
11304
- info(` Runs at: load + every ${Math.round(intervalSeconds / 60)}min (while you're logged in)`);
11305
- console.log();
11306
- info("The agent also runs once right now (RunAtLoad=true) \u2014 credentials synced.");
11307
- }
11308
- function uninstallAgent() {
11309
- const plist = plistPath();
11310
- const uid = process.getuid?.();
11311
- if (!existsSync32(plist)) {
11312
- info("No LaunchAgent installed \u2014 nothing to remove.");
11313
- return;
11314
- }
11315
- const result = spawnSync2("launchctl", ["bootout", `gui/${uid}`, plist], {
11316
- stdio: "pipe",
11317
- encoding: "utf-8"
11318
- });
11319
- if (result.status !== 0 && !(result.stderr || "").includes("Could not find")) {
11320
- warn(`launchctl bootout returned: ${result.stderr || result.stdout}`);
11321
- }
11322
- try {
11323
- unlinkSync4(plist);
11324
- } catch {
11325
- }
11326
- success(`LaunchAgent removed: ${LAUNCH_AGENT_LABEL}`);
11327
- }
11328
- function showStatus() {
11329
- const plist = plistPath();
11330
- const uid = process.getuid?.();
11331
- if (!existsSync32(plist)) {
11332
- info("LaunchAgent not installed.");
11333
- info("Install with: beastmode sync-claude-creds --install");
11334
- return;
11335
- }
11336
- const result = spawnSync2(
11337
- "launchctl",
11338
- ["print", `gui/${uid}/${LAUNCH_AGENT_LABEL}`],
11339
- { stdio: "pipe", encoding: "utf-8" }
11340
- );
11341
- if (result.status === 0) {
11342
- success(`LaunchAgent loaded: ${LAUNCH_AGENT_LABEL}`);
11343
- const stateMatch = result.stdout.match(/state = (\S+)/);
11344
- const lastExitMatch = result.stdout.match(/last exit code = (\S+)/);
11345
- if (stateMatch) info(` State: ${stateMatch[1]}`);
11346
- if (lastExitMatch) info(` Last exit code: ${lastExitMatch[1]}`);
11347
- info(` Plist: ${plist}`);
11348
- info(` Logs: ${agentLogPath()}`);
11349
- } else {
11350
- warn(`Plist exists but agent not loaded. Re-install with: beastmode sync-claude-creds --install`);
11351
- }
11352
- }
11353
- var syncClaudeCredsCommand = new Command18("sync-claude-creds").description(
11354
- "Sync Claude Code credentials from macOS Keychain to ~/.claude/.credentials.json (for Docker)"
11355
- ).option("--restart", "Restart the daemon container after syncing (one-shot only)").option("--install", "Install a launchd agent that re-syncs every 30 minutes").option("--uninstall", "Remove the launchd agent").option("--status", "Show launchd agent status").option("--interval <seconds>", "Sync interval for --install (default: 1800)", "1800").action(async (opts) => {
11356
- if (platform2() !== "darwin") {
11357
- info("Not macOS \u2014 Docker mounts ~/.claude/.credentials.json directly. Nothing to sync.");
11358
- return;
11359
- }
11360
- if (opts.install) {
11361
- header("Install Claude Credential Sync Agent");
11362
- console.log();
11363
- const seconds = parseInt(opts.interval || "1800", 10);
11364
- if (isNaN(seconds) || seconds < 60) {
11365
- error(`Invalid --interval: ${opts.interval} (minimum 60 seconds)`);
11366
- process.exit(1);
11367
- }
11368
- installAgent(seconds);
11369
- return;
11370
- }
11371
- if (opts.uninstall) {
11372
- header("Remove Claude Credential Sync Agent");
11373
- console.log();
11374
- uninstallAgent();
11375
- return;
11376
- }
11377
- if (opts.status) {
11378
- header("Claude Credential Sync Agent Status");
11379
- console.log();
11380
- showStatus();
11381
- return;
11382
- }
11383
- header("Sync Claude Code Credentials");
11384
- console.log();
11385
- const rawJson = readKeychainToken();
11386
- const credsPath = writeCredentialsFile(rawJson);
11387
- success(`Wrote ${credsPath}`);
11388
- if (opts.restart) {
11389
- console.log();
11390
- info("Restarting daemon container...");
11391
- const result = spawnSync2("docker", ["compose", "restart", "daemon"], { stdio: "inherit" });
11392
- if (result.status !== 0) {
11393
- warn("docker compose restart daemon failed \u2014 run it manually.");
11394
- process.exit(result.status ?? 1);
11395
- }
11396
- success("Daemon restarted.");
11397
- } else {
11398
- console.log();
11399
- info("Automate: beastmode sync-claude-creds --install");
11400
- }
11401
- });
11434
+ // src/index.ts
11435
+ init_sync_claude_creds();
11402
11436
 
11403
11437
  // src/cli/commands/up.ts
11404
11438
  import { Command as Command19 } from "commander";
11439
+ init_display();
11405
11440
  async function runUp(opts) {
11406
11441
  const cwd = opts.cwd ?? process.cwd();
11407
11442
  requireComposeFile(cwd);
@@ -11428,6 +11463,7 @@ var upCommand = new Command19("up").description("Start BeastMode services").opti
11428
11463
  // src/cli/commands/down.ts
11429
11464
  import { Command as Command20 } from "commander";
11430
11465
  import inquirer2 from "inquirer";
11466
+ init_display();
11431
11467
  async function runDown(opts) {
11432
11468
  const cwd = opts.cwd ?? process.cwd();
11433
11469
  requireComposeFile(cwd);
@@ -11464,6 +11500,7 @@ var downCommand = new Command20("down").description("Stop BeastMode services").o
11464
11500
 
11465
11501
  // src/cli/commands/logs-cmd.ts
11466
11502
  import { Command as Command21 } from "commander";
11503
+ init_display();
11467
11504
  var VALID_SERVICES = ["board", "ui", "daemon"];
11468
11505
  function buildLogsArgs(opts) {
11469
11506
  const args = ["logs", "-f", "--tail", opts.tail ?? "100"];
@@ -11491,6 +11528,7 @@ var logsCommand = new Command21("logs").description("Stream BeastMode service lo
11491
11528
  // src/cli/commands/update.ts
11492
11529
  import { Command as Command22 } from "commander";
11493
11530
  import { readFileSync as readFileSync29, writeFileSync as writeFileSync26 } from "fs";
11531
+ init_display();
11494
11532
  async function runUpdate(opts) {
11495
11533
  const cwd = opts.cwd ?? process.cwd();
11496
11534
  const composePath = requireComposeFile(cwd);