baro-ai 0.23.1 → 0.23.2

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/cli.mjs CHANGED
@@ -7414,32 +7414,53 @@ async function createOrCheckoutBranch(cwd, branchName, onLog) {
7414
7414
  );
7415
7415
  }
7416
7416
  }
7417
- async function safePullRebase(cwd, onLog) {
7418
- if (!await hasRemoteOrigin(cwd)) {
7419
- onLog?.("[git] no remote, skipping pull");
7420
- return;
7421
- }
7422
- let branch;
7423
- try {
7424
- branch = await getCurrentBranch(cwd);
7425
- } catch {
7426
- onLog?.("[git] no branch, skipping pull");
7427
- return;
7428
- }
7429
- if (!await hasRemoteBranch(cwd, branch)) {
7430
- onLog?.("[git] remote branch not found, skipping pull");
7431
- return;
7432
- }
7433
- onLog?.("[git] pulling latest...");
7434
- await execSafe("git", ["stash", "--include-untracked"], { cwd });
7417
+ async function safePullRebase(cwd, onLog, gate) {
7418
+ const release = gate ? await gate.acquire() : null;
7435
7419
  try {
7436
- await exec("git", ["pull", "--rebase", "origin", branch], { cwd });
7437
- onLog?.("[git] pull ok");
7438
- } catch {
7439
- onLog?.("[git] pull conflict, continuing without pull");
7440
- await execSafe("git", ["rebase", "--abort"], { cwd });
7420
+ if (!await hasRemoteOrigin(cwd)) {
7421
+ onLog?.("[git] no remote, skipping pull");
7422
+ return;
7423
+ }
7424
+ let branch;
7425
+ try {
7426
+ branch = await getCurrentBranch(cwd);
7427
+ } catch {
7428
+ onLog?.("[git] no branch, skipping pull");
7429
+ return;
7430
+ }
7431
+ if (!await hasRemoteBranch(cwd, branch)) {
7432
+ onLog?.("[git] remote branch not found, skipping pull");
7433
+ return;
7434
+ }
7435
+ onLog?.("[git] pulling latest...");
7436
+ let stashSha = null;
7437
+ try {
7438
+ const { stdout } = await exec("git", ["stash", "create"], { cwd });
7439
+ stashSha = stdout.trim() || null;
7440
+ if (stashSha) {
7441
+ await execSafe("git", ["reset", "--hard", "HEAD"], { cwd });
7442
+ }
7443
+ } catch {
7444
+ }
7445
+ try {
7446
+ await exec("git", ["pull", "--rebase", "origin", branch], { cwd });
7447
+ onLog?.("[git] pull ok");
7448
+ } catch {
7449
+ onLog?.("[git] pull conflict, continuing without pull");
7450
+ await execSafe("git", ["rebase", "--abort"], { cwd });
7451
+ }
7452
+ if (stashSha) {
7453
+ try {
7454
+ await exec("git", ["stash", "apply", stashSha], { cwd });
7455
+ } catch (e) {
7456
+ onLog?.(
7457
+ `[git] could not re-apply stashed edits (sha ${stashSha.slice(0, 8)}): ${e?.message ?? String(e)}`
7458
+ );
7459
+ }
7460
+ }
7461
+ } finally {
7462
+ release?.();
7441
7463
  }
7442
- await execSafe("git", ["stash", "pop"], { cwd });
7443
7464
  }
7444
7465
  async function gitPushWithRetry(gate, options) {
7445
7466
  const release = await gate.acquire();
@@ -7451,8 +7472,8 @@ async function gitPushWithRetry(gate, options) {
7451
7472
  const branch = await getCurrentBranch(options.cwd);
7452
7473
  const max = options.maxAttempts ?? GIT_PUSH_MAX_ATTEMPTS;
7453
7474
  let lastError = "";
7475
+ options.onLog?.("[git] pushing...");
7454
7476
  for (let attempt = 1; attempt <= max; attempt++) {
7455
- options.onLog?.("[git] pushing...");
7456
7477
  try {
7457
7478
  await exec("git", ["push", "origin", branch], { cwd: options.cwd });
7458
7479
  options.onLog?.("[git] push ok");
@@ -7461,7 +7482,9 @@ async function gitPushWithRetry(gate, options) {
7461
7482
  lastError = extractStderr(e);
7462
7483
  }
7463
7484
  if (attempt === max) break;
7464
- options.onLog?.("[git] push failed, pulling and retrying...");
7485
+ options.onLog?.(
7486
+ `[git] push rejected (attempt ${attempt}/${max}), pulling and retrying...`
7487
+ );
7465
7488
  try {
7466
7489
  await exec("git", ["pull", "--rebase", "origin", branch], {
7467
7490
  cwd: options.cwd
@@ -7472,7 +7495,10 @@ async function gitPushWithRetry(gate, options) {
7472
7495
  throw new Error("Rebase conflict detected, push skipped");
7473
7496
  }
7474
7497
  }
7475
- options.onLog?.(`[git] push failed after ${max} attempts`);
7498
+ const compactErr = lastError.split("\n")[0]?.trim() || lastError;
7499
+ options.onLog?.(
7500
+ `[git] push failed after ${max} attempts: ${compactErr}`
7501
+ );
7476
7502
  throw new Error(`Push failed after ${max} attempts: ${lastError}`);
7477
7503
  } finally {
7478
7504
  release();
@@ -7921,14 +7947,26 @@ var Auditor = class extends Participant {
7921
7947
  path;
7922
7948
  skipStreamChunks;
7923
7949
  filter;
7950
+ /**
7951
+ * Flips to true the first time a write fails (e.g. EACCES because
7952
+ * `~/.baro/runs/` is root-owned from a sudo install). Once disabled,
7953
+ * subsequent items are dropped silently — losing the audit log is
7954
+ * better than crashing the orchestrator on every bus event.
7955
+ */
7956
+ disabled = false;
7924
7957
  constructor(opts) {
7925
7958
  super();
7926
7959
  this.path = opts.path;
7927
7960
  this.skipStreamChunks = opts.skipStreamChunks ?? true;
7928
7961
  this.filter = opts.filter;
7929
- mkdirSync(dirname(this.path), { recursive: true });
7962
+ try {
7963
+ mkdirSync(dirname(this.path), { recursive: true });
7964
+ } catch (e) {
7965
+ this.disable(`mkdir failed: ${e?.message ?? String(e)}`);
7966
+ }
7930
7967
  }
7931
7968
  async onContextItem(source, item) {
7969
+ if (this.disabled) return;
7932
7970
  if (this.skipStreamChunks && item instanceof ClaudeStreamChunkItem) {
7933
7971
  return;
7934
7972
  }
@@ -7940,7 +7978,19 @@ var Auditor = class extends Participant {
7940
7978
  source: this.sourceLabel(source),
7941
7979
  item: item.toJSON()
7942
7980
  };
7943
- appendFileSync(this.path, JSON.stringify(entry) + "\n");
7981
+ try {
7982
+ appendFileSync(this.path, JSON.stringify(entry) + "\n");
7983
+ } catch (e) {
7984
+ this.disable(`append failed: ${e?.message ?? String(e)}`);
7985
+ }
7986
+ }
7987
+ disable(reason) {
7988
+ if (this.disabled) return;
7989
+ this.disabled = true;
7990
+ process.stderr.write(
7991
+ `[auditor] cannot write audit log at ${this.path}: ${reason} \u2014 continuing without audit
7992
+ `
7993
+ );
7944
7994
  }
7945
7995
  sourceLabel(source) {
7946
7996
  const ctor = source.constructor.name;
@@ -8153,7 +8203,7 @@ function stringifyToolResultContent(content) {
8153
8203
  }
8154
8204
 
8155
8205
  // ../baro-orchestrator/src/participants/claude-cli-participant.ts
8156
- var ClaudeCliParticipant = class extends Participant {
8206
+ var ClaudeCliParticipant = class _ClaudeCliParticipant extends Participant {
8157
8207
  constructor(agentId, opts) {
8158
8208
  super();
8159
8209
  this.agentId = agentId;
@@ -8172,6 +8222,22 @@ var ClaudeCliParticipant = class extends Participant {
8172
8222
  this.resolveDone = res;
8173
8223
  });
8174
8224
  }
8225
+ /**
8226
+ * Process-wide registry of every Claude child currently running.
8227
+ * Used by the orchestrator's SIGINT/SIGTERM handlers to nuke
8228
+ * orphaned Claude processes so a killed baro doesn't leave a swarm
8229
+ * of background agents burning quota.
8230
+ */
8231
+ static active = /* @__PURE__ */ new Set();
8232
+ /** Send a signal to every active Claude child. Idempotent. */
8233
+ static killAll(signal = "SIGTERM") {
8234
+ for (const p of _ClaudeCliParticipant.active) {
8235
+ try {
8236
+ p.proc?.kill(signal);
8237
+ } catch {
8238
+ }
8239
+ }
8240
+ }
8175
8241
  options;
8176
8242
  proc = null;
8177
8243
  buffer = "";
@@ -8221,6 +8287,7 @@ var ClaudeCliParticipant = class extends Participant {
8221
8287
  return;
8222
8288
  }
8223
8289
  this.proc = proc;
8290
+ _ClaudeCliParticipant.active.add(this);
8224
8291
  this.transition("starting");
8225
8292
  proc.stdout.setEncoding("utf8");
8226
8293
  proc.stderr.setEncoding("utf8");
@@ -8231,6 +8298,7 @@ var ClaudeCliParticipant = class extends Participant {
8231
8298
  this.rejectReady(err);
8232
8299
  });
8233
8300
  proc.on("exit", (code) => {
8301
+ _ClaudeCliParticipant.active.delete(this);
8234
8302
  this.exitCode = code;
8235
8303
  const finalPhase = this.spawnError != null || code != null && code !== 0 ? "failed" : "done";
8236
8304
  this.transition(finalPhase, code != null ? `exit code ${code}` : "no exit code");
@@ -9885,7 +9953,8 @@ async function orchestrate(config) {
9885
9953
  onStoryPassed: useGit ? async (storyId) => {
9886
9954
  await safePullRebase(
9887
9955
  config.cwd,
9888
- (line) => emitTui && emit({ type: "story_log", id: storyId, line })
9956
+ (line) => emitTui && emit({ type: "story_log", id: storyId, line }),
9957
+ gitGate
9889
9958
  );
9890
9959
  try {
9891
9960
  await gitPushWithRetry(gitGate, {
@@ -10303,14 +10372,42 @@ process.on("unhandledRejection", (reason) => {
10303
10372
  const stack = reason?.stack ?? String(reason);
10304
10373
  process.stderr.write(`[cli] unhandledRejection: ${stack}
10305
10374
  `);
10375
+ ClaudeCliParticipant.killAll("SIGTERM");
10306
10376
  process.exit(1);
10307
10377
  });
10308
10378
  process.on("uncaughtException", (err) => {
10309
10379
  const stack = err?.stack ?? String(err);
10310
10380
  process.stderr.write(`[cli] uncaughtException: ${stack}
10311
10381
  `);
10382
+ ClaudeCliParticipant.killAll("SIGTERM");
10312
10383
  process.exit(1);
10313
10384
  });
10385
+ var shuttingDown = false;
10386
+ function shutdown(signal) {
10387
+ if (shuttingDown) return;
10388
+ shuttingDown = true;
10389
+ process.stderr.write(`[cli] received ${signal}, killing in-flight Claude children...
10390
+ `);
10391
+ ClaudeCliParticipant.killAll("SIGTERM");
10392
+ setTimeout(() => {
10393
+ ClaudeCliParticipant.killAll("SIGKILL");
10394
+ process.exit(signal === "SIGINT" ? 130 : 143);
10395
+ }, 1500).unref();
10396
+ }
10397
+ process.on("SIGINT", () => shutdown("SIGINT"));
10398
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
10399
+ var initialPpid = process.ppid;
10400
+ var orphanWatchdog = setInterval(() => {
10401
+ if (process.ppid !== initialPpid) {
10402
+ process.stderr.write(
10403
+ `[cli] parent died (ppid ${initialPpid} \u2192 ${process.ppid}), shutting down
10404
+ `
10405
+ );
10406
+ clearInterval(orphanWatchdog);
10407
+ shutdown("SIGTERM");
10408
+ }
10409
+ }, 1e3);
10410
+ orphanWatchdog.unref();
10314
10411
  main().catch((e) => {
10315
10412
  process.stderr.write(`[cli] unhandled: ${e?.stack ?? String(e)}
10316
10413
  `);