codeam-cli 2.33.0 → 2.35.0

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/index.js +489 -339
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
498
498
  // package.json
499
499
  var package_default = {
500
500
  name: "codeam-cli",
501
- version: "2.33.0",
501
+ version: "2.35.0",
502
502
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
503
503
  type: "commonjs",
504
504
  main: "dist/index.js",
@@ -852,6 +852,28 @@ async function postPreviewEvent(input) {
852
852
  };
853
853
  }
854
854
  }
855
+ async function postBeadsProvisioning(input) {
856
+ try {
857
+ await _transport.postJsonAuthed(
858
+ `${API_BASE}/api/beads/provisioning`,
859
+ {
860
+ sessionId: input.sessionId,
861
+ pluginId: input.pluginId,
862
+ status: input.status,
863
+ ...input.projectKey ? { projectKey: input.projectKey } : {}
864
+ },
865
+ input.pluginAuthToken
866
+ );
867
+ return { ok: true };
868
+ } catch (err) {
869
+ const e = err;
870
+ return {
871
+ ok: false,
872
+ status: typeof e.statusCode === "number" ? e.statusCode : 0,
873
+ message: e.message || "unknown"
874
+ };
875
+ }
876
+ }
855
877
  async function _postJsonAuthed(url, body, pluginAuthToken) {
856
878
  return new Promise((resolve7, reject) => {
857
879
  const data = JSON.stringify(body);
@@ -1162,8 +1184,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
1162
1184
  return decodedFile;
1163
1185
  };
1164
1186
  }
1165
- function normalizeWindowsPath(path49) {
1166
- return path49.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1187
+ function normalizeWindowsPath(path50) {
1188
+ return path50.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1167
1189
  }
1168
1190
 
1169
1191
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3643,9 +3665,9 @@ async function addSourceContext(frames) {
3643
3665
  LRU_FILE_CONTENTS_CACHE.reduce();
3644
3666
  return frames;
3645
3667
  }
3646
- function getContextLinesFromFile(path49, ranges, output) {
3668
+ function getContextLinesFromFile(path50, ranges, output) {
3647
3669
  return new Promise((resolve7) => {
3648
- const stream = (0, import_node_fs.createReadStream)(path49);
3670
+ const stream = (0, import_node_fs.createReadStream)(path50);
3649
3671
  const lineReaded = (0, import_node_readline.createInterface)({
3650
3672
  input: stream
3651
3673
  });
@@ -3660,7 +3682,7 @@ function getContextLinesFromFile(path49, ranges, output) {
3660
3682
  let rangeStart = range[0];
3661
3683
  let rangeEnd = range[1];
3662
3684
  function onStreamError() {
3663
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path49, 1);
3685
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path50, 1);
3664
3686
  lineReaded.close();
3665
3687
  lineReaded.removeAllListeners();
3666
3688
  destroyStreamAndResolve();
@@ -3721,8 +3743,8 @@ function clearLineContext(frame) {
3721
3743
  delete frame.context_line;
3722
3744
  delete frame.post_context;
3723
3745
  }
3724
- function shouldSkipContextLinesForFile(path49) {
3725
- return path49.startsWith("node:") || path49.endsWith(".min.js") || path49.endsWith(".min.cjs") || path49.endsWith(".min.mjs") || path49.startsWith("data:");
3746
+ function shouldSkipContextLinesForFile(path50) {
3747
+ return path50.startsWith("node:") || path50.endsWith(".min.js") || path50.endsWith(".min.cjs") || path50.endsWith(".min.mjs") || path50.startsWith("data:");
3726
3748
  }
3727
3749
  function shouldSkipContextLinesForFrame(frame) {
3728
3750
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -5876,7 +5898,7 @@ function readAnonId() {
5876
5898
  }
5877
5899
  function superProperties() {
5878
5900
  return {
5879
- cliVersion: true ? "2.33.0" : "0.0.0-dev",
5901
+ cliVersion: true ? "2.35.0" : "0.0.0-dev",
5880
5902
  nodeVersion: process.version,
5881
5903
  platform: process.platform,
5882
5904
  arch: process.arch,
@@ -9948,13 +9970,13 @@ function detectStartupBanner(lines) {
9948
9970
  while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
9949
9971
  if (metaIdx - artStart < 2) return null;
9950
9972
  const pathLine = (lines[metaIdx + 1] ?? "").trim();
9951
- const path49 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9973
+ const path50 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9952
9974
  return {
9953
9975
  title: "",
9954
9976
  subtitle: lines[metaIdx].trim(),
9955
- path: path49,
9977
+ path: path50,
9956
9978
  startIdx: artStart,
9957
- endIdx: metaIdx + (path49 ? 1 : 0)
9979
+ endIdx: metaIdx + (path50 ? 1 : 0)
9958
9980
  };
9959
9981
  }
9960
9982
 
@@ -11290,11 +11312,11 @@ function parseReview(stdout) {
11290
11312
  for (const line of lines) {
11291
11313
  const m = line.match(HUNK_LINE_RE);
11292
11314
  if (!m) continue;
11293
- const [, path49, lineNo, sevToken, message] = m;
11294
- if (!path49 || !lineNo || !message) continue;
11315
+ const [, path50, lineNo, sevToken, message] = m;
11316
+ if (!path50 || !lineNo || !message) continue;
11295
11317
  const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
11296
11318
  hunks.push({
11297
- path: path49.trim(),
11319
+ path: path50.trim(),
11298
11320
  line: Number(lineNo),
11299
11321
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
11300
11322
  message: cleanedMessage
@@ -15688,11 +15710,11 @@ function extractSelectPrompt(text) {
15688
15710
  }
15689
15711
 
15690
15712
  // src/commands/start/handlers.ts
15691
- var fs31 = __toESM(require("fs"));
15713
+ var fs32 = __toESM(require("fs"));
15692
15714
  var os25 = __toESM(require("os"));
15693
- var path38 = __toESM(require("path"));
15715
+ var path39 = __toESM(require("path"));
15694
15716
  var import_crypto3 = require("crypto");
15695
- var import_child_process15 = require("child_process");
15717
+ var import_child_process16 = require("child_process");
15696
15718
 
15697
15719
  // src/lib/payload.ts
15698
15720
  var import_zod = require("zod");
@@ -17282,10 +17304,12 @@ var BdAdapter = class {
17282
17304
  return this.resolveBinary() !== null;
17283
17305
  }
17284
17306
  /**
17285
- * Run an arbitrary bd subcommand. Always injects `--global` (the home brain)
17286
- * unless a `beadsDir` override is set, in which case `BEADS_DIR` redirects bd
17287
- * to that dir instead (tests). Returns the raw result; callers decide how to
17288
- * interpret exit codes (e.g. `bd setup --check` uses 0/nonzero).
17307
+ * Run an arbitrary bd subcommand against the home brain. `BEADS_DIR` is set
17308
+ * to the configured `beadsDir` (tests) or `~/.beads` (the verified embedded
17309
+ * home brain) so every command resolves to the same single graph regardless
17310
+ * of the process cwd. Returns the raw result; callers decide how to interpret
17311
+ * exit codes — note bd exits 0 even for some error states (e.g. "no beads
17312
+ * database found"), so JSON-parsing callers must also inspect the payload.
17289
17313
  */
17290
17314
  async run(args2) {
17291
17315
  const binary = this.resolveBinary();
@@ -17293,14 +17317,9 @@ var BdAdapter = class {
17293
17317
  return { code: -1, stdout: "", stderr: "bd binary not resolved" };
17294
17318
  }
17295
17319
  const env = { ...process.env };
17296
- const finalArgs = [...args2];
17297
- if (this.opts.beadsDir) {
17298
- env.BEADS_DIR = this.opts.beadsDir;
17299
- } else if (!finalArgs.includes("--global")) {
17300
- finalArgs.push("--global");
17301
- }
17302
- log.trace("beads", `bd ${finalArgs.join(" ")}`);
17303
- return _spawnSeam.run(binary, finalArgs, { cwd: this.opts.cwd, env });
17320
+ env.BEADS_DIR = this.opts.beadsDir ?? defaultBeadsHomeDir();
17321
+ log.trace("beads", `bd ${args2.join(" ")} (BEADS_DIR=${env.BEADS_DIR})`);
17322
+ return _spawnSeam.run(binary, args2, { cwd: this.opts.cwd, env });
17304
17323
  }
17305
17324
  /**
17306
17325
  * `bd ready --json` → typed issue array. `bd list --json` shares the shape,
@@ -17372,92 +17391,243 @@ function defaultBeadsHomeDir() {
17372
17391
  return path35.join(os24.homedir(), ".beads");
17373
17392
  }
17374
17393
 
17375
- // src/beads/bootstrap.ts
17376
- var SETUP_RECIPE = {
17394
+ // src/beads/provisioner.ts
17395
+ var import_child_process14 = require("child_process");
17396
+ var fs30 = __toESM(require("fs"));
17397
+ var path36 = __toESM(require("path"));
17398
+
17399
+ // src/beads/install-bd.ts
17400
+ var import_child_process13 = require("child_process");
17401
+ var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
17402
+ var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
17403
+ function resolveInstallStrategy(platform2) {
17404
+ if (platform2 === "win32") {
17405
+ return {
17406
+ command: "powershell.exe",
17407
+ args: [
17408
+ "-NoProfile",
17409
+ "-NonInteractive",
17410
+ "-ExecutionPolicy",
17411
+ "Bypass",
17412
+ "-Command",
17413
+ `irm ${INSTALL_PS1_URL} | iex`
17414
+ ],
17415
+ description: `PowerShell: irm ${INSTALL_PS1_URL} | iex (requires Go 1.24+ and Git for Windows on PATH)`
17416
+ };
17417
+ }
17418
+ return {
17419
+ command: "bash",
17420
+ args: ["-c", `curl -fsSL ${INSTALL_SH_URL} | bash`],
17421
+ description: `curl -fsSL ${INSTALL_SH_URL} | bash`
17422
+ };
17423
+ }
17424
+ var _installSpawnSeam = {
17425
+ run: _defaultInstallSpawn
17426
+ };
17427
+ function _defaultInstallSpawn(strategy) {
17428
+ return new Promise((resolve7) => {
17429
+ let proc;
17430
+ try {
17431
+ proc = (0, import_child_process13.spawn)(strategy.command, strategy.args, { env: process.env });
17432
+ } catch (err) {
17433
+ resolve7({ ok: false, code: -1, stderr: err.message });
17434
+ return;
17435
+ }
17436
+ let stderr = "";
17437
+ proc.stderr?.on("data", (c2) => {
17438
+ stderr += c2.toString();
17439
+ });
17440
+ proc.on("error", (err) => resolve7({ ok: false, code: -1, stderr: err.message }));
17441
+ proc.on(
17442
+ "close",
17443
+ (code) => resolve7({ ok: code === 0, code: code ?? -1, stderr })
17444
+ );
17445
+ });
17446
+ }
17447
+ async function installBd(platform2 = process.platform) {
17448
+ const strategy = resolveInstallStrategy(platform2);
17449
+ log.info("beads", `installing bd via ${strategy.description}`);
17450
+ const result = await _installSpawnSeam.run(strategy);
17451
+ if (!result.ok) {
17452
+ log.warn(
17453
+ "beads",
17454
+ `bd install failed (code=${result.code}): ${result.stderr.slice(0, 200)}`
17455
+ );
17456
+ }
17457
+ return result;
17458
+ }
17459
+
17460
+ // src/beads/provisioner.ts
17461
+ var AGENT_SETUP_RECIPE = {
17377
17462
  claude: "claude",
17378
17463
  codex: "codex",
17464
+ copilot: "copilot",
17379
17465
  cursor: "cursor",
17380
- gemini: "gemini",
17381
17466
  aider: "aider",
17382
- copilot: "copilot"
17467
+ gemini: "gemini",
17468
+ coderabbit: null
17469
+ };
17470
+ var _provisionSeam = {
17471
+ install: installBd,
17472
+ homeBrainInitialized,
17473
+ /** GAP 1 — symlink the resolved bd onto PATH for the agent's own shell. */
17474
+ linkBdOntoPath,
17475
+ /** Silence bd's `beads.role not configured` warning. */
17476
+ setGitBeadsRole
17477
+ };
17478
+ var _linkSeam = {
17479
+ platform: () => process.platform,
17480
+ /**
17481
+ * The directory the `codeam` executable lives in — guaranteed on PATH (npm
17482
+ * puts global bins there, and that's how the user launched us). We symlink
17483
+ * `bd` alongside it so the AGENT's shell resolves `bd` natively. Derived from
17484
+ * `process.argv[1]` (the CLI entry script). Returns null when it can't be
17485
+ * determined.
17486
+ */
17487
+ cliBinDir: () => {
17488
+ const entry = process.argv[1];
17489
+ if (!entry) return null;
17490
+ try {
17491
+ return path36.dirname(fs30.realpathSync(entry));
17492
+ } catch {
17493
+ return path36.dirname(entry);
17494
+ }
17495
+ },
17496
+ /** Current symlink target at `linkPath`, or null when absent / not a link. */
17497
+ readlink: (linkPath) => {
17498
+ try {
17499
+ return fs30.readlinkSync(linkPath);
17500
+ } catch {
17501
+ return null;
17502
+ }
17503
+ },
17504
+ unlink: (linkPath) => fs30.unlinkSync(linkPath),
17505
+ symlink: (target, linkPath) => fs30.symlinkSync(target, linkPath)
17383
17506
  };
17384
- async function bootstrapBeads(opts) {
17507
+ function linkBdOntoPath(binaryPath) {
17508
+ if (_linkSeam.platform() === "win32") return;
17509
+ const binDir = _linkSeam.cliBinDir();
17510
+ if (!binDir) return;
17511
+ const linkPath = path36.join(binDir, "bd");
17512
+ if (linkPath === binaryPath) return;
17513
+ const current = _linkSeam.readlink(linkPath);
17514
+ if (current === binaryPath) return;
17515
+ if (current !== null) _linkSeam.unlink(linkPath);
17516
+ _linkSeam.symlink(binaryPath, linkPath);
17517
+ log.info("beads", `linked bd onto PATH: ${linkPath} -> ${binaryPath}`);
17518
+ }
17519
+ function setGitBeadsRole() {
17520
+ (0, import_child_process14.execFileSync)("git", ["config", "--global", "beads.role", "contributor"], {
17521
+ stdio: "ignore"
17522
+ });
17523
+ }
17524
+ function homeBrainInitialized(beadsDir) {
17525
+ try {
17526
+ return fs30.statSync(path36.join(beadsDir, "embeddeddolt")).isDirectory();
17527
+ } catch {
17528
+ return false;
17529
+ }
17530
+ }
17531
+ async function provisionBeads(opts = {}) {
17385
17532
  const bd = opts.adapter ?? new BdAdapter({ cwd: opts.cwd, beadsDir: opts.beadsDir });
17533
+ const beadsDir = opts.beadsDir ?? defaultBeadsHomeDir();
17386
17534
  const result = {
17387
17535
  bdAvailable: false,
17388
- serverUp: false,
17389
- agentsConfigured: [],
17390
- exportEnabled: false
17536
+ initialized: false,
17537
+ exportEnabled: false,
17538
+ agentsWired: []
17391
17539
  };
17392
17540
  if (!bd.isAvailable()) {
17393
- log.warn("beads", "bd binary unavailable \u2014 skipping bootstrap");
17541
+ log.info("beads", "bd binary missing \u2014 running OS installer fallback");
17542
+ const install = await _provisionSeam.install();
17543
+ if (!install.ok) {
17544
+ log.warn("beads", `bd install failed (code=${install.code}) \u2014 beads disabled this run`);
17545
+ return result;
17546
+ }
17547
+ }
17548
+ if (!bd.isAvailable()) {
17549
+ log.warn("beads", "bd still unavailable after install \u2014 beads disabled this run");
17394
17550
  return result;
17395
17551
  }
17396
17552
  result.bdAvailable = true;
17397
- result.serverUp = await ensureServer(bd);
17398
- if (!result.serverUp) {
17399
- log.warn("beads", "dolt sql-server not up after start \u2014 skipping wiring this run");
17400
- return result;
17553
+ const binaryPath = bd.resolveBinary();
17554
+ if (binaryPath) {
17555
+ try {
17556
+ _provisionSeam.linkBdOntoPath(binaryPath);
17557
+ } catch (err) {
17558
+ log.warn("beads", "linking bd onto PATH failed (non-fatal)", err);
17559
+ }
17401
17560
  }
17402
- for (const agent of opts.agents) {
17403
- const recipe = SETUP_RECIPE[agent];
17404
- if (!recipe) {
17405
- log.trace("beads", `no bd setup recipe for agent ${agent} \u2014 skipping`);
17406
- continue;
17561
+ try {
17562
+ _provisionSeam.setGitBeadsRole();
17563
+ } catch (err) {
17564
+ log.trace("beads", `git config beads.role failed (non-fatal): ${err.message}`);
17565
+ }
17566
+ if (_provisionSeam.homeBrainInitialized(beadsDir)) {
17567
+ log.trace("beads", `home brain already initialized at ${beadsDir}`);
17568
+ result.initialized = true;
17569
+ } else {
17570
+ log.info("beads", `initializing home brain at ${beadsDir}`);
17571
+ const init = await bd.run(["init", "--skip-agents", "--skip-hooks", "--non-interactive"]);
17572
+ if (init.code !== 0) {
17573
+ log.warn("beads", `bd init failed (code=${init.code}): ${init.stderr.slice(0, 200)}`);
17574
+ return result;
17407
17575
  }
17408
- const applied = await ensureRecipe(bd, recipe);
17409
- if (applied) result.agentsConfigured.push(agent);
17576
+ result.initialized = true;
17410
17577
  }
17411
- result.exportEnabled = await ensureAutoExport(bd);
17578
+ const exp = await bd.run(["config", "set", "export.auto", "true"]);
17579
+ result.exportEnabled = exp.code === 0;
17580
+ result.agentsWired = await setupAgents(bd, opts.agents ?? []);
17412
17581
  log.info(
17413
17582
  "beads",
17414
- `bootstrap done server=${result.serverUp} agents=[${result.agentsConfigured.join(",")}] export=${result.exportEnabled}`
17583
+ `provision done initialized=${result.initialized} export=${result.exportEnabled} agentsWired=[${result.agentsWired.join(",")}]`
17415
17584
  );
17416
17585
  return result;
17417
17586
  }
17418
- async function ensureServer(bd) {
17419
- const status2 = await bd.run(["dolt", "status"]);
17420
- if (status2.code === 0) {
17421
- log.trace("beads", "dolt sql-server already running");
17422
- return true;
17423
- }
17424
- log.info("beads", "dolt sql-server down \u2014 starting detached");
17425
- const start2 = await bd.run(["dolt", "start"]);
17426
- if (start2.code !== 0) {
17427
- log.warn("beads", `bd dolt start failed (code=${start2.code}): ${start2.stderr.slice(0, 200)}`);
17428
- return false;
17587
+ async function setupAgents(bd, agents) {
17588
+ const wired = [];
17589
+ for (const recipe of dedupeRecipes(agents)) {
17590
+ try {
17591
+ const check = await bd.run(["setup", recipe, "--global", "--check"]);
17592
+ if (check.code === 0) {
17593
+ log.trace("beads", `bd setup ${recipe} --global already installed \u2014 skipping`);
17594
+ wired.push(recipe);
17595
+ continue;
17596
+ }
17597
+ log.info("beads", `wiring agent natively: bd setup ${recipe} --global`);
17598
+ const setup = await bd.run(["setup", recipe, "--global"]);
17599
+ if (setup.code === 0) {
17600
+ wired.push(recipe);
17601
+ } else {
17602
+ log.warn(
17603
+ "beads",
17604
+ `bd setup ${recipe} --global failed (code=${setup.code}): ${setup.stderr.slice(0, 200)} \u2014 non-fatal, agent runs without native bd wiring`
17605
+ );
17606
+ }
17607
+ } catch (err) {
17608
+ log.warn("beads", `bd setup ${recipe} --global threw (non-fatal)`, err);
17609
+ }
17429
17610
  }
17430
- const recheck = await bd.run(["dolt", "status"]);
17431
- return recheck.code === 0;
17611
+ return wired;
17432
17612
  }
17433
- async function ensureRecipe(bd, recipe) {
17434
- const check = await bd.run(["setup", recipe, "--check"]);
17435
- if (check.code === 0) {
17436
- log.trace("beads", `recipe ${recipe} already configured`);
17437
- return false;
17438
- }
17439
- const setup = await bd.run(["setup", recipe]);
17440
- if (setup.code !== 0) {
17441
- log.warn("beads", `bd setup ${recipe} failed (code=${setup.code})`);
17442
- return false;
17613
+ function dedupeRecipes(agents) {
17614
+ const seen = /* @__PURE__ */ new Set();
17615
+ for (const agent of agents) {
17616
+ const recipe = AGENT_SETUP_RECIPE[agent];
17617
+ if (recipe) seen.add(recipe);
17443
17618
  }
17444
- log.info("beads", `configured agent recipe: ${recipe}`);
17445
- return true;
17446
- }
17447
- async function ensureAutoExport(bd) {
17448
- const res = await bd.run(["config", "set", "export.jsonl", "true"]);
17449
- return res.code === 0;
17619
+ return [...seen];
17450
17620
  }
17451
17621
 
17452
17622
  // src/beads/watcher.ts
17453
17623
  var crypto3 = __toESM(require("crypto"));
17454
- var path37 = __toESM(require("path"));
17624
+ var path38 = __toESM(require("path"));
17455
17625
 
17456
17626
  // src/beads/project-key.ts
17457
- var import_child_process13 = require("child_process");
17627
+ var import_child_process15 = require("child_process");
17458
17628
  var crypto2 = __toESM(require("crypto"));
17459
- var fs30 = __toESM(require("fs"));
17460
- var path36 = __toESM(require("path"));
17629
+ var fs31 = __toESM(require("fs"));
17630
+ var path37 = __toESM(require("path"));
17461
17631
  function normalizeOrigin(raw) {
17462
17632
  const trimmed = raw.trim();
17463
17633
  if (!trimmed) return null;
@@ -17483,17 +17653,17 @@ function normalizeOrigin(raw) {
17483
17653
  return `${host}/${pathPart}`;
17484
17654
  }
17485
17655
  function findRepoRoot(cwd) {
17486
- let dir = path36.resolve(cwd);
17656
+ let dir = path37.resolve(cwd);
17487
17657
  const seen = /* @__PURE__ */ new Set();
17488
17658
  for (let i = 0; i < 256; i++) {
17489
17659
  if (seen.has(dir)) return null;
17490
17660
  seen.add(dir);
17491
17661
  try {
17492
- const stat3 = fs30.statSync(path36.join(dir, ".git"), { throwIfNoEntry: false });
17662
+ const stat3 = fs31.statSync(path37.join(dir, ".git"), { throwIfNoEntry: false });
17493
17663
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
17494
17664
  } catch {
17495
17665
  }
17496
- const parent = path36.dirname(dir);
17666
+ const parent = path37.dirname(dir);
17497
17667
  if (parent === dir) return null;
17498
17668
  dir = parent;
17499
17669
  }
@@ -17501,10 +17671,10 @@ function findRepoRoot(cwd) {
17501
17671
  }
17502
17672
  var _execSeam2 = {
17503
17673
  exec: (file, args2, opts) => {
17504
- const out2 = (0, import_child_process13.execFileSync)(file, args2, opts);
17674
+ const out2 = (0, import_child_process15.execFileSync)(file, args2, opts);
17505
17675
  return typeof out2 === "string" ? out2 : out2.toString("utf8");
17506
17676
  },
17507
- realpath: (p2) => fs30.realpathSync(p2)
17677
+ realpath: (p2) => fs31.realpathSync(p2)
17508
17678
  };
17509
17679
  function readOrigin(cwd) {
17510
17680
  try {
@@ -17533,7 +17703,7 @@ function deriveProjectIdentity(cwd = process.cwd()) {
17533
17703
  } catch {
17534
17704
  }
17535
17705
  const hash = crypto2.createHash("sha256").update(real).digest("hex");
17536
- return { projectKey: `path:${hash}`, projectLabel: path36.basename(real) || "project" };
17706
+ return { projectKey: `path:${hash}`, projectLabel: path37.basename(real) || "project" };
17537
17707
  }
17538
17708
 
17539
17709
  // src/services/file-watcher/transport.ts
@@ -17588,6 +17758,14 @@ function _post2(url, headers, payload) {
17588
17758
  // src/beads/watcher.ts
17589
17759
  var API_BASE4 = resolveApiBaseUrl();
17590
17760
  var DEBOUNCE_MS = 400;
17761
+ var ZERO_SUMMARY = {
17762
+ open_issues: 0,
17763
+ ready_issues: 0,
17764
+ blocked_issues: 0,
17765
+ in_progress_issues: 0,
17766
+ closed_issues: 0,
17767
+ total_issues: 0
17768
+ };
17591
17769
  var _chokidarSeam = {
17592
17770
  load: () => {
17593
17771
  try {
@@ -17601,7 +17779,7 @@ var BeadsWatcher = class {
17601
17779
  constructor(opts) {
17602
17780
  this.opts = opts;
17603
17781
  this.bd = opts.adapter ?? new BdAdapter({ cwd: opts.cwd, beadsDir: opts.beadsDir });
17604
- this.feedPath = opts.feedPath ?? path37.join(defaultBeadsHomeDir(), "issues.jsonl");
17782
+ this.feedPath = opts.feedPath ?? path38.join(defaultBeadsHomeDir(), "issues.jsonl");
17605
17783
  this.apiBase = opts.apiBaseUrl ?? API_BASE4;
17606
17784
  }
17607
17785
  opts;
@@ -17687,8 +17865,14 @@ var BeadsWatcher = class {
17687
17865
  projectLabel,
17688
17866
  fullSnapshot: true,
17689
17867
  issues,
17868
+ // The backend DTO requires `dependencies` (not `deps`). We don't track
17869
+ // edges in the P0 snapshot yet, so always send an empty array rather than
17870
+ // omitting the field (an omitted/conditional field 400s the ingest).
17871
+ dependencies: [],
17690
17872
  memories: [],
17691
- ...summary ? { summary } : {}
17873
+ // Always send a summary a null `bd status` yields a zeroed block rather
17874
+ // than an omitted field, so the backend never has to special-case it.
17875
+ summary: summary ?? ZERO_SUMMARY
17692
17876
  };
17693
17877
  const body = JSON.stringify(payload);
17694
17878
  const hash = crypto3.createHash("sha256").update(body).digest("hex");
@@ -17772,85 +17956,12 @@ async function applyBeadsAction(action, deps) {
17772
17956
  return { ok: true, action: action.kind, code: 0 };
17773
17957
  }
17774
17958
 
17775
- // src/beads/install-bd.ts
17776
- var import_child_process14 = require("child_process");
17777
- var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
17778
- var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
17779
- function resolveInstallStrategy(platform2) {
17780
- if (platform2 === "win32") {
17781
- return {
17782
- command: "powershell.exe",
17783
- args: [
17784
- "-NoProfile",
17785
- "-NonInteractive",
17786
- "-ExecutionPolicy",
17787
- "Bypass",
17788
- "-Command",
17789
- `irm ${INSTALL_PS1_URL} | iex`
17790
- ],
17791
- description: `PowerShell: irm ${INSTALL_PS1_URL} | iex (requires Go 1.24+ and Git for Windows on PATH)`
17792
- };
17793
- }
17794
- return {
17795
- command: "bash",
17796
- args: ["-c", `curl -fsSL ${INSTALL_SH_URL} | bash`],
17797
- description: `curl -fsSL ${INSTALL_SH_URL} | bash`
17798
- };
17799
- }
17800
- var _installSpawnSeam = {
17801
- run: _defaultInstallSpawn
17802
- };
17803
- function _defaultInstallSpawn(strategy) {
17804
- return new Promise((resolve7) => {
17805
- let proc;
17806
- try {
17807
- proc = (0, import_child_process14.spawn)(strategy.command, strategy.args, { env: process.env });
17808
- } catch (err) {
17809
- resolve7({ ok: false, code: -1, stderr: err.message });
17810
- return;
17811
- }
17812
- let stderr = "";
17813
- proc.stderr?.on("data", (c2) => {
17814
- stderr += c2.toString();
17815
- });
17816
- proc.on("error", (err) => resolve7({ ok: false, code: -1, stderr: err.message }));
17817
- proc.on(
17818
- "close",
17819
- (code) => resolve7({ ok: code === 0, code: code ?? -1, stderr })
17820
- );
17821
- });
17822
- }
17823
- async function installBd(platform2 = process.platform) {
17824
- const strategy = resolveInstallStrategy(platform2);
17825
- log.info("beads", `installing bd via ${strategy.description}`);
17826
- const result = await _installSpawnSeam.run(strategy);
17827
- if (!result.ok) {
17828
- log.warn(
17829
- "beads",
17830
- `bd install failed (code=${result.code}): ${result.stderr.slice(0, 200)}`
17831
- );
17832
- }
17833
- return result;
17834
- }
17835
-
17836
17959
  // src/beads/index.ts
17837
- async function maybeStartBeads(opts) {
17838
- if (!opts.enabled) {
17839
- log.trace("beads", "beads flag off \u2014 skipping");
17840
- return null;
17841
- }
17960
+ async function startBeads(opts) {
17842
17961
  const adapter = new BdAdapter({ cwd: opts.cwd });
17843
- if (!adapter.isAvailable() && opts.allowInstall) {
17844
- log.info("beads", "bd not found \u2014 running OS installer");
17845
- await installBd();
17846
- }
17847
- if (!adapter.isAvailable()) {
17848
- log.warn("beads", "bd unavailable \u2014 beads disabled for this session");
17849
- return null;
17850
- }
17851
- const boot = await bootstrapBeads({ cwd: opts.cwd, agents: opts.agents, adapter });
17852
- if (!boot.serverUp) {
17853
- log.warn("beads", "beads server not up \u2014 watcher not started this run");
17962
+ const provision = await provisionBeads({ cwd: opts.cwd, adapter, agents: opts.agents });
17963
+ if (!provision.bdAvailable || !provision.initialized) {
17964
+ log.warn("beads", "home brain not provisioned \u2014 watcher not started this run");
17854
17965
  return null;
17855
17966
  }
17856
17967
  const watcher = new BeadsWatcher({
@@ -17876,28 +17987,43 @@ function beadsKilled() {
17876
17987
  const v = process.env.CODEAM_BEADS_DISABLED;
17877
17988
  return !!v && v !== "0" && v.toLowerCase() !== "false";
17878
17989
  }
17879
- async function startBeadsForSession(ctx) {
17990
+ async function provisionBeadsForStart(ctx) {
17880
17991
  if (beadsKilled()) {
17881
- log.trace("beads", "CODEAM_BEADS_DISABLED set \u2014 beads off for this session");
17992
+ log.trace("beads", "CODEAM_BEADS_DISABLED set \u2014 beads off this run");
17882
17993
  return null;
17883
17994
  }
17995
+ process.env.BEADS_DIR = defaultBeadsHomeDir();
17884
17996
  if (!ctx.pluginAuthToken) {
17885
- log.trace("beads", "no pluginAuthToken on session \u2014 beads off");
17997
+ log.trace("beads", "no pluginAuthToken \u2014 beads off");
17886
17998
  return null;
17887
17999
  }
18000
+ const pluginAuthToken = ctx.pluginAuthToken;
18001
+ let started = null;
17888
18002
  try {
17889
- return await maybeStartBeads({
17890
- enabled: true,
18003
+ started = await startBeads({
17891
18004
  sessionId: ctx.sessionId,
17892
18005
  pluginId: ctx.pluginId,
17893
- pluginAuthToken: ctx.pluginAuthToken,
17894
- agents: ctx.agents,
17895
- cwd: ctx.cwd
18006
+ pluginAuthToken,
18007
+ cwd: ctx.cwd,
18008
+ agents: ctx.agents
17896
18009
  });
17897
18010
  } catch (err) {
17898
- log.warn("beads", "startBeadsForSession failed (non-fatal)", err);
17899
- return null;
17900
- }
18011
+ log.warn("beads", "provisionBeadsForStart failed (non-fatal)", err);
18012
+ started = null;
18013
+ }
18014
+ const { projectKey } = deriveProjectIdentity(ctx.cwd);
18015
+ void postBeadsProvisioning({
18016
+ sessionId: ctx.sessionId,
18017
+ pluginId: ctx.pluginId,
18018
+ pluginAuthToken,
18019
+ status: started ? "ready" : "failed",
18020
+ projectKey
18021
+ }).then((res) => {
18022
+ if (!res.ok) {
18023
+ log.trace("beads", `provisioning signal POST non-ok (status=${res.status}) \u2014 ignoring`);
18024
+ }
18025
+ });
18026
+ return started;
17901
18027
  }
17902
18028
  var ACTION_KINDS = /* @__PURE__ */ new Set([
17903
18029
  "claim",
@@ -17930,7 +18056,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
17930
18056
  function cleanupAttachmentTempFiles() {
17931
18057
  for (const p2 of pendingAttachmentFiles) {
17932
18058
  try {
17933
- fs31.unlinkSync(p2);
18059
+ fs32.unlinkSync(p2);
17934
18060
  } catch {
17935
18061
  }
17936
18062
  }
@@ -17939,8 +18065,8 @@ function cleanupAttachmentTempFiles() {
17939
18065
  function saveFilesTemp(files) {
17940
18066
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
17941
18067
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
17942
- const tmpPath = path38.join(os25.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
17943
- fs31.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
18068
+ const tmpPath = path39.join(os25.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
18069
+ fs32.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
17944
18070
  pendingAttachmentFiles.add(tmpPath);
17945
18071
  return tmpPath;
17946
18072
  });
@@ -17960,7 +18086,7 @@ var startTask = (ctx, _cmd, parsed) => {
17960
18086
  setTimeout(() => {
17961
18087
  for (const p2 of paths) {
17962
18088
  try {
17963
- fs31.unlinkSync(p2);
18089
+ fs32.unlinkSync(p2);
17964
18090
  } catch {
17965
18091
  }
17966
18092
  pendingAttachmentFiles.delete(p2);
@@ -18085,7 +18211,7 @@ var sessionTerminated = async (ctx, cmd) => {
18085
18211
  } catch {
18086
18212
  }
18087
18213
  try {
18088
- const proc = (0, import_child_process15.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18214
+ const proc = (0, import_child_process16.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18089
18215
  detached: true,
18090
18216
  stdio: "ignore"
18091
18217
  });
@@ -18107,7 +18233,7 @@ var shutdownSession = async (ctx, cmd) => {
18107
18233
  }
18108
18234
  if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
18109
18235
  try {
18110
- const stopProc = (0, import_child_process15.spawn)(
18236
+ const stopProc = (0, import_child_process16.spawn)(
18111
18237
  "bash",
18112
18238
  ["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
18113
18239
  { detached: true, stdio: "ignore" }
@@ -18117,7 +18243,7 @@ var shutdownSession = async (ctx, cmd) => {
18117
18243
  }
18118
18244
  }
18119
18245
  try {
18120
- const proc = (0, import_child_process15.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18246
+ const proc = (0, import_child_process16.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18121
18247
  detached: true,
18122
18248
  stdio: "ignore"
18123
18249
  });
@@ -18533,8 +18659,8 @@ function normalizeDetectionForSpawn(detection, cwd) {
18533
18659
  if (args2.length === 0) return detection;
18534
18660
  const binName = args2[0];
18535
18661
  if (binName.startsWith("-")) return detection;
18536
- const binPath = path38.join(cwd, "node_modules", ".bin", binName);
18537
- if (!fs31.existsSync(binPath)) return detection;
18662
+ const binPath = path39.join(cwd, "node_modules", ".bin", binName);
18663
+ if (!fs32.existsSync(binPath)) return detection;
18538
18664
  return {
18539
18665
  ...detection,
18540
18666
  command: binPath,
@@ -18624,7 +18750,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
18624
18750
  "BOOT_SEQUENCE",
18625
18751
  `${spawnable.command} ${spawnable.args.join(" ")}`
18626
18752
  );
18627
- const devServer = (0, import_child_process15.spawn)(spawnable.command, spawnable.args, {
18753
+ const devServer = (0, import_child_process16.spawn)(spawnable.command, spawnable.args, {
18628
18754
  cwd: process.cwd(),
18629
18755
  env: { ...process.env, ...spawnable.env ?? {} },
18630
18756
  stdio: ["ignore", "pipe", "pipe"]
@@ -18750,7 +18876,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
18750
18876
  });
18751
18877
  return;
18752
18878
  }
18753
- tunnel = (0, import_child_process15.spawn)(bin, ["tunnel", "--url", `http://localhost:${detection.port}`], {
18879
+ tunnel = (0, import_child_process16.spawn)(bin, ["tunnel", "--url", `http://localhost:${detection.port}`], {
18754
18880
  stdio: ["ignore", "pipe", "pipe"]
18755
18881
  });
18756
18882
  let parsedUrl = null;
@@ -18848,7 +18974,7 @@ var previewStopH = (ctx) => {
18848
18974
  };
18849
18975
  function runOnce(cmd, args2, cwd, env) {
18850
18976
  return new Promise((resolve7) => {
18851
- const child = (0, import_child_process15.spawn)(cmd, args2, {
18977
+ const child = (0, import_child_process16.spawn)(cmd, args2, {
18852
18978
  cwd,
18853
18979
  env: { ...process.env, ...env ?? {} },
18854
18980
  stdio: ["ignore", "pipe", "pipe"]
@@ -18944,10 +19070,10 @@ async function dispatchCommand(ctx, cmd) {
18944
19070
  }
18945
19071
 
18946
19072
  // src/services/file-watcher.service.ts
18947
- var import_child_process16 = require("child_process");
18948
- var fs32 = __toESM(require("fs"));
19073
+ var import_child_process17 = require("child_process");
19074
+ var fs33 = __toESM(require("fs"));
18949
19075
  var os26 = __toESM(require("os"));
18950
- var path39 = __toESM(require("path"));
19076
+ var path40 = __toESM(require("path"));
18951
19077
  var import_ignore = __toESM(require("ignore"));
18952
19078
 
18953
19079
  // src/services/file-watcher/diff-parser.ts
@@ -19090,18 +19216,18 @@ var _findGitRootSeam = {
19090
19216
  resolve: _defaultFindGitRoot
19091
19217
  };
19092
19218
  function _defaultFindGitRoot(startDir) {
19093
- let dir = path39.resolve(startDir);
19219
+ let dir = path40.resolve(startDir);
19094
19220
  const seen = /* @__PURE__ */ new Set();
19095
19221
  for (let i = 0; i < 256; i++) {
19096
19222
  if (seen.has(dir)) return null;
19097
19223
  seen.add(dir);
19098
19224
  try {
19099
- const gitPath = path39.join(dir, ".git");
19100
- const stat3 = fs32.statSync(gitPath, { throwIfNoEntry: false });
19225
+ const gitPath = path40.join(dir, ".git");
19226
+ const stat3 = fs33.statSync(gitPath, { throwIfNoEntry: false });
19101
19227
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
19102
19228
  } catch {
19103
19229
  }
19104
- const parent = path39.dirname(dir);
19230
+ const parent = path40.dirname(dir);
19105
19231
  if (parent === dir) return null;
19106
19232
  dir = parent;
19107
19233
  }
@@ -19346,7 +19472,7 @@ var FileWatcherService = class {
19346
19472
  }
19347
19473
  async emitForFile(absPath, changeType) {
19348
19474
  if (this.stopped) return;
19349
- const fileDir = path39.dirname(absPath);
19475
+ const fileDir = path40.dirname(absPath);
19350
19476
  let gitRoot = this.gitRootByDir.get(fileDir);
19351
19477
  if (gitRoot === void 0) {
19352
19478
  gitRoot = findGitRoot2(fileDir);
@@ -19359,19 +19485,19 @@ var FileWatcherService = class {
19359
19485
  );
19360
19486
  return;
19361
19487
  }
19362
- const relPathInRepo = path39.relative(gitRoot, absPath);
19488
+ const relPathInRepo = path40.relative(gitRoot, absPath);
19363
19489
  if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
19364
19490
  const matcher = this.getGitIgnoreMatcher(gitRoot);
19365
19491
  if (matcher && matcher.ignores(relPathInRepo)) {
19366
19492
  log.trace(
19367
19493
  "fileWatcher",
19368
- `${relPathInRepo} ignored by ${path39.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
19494
+ `${relPathInRepo} ignored by ${path40.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
19369
19495
  );
19370
19496
  return;
19371
19497
  }
19372
19498
  this.opts.onRepoDirty?.(gitRoot);
19373
- const repoPath = path39.relative(this.opts.workingDir, gitRoot);
19374
- const repoName = path39.basename(gitRoot);
19499
+ const repoPath = path40.relative(this.opts.workingDir, gitRoot);
19500
+ const repoName = path40.basename(gitRoot);
19375
19501
  let diffText = "";
19376
19502
  let fileStatus = "modified";
19377
19503
  if (changeType === "unlink") {
@@ -19546,7 +19672,7 @@ var FileWatcherService = class {
19546
19672
  collectGitignoreFiles(repoRoot, dir, matcher) {
19547
19673
  let entries;
19548
19674
  try {
19549
- entries = fs32.readdirSync(dir, { withFileTypes: true });
19675
+ entries = fs33.readdirSync(dir, { withFileTypes: true });
19550
19676
  } catch {
19551
19677
  return;
19552
19678
  }
@@ -19555,16 +19681,16 @@ var FileWatcherService = class {
19555
19681
  );
19556
19682
  if (gitignoreEntry) {
19557
19683
  try {
19558
- const body = fs32.readFileSync(path39.join(dir, ".gitignore"), "utf8");
19559
- const rel = path39.relative(repoRoot, dir).replace(/\\/g, "/");
19684
+ const body = fs33.readFileSync(path40.join(dir, ".gitignore"), "utf8");
19685
+ const rel = path40.relative(repoRoot, dir).replace(/\\/g, "/");
19560
19686
  const prefixed = body.split(/\r?\n/).map((line) => {
19561
19687
  const trimmed = line.trim();
19562
19688
  if (!trimmed || trimmed.startsWith("#")) return line;
19563
19689
  if (!rel) return line;
19564
19690
  if (trimmed.startsWith("!")) {
19565
- return "!" + path39.posix.join(rel, trimmed.slice(1));
19691
+ return "!" + path40.posix.join(rel, trimmed.slice(1));
19566
19692
  }
19567
- return path39.posix.join(rel, trimmed);
19693
+ return path40.posix.join(rel, trimmed);
19568
19694
  }).join("\n");
19569
19695
  matcher.add(prefixed);
19570
19696
  } catch {
@@ -19573,7 +19699,7 @@ var FileWatcherService = class {
19573
19699
  for (const entry of entries) {
19574
19700
  if (!entry.isDirectory()) continue;
19575
19701
  if (entry.name === ".git") continue;
19576
- const childAbs = path39.join(dir, entry.name);
19702
+ const childAbs = path40.join(dir, entry.name);
19577
19703
  if (isIgnoredFilePath(childAbs)) continue;
19578
19704
  this.collectGitignoreFiles(repoRoot, childAbs, matcher);
19579
19705
  }
@@ -19710,7 +19836,7 @@ async function _runGitImpl(cwd, args2, opts = {}) {
19710
19836
  return new Promise((resolve7) => {
19711
19837
  let proc;
19712
19838
  try {
19713
- proc = (0, import_child_process16.spawn)("git", args2, { cwd, env: process.env });
19839
+ proc = (0, import_child_process17.spawn)("git", args2, { cwd, env: process.env });
19714
19840
  } catch {
19715
19841
  resolve7(null);
19716
19842
  return;
@@ -19742,8 +19868,8 @@ function _runGit(cwd, args2, opts = {}) {
19742
19868
  var import_crypto4 = require("crypto");
19743
19869
 
19744
19870
  // src/services/turn-files/git-changeset.ts
19745
- var import_child_process17 = require("child_process");
19746
- var path40 = __toESM(require("path"));
19871
+ var import_child_process18 = require("child_process");
19872
+ var path41 = __toESM(require("path"));
19747
19873
  async function collectRepoChangeset(opts) {
19748
19874
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
19749
19875
  if (status2 === null) return null;
@@ -19826,7 +19952,7 @@ function defaultRunGit(cwd, args2) {
19826
19952
  return new Promise((resolve7) => {
19827
19953
  let proc;
19828
19954
  try {
19829
- proc = (0, import_child_process17.spawn)("git", args2, { cwd, env: process.env });
19955
+ proc = (0, import_child_process18.spawn)("git", args2, { cwd, env: process.env });
19830
19956
  } catch {
19831
19957
  resolve7(null);
19832
19958
  return;
@@ -19854,7 +19980,7 @@ function defaultRunGit(cwd, args2) {
19854
19980
  });
19855
19981
  }
19856
19982
  async function discoverRepos(workingDir, maxDepth = 4) {
19857
- const fs38 = await import("fs/promises");
19983
+ const fs39 = await import("fs/promises");
19858
19984
  const out2 = [];
19859
19985
  await walk(workingDir, 0);
19860
19986
  return out2;
@@ -19862,7 +19988,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19862
19988
  if (depth > maxDepth) return;
19863
19989
  let entries = [];
19864
19990
  try {
19865
- const dirents = await fs38.readdir(dir, { withFileTypes: true });
19991
+ const dirents = await fs39.readdir(dir, { withFileTypes: true });
19866
19992
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
19867
19993
  } catch {
19868
19994
  return;
@@ -19873,8 +19999,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19873
19999
  if (hasGit) {
19874
20000
  out2.push({
19875
20001
  repoRoot: dir,
19876
- repoPath: path40.relative(workingDir, dir),
19877
- repoName: path40.basename(dir)
20002
+ repoPath: path41.relative(workingDir, dir),
20003
+ repoName: path41.basename(dir)
19878
20004
  });
19879
20005
  return;
19880
20006
  }
@@ -19882,14 +20008,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19882
20008
  if (!entry.isDirectory) continue;
19883
20009
  if (entry.name === "node_modules") continue;
19884
20010
  if (entry.name === "dist" || entry.name === "build") continue;
19885
- await walk(path40.join(dir, entry.name), depth + 1);
20011
+ await walk(path41.join(dir, entry.name), depth + 1);
19886
20012
  }
19887
20013
  }
19888
20014
  }
19889
20015
 
19890
20016
  // src/services/turn-files/files-outbox.ts
19891
- var fs33 = __toESM(require("fs/promises"));
19892
- var path41 = __toESM(require("path"));
20017
+ var fs34 = __toESM(require("fs/promises"));
20018
+ var path42 = __toESM(require("path"));
19893
20019
  var import_os7 = require("os");
19894
20020
  var HOME_OUTBOX_DIR = ".codeam/outbox";
19895
20021
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -19922,16 +20048,16 @@ var FilesOutbox = class {
19922
20048
  backoffIndex = 0;
19923
20049
  stopped = false;
19924
20050
  constructor(opts) {
19925
- const base = opts.baseDir ?? path41.join(homeDir(), HOME_OUTBOX_DIR);
19926
- this.filePath = path41.join(base, `${opts.sessionId}.jsonl`);
20051
+ const base = opts.baseDir ?? path42.join(homeDir(), HOME_OUTBOX_DIR);
20052
+ this.filePath = path42.join(base, `${opts.sessionId}.jsonl`);
19927
20053
  this.post = opts.post;
19928
20054
  this.autoSchedule = opts.autoSchedule !== false;
19929
20055
  }
19930
20056
  /** Persist the entry to disk and trigger a flush. Returns once the
19931
20057
  * line is durable on disk (not once the POST succeeds). */
19932
20058
  async enqueue(entry) {
19933
- await fs33.mkdir(path41.dirname(this.filePath), { recursive: true });
19934
- await fs33.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
20059
+ await fs34.mkdir(path42.dirname(this.filePath), { recursive: true });
20060
+ await fs34.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
19935
20061
  this.backoffIndex = 0;
19936
20062
  if (this.autoSchedule) this.scheduleFlush(0);
19937
20063
  }
@@ -20020,7 +20146,7 @@ var FilesOutbox = class {
20020
20146
  async readAll() {
20021
20147
  let raw = "";
20022
20148
  try {
20023
- raw = await fs33.readFile(this.filePath, "utf8");
20149
+ raw = await fs34.readFile(this.filePath, "utf8");
20024
20150
  } catch {
20025
20151
  return [];
20026
20152
  }
@@ -20044,12 +20170,12 @@ var FilesOutbox = class {
20044
20170
  async rewrite(entries) {
20045
20171
  const tmpPath = `${this.filePath}.${process.pid}.tmp`;
20046
20172
  if (entries.length === 0) {
20047
- await fs33.unlink(this.filePath).catch(() => void 0);
20173
+ await fs34.unlink(this.filePath).catch(() => void 0);
20048
20174
  return;
20049
20175
  }
20050
20176
  const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
20051
- await fs33.writeFile(tmpPath, payload, "utf8");
20052
- await fs33.rename(tmpPath, this.filePath);
20177
+ await fs34.writeFile(tmpPath, payload, "utf8");
20178
+ await fs34.rename(tmpPath, this.filePath);
20053
20179
  }
20054
20180
  };
20055
20181
  function applyJitter(ms) {
@@ -20670,16 +20796,7 @@ async function runAcpSession(opts) {
20670
20796
  }).catch((err) => {
20671
20797
  log.warn("acpRunner", `fileWatcher.start failed: ${describeError(err)}`);
20672
20798
  });
20673
- let beads = null;
20674
- void startBeadsForSession({
20675
- sessionId: opts.sessionId,
20676
- pluginId: opts.pluginId,
20677
- pluginAuthToken: opts.pluginAuthToken,
20678
- agents: [opts.agent],
20679
- cwd: opts.cwd
20680
- }).then((started) => {
20681
- beads = started;
20682
- });
20799
+ const getBeads = opts.getBeads ?? (() => null);
20683
20800
  const relay = new CommandRelayService(
20684
20801
  opts.pluginId,
20685
20802
  async (cmd) => {
@@ -20694,7 +20811,7 @@ async function runAcpSession(opts) {
20694
20811
  history,
20695
20812
  initialize.agentCapabilities,
20696
20813
  turnFiles,
20697
- () => beads
20814
+ getBeads
20698
20815
  );
20699
20816
  },
20700
20817
  { id: opts.agent, name: opts.agent, displayName: opts.agent }
@@ -20705,7 +20822,6 @@ async function runAcpSession(opts) {
20705
20822
  relay.stop();
20706
20823
  void fileWatcher.stop();
20707
20824
  turnFiles.stop();
20708
- void beads?.watcher.stop();
20709
20825
  closeAllTerminals();
20710
20826
  await client2.stop();
20711
20827
  process.exit(0);
@@ -21813,8 +21929,8 @@ var OutputService = class _OutputService {
21813
21929
  };
21814
21930
 
21815
21931
  // src/services/history.service.ts
21816
- var fs34 = __toESM(require("fs"));
21817
- var path42 = __toESM(require("path"));
21932
+ var fs35 = __toESM(require("fs"));
21933
+ var path43 = __toESM(require("path"));
21818
21934
  var os27 = __toESM(require("os"));
21819
21935
  var https7 = __toESM(require("https"));
21820
21936
  var http7 = __toESM(require("http"));
@@ -21842,7 +21958,7 @@ function parseJsonl(filePath) {
21842
21958
  const messages = [];
21843
21959
  let raw;
21844
21960
  try {
21845
- raw = fs34.readFileSync(filePath, "utf8");
21961
+ raw = fs35.readFileSync(filePath, "utf8");
21846
21962
  } catch (err) {
21847
21963
  if (err.code !== "ENOENT") {
21848
21964
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -21977,7 +22093,7 @@ var HistoryService = class _HistoryService {
21977
22093
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
21978
22094
  }
21979
22095
  get projectDir() {
21980
- return this.runtime.resolveHistoryDir(this.cwd) ?? path42.join(os27.homedir(), ".claude", "projects", encodeCwd(this.cwd));
22096
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path43.join(os27.homedir(), ".claude", "projects", encodeCwd(this.cwd));
21981
22097
  }
21982
22098
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
21983
22099
  setCurrentConversationId(id) {
@@ -21989,7 +22105,7 @@ var HistoryService = class _HistoryService {
21989
22105
  /** Return the current message count in the active conversation. */
21990
22106
  getCurrentMessageCount() {
21991
22107
  if (!this.currentConversationId) return 0;
21992
- const filePath = path42.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22108
+ const filePath = path43.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21993
22109
  return parseJsonl(filePath).length;
21994
22110
  }
21995
22111
  /**
@@ -22000,7 +22116,7 @@ var HistoryService = class _HistoryService {
22000
22116
  const deadline = Date.now() + timeoutMs;
22001
22117
  while (Date.now() < deadline) {
22002
22118
  if (!this.currentConversationId) return null;
22003
- const filePath = path42.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22119
+ const filePath = path43.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22004
22120
  const messages = parseJsonl(filePath);
22005
22121
  if (messages.length > previousCount) {
22006
22122
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -22026,16 +22142,16 @@ var HistoryService = class _HistoryService {
22026
22142
  const dir = this.projectDir;
22027
22143
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
22028
22144
  try {
22029
- const files = fs34.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22145
+ const files = fs35.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22030
22146
  try {
22031
- const stat3 = fs34.statSync(path42.join(dir, e.name));
22147
+ const stat3 = fs35.statSync(path43.join(dir, e.name));
22032
22148
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
22033
22149
  } catch {
22034
22150
  return { name: e.name, mtime: 0, birthtime: 0 };
22035
22151
  }
22036
22152
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
22037
22153
  if (files.length > 0) {
22038
- this.currentConversationId = path42.basename(files[0].name, ".jsonl");
22154
+ this.currentConversationId = path43.basename(files[0].name, ".jsonl");
22039
22155
  }
22040
22156
  } catch {
22041
22157
  }
@@ -22069,13 +22185,13 @@ var HistoryService = class _HistoryService {
22069
22185
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
22070
22186
  let entries;
22071
22187
  try {
22072
- entries = fs34.readdirSync(dir, { withFileTypes: true });
22188
+ entries = fs35.readdirSync(dir, { withFileTypes: true });
22073
22189
  } catch {
22074
22190
  return null;
22075
22191
  }
22076
22192
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22077
22193
  try {
22078
- const stat3 = fs34.statSync(path42.join(dir, e.name));
22194
+ const stat3 = fs35.statSync(path43.join(dir, e.name));
22079
22195
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
22080
22196
  } catch {
22081
22197
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -22084,12 +22200,12 @@ var HistoryService = class _HistoryService {
22084
22200
  if (files.length === 0) return null;
22085
22201
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
22086
22202
  if (!files.some((f) => f.name === targetFile)) return null;
22087
- return this.extractUsageFromFile(path42.join(dir, targetFile));
22203
+ return this.extractUsageFromFile(path43.join(dir, targetFile));
22088
22204
  }
22089
22205
  extractUsageFromFile(filePath) {
22090
22206
  let raw;
22091
22207
  try {
22092
- raw = fs34.readFileSync(filePath, "utf8");
22208
+ raw = fs35.readFileSync(filePath, "utf8");
22093
22209
  } catch {
22094
22210
  return null;
22095
22211
  }
@@ -22134,9 +22250,9 @@ var HistoryService = class _HistoryService {
22134
22250
  let totalCost = 0;
22135
22251
  let files;
22136
22252
  try {
22137
- files = fs34.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
22253
+ files = fs35.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
22138
22254
  try {
22139
- return fs34.statSync(path42.join(projectDir, f)).mtimeMs >= monthStartMs;
22255
+ return fs35.statSync(path43.join(projectDir, f)).mtimeMs >= monthStartMs;
22140
22256
  } catch {
22141
22257
  return false;
22142
22258
  }
@@ -22147,7 +22263,7 @@ var HistoryService = class _HistoryService {
22147
22263
  for (const file of files) {
22148
22264
  let raw;
22149
22265
  try {
22150
- raw = fs34.readFileSync(path42.join(projectDir, file), "utf8");
22266
+ raw = fs35.readFileSync(path43.join(projectDir, file), "utf8");
22151
22267
  } catch {
22152
22268
  continue;
22153
22269
  }
@@ -22211,7 +22327,7 @@ var HistoryService = class _HistoryService {
22211
22327
  * showing an empty conversation.
22212
22328
  */
22213
22329
  async loadConversation(sessionId) {
22214
- const filePath = path42.join(this.projectDir, `${sessionId}.jsonl`);
22330
+ const filePath = path43.join(this.projectDir, `${sessionId}.jsonl`);
22215
22331
  const messages = parseJsonl(filePath);
22216
22332
  if (messages.length === 0) return;
22217
22333
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -22265,7 +22381,7 @@ var HistoryService = class _HistoryService {
22265
22381
  if (!this.currentConversationId) return 0;
22266
22382
  }
22267
22383
  const sessionId = this.currentConversationId;
22268
- const filePath = path42.join(this.projectDir, `${sessionId}.jsonl`);
22384
+ const filePath = path43.join(this.projectDir, `${sessionId}.jsonl`);
22269
22385
  const messages = parseJsonl(filePath);
22270
22386
  if (messages.length === 0) return 0;
22271
22387
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -22691,13 +22807,13 @@ function fetchQuotaUsage(runtime, historySvc) {
22691
22807
  }
22692
22808
 
22693
22809
  // src/commands/start/keep-alive.ts
22694
- var import_child_process18 = require("child_process");
22810
+ var import_child_process19 = require("child_process");
22695
22811
  function buildKeepAlive(ctx) {
22696
22812
  let timer = null;
22697
22813
  async function setIdleTimeout(minutes) {
22698
22814
  if (!ctx.inCodespace || !ctx.codespaceName) return;
22699
22815
  await new Promise((resolve7) => {
22700
- const proc = (0, import_child_process18.spawn)(
22816
+ const proc = (0, import_child_process19.spawn)(
22701
22817
  "gh",
22702
22818
  [
22703
22819
  "api",
@@ -22787,6 +22903,20 @@ async function start(requestedAgent) {
22787
22903
  "pluginAuth",
22788
22904
  `boot triple sessionId=${session.id} pluginId=${pluginId} tokenLen=${tokenForLog.length} tokenHead=${tokenForLog.slice(0, 12)} tokenTail=${tokenForLog.slice(-8)} mintedEqualsCached=${refreshed === session.pluginAuthToken}`
22789
22905
  );
22906
+ let beads = null;
22907
+ const getBeads = () => beads;
22908
+ const beadsReady = provisionBeadsForStart({
22909
+ sessionId: session.id,
22910
+ pluginId,
22911
+ pluginAuthToken: session.pluginAuthToken ?? void 0,
22912
+ cwd,
22913
+ // Wire this session's agent natively via `bd setup <recipe> --global` so
22914
+ // the agent actually uses bd (D12 — REVISED). Covers BOTH ACP and PTY.
22915
+ agents: [session.agent]
22916
+ }).then((started) => {
22917
+ beads = started;
22918
+ return started;
22919
+ });
22790
22920
  const acpDisabled = process.env.CODEAM_ACP_DISABLED === "1";
22791
22921
  if (!acpDisabled && session.pluginAuthToken) {
22792
22922
  const adapter = getAcpAdapter(session.agent);
@@ -22797,7 +22927,8 @@ async function start(requestedAgent) {
22797
22927
  pluginId,
22798
22928
  pluginAuthToken: session.pluginAuthToken,
22799
22929
  adapter,
22800
- cwd
22930
+ cwd,
22931
+ getBeads
22801
22932
  });
22802
22933
  return;
22803
22934
  }
@@ -22859,7 +22990,6 @@ async function start(requestedAgent) {
22859
22990
  dirtyTracker: dirtyTracker ?? void 0
22860
22991
  }) : null;
22861
22992
  let streamingEmitter = null;
22862
- let beads = null;
22863
22993
  const agent = new AgentService(
22864
22994
  runtime,
22865
22995
  {
@@ -22910,14 +23040,7 @@ async function start(requestedAgent) {
22910
23040
  await dispatchCommand(ctx, cmd);
22911
23041
  }, runtime.meta);
22912
23042
  ctx.relay = relay;
22913
- void startBeadsForSession({
22914
- sessionId: session.id,
22915
- pluginId,
22916
- pluginAuthToken: session.pluginAuthToken ?? void 0,
22917
- agents: [session.agent],
22918
- cwd
22919
- }).then((started) => {
22920
- beads = started;
23043
+ void beadsReady.then((started) => {
22921
23044
  ctx.beads = started;
22922
23045
  });
22923
23046
  registerTerminalHandlers({
@@ -23175,7 +23298,7 @@ async function autoLinkAfterPair(opts) {
23175
23298
  }
23176
23299
 
23177
23300
  // src/commands/pair-auto.ts
23178
- var fs35 = __toESM(require("fs"));
23301
+ var fs36 = __toESM(require("fs"));
23179
23302
  var os28 = __toESM(require("os"));
23180
23303
  var import_crypto7 = require("crypto");
23181
23304
 
@@ -23236,6 +23359,15 @@ async function startInfraOnly(agentId) {
23236
23359
  pluginId,
23237
23360
  pluginAuthToken: session.pluginAuthToken
23238
23361
  }) : null;
23362
+ let beads = null;
23363
+ void provisionBeadsForStart({
23364
+ sessionId: session.id,
23365
+ pluginId,
23366
+ pluginAuthToken: session.pluginAuthToken ?? void 0,
23367
+ cwd
23368
+ }).then((started) => {
23369
+ beads = started;
23370
+ });
23239
23371
  let relayRef = null;
23240
23372
  const ctx = {
23241
23373
  // Agent-touching fields are not used by any of the commands
@@ -23257,6 +23389,23 @@ async function startInfraOnly(agentId) {
23257
23389
  const relay = new CommandRelayService(
23258
23390
  pluginId,
23259
23391
  async (cmd) => {
23392
+ if (cmd.type === "beads_action") {
23393
+ if (!beads) {
23394
+ log.trace("infra-only", "beads_action received but beads not running \u2014 dropping");
23395
+ return;
23396
+ }
23397
+ const action = beadsActionFromPayload(cmd.payload);
23398
+ if (!action) {
23399
+ log.warn("infra-only", "malformed beads_action payload \u2014 dropping");
23400
+ return;
23401
+ }
23402
+ try {
23403
+ await handleBeadsActionCommand(action, beads);
23404
+ } catch (err) {
23405
+ log.warn("infra-only", "handleBeadsActionCommand failed (non-fatal)", err);
23406
+ }
23407
+ return;
23408
+ }
23260
23409
  if (!INFRA_ONLY_COMMAND_TYPES.has(cmd.type)) {
23261
23410
  log.trace("infra-only", `dropping agent-only command type=${cmd.type}`);
23262
23411
  return;
@@ -23317,6 +23466,7 @@ async function startInfraOnly(agentId) {
23317
23466
  } catch {
23318
23467
  }
23319
23468
  void fileWatcher?.stop();
23469
+ void beads?.watcher.stop();
23320
23470
  closeAllTerminals();
23321
23471
  cleanupAttachmentTempFiles();
23322
23472
  process.exit(0);
@@ -23344,12 +23494,12 @@ function readTokenFromArgs(args2) {
23344
23494
  }
23345
23495
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
23346
23496
  if (fileFlag) {
23347
- const path49 = fileFlag.slice("--token-file=".length);
23497
+ const path50 = fileFlag.slice("--token-file=".length);
23348
23498
  try {
23349
- const content = fs35.readFileSync(path49, "utf8").trim();
23350
- if (content.length === 0) fail(`--token-file ${path49} is empty`);
23499
+ const content = fs36.readFileSync(path50, "utf8").trim();
23500
+ if (content.length === 0) fail(`--token-file ${path50} is empty`);
23351
23501
  try {
23352
- fs35.unlinkSync(path49);
23502
+ fs36.unlinkSync(path50);
23353
23503
  } catch {
23354
23504
  }
23355
23505
  return content;
@@ -23612,11 +23762,11 @@ async function logout() {
23612
23762
  var import_picocolors10 = __toESM(require("picocolors"));
23613
23763
 
23614
23764
  // src/services/providers/github-codespaces.ts
23615
- var import_child_process19 = require("child_process");
23765
+ var import_child_process20 = require("child_process");
23616
23766
  var import_util4 = require("util");
23617
23767
  var import_picocolors8 = __toESM(require("picocolors"));
23618
- var path43 = __toESM(require("path"));
23619
- var execFileP5 = (0, import_util4.promisify)(import_child_process19.execFile);
23768
+ var path44 = __toESM(require("path"));
23769
+ var execFileP5 = (0, import_util4.promisify)(import_child_process20.execFile);
23620
23770
  var MAX_BUFFER = 8 * 1024 * 1024;
23621
23771
  function resetStdinForChild() {
23622
23772
  if (process.stdin.isTTY) {
@@ -23660,7 +23810,7 @@ var GitHubCodespacesProvider = class {
23660
23810
  if (!isAuthed) {
23661
23811
  resetStdinForChild();
23662
23812
  await new Promise((resolve7, reject) => {
23663
- const proc = (0, import_child_process19.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
23813
+ const proc = (0, import_child_process20.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
23664
23814
  stdio: "inherit"
23665
23815
  });
23666
23816
  proc.on("exit", (code) => {
@@ -23694,7 +23844,7 @@ var GitHubCodespacesProvider = class {
23694
23844
  wt(noteLines.join("\n"), "One more permission needed");
23695
23845
  resetStdinForChild();
23696
23846
  const refreshCode = await new Promise((resolve7, reject) => {
23697
- const proc = (0, import_child_process19.spawn)(
23847
+ const proc = (0, import_child_process20.spawn)(
23698
23848
  "gh",
23699
23849
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
23700
23850
  { stdio: "inherit" }
@@ -23844,7 +23994,7 @@ var GitHubCodespacesProvider = class {
23844
23994
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
23845
23995
  resetStdinForChild();
23846
23996
  const ok = await new Promise((resolve7) => {
23847
- const proc = (0, import_child_process19.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
23997
+ const proc = (0, import_child_process20.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
23848
23998
  proc.on("exit", (code) => resolve7(code === 0));
23849
23999
  proc.on("error", () => resolve7(false));
23850
24000
  });
@@ -23871,7 +24021,7 @@ var GitHubCodespacesProvider = class {
23871
24021
  );
23872
24022
  resetStdinForChild();
23873
24023
  await new Promise((resolve7, reject) => {
23874
- const proc = (0, import_child_process19.spawn)(
24024
+ const proc = (0, import_child_process20.spawn)(
23875
24025
  "gh",
23876
24026
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
23877
24027
  { stdio: "inherit" }
@@ -24049,7 +24199,7 @@ var GitHubCodespacesProvider = class {
24049
24199
  async streamCommand(workspaceId, command2) {
24050
24200
  resetStdinForChild();
24051
24201
  return new Promise((resolve7, reject) => {
24052
- const proc = (0, import_child_process19.spawn)(
24202
+ const proc = (0, import_child_process20.spawn)(
24053
24203
  "gh",
24054
24204
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
24055
24205
  { stdio: "inherit" }
@@ -24076,11 +24226,11 @@ var GitHubCodespacesProvider = class {
24076
24226
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
24077
24227
  ];
24078
24228
  await new Promise((resolve7, reject) => {
24079
- const tar = (0, import_child_process19.spawn)("tar", tarArgs, {
24229
+ const tar = (0, import_child_process20.spawn)("tar", tarArgs, {
24080
24230
  stdio: ["ignore", "pipe", "pipe"],
24081
24231
  env: tarEnv
24082
24232
  });
24083
- const ssh = (0, import_child_process19.spawn)("gh", sshArgs, {
24233
+ const ssh = (0, import_child_process20.spawn)("gh", sshArgs, {
24084
24234
  stdio: [tar.stdout, "pipe", "pipe"]
24085
24235
  });
24086
24236
  let tarErr = "";
@@ -24104,7 +24254,7 @@ var GitHubCodespacesProvider = class {
24104
24254
  });
24105
24255
  }
24106
24256
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
24107
- const remoteDir = path43.posix.dirname(remotePath);
24257
+ const remoteDir = path44.posix.dirname(remotePath);
24108
24258
  const parts = [
24109
24259
  `mkdir -p ${shellQuote(remoteDir)}`,
24110
24260
  `cat > ${shellQuote(remotePath)}`
@@ -24114,7 +24264,7 @@ var GitHubCodespacesProvider = class {
24114
24264
  }
24115
24265
  const cmd = parts.join(" && ");
24116
24266
  await new Promise((resolve7, reject) => {
24117
- const proc = (0, import_child_process19.spawn)(
24267
+ const proc = (0, import_child_process20.spawn)(
24118
24268
  "gh",
24119
24269
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
24120
24270
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -24172,11 +24322,11 @@ function shellQuote(s) {
24172
24322
  }
24173
24323
 
24174
24324
  // src/services/providers/gitpod.ts
24175
- var import_child_process20 = require("child_process");
24325
+ var import_child_process21 = require("child_process");
24176
24326
  var import_util5 = require("util");
24177
- var path44 = __toESM(require("path"));
24327
+ var path45 = __toESM(require("path"));
24178
24328
  var import_picocolors9 = __toESM(require("picocolors"));
24179
- var execFileP6 = (0, import_util5.promisify)(import_child_process20.execFile);
24329
+ var execFileP6 = (0, import_util5.promisify)(import_child_process21.execFile);
24180
24330
  var MAX_BUFFER2 = 8 * 1024 * 1024;
24181
24331
  function resetStdinForChild2() {
24182
24332
  if (process.stdin.isTTY) {
@@ -24216,7 +24366,7 @@ var GitpodProvider = class {
24216
24366
  );
24217
24367
  resetStdinForChild2();
24218
24368
  await new Promise((resolve7, reject) => {
24219
- const proc = (0, import_child_process20.spawn)("gitpod", ["login"], { stdio: "inherit" });
24369
+ const proc = (0, import_child_process21.spawn)("gitpod", ["login"], { stdio: "inherit" });
24220
24370
  proc.on("exit", (code) => {
24221
24371
  if (code === 0) resolve7();
24222
24372
  else reject(new Error("gitpod login failed."));
@@ -24368,7 +24518,7 @@ var GitpodProvider = class {
24368
24518
  async streamCommand(workspaceId, command2) {
24369
24519
  resetStdinForChild2();
24370
24520
  return new Promise((resolve7, reject) => {
24371
- const proc = (0, import_child_process20.spawn)(
24521
+ const proc = (0, import_child_process21.spawn)(
24372
24522
  "gitpod",
24373
24523
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
24374
24524
  { stdio: "inherit" }
@@ -24388,11 +24538,11 @@ var GitpodProvider = class {
24388
24538
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
24389
24539
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
24390
24540
  await new Promise((resolve7, reject) => {
24391
- const tar = (0, import_child_process20.spawn)("tar", tarArgs, {
24541
+ const tar = (0, import_child_process21.spawn)("tar", tarArgs, {
24392
24542
  stdio: ["ignore", "pipe", "pipe"],
24393
24543
  env: tarEnv
24394
24544
  });
24395
- const ssh = (0, import_child_process20.spawn)(
24545
+ const ssh = (0, import_child_process21.spawn)(
24396
24546
  "gitpod",
24397
24547
  ["workspace", "ssh", workspaceId, "--", remoteCmd],
24398
24548
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -24414,7 +24564,7 @@ var GitpodProvider = class {
24414
24564
  });
24415
24565
  }
24416
24566
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
24417
- const remoteDir = path44.posix.dirname(remotePath);
24567
+ const remoteDir = path45.posix.dirname(remotePath);
24418
24568
  const parts = [
24419
24569
  `mkdir -p ${shellQuote2(remoteDir)}`,
24420
24570
  `cat > ${shellQuote2(remotePath)}`
@@ -24424,7 +24574,7 @@ var GitpodProvider = class {
24424
24574
  }
24425
24575
  const cmd = parts.join(" && ");
24426
24576
  await new Promise((resolve7, reject) => {
24427
- const proc = (0, import_child_process20.spawn)(
24577
+ const proc = (0, import_child_process21.spawn)(
24428
24578
  "gitpod",
24429
24579
  ["workspace", "ssh", workspaceId, "--", cmd],
24430
24580
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -24448,10 +24598,10 @@ function shellQuote2(s) {
24448
24598
  }
24449
24599
 
24450
24600
  // src/services/providers/gitlab-workspaces.ts
24451
- var import_child_process21 = require("child_process");
24601
+ var import_child_process22 = require("child_process");
24452
24602
  var import_util6 = require("util");
24453
- var path45 = __toESM(require("path"));
24454
- var execFileP7 = (0, import_util6.promisify)(import_child_process21.execFile);
24603
+ var path46 = __toESM(require("path"));
24604
+ var execFileP7 = (0, import_util6.promisify)(import_child_process22.execFile);
24455
24605
  var MAX_BUFFER3 = 8 * 1024 * 1024;
24456
24606
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
24457
24607
  function resetStdinForChild3() {
@@ -24493,7 +24643,7 @@ var GitLabWorkspacesProvider = class {
24493
24643
  );
24494
24644
  resetStdinForChild3();
24495
24645
  await new Promise((resolve7, reject) => {
24496
- const proc = (0, import_child_process21.spawn)(
24646
+ const proc = (0, import_child_process22.spawn)(
24497
24647
  "glab",
24498
24648
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
24499
24649
  { stdio: "inherit" }
@@ -24665,7 +24815,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
24665
24815
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
24666
24816
  resetStdinForChild3();
24667
24817
  return new Promise((resolve7, reject) => {
24668
- const proc = (0, import_child_process21.spawn)(
24818
+ const proc = (0, import_child_process22.spawn)(
24669
24819
  "ssh",
24670
24820
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
24671
24821
  { stdio: "inherit" }
@@ -24686,8 +24836,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
24686
24836
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
24687
24837
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
24688
24838
  await new Promise((resolve7, reject) => {
24689
- const tar = (0, import_child_process21.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
24690
- const ssh = (0, import_child_process21.spawn)(
24839
+ const tar = (0, import_child_process22.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
24840
+ const ssh = (0, import_child_process22.spawn)(
24691
24841
  "ssh",
24692
24842
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
24693
24843
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -24710,14 +24860,14 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
24710
24860
  }
24711
24861
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
24712
24862
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
24713
- const remoteDir = path45.posix.dirname(remotePath);
24863
+ const remoteDir = path46.posix.dirname(remotePath);
24714
24864
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
24715
24865
  if (options.mode != null) {
24716
24866
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
24717
24867
  }
24718
24868
  const cmd = parts.join(" && ");
24719
24869
  await new Promise((resolve7, reject) => {
24720
- const proc = (0, import_child_process21.spawn)(
24870
+ const proc = (0, import_child_process22.spawn)(
24721
24871
  "ssh",
24722
24872
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
24723
24873
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -24776,10 +24926,10 @@ function shellQuote3(s) {
24776
24926
  }
24777
24927
 
24778
24928
  // src/services/providers/railway.ts
24779
- var import_child_process22 = require("child_process");
24929
+ var import_child_process23 = require("child_process");
24780
24930
  var import_util7 = require("util");
24781
- var path46 = __toESM(require("path"));
24782
- var execFileP8 = (0, import_util7.promisify)(import_child_process22.execFile);
24931
+ var path47 = __toESM(require("path"));
24932
+ var execFileP8 = (0, import_util7.promisify)(import_child_process23.execFile);
24783
24933
  var MAX_BUFFER4 = 8 * 1024 * 1024;
24784
24934
  function resetStdinForChild4() {
24785
24935
  if (process.stdin.isTTY) {
@@ -24820,7 +24970,7 @@ var RailwayProvider = class {
24820
24970
  );
24821
24971
  resetStdinForChild4();
24822
24972
  await new Promise((resolve7, reject) => {
24823
- const proc = (0, import_child_process22.spawn)("railway", ["login"], { stdio: "inherit" });
24973
+ const proc = (0, import_child_process23.spawn)("railway", ["login"], { stdio: "inherit" });
24824
24974
  proc.on("exit", (code) => {
24825
24975
  if (code === 0) resolve7();
24826
24976
  else reject(new Error("railway login failed."));
@@ -24963,7 +25113,7 @@ var RailwayProvider = class {
24963
25113
  }
24964
25114
  resetStdinForChild4();
24965
25115
  return new Promise((resolve7, reject) => {
24966
- const proc = (0, import_child_process22.spawn)(
25116
+ const proc = (0, import_child_process23.spawn)(
24967
25117
  "railway",
24968
25118
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
24969
25119
  { stdio: "inherit" }
@@ -24987,8 +25137,8 @@ var RailwayProvider = class {
24987
25137
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
24988
25138
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
24989
25139
  await new Promise((resolve7, reject) => {
24990
- const tar = (0, import_child_process22.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
24991
- const sh = (0, import_child_process22.spawn)(
25140
+ const tar = (0, import_child_process23.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
25141
+ const sh = (0, import_child_process23.spawn)(
24992
25142
  "railway",
24993
25143
  ["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
24994
25144
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -25014,14 +25164,14 @@ var RailwayProvider = class {
25014
25164
  if (!projectId || !serviceId) {
25015
25165
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
25016
25166
  }
25017
- const remoteDir = path46.posix.dirname(remotePath);
25167
+ const remoteDir = path47.posix.dirname(remotePath);
25018
25168
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
25019
25169
  if (options.mode != null) {
25020
25170
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
25021
25171
  }
25022
25172
  const cmd = parts.join(" && ");
25023
25173
  await new Promise((resolve7, reject) => {
25024
- const proc = (0, import_child_process22.spawn)(
25174
+ const proc = (0, import_child_process23.spawn)(
25025
25175
  "railway",
25026
25176
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
25027
25177
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -25557,8 +25707,8 @@ async function stopWorkspaceFromLocal(target) {
25557
25707
  var import_node_dns = require("dns");
25558
25708
  var import_node_util4 = require("util");
25559
25709
  var import_node_crypto8 = require("crypto");
25560
- var fs36 = __toESM(require("fs"));
25561
- var path47 = __toESM(require("path"));
25710
+ var fs37 = __toESM(require("fs"));
25711
+ var path48 = __toESM(require("path"));
25562
25712
  var import_picocolors12 = __toESM(require("picocolors"));
25563
25713
  var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
25564
25714
  async function checkDns(apiBase) {
@@ -25614,13 +25764,13 @@ async function checkHealth(apiBase) {
25614
25764
  }
25615
25765
  }
25616
25766
  function checkConfigDir() {
25617
- const dir = path47.join(require("os").homedir(), ".codeam");
25767
+ const dir = path48.join(require("os").homedir(), ".codeam");
25618
25768
  try {
25619
- fs36.mkdirSync(dir, { recursive: true, mode: 448 });
25620
- const probe = path47.join(dir, ".doctor-probe");
25621
- fs36.writeFileSync(probe, "ok", { mode: 384 });
25622
- const read = fs36.readFileSync(probe, "utf8");
25623
- fs36.unlinkSync(probe);
25769
+ fs37.mkdirSync(dir, { recursive: true, mode: 448 });
25770
+ const probe = path48.join(dir, ".doctor-probe");
25771
+ fs37.writeFileSync(probe, "ok", { mode: 384 });
25772
+ const read = fs37.readFileSync(probe, "utf8");
25773
+ fs37.unlinkSync(probe);
25624
25774
  if (read !== "ok") throw new Error("write/read round-trip mismatch");
25625
25775
  return {
25626
25776
  id: "config-dir",
@@ -25684,7 +25834,7 @@ function checkNodePty() {
25684
25834
  detail: "not required on this platform"
25685
25835
  };
25686
25836
  }
25687
- const vendoredPath = path47.join(__dirname, "vendor", "node-pty");
25837
+ const vendoredPath = path48.join(__dirname, "vendor", "node-pty");
25688
25838
  for (const target of [vendoredPath, "node-pty"]) {
25689
25839
  try {
25690
25840
  require(target);
@@ -25726,7 +25876,7 @@ function checkChokidar() {
25726
25876
  }
25727
25877
  async function doctor(args2 = []) {
25728
25878
  const json = args2.includes("--json");
25729
- const cliVersion = true ? "2.33.0" : "0.0.0-dev";
25879
+ const cliVersion = true ? "2.35.0" : "0.0.0-dev";
25730
25880
  const apiBase = resolveApiBaseUrl();
25731
25881
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
25732
25882
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -25925,7 +26075,7 @@ async function completion(args2) {
25925
26075
  // src/commands/version.ts
25926
26076
  var import_picocolors13 = __toESM(require("picocolors"));
25927
26077
  function version2() {
25928
- const v = true ? "2.33.0" : "unknown";
26078
+ const v = true ? "2.35.0" : "unknown";
25929
26079
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
25930
26080
  }
25931
26081
 
@@ -26053,9 +26203,9 @@ function tryShowSubcommandHelp(cmd, args2) {
26053
26203
  var _subcommandHelpKeys = Object.keys(HELPS);
26054
26204
 
26055
26205
  // src/lib/updateNotifier.ts
26056
- var fs37 = __toESM(require("fs"));
26206
+ var fs38 = __toESM(require("fs"));
26057
26207
  var os29 = __toESM(require("os"));
26058
- var path48 = __toESM(require("path"));
26208
+ var path49 = __toESM(require("path"));
26059
26209
  var https8 = __toESM(require("https"));
26060
26210
  var import_node_child_process12 = require("child_process");
26061
26211
  var import_picocolors16 = __toESM(require("picocolors"));
@@ -26064,12 +26214,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
26064
26214
  var TTL_MS = 24 * 60 * 60 * 1e3;
26065
26215
  var REQUEST_TIMEOUT_MS = 1500;
26066
26216
  function cachePath() {
26067
- const dir = path48.join(os29.homedir(), ".codeam");
26068
- return path48.join(dir, "update-check.json");
26217
+ const dir = path49.join(os29.homedir(), ".codeam");
26218
+ return path49.join(dir, "update-check.json");
26069
26219
  }
26070
26220
  function readCache() {
26071
26221
  try {
26072
- const raw = fs37.readFileSync(cachePath(), "utf8");
26222
+ const raw = fs38.readFileSync(cachePath(), "utf8");
26073
26223
  const parsed = JSON.parse(raw);
26074
26224
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
26075
26225
  return parsed;
@@ -26080,10 +26230,10 @@ function readCache() {
26080
26230
  function writeCache(cache) {
26081
26231
  try {
26082
26232
  const file = cachePath();
26083
- fs37.mkdirSync(path48.dirname(file), { recursive: true });
26233
+ fs38.mkdirSync(path49.dirname(file), { recursive: true });
26084
26234
  const tmp = `${file}.${process.pid}.tmp`;
26085
- fs37.writeFileSync(tmp, JSON.stringify(cache));
26086
- fs37.renameSync(tmp, file);
26235
+ fs38.writeFileSync(tmp, JSON.stringify(cache));
26236
+ fs38.renameSync(tmp, file);
26087
26237
  } catch {
26088
26238
  }
26089
26239
  }
@@ -26157,8 +26307,8 @@ function isLinkedInstall() {
26157
26307
  timeout: 2e3
26158
26308
  }).trim();
26159
26309
  if (!root) return false;
26160
- const pkgPath = path48.join(root, PKG_NAME);
26161
- return fs37.lstatSync(pkgPath).isSymbolicLink();
26310
+ const pkgPath = path49.join(root, PKG_NAME);
26311
+ return fs38.lstatSync(pkgPath).isSymbolicLink();
26162
26312
  } catch {
26163
26313
  return false;
26164
26314
  }
@@ -26194,7 +26344,7 @@ function maybeAutoUpdate(currentVersion, latest) {
26194
26344
  return;
26195
26345
  }
26196
26346
  try {
26197
- fs37.unlinkSync(cachePath());
26347
+ fs38.unlinkSync(cachePath());
26198
26348
  } catch {
26199
26349
  }
26200
26350
  process.stderr.write(` ${import_picocolors16.default.green("\u2713")} Updated. Resuming session...
@@ -26211,7 +26361,7 @@ function checkForUpdates() {
26211
26361
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
26212
26362
  if (process.env.CI) return;
26213
26363
  if (!process.stdout.isTTY) return;
26214
- const current = true ? "2.33.0" : null;
26364
+ const current = true ? "2.35.0" : null;
26215
26365
  if (!current) return;
26216
26366
  const cache = readCache();
26217
26367
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;