@brainbase-labs/cli 0.28.0 → 0.29.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/README.md +7 -0
  2. package/dist/index.js +2217 -526
  3. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -35141,7 +35141,7 @@ var require_dist2 = __commonJS((exports, module) => {
35141
35141
  });
35142
35142
 
35143
35143
  // src/index.ts
35144
- var import_picocolors54 = __toESM(require_picocolors(), 1);
35144
+ var import_picocolors61 = __toESM(require_picocolors(), 1);
35145
35145
  import process14 from "node:process";
35146
35146
  import fs87 from "node:fs";
35147
35147
 
@@ -35949,6 +35949,16 @@ function me() {
35949
35949
  return new RegExp(r2, "g");
35950
35950
  }
35951
35951
 
35952
+ // src/ui/fail.ts
35953
+ function fail(message) {
35954
+ f2.error(message);
35955
+ process.exitCode = 1;
35956
+ }
35957
+ function failWarn(message) {
35958
+ f2.warn(message);
35959
+ process.exitCode = 1;
35960
+ }
35961
+
35952
35962
  // src/cli/pack.ts
35953
35963
  var import_picocolors7 = __toESM(require_picocolors(), 1);
35954
35964
 
@@ -36008,7 +36018,7 @@ function padStart(s, n) {
36008
36018
  // package.json
36009
36019
  var package_default = {
36010
36020
  name: "@brainbase-labs/cli",
36011
- version: "0.28.0",
36021
+ version: "0.29.0",
36012
36022
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36013
36023
  type: "module",
36014
36024
  bin: {
@@ -36024,7 +36034,7 @@ var package_default = {
36024
36034
  build: "bun run scripts/build.ts",
36025
36035
  test: "bun test",
36026
36036
  typecheck: "tsc --noEmit",
36027
- verify: "bun run typecheck && bun test && bun run build",
36037
+ verify: "bun run scripts/check-bun-version.ts && bun run typecheck && bun test && bun run build",
36028
36038
  prepublishOnly: "bun run verify"
36029
36039
  },
36030
36040
  repository: {
@@ -36051,6 +36061,7 @@ var package_default = {
36051
36061
  dependencies: {
36052
36062
  "@modelcontextprotocol/sdk": "^1.18.2",
36053
36063
  "@clack/prompts": "^0.7.0",
36064
+ eventsource: "^3.0.2",
36054
36065
  ink: "^5.1.0",
36055
36066
  picocolors: "^1.0.0",
36056
36067
  react: "^18.3.1",
@@ -36156,6 +36167,13 @@ function requireInteractive(flagHint) {
36156
36167
  function autoProceed(yes) {
36157
36168
  return Boolean(yes) || !isInteractive();
36158
36169
  }
36170
+ function autoProceedDestructive(yes, opts) {
36171
+ if (yes)
36172
+ return true;
36173
+ if (isInteractive())
36174
+ return false;
36175
+ throw new NonInteractiveError(`${opts.action} is destructive and needs confirmation, but there is no interactive terminal to ask. ` + (opts.flagHint ?? "Pass --yes to confirm."));
36176
+ }
36159
36177
 
36160
36178
  // src/ui/symbols.ts
36161
36179
  var import_picocolors5 = __toESM(require_picocolors(), 1);
@@ -53628,7 +53646,7 @@ async function runPack(cwd2) {
53628
53646
  packSpinner.stop(`Packed ${packed.length} component${packed.length === 1 ? "" : "s"}.`);
53629
53647
  const dest = registry.templateDir(name, version);
53630
53648
  if (exists(dest)) {
53631
- f2.error(`A template at ${name}@${version} already exists. Bump the version.`);
53649
+ fail(`A template at ${name}@${version} already exists. Bump the version.`);
53632
53650
  $e("Aborted.");
53633
53651
  return;
53634
53652
  }
@@ -54017,6 +54035,8 @@ var DEFINITELY_UNSENT_NETWORK_CODES = new Set([
54017
54035
  "FailedToOpenSocket",
54018
54036
  "UND_ERR_CONNECT_TIMEOUT"
54019
54037
  ]);
54038
+ var LEGACY_DEFAULT_PAGE = 200;
54039
+ var LEGACY_MAX_PAGE = 500;
54020
54040
 
54021
54041
  class ApiError extends Error {
54022
54042
  status;
@@ -54354,6 +54374,127 @@ async function masRequest(pathname, init) {
54354
54374
  return body;
54355
54375
  }
54356
54376
  }
54377
+ async function masReadRequest(pathname, callerSignal) {
54378
+ const credential = await resolveMasCredential();
54379
+ let currentSession = credential.session;
54380
+ let refreshSessionAvailable = credential.source === "session";
54381
+ let res;
54382
+ let text2;
54383
+ for (let attempt2 = 0;; attempt2 += 1) {
54384
+ if (callerSignal?.aborted)
54385
+ throw callerSignal.reason;
54386
+ try {
54387
+ res = await sendWithAuthRetry(refreshSessionAvailable ? currentSession : null, async (refreshed) => {
54388
+ if (refreshed) {
54389
+ currentSession = refreshed;
54390
+ refreshSessionAvailable = false;
54391
+ }
54392
+ return await sendRequest(`${masApiBase(currentSession)}${pathname}`, {
54393
+ method: "GET",
54394
+ signal: withRequestDeadline(callerSignal, MAS_REQUEST_TIMEOUT_MS)
54395
+ }, currentSession?.access_token ?? credential.bearer);
54396
+ });
54397
+ try {
54398
+ text2 = await res.text();
54399
+ } catch (error) {
54400
+ throw new NetworkApiError(`Network error while reading response: ${error.message}`, false);
54401
+ }
54402
+ break;
54403
+ } catch (error) {
54404
+ if (callerSignal?.aborted || !(error instanceof NetworkApiError) || attempt2 >= GET_NETWORK_RETRY_DELAYS_MS.length) {
54405
+ throw error;
54406
+ }
54407
+ await new Promise((resolve) => setTimeout(resolve, GET_NETWORK_RETRY_DELAYS_MS[attempt2]));
54408
+ }
54409
+ }
54410
+ let body = text2;
54411
+ try {
54412
+ body = text2 ? JSON.parse(text2) : null;
54413
+ } catch {}
54414
+ if (!res.ok) {
54415
+ const message = masApiErrorMessage(body, res.status);
54416
+ throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source, "managed-task") : message, res.status, body);
54417
+ }
54418
+ return body;
54419
+ }
54420
+ function withRequestDeadline(callerSignal, timeoutMs) {
54421
+ const deadline = AbortSignal.timeout(timeoutMs);
54422
+ if (!callerSignal)
54423
+ return deadline;
54424
+ if (callerSignal.aborted)
54425
+ return callerSignal;
54426
+ const combined = new AbortController;
54427
+ const abort = (reason) => combined.abort(reason);
54428
+ deadline.addEventListener("abort", () => abort(deadline.reason), { once: true });
54429
+ callerSignal.addEventListener("abort", () => abort(callerSignal.reason), {
54430
+ once: true
54431
+ });
54432
+ return combined.signal;
54433
+ }
54434
+ async function masResourceRequest(pathname, init = {}) {
54435
+ const credential = await resolveMasCredential();
54436
+ let currentSession = credential.session;
54437
+ const res = await sendWithAuthRetry(credential.source === "session" ? currentSession : null, async (refreshed) => {
54438
+ if (refreshed)
54439
+ currentSession = refreshed;
54440
+ return await sendRequest(`${masApiBase(currentSession)}${pathname}`, {
54441
+ ...init,
54442
+ signal: withRequestDeadline(init.signal ?? undefined, MAS_REQUEST_TIMEOUT_MS)
54443
+ }, currentSession?.access_token ?? credential.bearer);
54444
+ });
54445
+ let text2;
54446
+ try {
54447
+ text2 = await res.text();
54448
+ } catch (error) {
54449
+ throw new NetworkApiError(`Network error while reading response: ${error.message}`, false);
54450
+ }
54451
+ let body = text2;
54452
+ try {
54453
+ body = text2 ? JSON.parse(text2) : null;
54454
+ } catch {}
54455
+ if (!res.ok) {
54456
+ const message = masApiErrorMessage(body, res.status);
54457
+ throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source, "managed-task") : res.status === 404 && isUnroutedPath(body) ? `This control plane does not serve ${pathname} yet. Update the server, or use the web app.` : message, res.status, body);
54458
+ }
54459
+ return body;
54460
+ }
54461
+
54462
+ class StreamHostMovedError extends ApiError {
54463
+ }
54464
+ async function masStreamTarget(pathname) {
54465
+ const credential = await resolveMasCredential();
54466
+ const base2 = masApiBase(credential.session);
54467
+ return {
54468
+ url: `${base2}${pathname}`,
54469
+ resolveBearer: async () => {
54470
+ const fresh = await resolveMasCredential();
54471
+ const freshBase = masApiBase(fresh.session);
54472
+ if (freshBase !== base2) {
54473
+ throw new StreamHostMovedError(`The control plane moved from ${base2} to ${freshBase} while this stream was open, so the current credential was not sent. Re-run the command to follow against the new host.`);
54474
+ }
54475
+ return fresh.session?.access_token ?? fresh.bearer;
54476
+ }
54477
+ };
54478
+ }
54479
+ function masListQuery(params) {
54480
+ const search = new URLSearchParams;
54481
+ for (const [key2, value] of Object.entries(params)) {
54482
+ if (value !== undefined)
54483
+ search.set(key2, value);
54484
+ }
54485
+ const qs = search.toString();
54486
+ return qs ? `?${qs}` : "";
54487
+ }
54488
+ function masItems(body, what) {
54489
+ if (Array.isArray(body))
54490
+ return body;
54491
+ if (body && typeof body === "object") {
54492
+ const items = body.items;
54493
+ if (Array.isArray(items))
54494
+ return items;
54495
+ }
54496
+ throw new ApiError(`MAS returned an unreadable ${what} response.`, undefined, body);
54497
+ }
54357
54498
  var masApi = {
54358
54499
  async createTask(input, options) {
54359
54500
  const body = await masRequest("/tasks", {
@@ -54362,6 +54503,49 @@ var masApi = {
54362
54503
  body: JSON.stringify(input)
54363
54504
  });
54364
54505
  return parseMasTaskCreateResponse(body);
54506
+ },
54507
+ async listTasks(options = {}) {
54508
+ const params = new URLSearchParams;
54509
+ if (options.agentId)
54510
+ params.set("agent_id", options.agentId);
54511
+ if (options.limit !== undefined)
54512
+ params.set("limit", String(options.limit));
54513
+ const qs = params.toString() ? `?${params.toString()}` : "";
54514
+ return masItems(await masReadRequest(`/tasks${qs}`), "task list");
54515
+ },
54516
+ async getTask(taskId, options = {}) {
54517
+ const body = await masReadRequest(`/tasks/${encodeURIComponent(taskId)}`, options.signal);
54518
+ if (!body || typeof body !== "object" || typeof body.id !== "string" || typeof body.status !== "string") {
54519
+ throw new ApiError("MAS returned an unreadable task response.", undefined, body);
54520
+ }
54521
+ return body;
54522
+ },
54523
+ async listTaskEvents(taskId, options = {}) {
54524
+ const params = new URLSearchParams({ order_by_received: "true" });
54525
+ if (options.limit !== undefined)
54526
+ params.set("limit", String(options.limit));
54527
+ if (options.after) {
54528
+ params.set("after_received_at", options.after.receivedAt);
54529
+ params.set("after_id", options.after.id);
54530
+ }
54531
+ if (options.desc !== undefined)
54532
+ params.set("desc", String(options.desc));
54533
+ const body = await masReadRequest(`/tasks/${encodeURIComponent(taskId)}/events?${params.toString()}`);
54534
+ return masItems(body, "task events");
54535
+ },
54536
+ async listMachines(options = {}) {
54537
+ const body = await masResourceRequest(`/machines${masListQuery({
54538
+ kind: options.kind,
54539
+ include_dead: options.includeDead === undefined ? undefined : String(options.includeDead),
54540
+ limit: options.limit === undefined ? undefined : String(options.limit)
54541
+ })}`);
54542
+ if (!body || !Array.isArray(body.items)) {
54543
+ throw new ApiError("The control plane returned an unreadable machine list", undefined, body);
54544
+ }
54545
+ return body.items;
54546
+ },
54547
+ deleteMachine(machineId) {
54548
+ return masResourceRequest(`/machines/${encodeURIComponent(machineId)}`, { method: "DELETE" });
54365
54549
  }
54366
54550
  };
54367
54551
  function isUnroutedPath(body) {
@@ -54391,20 +54575,48 @@ var api = {
54391
54575
  });
54392
54576
  },
54393
54577
  async listAgents(orgId, teamId) {
54394
- const path58 = `/orgs/${encodeURIComponent(orgId)}/teams/${encodeURIComponent(teamId)}/agents`;
54395
- let body;
54578
+ const base2 = `/orgs/${encodeURIComponent(orgId)}/teams/${encodeURIComponent(teamId)}/agents`;
54579
+ const fetchPage = async (limit) => {
54580
+ const path58 = limit === undefined ? base2 : `${base2}?limit=${limit}`;
54581
+ let body;
54582
+ try {
54583
+ body = await request(path58);
54584
+ } catch (err) {
54585
+ if (err instanceof ApiError && err.status === 404) {
54586
+ throw new ApiError("This control plane does not support listing agents yet. Update the server, or use the web app to find the agent id.", 404, err.body);
54587
+ }
54588
+ throw err;
54589
+ }
54590
+ if (!Array.isArray(body)) {
54591
+ throw new ApiError(`Unexpected response listing agents: expected an array from ${path58}.`, undefined, body);
54592
+ }
54593
+ return body;
54594
+ };
54595
+ const agents = await fetchPage();
54596
+ if (agents.length !== LEGACY_DEFAULT_PAGE)
54597
+ return { agents, complete: true };
54598
+ let retried;
54396
54599
  try {
54397
- body = await request(path58);
54600
+ retried = await fetchPage(LEGACY_MAX_PAGE);
54398
54601
  } catch (err) {
54399
- if (err instanceof ApiError && err.status === 404) {
54400
- throw new ApiError("This control plane does not support listing agents yet. Update the server, or use the web app to find the agent id.", 404, err.body);
54602
+ if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
54603
+ throw err;
54401
54604
  }
54402
- throw err;
54403
- }
54404
- if (!Array.isArray(body)) {
54405
- throw new ApiError(`Unexpected response listing agents: expected an array from ${path58}.`, undefined, body);
54605
+ const detail = err instanceof Error ? err.message : String(err);
54606
+ return {
54607
+ agents,
54608
+ complete: false,
54609
+ warning: `Warning: showing ${agents.length} agents, and could not confirm that is all of them — ` + `the follow-up request failed (${detail}). ` + "Re-run to check; if it keeps failing, the control plane may need updating."
54610
+ };
54406
54611
  }
54407
- return body;
54612
+ const best = retried.length > agents.length ? retried : agents;
54613
+ if (best.length < LEGACY_MAX_PAGE)
54614
+ return { agents: best, complete: true };
54615
+ return {
54616
+ agents: best,
54617
+ complete: false,
54618
+ warning: `Warning: this control plane caps agent listings at ${best.length} and offers no way ` + "to read past that, so this list may be incomplete. Update the control plane to see every agent."
54619
+ };
54408
54620
  },
54409
54621
  createAgent(input) {
54410
54622
  if (usesLegacyControlPlane() && (input.machine_kind !== undefined || input.default_model !== undefined)) {
@@ -58918,7 +59130,7 @@ async function runOnboard(cwd2, args) {
58918
59130
  try {
58919
59131
  templateRef = await resolveWithRemoteFallback(registry, name, version);
58920
59132
  } catch (err) {
58921
- f2.error(err.message);
59133
+ fail(err.message);
58922
59134
  $e("Aborted.");
58923
59135
  return;
58924
59136
  }
@@ -59380,7 +59592,10 @@ async function runRemove(cwd2, args) {
59380
59592
  text: c2.slug
59381
59593
  }))
59382
59594
  });
59383
- if (!autoProceed(args.yes)) {
59595
+ if (!autoProceedDestructive(args.yes, {
59596
+ action: `Uninstalling ${inst.name}@${inst.version} from ${inst.harness} (${inst.scope})`,
59597
+ flagHint: "Pass --yes to uninstall without a prompt."
59598
+ })) {
59384
59599
  const ans = await se({ message: "Proceed?", initialValue: true });
59385
59600
  if (!ensureNotCancelled(ans))
59386
59601
  continue;
@@ -59842,11 +60057,11 @@ async function publishSkillForTemplate(opts) {
59842
60057
  try {
59843
60058
  probe = await skillsApi.canPublish(creator, pkgSlug);
59844
60059
  } catch (err) {
59845
- f2.error(err.message);
60060
+ fail(err.message);
59846
60061
  return false;
59847
60062
  }
59848
60063
  if (probe.exists && !probe.can_write) {
59849
- f2.error(`You can't publish to ${import_picocolors11.default.bold(creator + "/" + pkgSlug)}.`);
60064
+ fail(`You can't publish to ${import_picocolors11.default.bold(creator + "/" + pkgSlug)}.`);
59850
60065
  return false;
59851
60066
  }
59852
60067
  if (!probe.exists) {
@@ -59862,7 +60077,7 @@ async function publishSkillForTemplate(opts) {
59862
60077
  });
59863
60078
  } catch (err) {
59864
60079
  if (!(err instanceof ApiError && err.status === 409)) {
59865
- f2.error(err.message);
60080
+ fail(err.message);
59866
60081
  return false;
59867
60082
  }
59868
60083
  }
@@ -59895,7 +60110,7 @@ async function publishSkillForTemplate(opts) {
59895
60110
  sp.stop(`Published ${creator}/${pkgSlug}@${version}.`);
59896
60111
  } catch (err) {
59897
60112
  sp.stop("Skill publish failed.");
59898
- f2.error(err.message);
60113
+ fail(err.message);
59899
60114
  return false;
59900
60115
  }
59901
60116
  } finally {
@@ -59916,7 +60131,7 @@ async function runTemplatePublish(_cwd, args) {
59916
60131
  const registry = new LocalRegistry;
59917
60132
  const localList = await registry.list();
59918
60133
  if (localList.length === 0) {
59919
- f2.error("No templates in your local registry. Run `brainbase template pack` first.");
60134
+ fail("No templates in your local registry. Run `brainbase template pack` first.");
59920
60135
  return;
59921
60136
  }
59922
60137
  let name;
@@ -59926,7 +60141,7 @@ async function runTemplatePublish(_cwd, args) {
59926
60141
  name = parsed.name;
59927
60142
  const v3 = parsed.version ?? await registry.latest(name);
59928
60143
  if (!v3) {
59929
- f2.error(`No versions of ${name} in your local registry.`);
60144
+ fail(`No versions of ${name} in your local registry.`);
59930
60145
  return;
59931
60146
  }
59932
60147
  version = v3;
@@ -59965,7 +60180,7 @@ async function runTemplatePublish(_cwd, args) {
59965
60180
  `), import_picocolors11.default.yellow("Findings"));
59966
60181
  }
59967
60182
  if (report.hasBlocker) {
59968
- f2.error("Blocking issues found. Fix them and re-pack before publishing.");
60183
+ fail("Blocking issues found. Fix them and re-pack before publishing.");
59969
60184
  return;
59970
60185
  }
59971
60186
  if (report.findings.some((f4) => f4.severity === "warn") && !autoProceed(args.yes)) {
@@ -59980,7 +60195,7 @@ async function runTemplatePublish(_cwd, args) {
59980
60195
  }
59981
60196
  const status = authStatus();
59982
60197
  if (!status.ok || !status.session) {
59983
- f2.error("Not logged in. Run `brainbase login` first.");
60198
+ fail("Not logged in. Run `brainbase login` first.");
59984
60199
  return;
59985
60200
  }
59986
60201
  const me3 = status.session;
@@ -60024,6 +60239,7 @@ async function runTemplatePublish(_cwd, args) {
60024
60239
  }
60025
60240
  const skillEntries = ref.manifest.components.filter((c2) => c2.type === "skill");
60026
60241
  let manifestMutated = false;
60242
+ const inlinedAfterFailure = [];
60027
60243
  for (const entry of skillEntries) {
60028
60244
  const src = entry.source ?? { type: "inline" };
60029
60245
  if (src.type === "github" || src.type === "git" || src.type === "brainbase") {
@@ -60051,6 +60267,8 @@ async function runTemplatePublish(_cwd, args) {
60051
60267
  });
60052
60268
  if (ok)
60053
60269
  manifestMutated = true;
60270
+ else
60271
+ inlinedAfterFailure.push(entry.slug);
60054
60272
  }
60055
60273
  if (manifestMutated) {
60056
60274
  fs57.writeFileSync(path65.join(ref.rootDir, "brainbase.json"), JSON.stringify(ref.manifest, null, 2));
@@ -60064,7 +60282,7 @@ async function runTemplatePublish(_cwd, args) {
60064
60282
  await pack({ rootDir: ref.rootDir, outFile: tarPath });
60065
60283
  } catch (err) {
60066
60284
  buildSpinner.stop("Bundle failed.");
60067
- f2.error(err.message);
60285
+ fail(err.message);
60068
60286
  return;
60069
60287
  }
60070
60288
  const sha = await sha256OfFile(tarPath);
@@ -60141,15 +60359,25 @@ async function runTemplatePublish(_cwd, args) {
60141
60359
  fs57.rmSync(tmpDir, { recursive: true, force: true });
60142
60360
  } catch {}
60143
60361
  }
60144
- $e(`${import_picocolors11.default.bold(name)}@${version} published.`);
60362
+ const partial2 = inlinedAfterFailure.length > 0;
60363
+ if (partial2) {
60364
+ const plural = inlinedAfterFailure.length === 1 ? "" : "s";
60365
+ f2.warn(`${import_picocolors11.default.bold(name)}@${version} published, but ${inlinedAfterFailure.length} skill${plural} never reached the registry and shipped inline instead: ${inlinedAfterFailure.map((s3) => import_picocolors11.default.bold(s3)).join(", ")}. Re-run ${import_picocolors11.default.cyan("brainbase skill publish")} for ${inlinedAfterFailure.length === 1 ? "it" : "them"}, then publish the template again to swap the inline bytes for a reference.`);
60366
+ $e(`${import_picocolors11.default.bold(name)}@${version} published with ${inlinedAfterFailure.length} skill failure${plural}.`);
60367
+ } else {
60368
+ $e(`${import_picocolors11.default.bold(name)}@${version} published.`);
60369
+ }
60145
60370
  await showResultCard({
60146
- title: "PUBLISHED",
60147
- tone: "ok",
60371
+ title: partial2 ? "PUBLISHED (PARTIAL)" : "PUBLISHED",
60372
+ tone: partial2 ? "warn" : "ok",
60148
60373
  subtitle: `${name}@${version}`,
60149
60374
  meta: [
60150
60375
  ["id", result2.id],
60151
60376
  ["sha256", sha],
60152
60377
  ["visibility", visibility],
60378
+ ...partial2 ? [
60379
+ ["skills inlined after failure", inlinedAfterFailure.join(", ")]
60380
+ ] : [],
60153
60381
  ...visibility === "public" ? [["note", "Quarantined for review on first public publish."]] : []
60154
60382
  ],
60155
60383
  hint: `brainbase template info ${name}`
@@ -60440,7 +60668,7 @@ async function runTemplateInfo(args) {
60440
60668
  const { name, version } = parseRef2(args.ref);
60441
60669
  const [creator, slug] = name.split("/");
60442
60670
  if (!creator || !slug) {
60443
- f2.error("Expected creator/slug.");
60671
+ fail("Expected creator/slug.");
60444
60672
  return;
60445
60673
  }
60446
60674
  const spinner = de();
@@ -60748,17 +60976,17 @@ async function runSkillAdd(cwd2, args) {
60748
60976
  ensureSkillResolversRegistered();
60749
60977
  const source = parseSkillSource(args.source);
60750
60978
  if (source.type === "local" || source.type === "inline") {
60751
- f2.error(`${describeSource(source)} sources are not fetchable. Use a github / git / brainbase source.`);
60979
+ fail(`${describeSource(source)} sources are not fetchable. Use a github / git / brainbase source.`);
60752
60980
  return;
60753
60981
  }
60754
60982
  const resolver = getSkillResolver(source.type);
60755
60983
  if (!resolver) {
60756
- f2.error(`No resolver registered for ${source.type}.`);
60984
+ fail(`No resolver registered for ${source.type}.`);
60757
60985
  return;
60758
60986
  }
60759
60987
  const slug = args.as ?? defaultSkillSlug(source);
60760
60988
  if (!/^[a-zA-Z0-9_-]+$/.test(slug)) {
60761
- f2.error(`Invalid slug ${import_picocolors13.default.bold(slug)}; pass --as <slug>.`);
60989
+ fail(`Invalid slug ${import_picocolors13.default.bold(slug)}; pass --as <slug>.`);
60762
60990
  return;
60763
60991
  }
60764
60992
  let adapterId = args.harness;
@@ -60793,7 +61021,10 @@ async function runSkillAdd(cwd2, args) {
60793
61021
  const skillsRoot = skillsRootFor(adapterId, cwd2, scope);
60794
61022
  const dest = path68.join(skillsRoot, slug);
60795
61023
  if (exists(dest)) {
60796
- if (!autoProceed(args.yes)) {
61024
+ if (!autoProceedDestructive(args.yes, {
61025
+ action: `Overwriting the existing skill at ${dest}`,
61026
+ flagHint: "Pass --yes to overwrite it without a prompt."
61027
+ })) {
60797
61028
  const confirm = await se({
60798
61029
  message: `${import_picocolors13.default.bold(slug)} already exists at ${dest}. Overwrite?`,
60799
61030
  initialValue: false
@@ -60813,7 +61044,7 @@ async function runSkillAdd(cwd2, args) {
60813
61044
  sp.stop("Fetched.");
60814
61045
  } catch (err) {
60815
61046
  sp.stop("Fetch failed.");
60816
- f2.error(err.message);
61047
+ fail(err.message);
60817
61048
  return;
60818
61049
  }
60819
61050
  writeSkillMarker(dest, source);
@@ -60908,7 +61139,10 @@ async function runSkillRemove(cwd2, args) {
60908
61139
  });
60909
61140
  target = candidates[Number(choice)];
60910
61141
  }
60911
- if (!autoProceed(args.yes)) {
61142
+ if (!autoProceedDestructive(args.yes, {
61143
+ action: `Deleting ${target.dir}`,
61144
+ flagHint: "Pass --yes to delete it without a prompt."
61145
+ })) {
60912
61146
  const ok = await se({
60913
61147
  message: `Delete ${import_picocolors15.default.bold(target.dir)}?`,
60914
61148
  initialValue: false
@@ -60937,11 +61171,11 @@ async function runSkillPublish(cwd2, args) {
60937
61171
  banner("skill publish — send a skill to the registry");
60938
61172
  const skillDir = path71.resolve(cwd2, args.dir ?? ".");
60939
61173
  if (!exists(skillDir) || !fs63.statSync(skillDir).isDirectory()) {
60940
- f2.error(`Not a directory: ${import_picocolors16.default.bold(skillDir)}`);
61174
+ fail(`Not a directory: ${import_picocolors16.default.bold(skillDir)}`);
60941
61175
  return;
60942
61176
  }
60943
61177
  if (!exists(path71.join(skillDir, "SKILL.md"))) {
60944
- f2.error(`No ${import_picocolors16.default.bold("SKILL.md")} in ${import_picocolors16.default.bold(skillDir)}. Point at a folder that contains one, or create the file first.`);
61178
+ fail(`No ${import_picocolors16.default.bold("SKILL.md")} in ${import_picocolors16.default.bold(skillDir)}. Point at a folder that contains one, or create the file first.`);
60945
61179
  return;
60946
61180
  }
60947
61181
  const folderSlug = path71.basename(skillDir).toLowerCase();
@@ -60955,12 +61189,12 @@ async function runSkillPublish(cwd2, args) {
60955
61189
  }
60956
61190
  let name = args.name ?? suggestedName;
60957
61191
  if (name && !parseName(name)) {
60958
- f2.error(`Invalid --name "${name}". Use creator/slug.`);
61192
+ fail(`Invalid --name "${name}". Use creator/slug.`);
60959
61193
  return;
60960
61194
  }
60961
61195
  if (!name) {
60962
61196
  if (args.yes) {
60963
- f2.error("Non-interactive publish (--yes) requires --name <creator/slug>.");
61197
+ fail("Non-interactive publish (--yes) requires --name <creator/slug>.");
60964
61198
  return;
60965
61199
  }
60966
61200
  const ans = await text({
@@ -60975,12 +61209,12 @@ async function runSkillPublish(cwd2, args) {
60975
61209
  const { creator, slug: pkgSlug } = parseName(name);
60976
61210
  let version = args.version;
60977
61211
  if (version && !/^\d+\.\d+\.\d+$/.test(version)) {
60978
- f2.error(`Invalid --skill-version "${version}". Use MAJOR.MINOR.PATCH.`);
61212
+ fail(`Invalid --skill-version "${version}". Use MAJOR.MINOR.PATCH.`);
60979
61213
  return;
60980
61214
  }
60981
61215
  if (!version) {
60982
61216
  if (args.yes) {
60983
- f2.error("Non-interactive publish (--yes) requires --skill-version <MAJOR.MINOR.PATCH>.");
61217
+ fail("Non-interactive publish (--yes) requires --skill-version <MAJOR.MINOR.PATCH>.");
60984
61218
  return;
60985
61219
  }
60986
61220
  const ans = await text({
@@ -60995,17 +61229,17 @@ async function runSkillPublish(cwd2, args) {
60995
61229
  try {
60996
61230
  probe = await skillsApi.canPublish(creator, pkgSlug);
60997
61231
  } catch (err) {
60998
- f2.error(`can-publish probe failed: ${err.message}`);
61232
+ fail(`can-publish probe failed: ${err.message}`);
60999
61233
  return;
61000
61234
  }
61001
61235
  if (probe.exists && !probe.can_write) {
61002
- f2.error(`You can't publish to ${import_picocolors16.default.bold(creator + "/" + pkgSlug)}. Pick a name you own.`);
61236
+ fail(`You can't publish to ${import_picocolors16.default.bold(creator + "/" + pkgSlug)}. Pick a name you own.`);
61003
61237
  return;
61004
61238
  }
61005
61239
  if (!probe.exists) {
61006
61240
  const status = authStatus();
61007
61241
  if (!status.ok || !status.session) {
61008
- f2.error("Not logged in. Run `brainbase login` first.");
61242
+ fail("Not logged in. Run `brainbase login` first.");
61009
61243
  return;
61010
61244
  }
61011
61245
  let owner;
@@ -61052,7 +61286,7 @@ async function runSkillPublish(cwd2, args) {
61052
61286
  });
61053
61287
  } catch (err) {
61054
61288
  if (err instanceof ApiError && err.status === 409) {} else {
61055
- f2.error(err.message);
61289
+ fail(err.message);
61056
61290
  return;
61057
61291
  }
61058
61292
  }
@@ -61062,7 +61296,7 @@ async function runSkillPublish(cwd2, args) {
61062
61296
  try {
61063
61297
  const files = collectSkillFiles(skillDir).filter((f4) => f4 !== SKILL_MARKER_FILE);
61064
61298
  if (files.length === 0) {
61065
- f2.error("Skill folder is empty.");
61299
+ fail("Skill folder is empty.");
61066
61300
  return;
61067
61301
  }
61068
61302
  const buildSp = de();
@@ -61194,7 +61428,7 @@ async function runSkillUpdate(cwd2, args) {
61194
61428
  candidates.push({ harness: r2.harness, scope: r2.scope, dir });
61195
61429
  }
61196
61430
  if (candidates.length === 0) {
61197
- f2.warn(`No skill named ${import_picocolors17.default.bold(args.slug)} found.`);
61431
+ failWarn(`No skill named ${import_picocolors17.default.bold(args.slug)} found.`);
61198
61432
  return;
61199
61433
  }
61200
61434
  let target = candidates[0];
@@ -61211,16 +61445,16 @@ async function runSkillUpdate(cwd2, args) {
61211
61445
  }
61212
61446
  const marker = readSkillMarker(target.dir);
61213
61447
  if (!marker) {
61214
- f2.error("No marker file. Skills without provenance can't be updated automatically.");
61448
+ fail("No marker file. Skills without provenance can't be updated automatically.");
61215
61449
  return;
61216
61450
  }
61217
61451
  if (marker.source.type === "inline" || marker.source.type === "local") {
61218
- f2.error(`Skill source is ${describeSource(marker.source)} — nothing to update from.`);
61452
+ fail(`Skill source is ${describeSource(marker.source)} — nothing to update from.`);
61219
61453
  return;
61220
61454
  }
61221
61455
  const resolver = getSkillResolver(marker.source.type);
61222
61456
  if (!resolver) {
61223
- f2.error(`No resolver for ${marker.source.type}.`);
61457
+ fail(`No resolver for ${marker.source.type}.`);
61224
61458
  return;
61225
61459
  }
61226
61460
  if (!autoProceed(args.yes)) {
@@ -61241,7 +61475,7 @@ async function runSkillUpdate(cwd2, args) {
61241
61475
  sp.stop("Fetched.");
61242
61476
  } catch (err) {
61243
61477
  sp.stop("Fetch failed.");
61244
- f2.error(err.message);
61478
+ fail(err.message);
61245
61479
  return;
61246
61480
  }
61247
61481
  writeSkillMarker(target.dir, marker.source, marker.component);
@@ -62176,9 +62410,7 @@ var EvalSchema = exports_external.object({
62176
62410
  path: ["classification_values"]
62177
62411
  });
62178
62412
  var MODEL_ID_RE = /^[A-Za-z0-9._:/-]{1,128}$/;
62179
- var UNSYNCED_MANIFEST_KEYS = ["commands", "hooks", "files"];
62180
62413
  var UnsyncedBlockSchema = exports_external.array(exports_external.record(exports_external.unknown())).optional();
62181
- var UnsyncedBlocksShape = Object.fromEntries(UNSYNCED_MANIFEST_KEYS.map((key2) => [key2, UnsyncedBlockSchema]));
62182
62414
  var AgentManifestSchema = exports_external.object({
62183
62415
  schema: exports_external.literal(1),
62184
62416
  id: exports_external.string().min(1).optional(),
@@ -62205,8 +62437,29 @@ var AgentManifestSchema = exports_external.object({
62205
62437
  });
62206
62438
  }).default([]),
62207
62439
  capabilities: CapabilitiesSchema.optional(),
62208
- ...UnsyncedBlocksShape
62440
+ commands: UnsyncedBlockSchema,
62441
+ hooks: UnsyncedBlockSchema,
62442
+ files: UnsyncedBlockSchema
62209
62443
  });
62444
+ var MANIFEST_KEY_SYNC = {
62445
+ schema: "local",
62446
+ id: "synced",
62447
+ harness: "synced",
62448
+ machine_kind: "synced",
62449
+ default_model: "synced",
62450
+ agent: "synced",
62451
+ instructions: "synced",
62452
+ entrypoint: "synced",
62453
+ playbooks: "synced",
62454
+ skills: "synced",
62455
+ mcp: "synced",
62456
+ evals: "synced",
62457
+ capabilities: "synced",
62458
+ commands: "unsynced",
62459
+ hooks: "unsynced",
62460
+ files: "unsynced"
62461
+ };
62462
+ var UNSYNCED_MANIFEST_KEYS = Object.keys(AgentManifestSchema.shape).filter((key2) => (MANIFEST_KEY_SYNC[key2] ?? "unsynced") === "unsynced");
62210
62463
  function manifestPath(cwd2) {
62211
62464
  return path73.join(cwd2, AGENT_MANIFEST_FILE);
62212
62465
  }
@@ -62879,7 +63132,10 @@ async function handleAlreadyLinked(cwd2, link2, args) {
62879
63132
  return;
62880
63133
  }
62881
63134
  if (next === "unlink") {
62882
- if (!autoProceed(args.yes)) {
63135
+ if (!autoProceedDestructive(args.yes, {
63136
+ action: `Unlinking this folder from ${link2.name}`,
63137
+ flagHint: "Pass --yes to unlink without a prompt."
63138
+ })) {
62883
63139
  const confirmed = await se({
62884
63140
  message: "Remove the link from this folder? (the cloud agent will stay)",
62885
63141
  initialValue: true
@@ -62901,7 +63157,10 @@ async function handleAlreadyLinked(cwd2, link2, args) {
62901
63157
  flagHint: "Pass --agent <id>."
62902
63158
  });
62903
63159
  const newId = ans.trim();
62904
- if (!autoProceed(args.yes)) {
63160
+ if (!autoProceedDestructive(args.yes, {
63161
+ action: `Replacing this folder's link to ${link2.name} with ${newId}`,
63162
+ flagHint: "Pass --yes to re-link without a prompt."
63163
+ })) {
62905
63164
  const confirmed = await se({
62906
63165
  message: "This will replace the current link. Continue?",
62907
63166
  initialValue: false
@@ -63072,7 +63331,10 @@ async function runUnlink(cwd2, args) {
63072
63331
  return;
63073
63332
  }
63074
63333
  f2.info(`Currently linked to ${import_picocolors23.default.bold(link2.name)} ${import_picocolors23.default.dim(`(${link2.slug})`)}.`);
63075
- if (!autoProceed(args.yes)) {
63334
+ if (!autoProceedDestructive(args.yes, {
63335
+ action: `Unlinking this folder from ${link2.name}`,
63336
+ flagHint: "Pass --yes to unlink without a prompt."
63337
+ })) {
63076
63338
  const ok = await se({
63077
63339
  message: "Remove the link from this folder? (the cloud agent will stay)",
63078
63340
  initialValue: true
@@ -63366,7 +63628,7 @@ async function runSync(cwd2, args) {
63366
63628
  banner("sync — bring in the latest changes from your team");
63367
63629
  const link2 = readLink(cwd2);
63368
63630
  if (!link2) {
63369
- f2.warn("This folder is not linked to any agent.");
63631
+ failWarn("This folder is not linked to any agent.");
63370
63632
  f2.info(`Run ${import_picocolors24.default.cyan("brainbase link")} first.`);
63371
63633
  return;
63372
63634
  }
@@ -63379,9 +63641,9 @@ async function runSync(cwd2, args) {
63379
63641
  } catch (err) {
63380
63642
  manifestSpinner.stop("Failed.");
63381
63643
  if (err instanceof ApiError && err.status === 401) {
63382
- f2.error("Your session is invalid. Run `brainbase login` and try again.");
63644
+ fail("Your session is invalid. Run `brainbase login` and try again.");
63383
63645
  } else {
63384
- f2.error(err.message);
63646
+ fail(err.message);
63385
63647
  }
63386
63648
  return;
63387
63649
  }
@@ -64418,7 +64680,7 @@ function componentsForNativeInstall(components, acp) {
64418
64680
  async function runAgentUnpack(cwd2, args) {
64419
64681
  banner("agent unpack — install this agent into a harness layout");
64420
64682
  if (!hasManifest(cwd2)) {
64421
- f2.error(`No ${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} here.`);
64683
+ fail(`No ${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} here.`);
64422
64684
  f2.info(`Run ${import_picocolors25.default.cyan("brainbase agent pull <id>")} to bring an agent into this folder first.`);
64423
64685
  return;
64424
64686
  }
@@ -64431,7 +64693,7 @@ async function runAgentUnpack(cwd2, args) {
64431
64693
  return;
64432
64694
  }
64433
64695
  if (!manifest.id) {
64434
- f2.error(`${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors25.default.cyan("id")}).`);
64696
+ fail(`${import_picocolors25.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors25.default.cyan("id")}).`);
64435
64697
  f2.info(`Run ${import_picocolors25.default.cyan("brainbase agent create")} to claim it, ` + `or ${import_picocolors25.default.cyan("brainbase agent pull <id>")} to link it to an existing agent.`);
64436
64698
  return;
64437
64699
  }
@@ -64440,7 +64702,7 @@ async function runAgentUnpack(cwd2, args) {
64440
64702
  harness = normalizeHarnessId(args.harness);
64441
64703
  } else if (args.yes) {
64442
64704
  if (!manifest.harness) {
64443
- f2.error(`--yes mode but no harness — set ${import_picocolors25.default.cyan("harness")} in the manifest or pass ${import_picocolors25.default.cyan("--harness")}.`);
64705
+ fail(`--yes mode but no harness — set ${import_picocolors25.default.cyan("harness")} in the manifest or pass ${import_picocolors25.default.cyan("--harness")}.`);
64444
64706
  return;
64445
64707
  }
64446
64708
  harness = normalizeHarnessId(manifest.harness);
@@ -64540,7 +64802,7 @@ async function runAgentUnpack(cwd2, args) {
64540
64802
  runHarnessRemoveMcp(harness, builtinRemoveSlugs, { cwd: cwd2, scope });
64541
64803
  }
64542
64804
  } catch (err) {
64543
- f2.error(`Install failed: ${err.message}`);
64805
+ fail(`Install failed: ${err.message}`);
64544
64806
  return;
64545
64807
  } finally {
64546
64808
  try {
@@ -64989,7 +65251,7 @@ function resolveTargetAgentId(cwd2, args) {
64989
65251
  const manifestId = manifest?.id;
64990
65252
  if (arg && manifestId && arg !== manifestId) {
64991
65253
  if (!args.force) {
64992
- f2.error(`This folder is already linked to a different agent (${import_picocolors26.default.dim(manifestId)}).`);
65254
+ fail(`This folder is already linked to a different agent (${import_picocolors26.default.dim(manifestId)}).`);
64993
65255
  f2.info(`Run ${import_picocolors26.default.cyan(`brainbase agent pull ${arg} --force`)} to override. ` + import_picocolors26.default.yellow("This will overwrite brainbase.agent.yaml and any local progress will be lost."));
64994
65256
  return null;
64995
65257
  }
@@ -65000,10 +65262,10 @@ function resolveTargetAgentId(cwd2, args) {
65000
65262
  if (manifestId)
65001
65263
  return { agentId: manifestId, override: false };
65002
65264
  if (manifest) {
65003
- f2.error(`${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors26.default.cyan("id")}).`);
65265
+ fail(`${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors26.default.cyan("id")}).`);
65004
65266
  f2.info(`Run ${import_picocolors26.default.cyan("brainbase agent create")} to create a new agent from this manifest, ` + `or ${import_picocolors26.default.cyan("brainbase agent pull <id>")} to pull an existing one.`);
65005
65267
  } else {
65006
- f2.error(`No ${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} here and no ${import_picocolors26.default.cyan("<id>")} given.`);
65268
+ fail(`No ${import_picocolors26.default.bold(AGENT_MANIFEST_FILE)} here and no ${import_picocolors26.default.cyan("<id>")} given.`);
65007
65269
  f2.info(`Run ${import_picocolors26.default.cyan("brainbase agent pull <id>")} to pull an existing agent into this folder.`);
65008
65270
  }
65009
65271
  return null;
@@ -65366,42 +65628,87 @@ async function pullSecrets(cwd2, agentId) {
65366
65628
  function handleApiError2(err) {
65367
65629
  if (err instanceof ApiError) {
65368
65630
  if (err.status === 401) {
65369
- f2.error("Your session is invalid. Run `brainbase login` and try again.");
65631
+ fail("Your session is invalid. Run `brainbase login` and try again.");
65370
65632
  } else if (err.status === 404) {
65371
- f2.error(`Agent not found, or you don't have access. Double-check the id.`);
65633
+ fail(`Agent not found, or you don't have access. Double-check the id.`);
65372
65634
  } else {
65373
- f2.error(err.message);
65635
+ fail(err.message);
65374
65636
  }
65375
65637
  } else {
65376
- f2.error(err.message);
65638
+ fail(err.message);
65377
65639
  }
65378
65640
  }
65379
65641
 
65380
65642
  // src/cli/agent-push.ts
65381
65643
  var import_picocolors28 = __toESM(require_picocolors(), 1);
65382
65644
 
65383
- // src/core/eval-reconcile.ts
65384
- function manifestClaimsEvals(manifest) {
65385
- return (manifest.evals ?? []).length > 0;
65386
- }
65387
- function decideEvalReconcile(input) {
65388
- const evalRows = input.rows.filter((r2) => r2.type === "eval");
65389
- const unseen = evalRows.filter((r2) => r2.status === "added-cloud").map((r2) => r2.slug).sort();
65390
- const cloudModified = evalRows.filter((r2) => r2.status === "modified-cloud" || r2.status === "modified-both" || r2.status === "added-local" && r2.localHash !== undefined && r2.cloudHash !== undefined && r2.localHash !== r2.cloudHash).map((r2) => r2.slug).sort();
65391
- const archives = evalRows.filter((r2) => r2.status === "removed-local").map((r2) => r2.slug).sort();
65392
- if (!input.claimsEvals && archives.length === 0) {
65645
+ // src/core/collection-reconcile.ts
65646
+ function untouchedRows(decision) {
65647
+ if (decision.kind === "reconcile")
65648
+ return [];
65649
+ return [...decision.unseen, ...decision.cloudModified].sort();
65650
+ }
65651
+ function decideCollectionReconcile(input) {
65652
+ const rows = input.rows.filter((r2) => r2.type === input.type);
65653
+ const unseen = rows.filter((r2) => r2.status === "added-cloud").map((r2) => r2.slug).sort();
65654
+ const cloudModified = rows.filter((r2) => r2.status === "modified-cloud" || r2.status === "modified-both" || r2.status === "added-local" && r2.localHash !== undefined && r2.cloudHash !== undefined && r2.localHash !== r2.cloudHash).map((r2) => r2.slug).sort();
65655
+ const removedLocal = rows.filter((r2) => r2.status === "removed-local");
65656
+ const archives = removedLocal.map((r2) => r2.slug).sort();
65657
+ const staleArchives = removedLocal.filter((r2) => r2.cloudHash !== undefined && r2.lockHash !== undefined && r2.cloudHash !== r2.lockHash).map((r2) => r2.slug).sort();
65658
+ if (!input.claims && archives.length === 0) {
65393
65659
  if (unseen.length > 0 || cloudModified.length > 0) {
65394
- return { kind: "skip", unseen: [...unseen, ...cloudModified].sort() };
65660
+ return { kind: "skip", unseen, cloudModified };
65395
65661
  }
65396
65662
  return { kind: "reconcile", archives: [] };
65397
65663
  }
65398
65664
  if (input.force) {
65399
65665
  return { kind: "reconcile", archives: [...archives, ...unseen].sort() };
65400
65666
  }
65401
- if (unseen.length === 0 && cloudModified.length === 0) {
65667
+ if (unseen.length === 0 && cloudModified.length === 0 && staleArchives.length === 0) {
65402
65668
  return { kind: "reconcile", archives };
65403
65669
  }
65404
- return { kind: "blocked", unseen, cloudModified, archives };
65670
+ return { kind: "blocked", unseen, cloudModified, staleArchives, archives };
65671
+ }
65672
+
65673
+ // src/core/eval-reconcile.ts
65674
+ function manifestClaimsEvals(manifest) {
65675
+ return (manifest.evals ?? []).length > 0;
65676
+ }
65677
+ function decideEvalReconcile(input) {
65678
+ return decideCollectionReconcile({
65679
+ rows: input.rows,
65680
+ type: "eval",
65681
+ claims: input.claimsEvals,
65682
+ force: input.force
65683
+ });
65684
+ }
65685
+
65686
+ // src/core/playbook-reconcile.ts
65687
+ function manifestClaimsPlaybooks(manifest) {
65688
+ return (manifest.playbooks ?? []).length > 0;
65689
+ }
65690
+ function decidePlaybookReconcile(input) {
65691
+ return decideCollectionReconcile({
65692
+ rows: input.rows,
65693
+ type: "playbook",
65694
+ claims: input.claimsPlaybooks,
65695
+ force: input.force
65696
+ });
65697
+ }
65698
+ function playbookLabels(slugs, cloudComponents) {
65699
+ const titleBySlug = new Map;
65700
+ for (const c2 of cloudComponents) {
65701
+ if (c2.type !== "playbook")
65702
+ continue;
65703
+ const title = c2.meta?.playbook?.title;
65704
+ if (typeof title === "string" && title.trim()) {
65705
+ titleBySlug.set(c2.slug, title);
65706
+ }
65707
+ }
65708
+ return slugs.map((slug) => {
65709
+ const title = titleBySlug.get(slug);
65710
+ return title && title !== slug ? `${title} (${slug})` : slug;
65711
+ });
65405
65712
  }
65406
65713
 
65407
65714
  // src/core/agent-outgoing.ts
@@ -65606,7 +65913,7 @@ function planRegistrySkillUpdates(skills, cloudComponents, latestByName) {
65606
65913
  async function runAgentPush(cwd2, args) {
65607
65914
  banner("agent push — send your local changes to the cloud");
65608
65915
  if (!hasManifest(cwd2)) {
65609
- f2.warn(`No ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} here.`);
65916
+ failWarn(`No ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} here.`);
65610
65917
  f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent create")} to claim a new agent from a manifest, ` + `or ${import_picocolors28.default.cyan("brainbase agent pull <id>")} to pull an existing one.`);
65611
65918
  return;
65612
65919
  }
@@ -65623,7 +65930,7 @@ async function runAgentPush(cwd2, args) {
65623
65930
  return;
65624
65931
  }
65625
65932
  if (!manifest.id) {
65626
- f2.warn(`${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors28.default.cyan("id")}). Nothing to push to.`);
65933
+ failWarn(`${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} is unclaimed (no ${import_picocolors28.default.cyan("id")}). Nothing to push to.`);
65627
65934
  f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent create")} first — that creates the cloud agent and stamps an id here.`);
65628
65935
  return;
65629
65936
  }
@@ -65791,7 +66098,7 @@ async function runAgentPush(cwd2, args) {
65791
66098
  force: !!args.force
65792
66099
  });
65793
66100
  if (evalDecision.kind === "blocked") {
65794
- const { unseen, cloudModified } = evalDecision;
66101
+ const { unseen, cloudModified, staleArchives } = evalDecision;
65795
66102
  f2.error(unseen.length > 0 ? "Cannot push: the cloud has eval changes this folder has never seen." : "Cannot push: evals changed both locally and in the cloud.");
65796
66103
  for (const slug of unseen) {
65797
66104
  console.error(` ${import_picocolors28.default.red("!")} ${fmtType("eval")} ${import_picocolors28.default.bold(slug)} ${import_picocolors28.default.dim("(only in the cloud — pushing would archive it)")}`);
@@ -65799,12 +66106,17 @@ async function runAgentPush(cwd2, args) {
65799
66106
  for (const slug of cloudModified) {
65800
66107
  console.error(` ${import_picocolors28.default.red("!")} ${fmtType("eval")} ${import_picocolors28.default.bold(slug)} ${import_picocolors28.default.dim("(edited in the cloud — pushing would overwrite that edit)")}`);
65801
66108
  }
65802
- f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent pull")} to bring ${unseen.length + cloudModified.length === 1 ? "it" : "them"} into ${import_picocolors28.default.bold("brainbase.agent.yaml")}, then push — or ${import_picocolors28.default.cyan("brainbase agent push --force")} to make your local evals authoritative and archive the rest.`);
66109
+ for (const slug of staleArchives) {
66110
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("eval")} ${import_picocolors28.default.bold(slug)} ${import_picocolors28.default.dim("(deleted here, edited in the cloud since — pushing would archive that edit)")}`);
66111
+ }
66112
+ const total = unseen.length + cloudModified.length + staleArchives.length;
66113
+ f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent pull")} to bring ${total === 1 ? "it" : "them"} into ${import_picocolors28.default.bold("brainbase.agent.yaml")}, then push — or ${import_picocolors28.default.cyan("brainbase agent push --force")} to make your local evals authoritative and archive the rest.`);
65803
66114
  process.exitCode = 1;
65804
66115
  return;
65805
66116
  }
65806
66117
  if (evalDecision.kind === "skip") {
65807
- f2.warn(`${evalDecision.unseen.length} cloud eval${evalDecision.unseen.length === 1 ? "" : "s"} (${evalDecision.unseen.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}) ${evalDecision.unseen.length === 1 ? "is" : "are"} not in ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be left untouched. Run ${import_picocolors28.default.cyan("brainbase agent pull")} to manage ${evalDecision.unseen.length === 1 ? "it" : "them"} from here.`);
66118
+ const left = untouchedRows(evalDecision);
66119
+ f2.warn(`${left.length} cloud eval${left.length === 1 ? "" : "s"} (${left.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}) ${left.length === 1 ? "is" : "are"} not managed by ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be left untouched. Run ${import_picocolors28.default.cyan("brainbase agent pull")} to manage ${left.length === 1 ? "it" : "them"} from here.`);
65808
66120
  }
65809
66121
  for (const entry of manifest.skills) {
65810
66122
  let parsed;
@@ -65831,6 +66143,32 @@ async function runAgentPush(cwd2, args) {
65831
66143
  process.exitCode = 1;
65832
66144
  return;
65833
66145
  }
66146
+ const playbookDecision = decidePlaybookReconcile({
66147
+ rows,
66148
+ claimsPlaybooks: manifestClaimsPlaybooks(manifest),
66149
+ force: !!args.force
66150
+ });
66151
+ if (playbookDecision.kind === "blocked") {
66152
+ const { unseen, cloudModified, staleArchives } = playbookDecision;
66153
+ f2.error(unseen.length > 0 ? "Cannot push: the cloud has playbooks this folder has never seen." : "Cannot push: playbooks changed both locally and in the cloud.");
66154
+ for (const label of playbookLabels(unseen, cloud.components)) {
66155
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("playbook")} ${import_picocolors28.default.bold(label)} ${import_picocolors28.default.dim("(only in the cloud — pushing would archive it)")}`);
66156
+ }
66157
+ for (const label of playbookLabels(cloudModified, cloud.components)) {
66158
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("playbook")} ${import_picocolors28.default.bold(label)} ${import_picocolors28.default.dim("(edited in the cloud — pushing would overwrite that edit)")}`);
66159
+ }
66160
+ for (const label of playbookLabels(staleArchives, cloud.components)) {
66161
+ console.error(` ${import_picocolors28.default.red("!")} ${fmtType("playbook")} ${import_picocolors28.default.bold(label)} ${import_picocolors28.default.dim("(deleted here, edited in the cloud since — pushing would archive that edit)")}`);
66162
+ }
66163
+ const total = unseen.length + cloudModified.length + staleArchives.length;
66164
+ f2.info(`Run ${import_picocolors28.default.cyan("brainbase agent pull")} to bring ${total === 1 ? "it" : "them"} into ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)}, then push — or ${import_picocolors28.default.cyan("brainbase agent push --force")} to make your local playbooks authoritative and archive the rest.`);
66165
+ process.exitCode = 1;
66166
+ return;
66167
+ }
66168
+ if (playbookDecision.kind === "skip") {
66169
+ const labels = playbookLabels(untouchedRows(playbookDecision), cloud.components);
66170
+ f2.warn(`${labels.length} cloud playbook${labels.length === 1 ? "" : "s"} (${labels.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}) ${labels.length === 1 ? "is" : "are"} not managed by ${import_picocolors28.default.bold(AGENT_MANIFEST_FILE)} and will be left untouched. Run ${import_picocolors28.default.cyan("brainbase agent pull")} to manage ${labels.length === 1 ? "it" : "them"} from here.`);
66171
+ }
65834
66172
  const { toSend, conflicts, upstreamOnly } = partitionPushRows(rows, !!args.force);
65835
66173
  const forcedOverrides = args.force ? rows.filter((r2) => r2.status === "modified-both") : [];
65836
66174
  if (conflicts.length > 0) {
@@ -65964,6 +66302,15 @@ async function runAgentPush(cwd2, args) {
65964
66302
  text: `${evalArchives.length} · ${evalArchives.join(", ")}`
65965
66303
  });
65966
66304
  }
66305
+ const playbookArchives = playbookDecision.kind === "reconcile" ? playbookDecision.archives : [];
66306
+ const playbookArchiveLabels = playbookLabels(playbookArchives, cloud.components);
66307
+ if (playbookArchives.length) {
66308
+ resultRows.push({
66309
+ type: "rem",
66310
+ label: "archive",
66311
+ text: `${playbookArchives.length} playbook${playbookArchives.length === 1 ? "" : "s"} · ${playbookArchiveLabels.join(", ")}`
66312
+ });
66313
+ }
65967
66314
  await showResultCard({
65968
66315
  title: "PUSH",
65969
66316
  tone: "info",
@@ -65973,10 +66320,14 @@ async function runAgentPush(cwd2, args) {
65973
66320
  if (evalArchives.length) {
65974
66321
  f2.warn(`${evalArchives.length} cloud eval${evalArchives.length === 1 ? "" : "s"} ${evalArchives.length === 1 ? "is" : "are"} missing from ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be archived: ${evalArchives.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}. ${import_picocolors28.default.dim("Archived, not deleted — past verdicts are kept, and re-adding the eval restores it.")}`);
65975
66322
  }
66323
+ if (playbookArchives.length) {
66324
+ f2.warn(`${playbookArchives.length} cloud playbook${playbookArchives.length === 1 ? "" : "s"} ${playbookArchives.length === 1 ? "is" : "are"} missing from ${import_picocolors28.default.bold("brainbase.agent.yaml")} and will be archived: ${playbookArchiveLabels.map((s3) => import_picocolors28.default.bold(s3)).join(", ")}. ${import_picocolors28.default.dim("Archived, not deleted — the row is kept, and re-adding the playbook restores it.")}`);
66325
+ }
66326
+ const destructive = evalArchives.length + playbookArchives.length;
65976
66327
  if (!autoProceed(args.yes)) {
65977
66328
  const ok = await se({
65978
- message: evalArchives.length ? `Send these changes, archiving ${evalArchives.length} eval${evalArchives.length === 1 ? "" : "s"}?` : "Send these changes?",
65979
- initialValue: evalArchives.length === 0
66329
+ message: destructive ? `Send these changes, archiving ${describeArchiveSet(evalArchives.length, playbookArchives.length)}?` : "Send these changes?",
66330
+ initialValue: destructive === 0
65980
66331
  });
65981
66332
  if (!ensureNotCancelled(ok)) {
65982
66333
  $e("Aborted.");
@@ -66043,11 +66394,12 @@ async function runAgentPush(cwd2, args) {
66043
66394
  let updatedCloud;
66044
66395
  try {
66045
66396
  const reconcileEvals = evalDecision.kind !== "skip";
66046
- const components = reconcileEvals ? outgoing : outgoing.filter((c2) => c2.type !== "eval");
66397
+ const reconcilePlaybooks = playbookDecision.kind !== "skip";
66398
+ const components = outgoing.filter((c2) => (reconcileEvals || c2.type !== "eval") && (reconcilePlaybooks || c2.type !== "playbook"));
66047
66399
  updatedCloud = await api.pushAgentManifest(agentId, {
66048
66400
  components,
66049
66401
  base_revision: cloud.revision,
66050
- reconcile_playbooks: true,
66402
+ reconcile_playbooks: reconcilePlaybooks,
66051
66403
  reconcile_evals: reconcileEvals
66052
66404
  });
66053
66405
  pushSpinner.stop(`Pushed. New revision ${updatedCloud.revision}.`);
@@ -66095,16 +66447,21 @@ async function runAgentPush(cwd2, args) {
66095
66447
  localHashByKey.set(`eval/${entry.slug}`, hashEvalEntry(entry));
66096
66448
  }
66097
66449
  }
66098
- const lockSource = evalDecision.kind === "skip" ? [
66099
- ...updatedCloud.components.filter((c2) => c2.type !== "eval"),
66100
- ...(lock?.components ?? []).filter((c2) => c2.type === "eval").map((c2) => ({
66450
+ const skipped = new Set;
66451
+ if (evalDecision.kind === "skip")
66452
+ skipped.add("eval");
66453
+ if (playbookDecision.kind === "skip")
66454
+ skipped.add("playbook");
66455
+ const lockSource = skipped.size === 0 ? updatedCloud.components : [
66456
+ ...updatedCloud.components.filter((c2) => !skipped.has(c2.type)),
66457
+ ...(lock?.components ?? []).filter((c2) => skipped.has(c2.type)).map((c2) => ({
66101
66458
  type: c2.type,
66102
66459
  slug: c2.slug,
66103
66460
  hash: c2.hash,
66104
66461
  files: [],
66105
66462
  meta: undefined
66106
66463
  }))
66107
- ] : updatedCloud.components;
66464
+ ];
66108
66465
  const newLock = {
66109
66466
  schemaVersion: 1,
66110
66467
  agent_id: agentId,
@@ -66186,6 +66543,15 @@ async function pushSecrets(agentId, plan) {
66186
66543
  return false;
66187
66544
  }
66188
66545
  }
66546
+ function describeArchiveSet(evals, playbooks) {
66547
+ const parts = [];
66548
+ if (evals)
66549
+ parts.push(`${evals} eval${evals === 1 ? "" : "s"}`);
66550
+ if (playbooks) {
66551
+ parts.push(`${playbooks} playbook${playbooks === 1 ? "" : "s"}`);
66552
+ }
66553
+ return parts.join(" and ");
66554
+ }
66189
66555
  function secretDiffSummary(diff2) {
66190
66556
  return `${diff2.localOnly.length} added, ${diff2.changed.length} updated, ${diff2.cloudOnly.length} removed`;
66191
66557
  }
@@ -66216,9 +66582,10 @@ async function runAgentStatus(cwd2, args = {}) {
66216
66582
  if (!link2) {
66217
66583
  if (json) {
66218
66584
  emitJson({ linked: false, ignored: [], unchecked: [] });
66585
+ process.exitCode = 1;
66219
66586
  return;
66220
66587
  }
66221
- f2.warn("This folder is not linked to any agent.");
66588
+ failWarn("This folder is not linked to any agent.");
66222
66589
  f2.info(`Run ${import_picocolors29.default.cyan("brainbase link")} first.`);
66223
66590
  return;
66224
66591
  }
@@ -66243,9 +66610,9 @@ async function runAgentStatus(cwd2, args = {}) {
66243
66610
  } catch (err) {
66244
66611
  const unauthorized = err instanceof ApiError && err.status === 401;
66245
66612
  const message = unauthorized ? "Your session is invalid. Run `brainbase login` and try again." : err.message;
66613
+ process.exitCode = 1;
66246
66614
  if (json) {
66247
66615
  console.error(message);
66248
- process.exitCode = 1;
66249
66616
  return;
66250
66617
  }
66251
66618
  sp?.stop("Failed to reach brainbase.");
@@ -66310,20 +66677,25 @@ async function runAgentStatus(cwd2, args = {}) {
66310
66677
  };
66311
66678
  let secretsChecked = true;
66312
66679
  let secretsUncheckedReason = "";
66680
+ let cloudSecretNames = null;
66313
66681
  try {
66314
- const localSecrets = readLocalSecrets(cwd2);
66315
66682
  const cloudRes = await api.getAgentSecrets(link2.agent_id);
66316
- secretDrift = diffSecrets(localSecrets, cloudRes.secrets);
66683
+ cloudSecretNames = new Set(Object.keys(cloudRes.secrets));
66684
+ secretDrift = diffSecrets(readLocalSecrets(cwd2), cloudRes.secrets);
66317
66685
  } catch (err) {
66318
- if (!(err instanceof ApiError && err.status === 404)) {
66319
- secretsChecked = false;
66320
- secretsUncheckedReason = describeSecretsFailure(err);
66321
- }
66686
+ secretsChecked = false;
66687
+ secretsUncheckedReason = describeSecretsFailure(err);
66322
66688
  }
66323
66689
  const componentsDrifted = conflicts.length > 0 || toPush.length > 0 || toPull.length > 0;
66324
66690
  const metaDrifted = meta.localChanged || meta.cloudChanged;
66325
66691
  const configDrifted = config.unsupported.length > 0 || config.machineMismatch || config.machineCloudChanged || config.defaultModelLocalChanged || config.defaultModelCloudChanged || capabilityDrifted(config.memory) || capabilityDrifted(config.browser);
66326
66692
  const secretsDrifted = secretDrift.localOnly.length > 0 || secretDrift.cloudOnly.length > 0 || secretDrift.changed.length > 0;
66693
+ const secretConflicts = cloudAgent.secret_conflicts ?? [];
66694
+ const inheritedSecrets = (cloudAgent.inherited_secret_keys ?? []).map((entry) => ({
66695
+ ...entry,
66696
+ overridden: entry.overridden ?? cloudSecretNames?.has(entry.key) ?? false
66697
+ }));
66698
+ const hasInheritedSecrets = inheritedSecrets.length > 0 || secretConflicts.length > 0;
66327
66699
  const everythingInSync = !componentsDrifted && !metaDrifted && !configDrifted && !secretsDrifted;
66328
66700
  const unchecked = secretsChecked ? [] : [{ signal: "secrets", reason: secretsUncheckedReason }];
66329
66701
  const evalPlan = decideEvalReconcile({
@@ -66331,12 +66703,13 @@ async function runAgentStatus(cwd2, args = {}) {
66331
66703
  claimsEvals: manifestClaimsEvals(manifest),
66332
66704
  force: false
66333
66705
  });
66334
- const evalReport = {
66335
- archive: evalPlan.kind === "reconcile" ? evalPlan.archives : [],
66336
- unseen: evalPlan.kind === "reconcile" ? [] : evalPlan.unseen,
66337
- cloudModified: evalPlan.kind === "blocked" ? evalPlan.cloudModified : [],
66338
- pushBlocked: evalPlan.kind === "blocked"
66339
- };
66706
+ const evalReport = reconcileReport(evalPlan);
66707
+ const playbookPlan = decidePlaybookReconcile({
66708
+ rows,
66709
+ claimsPlaybooks: manifestClaimsPlaybooks(manifest),
66710
+ force: false
66711
+ });
66712
+ const playbookReport = reconcileReport(playbookPlan);
66340
66713
  if (json) {
66341
66714
  emitJson({
66342
66715
  linked: true,
@@ -66358,17 +66731,26 @@ async function runAgentStatus(cwd2, args = {}) {
66358
66731
  secrets: secretsChecked ? {
66359
66732
  localOnly: secretDrift.localOnly,
66360
66733
  cloudOnly: secretDrift.cloudOnly,
66361
- changed: secretDrift.changed
66734
+ changed: secretDrift.changed,
66735
+ inherited: inheritedSecrets,
66736
+ conflicts: secretConflicts
66362
66737
  } : null,
66738
+ orchestrationSecrets: {
66739
+ inherited: inheritedSecrets,
66740
+ conflicts: secretConflicts
66741
+ },
66363
66742
  components: {
66364
66743
  push: toPush.map(rowJson),
66365
66744
  pull: toPull.map(rowJson),
66366
66745
  conflicts: conflicts.map(rowJson)
66367
66746
  },
66368
66747
  evals: evalReport,
66748
+ playbooks: playbookReport,
66369
66749
  inSync: everythingInSync,
66370
66750
  unchecked
66371
66751
  });
66752
+ if (!secretsChecked)
66753
+ process.exitCode = 1;
66372
66754
  return;
66373
66755
  }
66374
66756
  const lines = [];
@@ -66430,7 +66812,7 @@ async function runAgentStatus(cwd2, args = {}) {
66430
66812
  }
66431
66813
  lines.push("");
66432
66814
  }
66433
- if (secretsDrifted || !secretsChecked) {
66815
+ if (secretsDrifted || !secretsChecked || hasInheritedSecrets) {
66434
66816
  lines.push(` ${import_picocolors29.default.bold("secrets")}`);
66435
66817
  if (!secretsChecked) {
66436
66818
  lines.push(` ${import_picocolors29.default.dim("? unchecked")} ${secretsUncheckedReason}`);
@@ -66441,16 +66823,34 @@ async function runAgentStatus(cwd2, args = {}) {
66441
66823
  lines.push(` ${import_picocolors29.default.yellow("→ push")} values changed: ${secretDrift.changed.join(", ")}`);
66442
66824
  if (secretDrift.cloudOnly.length)
66443
66825
  lines.push(` ${import_picocolors29.default.cyan("← pull")} new on cloud: ${secretDrift.cloudOnly.join(", ")}`);
66826
+ for (const entry of inheritedSecrets) {
66827
+ const source = entry.orchestration_name || entry.orchestration_id;
66828
+ const pendingOverride = !entry.overridden && (secretDrift.localOnly.includes(entry.key) || secretDrift.changed.includes(entry.key));
66829
+ lines.push(entry.overridden ? ` ${entry.key} ${import_picocolors29.default.dim(`(inherited from ${source}, overridden by this agent's own secret)`)}` : pendingOverride ? ` ${entry.key} ${import_picocolors29.default.dim(`(inherited from ${source}, will be overridden once pushed)`)}` : ` ${entry.key} ${import_picocolors29.default.dim(`(inherited from ${source})`)}`);
66830
+ }
66831
+ for (const entry of secretConflicts) {
66832
+ lines.push(` ${import_picocolors29.default.dim("! conflict")} ${entry.key} ${import_picocolors29.default.dim(`has different values ${conflictSources(entry)}; neither value is applied until you set one on this agent`)}`);
66833
+ }
66834
+ if (secretConflicts.length > 0 && everythingInSync) {
66835
+ lines.push(` ${import_picocolors29.default.dim("conflicts are informational; they never block sync")}`);
66836
+ }
66444
66837
  lines.push("");
66445
66838
  }
66446
66839
  if (everythingInSync) {
66447
- const qualifier = secretsChecked ? "" : ` ${import_picocolors29.default.dim("(secrets not checked)")}`;
66448
- lines.push(` ${import_picocolors29.default.green("✓")} everything is in sync${qualifier}`);
66840
+ if (secretsChecked) {
66841
+ lines.push(` ${import_picocolors29.default.green("✓")} everything is in sync`);
66842
+ } else {
66843
+ lines.push(` ${import_picocolors29.default.yellow("?")} cannot confirm sync — secrets were not compared`);
66844
+ lines.push(` ${import_picocolors29.default.dim('everything else came back clean; the reason is under "secrets" above')}`);
66845
+ process.exitCode = 1;
66846
+ }
66449
66847
  lines.push("");
66450
66848
  console.log(lines.join(`
66451
66849
  `));
66452
66850
  return;
66453
66851
  }
66852
+ if (!secretsChecked)
66853
+ process.exitCode = 1;
66454
66854
  if (toPush.length) {
66455
66855
  lines.push(` ${import_picocolors29.default.bold("changes to push")} ${import_picocolors29.default.dim(`(${toPush.length})`)}`);
66456
66856
  for (const r2 of toPush)
@@ -66485,13 +66885,55 @@ async function runAgentStatus(cwd2, args = {}) {
66485
66885
  for (const slug of evalReport.cloudModified) {
66486
66886
  lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— edited in the cloud; pushing would overwrite that edit")}`);
66487
66887
  }
66888
+ for (const slug of evalReport.staleArchives) {
66889
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— deleted here, edited in the cloud since; pushing would archive that edit")}`);
66890
+ }
66488
66891
  lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("first")}`);
66489
66892
  lines.push("");
66490
- } else if (evalReport.unseen.length) {
66491
- lines.push(` ${import_picocolors29.default.bold("evals only in the cloud")} ${import_picocolors29.default.dim(`(${evalReport.unseen.length})`)}`);
66893
+ } else if (evalReport.unseen.length || evalReport.cloudModified.length) {
66894
+ const total = evalReport.unseen.length + evalReport.cloudModified.length;
66895
+ lines.push(` ${import_picocolors29.default.bold("evals this manifest does not manage")} ${import_picocolors29.default.dim(`(${total})`)}`);
66492
66896
  for (const slug of evalReport.unseen) {
66493
- lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— left untouched by push; pull to manage it here")}`);
66897
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— only in the cloud; left untouched by push")}`);
66898
+ }
66899
+ for (const slug of evalReport.cloudModified) {
66900
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(slug)} ${import_picocolors29.default.dim("— edited in the cloud; left untouched by push")}`);
66901
+ }
66902
+ lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("to manage them from here")}`);
66903
+ lines.push("");
66904
+ }
66905
+ const pbLabel = (slug) => playbookLabels([slug], cloud.components)[0] ?? slug;
66906
+ if (playbookReport.archive.length) {
66907
+ lines.push(` ${import_picocolors29.default.bold("playbooks a push would archive")} ${import_picocolors29.default.dim(`(${playbookReport.archive.length})`)}`);
66908
+ for (const slug of playbookReport.archive) {
66909
+ lines.push(` ${import_picocolors29.default.yellow("⨯")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— in the cloud, not in this manifest")}`);
66910
+ }
66911
+ lines.push(` ${import_picocolors29.default.dim("archived, not deleted: re-adding the playbook restores it")}`);
66912
+ lines.push("");
66913
+ }
66914
+ if (playbookReport.pushBlocked) {
66915
+ lines.push(` ${import_picocolors29.default.bold(import_picocolors29.default.red("playbooks: push blocked"))}`);
66916
+ for (const slug of playbookReport.unseen) {
66917
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— only in the cloud; pushing would archive it")}`);
66918
+ }
66919
+ for (const slug of playbookReport.cloudModified) {
66920
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— edited in the cloud; pushing would overwrite that edit")}`);
66921
+ }
66922
+ for (const slug of playbookReport.staleArchives) {
66923
+ lines.push(` ${import_picocolors29.default.red("!")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— deleted here, edited in the cloud since; pushing would archive that edit")}`);
66924
+ }
66925
+ lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("first")}`);
66926
+ lines.push("");
66927
+ } else if (playbookReport.unseen.length || playbookReport.cloudModified.length) {
66928
+ const total = playbookReport.unseen.length + playbookReport.cloudModified.length;
66929
+ lines.push(` ${import_picocolors29.default.bold("playbooks this manifest does not manage")} ${import_picocolors29.default.dim(`(${total})`)}`);
66930
+ for (const slug of playbookReport.unseen) {
66931
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— only in the cloud; left untouched by push")}`);
66494
66932
  }
66933
+ for (const slug of playbookReport.cloudModified) {
66934
+ lines.push(` ${import_picocolors29.default.cyan("←")} ${import_picocolors29.default.bold(pbLabel(slug))} ${import_picocolors29.default.dim("— edited in the cloud; left untouched by push")}`);
66935
+ }
66936
+ lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("to manage them from here")}`);
66495
66937
  lines.push("");
66496
66938
  }
66497
66939
  lines.push(` ${import_picocolors29.default.dim("run")} ${import_picocolors29.default.cyan("brainbase agent pull")} ${import_picocolors29.default.dim("to apply cloud changes,")} ${import_picocolors29.default.cyan("brainbase agent push")} ${import_picocolors29.default.dim("to send yours")}`);
@@ -66499,6 +66941,25 @@ async function runAgentStatus(cwd2, args = {}) {
66499
66941
  console.log(lines.join(`
66500
66942
  `));
66501
66943
  }
66944
+ function reconcileReport(decision) {
66945
+ return {
66946
+ archive: decision.kind === "reconcile" ? decision.archives : [],
66947
+ unseen: decision.kind === "reconcile" ? [] : decision.unseen,
66948
+ cloudModified: decision.kind === "reconcile" ? [] : decision.cloudModified,
66949
+ staleArchives: decision.kind === "blocked" ? decision.staleArchives : [],
66950
+ pushBlocked: decision.kind === "blocked"
66951
+ };
66952
+ }
66953
+ function conflictSources(entry) {
66954
+ const names = (entry.orchestration_names ?? []).filter((name) => name);
66955
+ if (names.length > 1) {
66956
+ return `in ${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
66957
+ }
66958
+ if (names.length === 1)
66959
+ return `in ${names[0]}`;
66960
+ const count = entry.orchestration_ids?.length ?? 0;
66961
+ return count > 0 ? `in ${count} orchestrations` : "in its orchestrations";
66962
+ }
66502
66963
  function emitJson(report) {
66503
66964
  console.log(JSON.stringify(report, null, 2));
66504
66965
  }
@@ -66515,6 +66976,9 @@ function describeSecretsFailure(err) {
66515
66976
  if (remote && err.status === 401) {
66516
66977
  return "could not fetch secrets: your session is invalid — run `brainbase login`";
66517
66978
  }
66979
+ if (remote && err.status === 404) {
66980
+ return "could not fetch secrets: the control plane answered 404 — no secrets " + "route on this deployment, no access to this agent, or the agent is " + "pinned to a benchmark thread";
66981
+ }
66518
66982
  const where = remote ? "could not fetch secrets from the control plane" : `could not read ${path81.join(LINK_DIR, SECRETS_FILE)}`;
66519
66983
  const detail = oneLine(err instanceof Error ? err.message : typeof err === "string" ? err : "");
66520
66984
  return detail ? `${where}: ${detail}` : where;
@@ -66920,7 +67384,7 @@ async function runAgentCreate(cwd2, args) {
66920
67384
  if (!manifest)
66921
67385
  return;
66922
67386
  if (manifest.id) {
66923
- f2.warn(`This folder already belongs to an agent — ${import_picocolors32.default.bold(manifest.agent.name)} (${import_picocolors32.default.dim(manifest.id)}).`);
67387
+ failWarn(`This folder already belongs to an agent — ${import_picocolors32.default.bold(manifest.agent.name)} (${import_picocolors32.default.dim(manifest.id)}).`);
66924
67388
  f2.info(`If you want to detach it, run ${import_picocolors32.default.cyan("brainbase unlink")} first; or move to a different directory.`);
66925
67389
  return;
66926
67390
  }
@@ -66974,9 +67438,9 @@ async function runAgentCreate(cwd2, args) {
66974
67438
  const body = resolveEntrypoint(cwd2, manifest);
66975
67439
  if (body === null) {
66976
67440
  if (manifest.entrypoint.file) {
66977
- f2.error(`Entrypoint file ${import_picocolors32.default.bold(manifest.entrypoint.file)} not found.`);
67441
+ fail(`Entrypoint file ${import_picocolors32.default.bold(manifest.entrypoint.file)} not found.`);
66978
67442
  } else {
66979
- f2.error("Entrypoint block is empty.");
67443
+ fail("Entrypoint block is empty.");
66980
67444
  }
66981
67445
  return;
66982
67446
  }
@@ -67000,7 +67464,6 @@ async function runAgentCreate(cwd2, args) {
67000
67464
  } catch (err) {
67001
67465
  createSpinner.stop("Failed.");
67002
67466
  handleApiError4(err);
67003
- process.exitCode = 1;
67004
67467
  return;
67005
67468
  }
67006
67469
  const machineConfigMissing = manifest.machine_kind !== undefined && agent.machine_kind !== manifest.machine_kind;
@@ -67097,10 +67560,12 @@ async function runAgentCreate(cwd2, args) {
67097
67560
  manifest = readManifest(cwd2);
67098
67561
  let updatedCloud = null;
67099
67562
  const hasContent = !!manifest.instructions || manifest.skills.length > 0 || (manifest.mcp ?? []).length > 0 || (manifest.playbooks ?? []).length > 0 || (manifest.evals ?? []).length > 0;
67563
+ let contentUploadFailed = false;
67100
67564
  if (hasContent) {
67101
67565
  const outgoing = await buildOutgoingComponents(cwd2, manifest, null);
67102
67566
  if (outgoing === null) {
67103
- f2.warn(`Agent created, but local content wasn't uploaded. Fix the issue above and run ${import_picocolors32.default.cyan("brainbase agent push")}.`);
67567
+ contentUploadFailed = true;
67568
+ failWarn(`Agent created, but local content wasn't uploaded. Fix the issue above and run ${import_picocolors32.default.cyan("brainbase agent push")}.`);
67104
67569
  } else if (outgoing.length > 0) {
67105
67570
  const pushSpinner = de();
67106
67571
  pushSpinner.start("Pushing local content…");
@@ -67114,10 +67579,11 @@ async function runAgentCreate(cwd2, args) {
67114
67579
  pushSpinner.stop(`Pushed at revision ${updatedCloud.revision}.`);
67115
67580
  } catch (err) {
67116
67581
  pushSpinner.stop("Failed.");
67582
+ contentUploadFailed = true;
67117
67583
  if (err instanceof ApiError) {
67118
- f2.warn(`Content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67584
+ failWarn(`Agent created and this folder linked, but the content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67119
67585
  } else {
67120
- f2.warn(`Content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67586
+ failWarn(`Agent created and this folder linked, but the content upload failed: ${err.message}. Run ${import_picocolors32.default.cyan("brainbase agent push")} to retry.`);
67121
67587
  }
67122
67588
  }
67123
67589
  }
@@ -67164,15 +67630,18 @@ async function runAgentCreate(cwd2, args) {
67164
67630
  }
67165
67631
  };
67166
67632
  writeSyncState(cwd2, state);
67167
- $e(`Created ${import_picocolors32.default.bold(agent.name)} and linked this folder.`);
67633
+ $e(contentUploadFailed ? `Created ${import_picocolors32.default.bold(agent.name)} and linked this folder, without its local content.` : `Created ${import_picocolors32.default.bold(agent.name)} and linked this folder.`);
67168
67634
  await showResultCard({
67169
- title: "CREATED",
67170
- tone: "ok",
67635
+ title: contentUploadFailed ? "CREATED (NO CONTENT)" : "CREATED",
67636
+ tone: contentUploadFailed ? "warn" : "ok",
67171
67637
  subtitle: link2.tagline ? `${link2.name} — ${link2.tagline}` : link2.name,
67172
67638
  meta: [
67173
67639
  ["slug", link2.slug],
67174
67640
  ["agent", link2.agent_id],
67175
- ...link2.url ? [["url", link2.url]] : []
67641
+ ...link2.url ? [["url", link2.url]] : [],
67642
+ ...contentUploadFailed ? [
67643
+ ["content", "not uploaded — run `brainbase agent push`"]
67644
+ ] : []
67176
67645
  ]
67177
67646
  });
67178
67647
  console.log();
@@ -67239,12 +67708,12 @@ async function pickHarness2(cwd2) {
67239
67708
  function handleApiError4(err) {
67240
67709
  if (err instanceof ApiError) {
67241
67710
  if (err.status === 401) {
67242
- f2.error("Your session is invalid. Run `brainbase login` and try again.");
67711
+ fail("Your session is invalid. Run `brainbase login` and try again.");
67243
67712
  } else {
67244
- f2.error(err.message);
67713
+ fail(err.message);
67245
67714
  }
67246
67715
  } else {
67247
- f2.error(err.message);
67716
+ fail(err.message);
67248
67717
  }
67249
67718
  $e("Aborted.");
67250
67719
  }
@@ -67335,12 +67804,18 @@ async function runAgentList(args) {
67335
67804
  allowPrompt: !args.json,
67336
67805
  announce: !args.json
67337
67806
  });
67338
- const agents = await api.listAgents(org.id, team.id);
67807
+ const { agents, complete, warning } = await api.listAgents(org.id, team.id);
67808
+ if (!complete && warning)
67809
+ console.error(warning);
67339
67810
  if (args.json) {
67340
67811
  console.log(JSON.stringify(agents, null, 2));
67341
67812
  return;
67342
67813
  }
67343
- console.log(formatAgentList(agents, { orgName: org.name, teamName: team.name }));
67814
+ console.log(formatAgentList(agents, {
67815
+ orgName: org.name,
67816
+ teamName: team.name,
67817
+ complete
67818
+ }));
67344
67819
  }
67345
67820
  function formatAgentList(agents, labels) {
67346
67821
  const lines = [""];
@@ -67349,6 +67824,8 @@ function formatAgentList(agents, labels) {
67349
67824
  return lines.join(`
67350
67825
  `);
67351
67826
  }
67827
+ const count = `${agents.length} agent${agents.length === 1 ? "" : "s"}`;
67828
+ lines.push(` ${import_picocolors34.default.dim(labels.complete ? `${count} in ${labels.orgName} → ${labels.teamName}` : `${count} in ${labels.orgName} → ${labels.teamName} (may be incomplete)`)}`, "");
67352
67829
  for (const agent of agents) {
67353
67830
  lines.push(` ${import_picocolors34.default.bold(agent.name)} ${import_picocolors34.default.dim(agent.slug)}`);
67354
67831
  if (agent.tagline)
@@ -67738,7 +68215,10 @@ async function disconnect(cwd2, target, args, json) {
67738
68215
  if (!link2) {
67739
68216
  throw new Error("This folder is not linked to any agent. Run `brainbase link` first.");
67740
68217
  }
67741
- if (!json && !autoProceed(args.yes)) {
68218
+ if (!json && !autoProceedDestructive(args.yes, {
68219
+ action: `Disconnecting ${target} from ${link2.name}`,
68220
+ flagHint: "Pass --yes to disconnect without a prompt."
68221
+ })) {
67742
68222
  const ok = ensureNotCancelled(await se({ message: `Disconnect ${target} from ${link2.name}?` }));
67743
68223
  if (!ok) {
67744
68224
  f2.info("Nothing changed.");
@@ -68281,13 +68761,26 @@ var SyncedEdgeSchema = exports_external.object({
68281
68761
  description: exports_external.string().default(""),
68282
68762
  payload_schema: exports_external.record(exports_external.unknown()).default({})
68283
68763
  });
68764
+ var SyncedScheduleTriggerSchema = exports_external.object({
68765
+ node_id: exports_external.string(),
68766
+ hash: exports_external.string()
68767
+ });
68768
+ var SyncedOrchestrationMetaSchema = exports_external.object({
68769
+ name: exports_external.string().optional(),
68770
+ description: exports_external.string().optional(),
68771
+ icon: exports_external.string().optional(),
68772
+ icon_color: exports_external.string().optional(),
68773
+ credit_limit: exports_external.number().optional()
68774
+ });
68284
68775
  var OrchestrationSyncStateSchema = exports_external.object({
68285
68776
  schemaVersion: exports_external.literal(1),
68286
68777
  orchestration_id: exports_external.string(),
68287
68778
  revision: exports_external.number(),
68288
68779
  synced_at: exports_external.string(),
68289
68780
  members: exports_external.array(SyncedMemberSchema),
68290
- edges: exports_external.array(SyncedEdgeSchema)
68781
+ edges: exports_external.array(SyncedEdgeSchema),
68782
+ triggers: exports_external.array(SyncedScheduleTriggerSchema).optional(),
68783
+ meta: SyncedOrchestrationMetaSchema.optional()
68291
68784
  });
68292
68785
  function orchLinkPath(cwd2) {
68293
68786
  return path85.join(cwd2, LINK_DIR, ORCH_LINK_FILE);
@@ -68629,6 +69122,158 @@ function formatScheduleTriggerLabel(input) {
68629
69122
  return `schedule ${input.nodeId} (${cron}, ${state})${targetText}`;
68630
69123
  }
68631
69124
 
69125
+ // src/core/orchestration-reconcile.ts
69126
+ import crypto5 from "node:crypto";
69127
+ var SCHEDULE_TRIGGER = "schedule-trigger";
69128
+ function stableJson(value) {
69129
+ if (value == null || typeof value !== "object")
69130
+ return JSON.stringify(value);
69131
+ if (Array.isArray(value))
69132
+ return `[${value.map(stableJson).join(",")}]`;
69133
+ const obj = value;
69134
+ return `{${Object.keys(obj).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson(obj[key2])}`).join(",")}}`;
69135
+ }
69136
+ function scheduleTriggerHash(t) {
69137
+ const canonical = {
69138
+ is_active: t.is_active,
69139
+ config: normalizeScheduleTriggerConfig(t.config),
69140
+ targets: t.targets.map((e2) => ({
69141
+ agent: e2.agent,
69142
+ description: e2.description,
69143
+ payload_schema: e2.payload_schema
69144
+ })).sort((a3, b4) => stableJson(a3) < stableJson(b4) ? -1 : 1)
69145
+ };
69146
+ return crypto5.createHash("sha256").update(stableJson(canonical)).digest("hex");
69147
+ }
69148
+ function scheduleTriggerLabel(t) {
69149
+ return formatScheduleTriggerLabel({
69150
+ nodeId: t.node_id,
69151
+ isActive: t.is_active,
69152
+ config: t.config,
69153
+ targets: t.targets.map((e2) => e2.agent)
69154
+ });
69155
+ }
69156
+ function cloudScheduleTriggers(cloud, slugForAgent) {
69157
+ return (cloud.triggers ?? []).filter((t) => t.trigger_type === "schedule").map((t) => ({
69158
+ node_id: t.node_id,
69159
+ is_active: t.is_active ?? false,
69160
+ config: t.config ?? {},
69161
+ targets: t.edges.map((e2) => ({
69162
+ agent: slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id),
69163
+ description: e2.description ?? "",
69164
+ payload_schema: e2.payload_schema ?? {}
69165
+ }))
69166
+ }));
69167
+ }
69168
+ function manifestScheduleTriggers(manifest) {
69169
+ return (manifest?.triggers ?? []).filter((t) => t.type === "schedule").map((t) => ({
69170
+ node_id: t.node_id,
69171
+ is_active: t.is_active ?? false,
69172
+ config: t.config ?? {},
69173
+ targets: t.to.map((e2) => ({
69174
+ agent: e2.agent,
69175
+ description: e2.description ?? "",
69176
+ payload_schema: e2.payload_schema ?? {}
69177
+ }))
69178
+ }));
69179
+ }
69180
+ function decideScheduleTriggerReconcile(input) {
69181
+ const rows = threeWayDiff({
69182
+ local: input.local.map((t) => ({
69183
+ type: SCHEDULE_TRIGGER,
69184
+ slug: t.node_id,
69185
+ hash: scheduleTriggerHash(t)
69186
+ })),
69187
+ lock: (input.baseline ?? []).map((t) => ({
69188
+ type: SCHEDULE_TRIGGER,
69189
+ slug: t.node_id,
69190
+ hash: t.hash,
69191
+ installedPaths: []
69192
+ })),
69193
+ cloud: input.cloud.map((t) => ({
69194
+ type: SCHEDULE_TRIGGER,
69195
+ slug: t.node_id,
69196
+ hash: scheduleTriggerHash(t),
69197
+ files: []
69198
+ }))
69199
+ });
69200
+ return decideCollectionReconcile({
69201
+ rows,
69202
+ type: SCHEDULE_TRIGGER,
69203
+ claims: input.local.length > 0,
69204
+ force: input.force
69205
+ });
69206
+ }
69207
+ function syncedScheduleTriggers(triggers) {
69208
+ return triggers.map((t) => ({
69209
+ node_id: t.node_id,
69210
+ hash: scheduleTriggerHash(t)
69211
+ }));
69212
+ }
69213
+ var ORCH_META_FIELDS = [
69214
+ "name",
69215
+ "description",
69216
+ "icon",
69217
+ "icon_color",
69218
+ "credit_limit"
69219
+ ];
69220
+ function metaText(value) {
69221
+ if (value === undefined)
69222
+ return;
69223
+ return value == null ? "" : String(value);
69224
+ }
69225
+ function decideOrchestrationMeta(input) {
69226
+ const meta = input.manifest?.orchestration;
69227
+ if (!meta)
69228
+ return [];
69229
+ const out = [];
69230
+ for (const field of ORCH_META_FIELDS) {
69231
+ const local = metaText(meta[field]);
69232
+ const cloud = metaText(input.cloud[field]) ?? "";
69233
+ if (local === undefined)
69234
+ continue;
69235
+ if (local === cloud)
69236
+ continue;
69237
+ const base2 = input.baseline === undefined ? undefined : metaText(input.baseline[field]) ?? "";
69238
+ let kind;
69239
+ if (base2 === undefined) {
69240
+ kind = "conflict";
69241
+ } else if (local === base2) {
69242
+ kind = "keep";
69243
+ } else if (cloud === base2) {
69244
+ kind = "push";
69245
+ } else {
69246
+ kind = "conflict";
69247
+ }
69248
+ if (kind === "conflict" && input.force)
69249
+ kind = "push";
69250
+ out.push({ field, kind, local, cloud });
69251
+ }
69252
+ return out;
69253
+ }
69254
+ function nextOrchestrationMeta(input) {
69255
+ const kept = new Set(input.decisions.filter((d3) => d3.kind === "keep").map((d3) => d3.field));
69256
+ const out = {};
69257
+ for (const field of ORCH_META_FIELDS) {
69258
+ const value = kept.has(field) ? input.previous?.[field] : input.applied[field];
69259
+ if (value == null)
69260
+ continue;
69261
+ if (field === "credit_limit") {
69262
+ out.credit_limit = typeof value === "number" ? value : Number(value);
69263
+ } else {
69264
+ out[field] = String(value);
69265
+ }
69266
+ }
69267
+ return out;
69268
+ }
69269
+ function syncedOrchestrationMeta(cloud) {
69270
+ return nextOrchestrationMeta({
69271
+ applied: cloud,
69272
+ decisions: [],
69273
+ previous: undefined
69274
+ });
69275
+ }
69276
+
68632
69277
  // src/cli/orchestration-pull.ts
68633
69278
  function triggersForManifest(triggers, slugFor) {
68634
69279
  return triggers.map((trigger) => {
@@ -68656,7 +69301,7 @@ async function runOrchestrationPull(cwd2, args) {
68656
69301
  } else if (args.orchestrationId) {
68657
69302
  orchId = args.orchestrationId;
68658
69303
  } else {
68659
- f2.warn("This folder is not linked to any orchestration.");
69304
+ failWarn("This folder is not linked to any orchestration.");
68660
69305
  f2.info(`Run ${import_picocolors42.default.cyan("brainbase orchestration pull <id>")} with an orchestration id,
68661
69306
  or ${import_picocolors42.default.cyan("brainbase orchestration list")} to find one.`);
68662
69307
  return;
@@ -68714,13 +69359,15 @@ async function runOrchestrationPull(cwd2, args) {
68714
69359
  const fallbackHarness = args.harness ?? "claude-code";
68715
69360
  fs78.mkdirSync(cwd2, { recursive: true });
68716
69361
  if (hasOrchManifest(cwd2) && existingLink && existingLink.orchestration_id !== orchId) {
68717
- f2.error(`This folder is linked to orchestration ${existingLink.orchestration_id}, not ${orchId}. Move to a fresh directory or unlink first.`);
69362
+ fail(`This folder is linked to orchestration ${existingLink.orchestration_id}, not ${orchId}. Move to a fresh directory or unlink first.`);
68718
69363
  return;
68719
69364
  }
68720
69365
  const installedMembers = [];
69366
+ const missingMembers = [];
68721
69367
  for (const m3 of cloud.members) {
68722
69368
  if (!m3.manifest) {
68723
- f2.warn(`Skipping member ${m3.slug}: server did not return a manifest.`);
69369
+ missingMembers.push(m3.slug);
69370
+ failWarn(`Skipping member ${m3.slug}: server did not return a manifest.`);
68724
69371
  continue;
68725
69372
  }
68726
69373
  const slug = slugFor(m3.agent_id);
@@ -68755,7 +69402,8 @@ async function runOrchestrationPull(cwd2, args) {
68755
69402
  });
68756
69403
  } catch (err) {
68757
69404
  memberSp.stop(`Failed to install ${slug}.`);
68758
- f2.error(err.message);
69405
+ missingMembers.push(slug);
69406
+ fail(err.message);
68759
69407
  }
68760
69408
  }
68761
69409
  const manifestTriggers = triggersForManifest(cloud.triggers ?? [], slugFor);
@@ -68806,8 +69454,15 @@ async function runOrchestrationPull(cwd2, args) {
68806
69454
  to_slug: slugFor(e2.to_agent_id),
68807
69455
  description: e2.description ?? "",
68808
69456
  payload_schema: e2.payload_schema ?? {}
68809
- }))
69457
+ })),
69458
+ triggers: syncedScheduleTriggers(cloudScheduleTriggers(cloud, (agentId, fallback) => slugFor(agentId) || fallback)),
69459
+ meta: syncedOrchestrationMeta(cloud)
68810
69460
  });
69461
+ if (missingMembers.length > 0) {
69462
+ f2.warn(`${installedMembers.length} of ${cloud.members.length} members landed in ${path87.basename(cwd2)}/ and the orchestration is linked, but ${missingMembers.length === 1 ? "this member is" : "these members are"} missing: ${missingMembers.map((s3) => import_picocolors42.default.bold(s3)).join(", ")}. ${import_picocolors42.default.cyan("brainbase orchestration push")} will refuse until ${missingMembers.length === 1 ? "it" : "they"} ${missingMembers.length === 1 ? "is" : "are"} pulled, so re-run this after fixing the cause.`);
69463
+ $e(`Pulled ${cloud.name} at revision ${cloud.revision} into ${path87.basename(cwd2)}/ ${import_picocolors42.default.dim(`(${installedMembers.length}/${cloud.members.length} members — incomplete)`)}.`);
69464
+ return;
69465
+ }
68811
69466
  $e(`Pulled ${cloud.name} at revision ${cloud.revision} into ${path87.basename(cwd2)}/ ${import_picocolors42.default.dim(`(${installedMembers.length}/${cloud.members.length} members)`)}.`);
68812
69467
  }
68813
69468
  function handleApiError5(err) {
@@ -68890,6 +69545,11 @@ function buildOrchestrationGraphPayload(manifest, slugToAgentId) {
68890
69545
  }
68891
69546
 
68892
69547
  // src/cli/orchestration-push.ts
69548
+ function joinList(items) {
69549
+ if (items.length <= 1)
69550
+ return items[0] ?? "";
69551
+ return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
69552
+ }
68893
69553
  function findUnpushableMembers(cwd2, members) {
68894
69554
  const blocked = [];
68895
69555
  for (const m3 of members) {
@@ -68922,12 +69582,12 @@ async function runOrchestrationPush(cwd2, args) {
68922
69582
  banner("orchestration push — recursively push each member, then update the graph");
68923
69583
  const link2 = readOrchLink(cwd2);
68924
69584
  if (!link2) {
68925
- f2.warn("This folder is not linked to any orchestration.");
69585
+ failWarn("This folder is not linked to any orchestration.");
68926
69586
  f2.info(`Run ${import_picocolors43.default.cyan("brainbase orchestration pull <id>")} first.`);
68927
69587
  return;
68928
69588
  }
68929
69589
  if (!hasOrchManifest(cwd2)) {
68930
- f2.warn(`No ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} here.`);
69590
+ failWarn(`No ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} here.`);
68931
69591
  f2.info(`Run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to materialise the manifest before pushing.`);
68932
69592
  return;
68933
69593
  }
@@ -68972,6 +69632,95 @@ async function runOrchestrationPush(cwd2, args) {
68972
69632
  return;
68973
69633
  }
68974
69634
  }
69635
+ const lock = readOrchSyncState(cwd2);
69636
+ const fetchSpinner = de();
69637
+ fetchSpinner.start("Fetching cloud state…");
69638
+ let cloud;
69639
+ try {
69640
+ cloud = await api.getOrchestration(link2.orchestration_id);
69641
+ fetchSpinner.stop(`Cloud revision ${cloud.revision}.`);
69642
+ } catch (err) {
69643
+ fetchSpinner.stop("Failed.");
69644
+ handleApiError6(err);
69645
+ process.exitCode = 1;
69646
+ return;
69647
+ }
69648
+ const agentIdToSlug = new Map([...slugToAgentId.entries()].map(([slug, id]) => [id, slug]));
69649
+ const slugForAgent = (agentId, fallback) => agentIdToSlug.get(agentId) ?? fallback;
69650
+ const localTriggers = manifestScheduleTriggers(manifest);
69651
+ const cloudTriggers = cloudScheduleTriggers(cloud, slugForAgent);
69652
+ const triggerDecision = decideScheduleTriggerReconcile({
69653
+ local: localTriggers,
69654
+ cloud: cloudTriggers,
69655
+ baseline: lock?.triggers,
69656
+ force: !!args.force
69657
+ });
69658
+ const triggerByNode = new Map([...cloudTriggers, ...localTriggers].map((t) => [t.node_id, t]));
69659
+ const labelFor = (nodeId) => {
69660
+ const t = triggerByNode.get(nodeId);
69661
+ return t ? scheduleTriggerLabel(t) : nodeId;
69662
+ };
69663
+ const metaDecisions = decideOrchestrationMeta({
69664
+ manifest,
69665
+ cloud,
69666
+ baseline: lock?.meta,
69667
+ force: !!args.force
69668
+ });
69669
+ const metaConflicts = metaDecisions.filter((d3) => d3.kind === "conflict");
69670
+ const metaOverwrites = metaDecisions.filter((d3) => d3.kind === "push");
69671
+ const metaKept = metaDecisions.filter((d3) => d3.kind === "keep");
69672
+ const blockedTriggers = triggerDecision.kind === "blocked" ? triggerDecision : null;
69673
+ if (blockedTriggers || metaConflicts.length > 0) {
69674
+ const pullTargets = [];
69675
+ const forceEffects = [];
69676
+ if (blockedTriggers) {
69677
+ const { unseen, cloudModified, staleArchives } = blockedTriggers;
69678
+ f2.error(unseen.length > 0 ? "Cannot push: the cloud has schedule triggers this folder has never seen." : "Cannot push: schedule triggers changed both locally and in the cloud.");
69679
+ for (const nodeId of unseen) {
69680
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(labelFor(nodeId))} ${import_picocolors43.default.dim("(only in the cloud — pushing would delete it)")}`);
69681
+ }
69682
+ for (const nodeId of cloudModified) {
69683
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(labelFor(nodeId))} ${import_picocolors43.default.dim("(edited in the cloud — pushing would overwrite that edit)")}`);
69684
+ }
69685
+ for (const nodeId of staleArchives) {
69686
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(labelFor(nodeId))} ${import_picocolors43.default.dim("(deleted here, edited in the cloud since — pushing would delete that edit)")}`);
69687
+ }
69688
+ const total = unseen.length + cloudModified.length + staleArchives.length;
69689
+ pullTargets.push(`${total} schedule trigger${total === 1 ? "" : "s"}`);
69690
+ forceEffects.push("make your local schedule triggers authoritative (deleting the cloud ones above)");
69691
+ }
69692
+ if (metaConflicts.length > 0) {
69693
+ f2.error("Cannot push: orchestration metadata changed both locally and in the cloud.");
69694
+ for (const d3 of metaConflicts) {
69695
+ console.error(` ${import_picocolors43.default.red("!")} ${import_picocolors43.default.bold(d3.field)} ${import_picocolors43.default.dim(`(local "${d3.local}" vs cloud "${d3.cloud}")`)}`);
69696
+ }
69697
+ if (!lock?.meta) {
69698
+ f2.info("This checkout has no metadata baseline, so the CLI cannot tell which side changed.");
69699
+ }
69700
+ const fields = metaConflicts.map((d3) => d3.field);
69701
+ pullTargets.push(`${fields.length} metadata field${fields.length === 1 ? "" : "s"}`);
69702
+ forceEffects.push(`overwrite the cloud's ${joinList(fields)} with the value${fields.length === 1 ? "" : "s"} in ${ORCH_MANIFEST_FILE}`);
69703
+ }
69704
+ f2.info(`Run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to bring the cloud's ${joinList(pullTargets)} into ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)}, then push.`);
69705
+ f2.info(`Or ${import_picocolors43.default.cyan("brainbase orchestration push --force")} to ${joinList(forceEffects)}.${forceEffects.length > 1 ? " One flag, both of those — it settles everything listed above, not whichever conflict you had in mind." : ""}`);
69706
+ process.exitCode = 1;
69707
+ return;
69708
+ }
69709
+ if (triggerDecision.kind === "skip") {
69710
+ graph.triggers = cloudTriggers.map((t) => ({
69711
+ node_id: t.node_id,
69712
+ type: "schedule",
69713
+ is_active: t.is_active,
69714
+ config: t.config,
69715
+ edges: t.targets.map((e2) => ({
69716
+ to_agent_id: slugToAgentId.get(e2.agent) ?? e2.agent,
69717
+ description: e2.description,
69718
+ payload_schema: e2.payload_schema
69719
+ }))
69720
+ }));
69721
+ f2.warn(`${cloudTriggers.length} cloud schedule trigger${cloudTriggers.length === 1 ? "" : "s"} ${cloudTriggers.length === 1 ? "is" : "are"} not in ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} and will be left untouched. Run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to manage ${cloudTriggers.length === 1 ? "it" : "them"} from here.`);
69722
+ }
69723
+ const triggerDeletes = triggerDecision.kind === "reconcile" ? triggerDecision.archives : [];
68975
69724
  const plan = [""];
68976
69725
  plan.push(` ${import_picocolors43.default.bold(link2.name)} ${import_picocolors43.default.dim(`(${link2.orchestration_id})`)}`);
68977
69726
  plan.push(` ${import_picocolors43.default.dim(`${manifest.members.length} member${manifest.members.length === 1 ? "" : "s"}, ${manifest.edges.length} edge${manifest.edges.length === 1 ? "" : "s"}, ${graph.triggers.length} trigger${graph.triggers.length === 1 ? "" : "s"}`)}`);
@@ -68983,12 +69732,34 @@ async function runOrchestrationPush(cwd2, args) {
68983
69732
  }
68984
69733
  plan.push("");
68985
69734
  }
69735
+ if (triggerDeletes.length) {
69736
+ plan.push(` ${import_picocolors43.default.bold("schedule triggers this push will delete")} ${import_picocolors43.default.dim(`(${triggerDeletes.length})`)}`);
69737
+ for (const nodeId of triggerDeletes) {
69738
+ plan.push(` ${import_picocolors43.default.yellow("⨯")} ${labelFor(nodeId)}`);
69739
+ }
69740
+ plan.push("");
69741
+ }
69742
+ if (metaOverwrites.length) {
69743
+ plan.push(` ${import_picocolors43.default.bold("orchestration metadata this push will overwrite")}`);
69744
+ for (const m3 of metaOverwrites) {
69745
+ plan.push(` ${import_picocolors43.default.yellow("⤒")} ${import_picocolors43.default.bold(m3.field)} ${import_picocolors43.default.dim(`cloud "${m3.cloud}" → local "${m3.local}"`)}`);
69746
+ }
69747
+ plan.push("");
69748
+ }
69749
+ if (metaKept.length) {
69750
+ plan.push(` ${import_picocolors43.default.bold("orchestration metadata changed in the cloud")}`);
69751
+ for (const m3 of metaKept) {
69752
+ plan.push(` ${import_picocolors43.default.cyan("=")} ${import_picocolors43.default.bold(m3.field)} ${import_picocolors43.default.dim(`keeping cloud "${m3.cloud}"; ${import_picocolors43.default.bold(ORCH_MANIFEST_FILE)} still has "${m3.local}"`)}`);
69753
+ }
69754
+ plan.push(` ${import_picocolors43.default.dim(`run ${import_picocolors43.default.cyan("brainbase orchestration pull")} to bring ${metaKept.length === 1 ? "it" : "them"} into the file`)}`);
69755
+ plan.push("");
69756
+ }
68986
69757
  console.log(plan.join(`
68987
69758
  `));
68988
69759
  if (!autoProceed(args.yes)) {
68989
69760
  const ok = await se({
68990
- message: args.graphOnly ? "Push graph (members, edges + triggers) only?" : "Push each member, then update the graph?",
68991
- initialValue: true
69761
+ message: triggerDeletes.length ? `Push, deleting ${triggerDeletes.length} schedule trigger${triggerDeletes.length === 1 ? "" : "s"}?` : args.graphOnly ? "Push graph (members, edges + triggers) only?" : "Push each member, then update the graph?",
69762
+ initialValue: triggerDeletes.length === 0
68992
69763
  });
68993
69764
  if (!ensureNotCancelled(ok)) {
68994
69765
  $e("Aborted.");
@@ -69015,16 +69786,22 @@ async function runOrchestrationPush(cwd2, args) {
69015
69786
  }
69016
69787
  }
69017
69788
  }
69789
+ const keptFields = new Set(metaKept.map((d3) => d3.field));
69790
+ const metaField = (field) => {
69791
+ const value = manifest.orchestration[field];
69792
+ if (value === undefined || keptFields.has(field))
69793
+ return {};
69794
+ return { [field]: value };
69795
+ };
69018
69796
  const sp = de();
69019
69797
  sp.start("Updating orchestration graph…");
69020
- const lock = readOrchSyncState(cwd2);
69021
69798
  try {
69022
69799
  const updated = await api.updateOrchestration(link2.orchestration_id, {
69023
- name: manifest.orchestration.name,
69024
- description: manifest.orchestration.description ?? "",
69025
- icon: manifest.orchestration.icon,
69026
- icon_color: manifest.orchestration.icon_color,
69027
- credit_limit: manifest.orchestration.credit_limit,
69800
+ ...metaField("name"),
69801
+ ...metaField("description"),
69802
+ ...metaField("icon"),
69803
+ ...metaField("icon_color"),
69804
+ ...metaField("credit_limit"),
69028
69805
  members: graph.memberIds,
69029
69806
  edges: graph.edges,
69030
69807
  triggers: graph.triggers,
@@ -69046,7 +69823,13 @@ async function runOrchestrationPush(cwd2, args) {
69046
69823
  to_slug: e2.to_slug ?? e2.to_agent_id,
69047
69824
  description: e2.description ?? "",
69048
69825
  payload_schema: e2.payload_schema ?? {}
69049
- }))
69826
+ })),
69827
+ triggers: triggerDecision.kind === "skip" ? lock?.triggers ?? [] : syncedScheduleTriggers(cloudScheduleTriggers(updated, slugForAgent)),
69828
+ meta: nextOrchestrationMeta({
69829
+ applied: updated,
69830
+ decisions: metaDecisions,
69831
+ previous: lock?.meta
69832
+ })
69050
69833
  });
69051
69834
  $e(`Pushed ${link2.name} at revision ${updated.revision}.`);
69052
69835
  } catch (err) {
@@ -69078,7 +69861,7 @@ async function runOrchestrationStatus(cwd2) {
69078
69861
  banner("orchestration status — what changed locally, remotely, both");
69079
69862
  const link2 = readOrchLink(cwd2);
69080
69863
  if (!link2) {
69081
- f2.warn("This folder is not linked to any orchestration.");
69864
+ failWarn("This folder is not linked to any orchestration.");
69082
69865
  f2.info(`Run ${import_picocolors44.default.cyan("brainbase orchestration pull <id>")} first.`);
69083
69866
  return;
69084
69867
  }
@@ -69126,8 +69909,8 @@ async function runOrchestrationStatus(cwd2) {
69126
69909
  }
69127
69910
  lines.push("");
69128
69911
  }
69129
- const cloudEdgeKey = (e2) => `${slugForAgent(e2.from_agent_id, e2.from_slug ?? e2.from_agent_id)}->${slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id)}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}|${stableJson(e2.settings ?? {})}`;
69130
- const localEdgeKey = (e2) => `${e2.from}->${e2.to}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}|${stableJson(e2.settings ?? {})}`;
69912
+ const cloudEdgeKey = (e2) => `${slugForAgent(e2.from_agent_id, e2.from_slug ?? e2.from_agent_id)}->${slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id)}|${e2.description ?? ""}|${stableJson2(e2.payload_schema ?? {})}|${stableJson2(e2.settings ?? {})}`;
69913
+ const localEdgeKey = (e2) => `${e2.from}->${e2.to}|${e2.description ?? ""}|${stableJson2(e2.payload_schema ?? {})}|${stableJson2(e2.settings ?? {})}`;
69131
69914
  const cloudEdges = new Map;
69132
69915
  for (const e2 of cloud.edges)
69133
69916
  cloudEdges.set(cloudEdgeKey(e2), true);
@@ -69144,46 +69927,74 @@ async function runOrchestrationStatus(cwd2) {
69144
69927
  lines.push(` ${import_picocolors44.default.cyan("← pull")} added on cloud: ${k3}`);
69145
69928
  lines.push("");
69146
69929
  }
69147
- const cloudTriggerKey = (t) => {
69148
- const edges = t.edges.map((e2) => `${slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id)}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}`).sort().join(",");
69149
- return `${t.node_id}|${t.is_active ?? false}|${stableJson(normalizeScheduleTriggerConfig(t.config))}|${edges}`;
69150
- };
69151
- const cloudTriggerLabel = (t) => formatScheduleTriggerLabel({
69152
- nodeId: t.node_id,
69153
- isActive: t.is_active ?? false,
69154
- config: t.config,
69155
- targets: t.edges.map((e2) => slugForAgent(e2.to_agent_id, e2.to_slug ?? e2.to_agent_id))
69930
+ const metaDecisions = decideOrchestrationMeta({
69931
+ manifest: localManifest,
69932
+ cloud,
69933
+ baseline: lock?.meta,
69934
+ force: false
69156
69935
  });
69157
- const localTriggerKey = (t) => {
69158
- const edges = t.to.map((e2) => `${e2.agent}|${e2.description ?? ""}|${stableJson(e2.payload_schema ?? {})}`).sort().join(",");
69159
- return `${t.node_id}|${t.is_active ?? false}|${stableJson(normalizeScheduleTriggerConfig(t.config))}|${edges}`;
69936
+ if (metaDecisions.length) {
69937
+ lines.push(` ${import_picocolors44.default.bold("orchestration metadata")}`);
69938
+ for (const m3 of metaDecisions) {
69939
+ if (m3.kind === "push") {
69940
+ lines.push(` ${import_picocolors44.default.yellow("→ push")} ${import_picocolors44.default.bold(m3.field)}: cloud ${import_picocolors44.default.dim(`"${m3.cloud}"`)} → local ${import_picocolors44.default.dim(`"${m3.local}"`)}`);
69941
+ } else if (m3.kind === "keep") {
69942
+ lines.push(` ${import_picocolors44.default.cyan("← pull")} ${import_picocolors44.default.bold(m3.field)}: changed in the cloud to ${import_picocolors44.default.dim(`"${m3.cloud}"`)}; push leaves it alone ${import_picocolors44.default.dim(`(yaml still has "${m3.local}")`)}`);
69943
+ } else {
69944
+ lines.push(` ${import_picocolors44.default.red("! conflict")} ${import_picocolors44.default.bold(m3.field)}: local ${import_picocolors44.default.dim(`"${m3.local}"`)} vs cloud ${import_picocolors44.default.dim(`"${m3.cloud}"`)} — push refuses until you pull or ${import_picocolors44.default.cyan("--force")}`);
69945
+ }
69946
+ }
69947
+ lines.push(` ${import_picocolors44.default.dim("fields the manifest omits, and fields only the cloud changed, are left alone")}`);
69948
+ lines.push("");
69949
+ }
69950
+ const cloudTriggers = cloudScheduleTriggers(cloud, slugForAgent);
69951
+ const localTriggers = manifestScheduleTriggers(localManifest);
69952
+ const triggerPlan = decideScheduleTriggerReconcile({
69953
+ local: localTriggers,
69954
+ cloud: cloudTriggers,
69955
+ baseline: lock?.triggers,
69956
+ force: false
69957
+ });
69958
+ const triggerByNode = new Map([...cloudTriggers, ...localTriggers].map((t) => [t.node_id, t]));
69959
+ const triggerLabel = (nodeId) => {
69960
+ const t = triggerByNode.get(nodeId);
69961
+ return t ? scheduleTriggerLabel(t) : nodeId;
69160
69962
  };
69161
- const localTriggerLabel = (t) => formatScheduleTriggerLabel({
69162
- nodeId: t.node_id,
69163
- isActive: t.is_active ?? false,
69164
- config: t.config,
69165
- targets: t.to.map((e2) => e2.agent)
69963
+ const localNodes = new Set(localTriggers.map((t) => t.node_id));
69964
+ const cloudByNode = new Map(cloudTriggers.map((t) => [t.node_id, t]));
69965
+ const triggersToPush = localTriggers.filter((t) => {
69966
+ const remote = cloudByNode.get(t.node_id);
69967
+ return !remote || scheduleTriggerHash(remote) !== scheduleTriggerHash(t);
69166
69968
  });
69167
- const cloudTriggers = new Map;
69168
- for (const t of cloud.triggers ?? []) {
69169
- if (t.trigger_type === "schedule") {
69170
- cloudTriggers.set(cloudTriggerKey(t), cloudTriggerLabel(t));
69969
+ const triggersToPull = cloudTriggers.filter((t) => !localNodes.has(t.node_id));
69970
+ const triggerDeletes = triggerPlan.kind === "reconcile" ? triggerPlan.archives : [];
69971
+ if (triggersToPush.length || triggersToPull.length) {
69972
+ lines.push(` ${import_picocolors44.default.bold("schedule triggers")}`);
69973
+ for (const t of triggersToPush) {
69974
+ lines.push(` ${import_picocolors44.default.yellow("→ push")} added/changed in yaml: ${scheduleTriggerLabel(t)}`);
69171
69975
  }
69172
- }
69173
- const localTriggers = new Map;
69174
- for (const t of localManifest?.triggers ?? []) {
69175
- if (t.type === "schedule") {
69176
- localTriggers.set(localTriggerKey(t), localTriggerLabel(t));
69976
+ for (const t of triggersToPull) {
69977
+ const doomed = triggerDeletes.includes(t.node_id);
69978
+ lines.push(doomed ? ` ${import_picocolors44.default.red("⨯ push deletes")} ${scheduleTriggerLabel(t)}` : ` ${import_picocolors44.default.cyan("← pull")} only on cloud: ${scheduleTriggerLabel(t)}`);
69177
69979
  }
69980
+ lines.push("");
69178
69981
  }
69179
- const triggersAdded = [...localTriggers.keys()].filter((k3) => !cloudTriggers.has(k3));
69180
- const triggersRemoved = [...cloudTriggers.keys()].filter((k3) => !localTriggers.has(k3));
69181
- if (triggersAdded.length || triggersRemoved.length) {
69182
- lines.push(` ${import_picocolors44.default.bold("schedule triggers")}`);
69183
- for (const k3 of triggersAdded)
69184
- lines.push(` ${import_picocolors44.default.yellow("→ push")} added/changed in yaml: ${localTriggers.get(k3) ?? k3}`);
69185
- for (const k3 of triggersRemoved)
69186
- lines.push(` ${import_picocolors44.default.cyan("← pull")} added/changed on cloud: ${cloudTriggers.get(k3) ?? k3}`);
69982
+ if (triggerPlan.kind === "blocked") {
69983
+ lines.push(` ${import_picocolors44.default.bold(import_picocolors44.default.red("schedule triggers: push blocked"))}`);
69984
+ for (const nodeId of triggerPlan.unseen) {
69985
+ lines.push(` ${import_picocolors44.default.red("!")} ${triggerLabel(nodeId)} ${import_picocolors44.default.dim("— only in the cloud; pushing would delete it")}`);
69986
+ }
69987
+ for (const nodeId of triggerPlan.cloudModified) {
69988
+ lines.push(` ${import_picocolors44.default.red("!")} ${triggerLabel(nodeId)} ${import_picocolors44.default.dim("— edited in the cloud; pushing would overwrite that edit")}`);
69989
+ }
69990
+ for (const nodeId of triggerPlan.staleArchives) {
69991
+ lines.push(` ${import_picocolors44.default.red("!")} ${triggerLabel(nodeId)} ${import_picocolors44.default.dim("— deleted here, edited in the cloud since; pushing would delete that edit")}`);
69992
+ }
69993
+ lines.push(` ${import_picocolors44.default.dim("run")} ${import_picocolors44.default.cyan("brainbase orchestration pull")} ${import_picocolors44.default.dim("first")}`);
69994
+ lines.push("");
69995
+ } else if (triggerPlan.kind === "skip") {
69996
+ const left = untouchedRows(triggerPlan);
69997
+ lines.push(` ${import_picocolors44.default.dim(`${left.length} cloud schedule trigger${left.length === 1 ? "" : "s"} not managed by this manifest — push leaves ${left.length === 1 ? "it" : "them"} untouched`)}`);
69187
69998
  lines.push("");
69188
69999
  }
69189
70000
  const lockByAgentId = new Map((lock?.members ?? []).map((m3) => [m3.agent_id, m3]));
@@ -69219,7 +70030,7 @@ async function runOrchestrationStatus(cwd2) {
69219
70030
  lines.push(` ${import_picocolors44.default.cyan("← pull")} cloud changed since last pull: ${import_picocolors44.default.dim(`lock ${lock.revision} → cloud ${cloud.revision}`)}`);
69220
70031
  lines.push("");
69221
70032
  }
69222
- if (!membersAdded.length && !membersRemoved.length && !edgesAdded.length && !edgesRemoved.length && !triggersAdded.length && !triggersRemoved.length && !memberDrift.length && !revisionDrift) {
70033
+ if (!membersAdded.length && !membersRemoved.length && !edgesAdded.length && !edgesRemoved.length && !triggersToPush.length && !triggersToPull.length && !metaDecisions.length && !memberDrift.length && !revisionDrift) {
69223
70034
  lines.push(` ${import_picocolors44.default.green("✓")} everything is in sync`);
69224
70035
  lines.push("");
69225
70036
  console.log(lines.join(`
@@ -69231,13 +70042,13 @@ async function runOrchestrationStatus(cwd2) {
69231
70042
  console.log(lines.join(`
69232
70043
  `));
69233
70044
  }
69234
- function stableJson(value) {
70045
+ function stableJson2(value) {
69235
70046
  if (value == null || typeof value !== "object")
69236
70047
  return JSON.stringify(value);
69237
70048
  if (Array.isArray(value))
69238
- return `[${value.map(stableJson).join(",")}]`;
70049
+ return `[${value.map(stableJson2).join(",")}]`;
69239
70050
  const obj = value;
69240
- return `{${Object.keys(obj).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson(obj[key2])}`).join(",")}}`;
70051
+ return `{${Object.keys(obj).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson2(obj[key2])}`).join(",")}}`;
69241
70052
  }
69242
70053
 
69243
70054
  // src/cli/orchestration-list.ts
@@ -69343,7 +70154,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69343
70154
  banner("orchestration add-agent — create a member agent and wire it into the graph");
69344
70155
  const link2 = readOrchLink(cwd2);
69345
70156
  if (!link2 || !hasOrchManifest(cwd2)) {
69346
- f2.warn("This folder is not a linked orchestration.");
70157
+ failWarn("This folder is not a linked orchestration.");
69347
70158
  f2.info(`Run ${import_picocolors46.default.cyan("brainbase orchestration pull <id>")} first.`);
69348
70159
  return;
69349
70160
  }
@@ -69351,7 +70162,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69351
70162
  try {
69352
70163
  manifest = readOrchManifest(cwd2);
69353
70164
  } catch (err) {
69354
- f2.error(err.message);
70165
+ fail(err.message);
69355
70166
  return;
69356
70167
  }
69357
70168
  let name = args.name?.trim();
@@ -69377,7 +70188,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69377
70188
  try {
69378
70189
  payloadSchema = parseEdgeSchema(args.schema);
69379
70190
  } catch (err) {
69380
- f2.error(err.message);
70191
+ fail(err.message);
69381
70192
  return;
69382
70193
  }
69383
70194
  let orgId = args.orgId;
@@ -69394,14 +70205,14 @@ async function runOrchestrationAddAgent(cwd2, args) {
69394
70205
  const resolved = resolveOrgIdForGroup(link2.group_id, orgsWithTeams);
69395
70206
  if (!resolved) {
69396
70207
  sp.stop("Failed.");
69397
- f2.error(`Could not find an org that owns group ${import_picocolors46.default.bold(link2.group_id)}. ` + `Pass ${import_picocolors46.default.cyan("--org <id>")} explicitly.`);
70208
+ fail(`Could not find an org that owns group ${import_picocolors46.default.bold(link2.group_id)}. ` + `Pass ${import_picocolors46.default.cyan("--org <id>")} explicitly.`);
69398
70209
  return;
69399
70210
  }
69400
70211
  orgId = resolved;
69401
70212
  sp.stop("Resolved org.");
69402
70213
  } catch (err) {
69403
70214
  sp.stop("Failed.");
69404
- f2.error(err.message);
70215
+ fail(err.message);
69405
70216
  return;
69406
70217
  }
69407
70218
  }
@@ -69434,7 +70245,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69434
70245
  try {
69435
70246
  updated = mergeMemberAndEdges(manifest, { slug, name, from, to: to2, description, payloadSchema });
69436
70247
  } catch (err) {
69437
- f2.error(err.message);
70248
+ fail(err.message);
69438
70249
  return;
69439
70250
  }
69440
70251
  const dest = memberDir(cwd2, slug);
@@ -69452,7 +70263,7 @@ async function runOrchestrationAddAgent(cwd2, args) {
69452
70263
  try {
69453
70264
  fs79.rmSync(dest, { recursive: true, force: true });
69454
70265
  } catch {}
69455
- f2.error(`Failed to create ${slug}: ${err.message}`);
70266
+ fail(`Failed to create ${slug}: ${err.message}`);
69456
70267
  return;
69457
70268
  }
69458
70269
  writeOrchManifest(cwd2, updated);
@@ -69468,12 +70279,12 @@ var import_picocolors47 = __toESM(require_picocolors(), 1);
69468
70279
  async function runOrchestrationCreate(cwd2, args) {
69469
70280
  banner("orchestration create — claim a brainbase-orchestration.yaml");
69470
70281
  if (readOrchLink(cwd2)) {
69471
- f2.warn("This folder is already linked to an orchestration.");
70282
+ failWarn("This folder is already linked to an orchestration.");
69472
70283
  f2.info(`Run ${import_picocolors47.default.cyan("brainbase orchestration push")} to update it.`);
69473
70284
  return;
69474
70285
  }
69475
70286
  if (!hasOrchManifest(cwd2)) {
69476
- f2.warn(`No ${import_picocolors47.default.bold(ORCH_MANIFEST_FILE)} here.`);
70287
+ failWarn(`No ${import_picocolors47.default.bold(ORCH_MANIFEST_FILE)} here.`);
69477
70288
  f2.info(`Create one, or pull an existing orchestration first.`);
69478
70289
  return;
69479
70290
  }
@@ -69612,7 +70423,8 @@ async function runOrchestration(cwd2, sub, args, opts) {
69612
70423
  case "push":
69613
70424
  await runOrchestrationPush(cwd2, {
69614
70425
  yes: opts.yes,
69615
- graphOnly: opts.graphOnly
70426
+ graphOnly: opts.graphOnly,
70427
+ force: opts.force
69616
70428
  });
69617
70429
  return;
69618
70430
  case "status":
@@ -69664,7 +70476,7 @@ function printHelp3() {
69664
70476
  out.push(` ${import_picocolors48.default.bold("Flags")}`);
69665
70477
  out.push(` ${import_picocolors48.default.dim("--yes, -y")} skip confirmations`);
69666
70478
  out.push(` ${import_picocolors48.default.dim("--harness <id>")} harness for newly-created member folders (default claude-code)`);
69667
- out.push(` ${import_picocolors48.default.dim("--graph-only")} for push: only update members + edges, skip per-member push`);
70479
+ out.push(` ${import_picocolors48.default.dim("--graph-only")} for push: only update members + edges, skip per-member push`, ` ${import_picocolors48.default.dim("--force")} for push: settle every conflict in your file's favour — deletes cloud schedule`, ` triggers this folder has never seen, and overwrites cloud-side edits to`, ` name, description, icon, icon_color and credit_limit`);
69668
70480
  out.push(` ${import_picocolors48.default.dim("--org <id>")} for create/list: org id or slug (CLI vocab — DB teams.id)`);
69669
70481
  out.push(` ${import_picocolors48.default.dim("--team <id>")} for create/list: team id (CLI vocab — DB groups.id)`);
69670
70482
  out.push("");
@@ -70467,7 +71279,10 @@ async function runTokenRevoke(args) {
70467
71279
  }
70468
71280
  const stored = readToken();
70469
71281
  const isLocalToken = Boolean(stored && stored.token.startsWith(target.prefix));
70470
- if (!autoProceed(args.yes)) {
71282
+ if (!autoProceedDestructive(args.yes, {
71283
+ action: `Revoking ${target.name} (${args.id})`,
71284
+ flagHint: "Pass --yes to revoke it without a prompt."
71285
+ })) {
70471
71286
  const ok = await se({
70472
71287
  message: isLocalToken ? `Revoke ${import_picocolors50.default.bold(target.name)} (${args.id})? This is the token this CLI is using, so it will stop working here too.` : `Revoke ${import_picocolors50.default.bold(target.name)} (${args.id})? CIs and machines using it will stop working.`,
70473
71288
  initialValue: false
@@ -77104,10 +77919,10 @@ function createFetchWithInit(baseFetch = fetch, baseInit) {
77104
77919
  }
77105
77920
 
77106
77921
  // node_modules/pkce-challenge/dist/index.node.js
77107
- var crypto5;
77108
- crypto5 = globalThis.crypto?.webcrypto ?? globalThis.crypto ?? import("node:crypto").then((m3) => m3.webcrypto);
77922
+ var crypto6;
77923
+ crypto6 = globalThis.crypto?.webcrypto ?? globalThis.crypto ?? import("node:crypto").then((m3) => m3.webcrypto);
77109
77924
  async function getRandomValues(size2) {
77110
- return (await crypto5).getRandomValues(new Uint8Array(size2));
77925
+ return (await crypto6).getRandomValues(new Uint8Array(size2));
77111
77926
  }
77112
77927
  async function random2(size2) {
77113
77928
  const mask = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
@@ -77127,7 +77942,7 @@ async function generateVerifier(length) {
77127
77942
  return await random2(length);
77128
77943
  }
77129
77944
  async function generateChallenge(code_verifier) {
77130
- const buffer = await (await crypto5).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
77945
+ const buffer = await (await crypto6).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
77131
77946
  return btoa(String.fromCharCode(...new Uint8Array(buffer))).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
77132
77947
  }
77133
77948
  async function pkceChallenge(length) {
@@ -79169,10 +79984,99 @@ async function runMcp(cwd2, sub, _argv, options) {
79169
79984
  }
79170
79985
 
79171
79986
  // src/cli/task.ts
79172
- var import_picocolors52 = __toESM(require_picocolors(), 1);
79987
+ var import_picocolors56 = __toESM(require_picocolors(), 1);
79173
79988
 
79174
79989
  // src/cli/task-create.ts
79990
+ var import_picocolors52 = __toESM(require_picocolors(), 1);
79175
79991
  import { randomUUID as randomUUID2 } from "node:crypto";
79992
+
79993
+ // src/cli/task-status.ts
79994
+ var TERMINAL_TASK_STATUSES = new Set([
79995
+ "success",
79996
+ "fail",
79997
+ "need_more_info"
79998
+ ]);
79999
+ var SUCCESS_STATUS = "success";
80000
+ function isTerminalTaskStatus(status) {
80001
+ return TERMINAL_TASK_STATUSES.has(status);
80002
+ }
80003
+ function exitCodeForTaskStatus(status) {
80004
+ return status === SUCCESS_STATUS ? 0 : 1;
80005
+ }
80006
+ var SIGINT_EXIT_CODE = 130;
80007
+ async function waitForTask(taskId, options) {
80008
+ const stop = new AbortController;
80009
+ const onExternalAbort = () => stop.abort();
80010
+ options.signal?.addEventListener("abort", onExternalAbort, { once: true });
80011
+ if (options.signal?.aborted)
80012
+ stop.abort();
80013
+ const deadlineTimer = options.timeoutMs === undefined ? null : setTimeout(() => stop.abort(), options.timeoutMs);
80014
+ let latest = null;
80015
+ let lastStatus = null;
80016
+ const givenUp = () => options.signal?.aborted ? { kind: "interrupted", task: latest } : { kind: "timeout", task: latest };
80017
+ try {
80018
+ while (true) {
80019
+ if (stop.signal.aborted)
80020
+ return givenUp();
80021
+ const polled = await raceAbort(masApi.getTask(taskId, { signal: stop.signal }), stop.signal);
80022
+ if (polled.kind === "aborted")
80023
+ return givenUp();
80024
+ if (polled.kind === "failed") {
80025
+ if (stop.signal.aborted)
80026
+ return givenUp();
80027
+ throw polled.error;
80028
+ }
80029
+ latest = polled.value;
80030
+ if (latest.status !== lastStatus) {
80031
+ lastStatus = latest.status;
80032
+ options.onStatus?.(latest);
80033
+ }
80034
+ if (isTerminalTaskStatus(latest.status)) {
80035
+ return { kind: "terminal", task: latest };
80036
+ }
80037
+ const cutShort = await sleep2(options.intervalMs, stop.signal);
80038
+ if (cutShort)
80039
+ return givenUp();
80040
+ }
80041
+ } finally {
80042
+ if (deadlineTimer !== null)
80043
+ clearTimeout(deadlineTimer);
80044
+ options.signal?.removeEventListener("abort", onExternalAbort);
80045
+ }
80046
+ }
80047
+ function raceAbort(work, signal) {
80048
+ const settled = work.then((value) => ({ kind: "value", value }), (error2) => ({ kind: "failed", error: error2 }));
80049
+ if (signal.aborted) {
80050
+ settled.catch(() => {});
80051
+ return Promise.resolve({ kind: "aborted" });
80052
+ }
80053
+ return new Promise((resolve) => {
80054
+ const onAbort = () => resolve({ kind: "aborted" });
80055
+ signal.addEventListener("abort", onAbort, { once: true });
80056
+ settled.then((outcome) => {
80057
+ signal.removeEventListener("abort", onAbort);
80058
+ resolve(outcome);
80059
+ });
80060
+ });
80061
+ }
80062
+ function sleep2(ms2, signal) {
80063
+ if (signal.aborted)
80064
+ return Promise.resolve(true);
80065
+ return new Promise((resolve) => {
80066
+ const onAbort = () => {
80067
+ clearTimeout(timer);
80068
+ resolve(true);
80069
+ };
80070
+ const timer = setTimeout(() => {
80071
+ signal.removeEventListener("abort", onAbort);
80072
+ resolve(false);
80073
+ }, ms2);
80074
+ signal.addEventListener("abort", onAbort, { once: true });
80075
+ });
80076
+ }
80077
+
80078
+ // src/cli/task-create.ts
80079
+ var DEFAULT_POLL_INTERVAL_MS2 = 2000;
79176
80080
  async function createTask(input, options) {
79177
80081
  return await masApi.createTask(input, options);
79178
80082
  }
@@ -79231,21 +80135,633 @@ async function runTaskCreate(cwd2, options, dependencies = {}) {
79231
80135
  }
79232
80136
  throw error2;
79233
80137
  }
80138
+ if (!options.wait) {
80139
+ if (options.json) {
80140
+ console.log(JSON.stringify({
80141
+ task_id: created.id,
80142
+ agent_id: created.agent_id,
80143
+ status: created.status
80144
+ }));
80145
+ return;
80146
+ }
80147
+ console.log([
80148
+ `Task ID: ${created.id}`,
80149
+ `Agent ID: ${created.agent_id}`,
80150
+ `Status: ${created.status}`,
80151
+ "First run accepted."
80152
+ ].join(`
80153
+ `));
80154
+ return;
80155
+ }
80156
+ await waitForCreatedTask(created, options, dependencies);
80157
+ }
80158
+ async function waitForCreatedTask(created, options, dependencies) {
80159
+ const setExitCode = dependencies.setExitCode ?? ((code) => process.exitCode = code);
80160
+ const controller = new AbortController;
80161
+ const onSigint = () => controller.abort();
80162
+ process.on("SIGINT", onSigint);
80163
+ if (!options.json) {
80164
+ console.log(`Task ID: ${created.id}`);
80165
+ console.log(`Agent ID: ${created.agent_id}`);
80166
+ }
80167
+ try {
80168
+ const outcome = await waitForTask(created.id, {
80169
+ intervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2,
80170
+ timeoutMs: options.timeoutSeconds === undefined ? undefined : options.timeoutSeconds * 1000,
80171
+ signal: controller.signal,
80172
+ onStatus: options.json ? undefined : (task) => console.log(`Status: ${task.status}`)
80173
+ });
80174
+ const status = outcome.task?.status ?? created.status;
80175
+ const code = outcome.kind === "terminal" ? exitCodeForTaskStatus(status) : outcome.kind === "interrupted" ? SIGINT_EXIT_CODE : 1;
80176
+ setExitCode(code);
80177
+ if (options.json) {
80178
+ console.log(JSON.stringify({
80179
+ task_id: created.id,
80180
+ agent_id: created.agent_id,
80181
+ status,
80182
+ outcome: outcome.kind,
80183
+ exit_code: code
80184
+ }));
80185
+ return;
80186
+ }
80187
+ if (outcome.kind === "timeout") {
80188
+ console.log(import_picocolors52.default.yellow(`Timed out after ${options.timeoutSeconds}s with the task still ${status}.`));
80189
+ return;
80190
+ }
80191
+ if (outcome.kind === "interrupted") {
80192
+ console.log(import_picocolors52.default.yellow("Interrupted. The task is still running."));
80193
+ return;
80194
+ }
80195
+ console.log(code === 0 ? import_picocolors52.default.green("Task completed.") : import_picocolors52.default.red(`Task finished as ${status}.`));
80196
+ } finally {
80197
+ process.removeListener("SIGINT", onSigint);
80198
+ }
80199
+ }
80200
+
80201
+ // src/cli/task-get.ts
80202
+ var import_picocolors54 = __toESM(require_picocolors(), 1);
80203
+
80204
+ // src/cli/task-list.ts
80205
+ var import_picocolors53 = __toESM(require_picocolors(), 1);
80206
+ async function runTaskList(options) {
80207
+ if (!options.json)
80208
+ banner("task list — recent tasks");
80209
+ const tasks = await masApi.listTasks({
80210
+ agentId: options.agentId,
80211
+ limit: options.limit
80212
+ });
79234
80213
  if (options.json) {
79235
- console.log(JSON.stringify({
79236
- task_id: created.id,
79237
- agent_id: created.agent_id,
79238
- status: created.status
79239
- }));
80214
+ console.log(JSON.stringify(tasks, null, 2));
79240
80215
  return;
79241
80216
  }
79242
- console.log([
79243
- `Task ID: ${created.id}`,
79244
- `Agent ID: ${created.agent_id}`,
79245
- `Status: ${created.status}`,
79246
- "First run accepted."
79247
- ].join(`
79248
- `));
80217
+ console.log(formatTaskList(tasks, options.agentId));
80218
+ }
80219
+ function formatTaskList(tasks, agentId) {
80220
+ const lines = [""];
80221
+ if (tasks.length === 0) {
80222
+ lines.push(` ${import_picocolors53.default.dim(agentId ? `No tasks for agent ${agentId}.` : "No tasks yet.")}`, "", ` ${import_picocolors53.default.dim("start one with")} ${import_picocolors53.default.cyan('brainbase task create --message "..."')}`, "");
80223
+ return lines.join(`
80224
+ `);
80225
+ }
80226
+ for (const task of tasks) {
80227
+ const title = task.title?.trim() || import_picocolors53.default.dim("(untitled)");
80228
+ lines.push(` ${statusLabel(task.status)} ${import_picocolors53.default.bold(title)}`);
80229
+ lines.push(` ${import_picocolors53.default.dim(task.id)}`);
80230
+ lines.push(` ${import_picocolors53.default.dim(`agent ${task.agent_id} · created ${task.created_at}`)}`);
80231
+ lines.push("");
80232
+ }
80233
+ lines.push(` ${import_picocolors53.default.dim("inspect one with")} ${import_picocolors53.default.cyan("brainbase task get <id>")}`, "");
80234
+ return lines.join(`
80235
+ `);
80236
+ }
80237
+ function statusLabel(status) {
80238
+ const padded = status.padEnd(14);
80239
+ switch (status) {
80240
+ case "success":
80241
+ return import_picocolors53.default.green(padded);
80242
+ case "fail":
80243
+ return import_picocolors53.default.red(padded);
80244
+ case "need_more_info":
80245
+ return import_picocolors53.default.yellow(padded);
80246
+ case "running":
80247
+ case "initializing":
80248
+ return import_picocolors53.default.cyan(padded);
80249
+ default:
80250
+ return import_picocolors53.default.dim(padded);
80251
+ }
80252
+ }
80253
+
80254
+ // src/cli/task-get.ts
80255
+ async function runTaskGet(taskId, options = {}) {
80256
+ if (!options.json)
80257
+ banner("task get — one task");
80258
+ const task = await masApi.getTask(taskId);
80259
+ const evals = await readEvalRuns(task);
80260
+ if (options.json) {
80261
+ const payload = { task, eval_runs: evals.runs };
80262
+ if (evals.unavailable)
80263
+ payload.eval_runs_unavailable = evals.unavailable;
80264
+ console.log(JSON.stringify(payload, null, 2));
80265
+ return;
80266
+ }
80267
+ console.log(format(task, evals));
80268
+ }
80269
+ async function readEvalRuns(task) {
80270
+ try {
80271
+ return { runs: await api.listAgentEvalRuns(task.agent_id, { taskId: task.id }) };
80272
+ } catch (error2) {
80273
+ if (error2 instanceof ApiError && (error2.status === 401 || error2.status === 403)) {
80274
+ return { runs: [], unavailable: error2.message };
80275
+ }
80276
+ if (error2 instanceof ApiError && error2.status === 404) {
80277
+ return { runs: [], unavailable: "This control plane does not expose eval runs." };
80278
+ }
80279
+ throw error2;
80280
+ }
80281
+ }
80282
+ function format(task, evals) {
80283
+ const lines = [""];
80284
+ const title = task.title?.trim() || import_picocolors54.default.dim("(untitled)");
80285
+ lines.push(` ${statusLabel(task.status)} ${import_picocolors54.default.bold(title)}`);
80286
+ lines.push("");
80287
+ lines.push(` ${import_picocolors54.default.dim("task")} ${task.id}`);
80288
+ lines.push(` ${import_picocolors54.default.dim("agent")} ${task.agent_id}`);
80289
+ lines.push(` ${import_picocolors54.default.dim("created")} ${task.created_at}`);
80290
+ if (task.parent_task_id) {
80291
+ lines.push(` ${import_picocolors54.default.dim("parent")} ${task.parent_task_id}`);
80292
+ }
80293
+ if (task.machine_id) {
80294
+ lines.push(` ${import_picocolors54.default.dim("machine")} ${task.machine_id}${task.machine_size ? ` (${task.machine_size})` : ""}`);
80295
+ }
80296
+ if (task.sandbox_id)
80297
+ lines.push(` ${import_picocolors54.default.dim("sandbox")} ${task.sandbox_id}`);
80298
+ const failure = failureSummary(task);
80299
+ if (failure) {
80300
+ lines.push("");
80301
+ lines.push(` ${import_picocolors54.default.red("failure")} ${failure}`);
80302
+ }
80303
+ const metadata = Object.entries(task.metadata ?? {});
80304
+ if (metadata.length > 0) {
80305
+ lines.push("");
80306
+ lines.push(` ${import_picocolors54.default.bold(import_picocolors54.default.dim("METADATA"))}`);
80307
+ for (const [key2, value] of metadata) {
80308
+ lines.push(` ${import_picocolors54.default.dim(key2)} ${value}`);
80309
+ }
80310
+ }
80311
+ lines.push("");
80312
+ lines.push(` ${import_picocolors54.default.bold(import_picocolors54.default.dim("EVAL VERDICTS"))}`);
80313
+ if (evals.unavailable) {
80314
+ lines.push(` ${import_picocolors54.default.dim(evals.unavailable)}`);
80315
+ } else if (evals.runs.length === 0) {
80316
+ lines.push(` ${import_picocolors54.default.dim("none")}`);
80317
+ } else {
80318
+ for (const run of evals.runs) {
80319
+ const verdict = run.passed === true ? import_picocolors54.default.green("pass") : run.passed === false ? import_picocolors54.default.red("fail") : import_picocolors54.default.dim(run.status);
80320
+ lines.push(` ${verdict} ${run.eval_slug ?? run.eval_id}`);
80321
+ if (run.reasoning)
80322
+ lines.push(` ${import_picocolors54.default.dim(run.reasoning)}`);
80323
+ }
80324
+ }
80325
+ lines.push("");
80326
+ lines.push(` ${import_picocolors54.default.dim("read its transcript with")} ${import_picocolors54.default.cyan(`brainbase task logs ${task.id}`)}`, "");
80327
+ return lines.join(`
80328
+ `);
80329
+ }
80330
+ function failureSummary(task) {
80331
+ const info = task.status_info;
80332
+ if (!info)
80333
+ return;
80334
+ for (const key2 of ["failure_summary", "error"]) {
80335
+ const value = info[key2];
80336
+ if (typeof value === "string" && value.trim())
80337
+ return value.trim();
80338
+ }
80339
+ return;
80340
+ }
80341
+
80342
+ // src/cli/task-logs.ts
80343
+ var import_picocolors55 = __toESM(require_picocolors(), 1);
80344
+
80345
+ // src/core/task-events.ts
80346
+ var DEFAULT_RETRY_BUDGET_MS = 60000;
80347
+ function followTaskEvents(options) {
80348
+ const {
80349
+ url: url2,
80350
+ resolveBearer,
80351
+ onFrame,
80352
+ onOpen,
80353
+ onReconnect,
80354
+ signal,
80355
+ retryBudgetMs = DEFAULT_RETRY_BUDGET_MS
80356
+ } = options;
80357
+ return new Promise((resolve, reject2) => {
80358
+ if (signal?.aborted) {
80359
+ resolve();
80360
+ return;
80361
+ }
80362
+ let source;
80363
+ let settled = false;
80364
+ let closeRequested = false;
80365
+ let downTimer;
80366
+ let lastReason = "connection lost";
80367
+ const clearBudget = () => {
80368
+ if (downTimer !== undefined)
80369
+ clearTimeout(downTimer);
80370
+ downTimer = undefined;
80371
+ };
80372
+ const finish = (error2) => {
80373
+ if (settled)
80374
+ return;
80375
+ settled = true;
80376
+ closeRequested = true;
80377
+ clearBudget();
80378
+ source?.close();
80379
+ if (error2)
80380
+ reject2(error2);
80381
+ else
80382
+ resolve();
80383
+ };
80384
+ source = new EventSource(url2, {
80385
+ fetch: async (input, init) => {
80386
+ let bearer;
80387
+ try {
80388
+ bearer = await resolveBearer();
80389
+ } catch (error2) {
80390
+ if (error2 instanceof StreamHostMovedError) {
80391
+ finish(error2);
80392
+ throw error2;
80393
+ }
80394
+ throw error2;
80395
+ }
80396
+ const headers = new Headers(init?.headers);
80397
+ headers.set("Authorization", `Bearer ${bearer}`);
80398
+ return await fetch(input, { ...init, headers });
80399
+ }
80400
+ });
80401
+ if (closeRequested)
80402
+ source.close();
80403
+ const dispatch = source.dispatchEvent.bind(source);
80404
+ source.dispatchEvent = (event) => {
80405
+ if (event instanceof MessageEvent) {
80406
+ onFrame({
80407
+ id: event.lastEventId,
80408
+ type: event.type,
80409
+ data: parseFrameData(event.data)
80410
+ });
80411
+ }
80412
+ return dispatch(event);
80413
+ };
80414
+ source.addEventListener("open", () => {
80415
+ clearBudget();
80416
+ onOpen?.();
80417
+ });
80418
+ source.addEventListener("error", (event) => {
80419
+ const { message, code } = event;
80420
+ if (source.readyState === source.CLOSED) {
80421
+ finish(streamError(message, code));
80422
+ return;
80423
+ }
80424
+ lastReason = message?.trim() || "connection lost";
80425
+ onReconnect?.(lastReason);
80426
+ if (downTimer === undefined) {
80427
+ downTimer = setTimeout(() => {
80428
+ finish(new ApiError(`The log stream has been unreachable for ${Math.round(retryBudgetMs / 1000)}s (${lastReason}). Giving up — the task may still be running; re-run to reattach.`));
80429
+ }, retryBudgetMs);
80430
+ }
80431
+ });
80432
+ signal?.addEventListener("abort", () => finish(), { once: true });
80433
+ });
80434
+ }
80435
+ function parseFrameData(raw) {
80436
+ if (typeof raw !== "string")
80437
+ return raw;
80438
+ try {
80439
+ return JSON.parse(raw);
80440
+ } catch {
80441
+ return raw;
80442
+ }
80443
+ }
80444
+ function streamError(message, code) {
80445
+ const detail = message?.trim() || "the log stream closed";
80446
+ switch (code) {
80447
+ case 401:
80448
+ return new ApiError(`Not authorized to read this task's events: ${detail}`, 401);
80449
+ case 403:
80450
+ return new ApiError(`Not permitted to read this task's events: ${detail}`, 403);
80451
+ case 404:
80452
+ return new ApiError("No such task, or this control plane does not stream task events yet.", 404);
80453
+ default:
80454
+ return new ApiError(detail, code);
80455
+ }
80456
+ }
80457
+
80458
+ // src/cli/task-logs.ts
80459
+ var MAX_PAGE = 1000;
80460
+ var PAGE_SIZE = 500;
80461
+ async function runTaskLogs(taskId, options = {}, dependencies = {}) {
80462
+ if (options.follow) {
80463
+ await followTranscript(taskId, options, dependencies);
80464
+ return;
80465
+ }
80466
+ const events = await readTranscript(taskId, options.limit);
80467
+ if (options.json) {
80468
+ console.log(JSON.stringify({ items: events }, null, 2));
80469
+ return;
80470
+ }
80471
+ const ordered = [...events].sort((a3, b4) => a3.ts.localeCompare(b4.ts));
80472
+ const rolledUp = rolledUpLanes(ordered);
80473
+ for (const event of ordered) {
80474
+ if (isSupersededChunk(event, rolledUp))
80475
+ continue;
80476
+ console.log(renderEvent(event));
80477
+ }
80478
+ if (ordered.length === 0) {
80479
+ console.log(import_picocolors55.default.dim("No events for this task yet."));
80480
+ }
80481
+ }
80482
+ async function followTranscript(taskId, options, dependencies) {
80483
+ const backfill = options.limit === undefined ? "" : `?backfill=${options.limit}`;
80484
+ const { url: url2, resolveBearer } = await masStreamTarget(`/tasks/${encodeURIComponent(taskId)}/events/stream${backfill}`);
80485
+ if (!options.json) {
80486
+ console.log(import_picocolors55.default.dim(`Following task ${taskId} — Ctrl-C to stop.`));
80487
+ }
80488
+ const setExitCode = dependencies.setExitCode ?? ((code) => process.exitCode = code);
80489
+ const controller = new AbortController;
80490
+ let interruptCode;
80491
+ const stopOn = (code) => () => {
80492
+ interruptCode = code;
80493
+ controller.abort();
80494
+ };
80495
+ const onSigint = stopOn(SIGINT_EXIT_CODE);
80496
+ const onSigterm = stopOn(143);
80497
+ process.once("SIGINT", onSigint);
80498
+ process.once("SIGTERM", onSigterm);
80499
+ try {
80500
+ await followTaskEvents({
80501
+ url: url2,
80502
+ resolveBearer,
80503
+ signal: controller.signal,
80504
+ retryBudgetMs: dependencies.retryBudgetMs,
80505
+ onFrame: (frame) => {
80506
+ const event = frame.data;
80507
+ if (isTaskEvent(event)) {
80508
+ console.log(options.json ? JSON.stringify(event) : renderEvent(event));
80509
+ if (isTaskSettled(event))
80510
+ controller.abort();
80511
+ } else if (options.json) {
80512
+ console.log(JSON.stringify(frame));
80513
+ } else {
80514
+ console.log(import_picocolors55.default.dim(frame.type));
80515
+ }
80516
+ },
80517
+ onReconnect: (reason) => console.error(import_picocolors55.default.dim(`reconnecting — ${reason}`))
80518
+ });
80519
+ if (interruptCode !== undefined)
80520
+ setExitCode(interruptCode);
80521
+ } finally {
80522
+ process.off("SIGINT", onSigint);
80523
+ process.off("SIGTERM", onSigterm);
80524
+ }
80525
+ }
80526
+ function isTaskSettled(event) {
80527
+ if (event.type !== "idle")
80528
+ return false;
80529
+ const status = event.data?.status;
80530
+ return typeof status === "string" && isTerminalTaskStatus(status);
80531
+ }
80532
+ function isTaskEvent(value) {
80533
+ return !!value && typeof value === "object" && typeof value.type === "string" && typeof value.ts === "string";
80534
+ }
80535
+ function rolledUpLanes(events) {
80536
+ const lanes = new Set;
80537
+ for (const event of events) {
80538
+ if (event.type === "assistant.message" || event.type === "subagent.assistant.message") {
80539
+ lanes.add(laneOf(event));
80540
+ }
80541
+ }
80542
+ return lanes;
80543
+ }
80544
+ function isSupersededChunk(event, rolledUp) {
80545
+ return event.type.endsWith(".message.chunk") && rolledUp.has(laneOf(event));
80546
+ }
80547
+ function laneOf(event) {
80548
+ return `${event.subagent_id ?? ""}\x00${event.turn_id ?? ""}`;
80549
+ }
80550
+ async function readTranscript(taskId, limit) {
80551
+ const collected = [];
80552
+ let after2;
80553
+ while (true) {
80554
+ const remaining = limit === undefined ? PAGE_SIZE : limit - collected.length;
80555
+ if (remaining <= 0)
80556
+ break;
80557
+ const pageSize = Math.min(MAX_PAGE, remaining === PAGE_SIZE ? PAGE_SIZE : remaining);
80558
+ const page = await masApi.listTaskEvents(taskId, { limit: pageSize, after: after2 });
80559
+ if (page.length < pageSize) {
80560
+ collected.push(...page);
80561
+ break;
80562
+ }
80563
+ const last2 = page[page.length - 1];
80564
+ const next = { receivedAt: last2.received_at, id: last2.id };
80565
+ if (after2 && next.receivedAt === after2.receivedAt && next.id === after2.id) {
80566
+ break;
80567
+ }
80568
+ collected.push(...page);
80569
+ after2 = next;
80570
+ }
80571
+ return collected;
80572
+ }
80573
+ function renderEvent(event) {
80574
+ const summary = summarize(event);
80575
+ const head3 = `${event.ts} ${event.type}`;
80576
+ return summary ? `${head3} ${summary}` : head3;
80577
+ }
80578
+ function summarize(event) {
80579
+ const data = event.data ?? {};
80580
+ switch (baseType(event.type)) {
80581
+ case "user.message":
80582
+ case "assistant.message":
80583
+ case "assistant.message.chunk":
80584
+ return oneLine2(textOf(data.content));
80585
+ case "assistant.thinking":
80586
+ return oneLine2(asString(data.thought));
80587
+ case "tool_call.start":
80588
+ return oneLine2(`${asString(data.name)} ${compact2(data.args)}`);
80589
+ case "tool_call.end":
80590
+ return oneLine2(`${asString(data.name)} → ${asString(data.status)}`);
80591
+ case "idle":
80592
+ return oneLine2([asString(data.status), asString(data.summary) || asString(data.message)].filter(Boolean).join(" — "));
80593
+ default:
80594
+ return oneLine2(compact2(data));
80595
+ }
80596
+ }
80597
+ function baseType(type) {
80598
+ return type.startsWith("subagent.") ? type.slice("subagent.".length) : type;
80599
+ }
80600
+ function textOf(content) {
80601
+ if (!Array.isArray(content))
80602
+ return "";
80603
+ return content.map((item) => {
80604
+ if (!item || typeof item !== "object")
80605
+ return "";
80606
+ const record3 = item;
80607
+ return record3.type === "text" ? asString(record3.content) : "";
80608
+ }).filter(Boolean).join(" ");
80609
+ }
80610
+ function asString(value) {
80611
+ return typeof value === "string" ? value : "";
80612
+ }
80613
+ function compact2(value) {
80614
+ if (value === undefined || value === null)
80615
+ return "";
80616
+ try {
80617
+ const encoded = JSON.stringify(value);
80618
+ return encoded === "{}" ? "" : encoded ?? "";
80619
+ } catch {
80620
+ return "";
80621
+ }
80622
+ }
80623
+ function oneLine2(value) {
80624
+ const flattened = value.replace(/\s+/g, " ").trim();
80625
+ return flattened.length > 300 ? `${flattened.slice(0, 299)}…` : flattened;
80626
+ }
80627
+
80628
+ // src/core/argv.ts
80629
+ class ArgvParseError extends Error {
80630
+ code = "invalid_arguments";
80631
+ constructor(message) {
80632
+ super(message);
80633
+ this.name = "ArgvParseError";
80634
+ }
80635
+ }
80636
+ function parseBoolean(value, name) {
80637
+ switch (value.trim().toLowerCase()) {
80638
+ case "":
80639
+ case "0":
80640
+ case "false":
80641
+ case "no":
80642
+ case "off":
80643
+ return false;
80644
+ case "1":
80645
+ case "true":
80646
+ case "yes":
80647
+ case "on":
80648
+ return true;
80649
+ default:
80650
+ throw new ArgvParseError(`Unrecognised value for ${name}: ${value}`);
80651
+ }
80652
+ }
80653
+ function validateDefinitions(definitions) {
80654
+ const names = new Map;
80655
+ const keys2 = new Set;
80656
+ for (const definition of definitions) {
80657
+ if (!definition.key) {
80658
+ throw new Error("argv option keys must not be empty");
80659
+ }
80660
+ if (keys2.has(definition.key)) {
80661
+ throw new Error(`duplicate argv option key: ${definition.key}`);
80662
+ }
80663
+ keys2.add(definition.key);
80664
+ for (const name of definition.names) {
80665
+ if (!name.startsWith("-") || name === "-") {
80666
+ throw new Error(`invalid argv option name: ${name}`);
80667
+ }
80668
+ if (name.includes("=")) {
80669
+ throw new Error(`argv option names must not contain "=": ${name}`);
80670
+ }
80671
+ if (names.has(name)) {
80672
+ throw new Error(`duplicate argv option name: ${name}`);
80673
+ }
80674
+ names.set(name, definition);
80675
+ }
80676
+ }
80677
+ return names;
80678
+ }
80679
+ function findDefinition(token, definitions) {
80680
+ const exact = definitions.get(token);
80681
+ if (exact)
80682
+ return { definition: exact, name: token };
80683
+ const equals = token.indexOf("=");
80684
+ if (equals < 1)
80685
+ return null;
80686
+ const name = token.slice(0, equals);
80687
+ const definition = definitions.get(name);
80688
+ return definition ? { definition, name, joined: token.slice(equals + 1) } : null;
80689
+ }
80690
+ function assignOption(options, definition, value, name) {
80691
+ const current = options[definition.key];
80692
+ if (definition.multiple) {
80693
+ if (typeof value !== "string") {
80694
+ throw new Error(`boolean argv option cannot be repeated: ${name}`);
80695
+ }
80696
+ options[definition.key] = [
80697
+ ...Array.isArray(current) ? current : [],
80698
+ value
80699
+ ];
80700
+ return;
80701
+ }
80702
+ if (current !== undefined) {
80703
+ throw new ArgvParseError(`Option ${name} may only be provided once`);
80704
+ }
80705
+ options[definition.key] = value;
80706
+ }
80707
+ function parseArgv(argv, definitions) {
80708
+ const byName = validateDefinitions(definitions);
80709
+ const options = Object.create(null);
80710
+ const positionals = [];
80711
+ let optionsEnabled = true;
80712
+ for (let index = 0;index < argv.length; index += 1) {
80713
+ const token = argv[index];
80714
+ if (!optionsEnabled) {
80715
+ positionals.push(token);
80716
+ continue;
80717
+ }
80718
+ if (token === "--") {
80719
+ optionsEnabled = false;
80720
+ continue;
80721
+ }
80722
+ if (!token.startsWith("-") || token === "-") {
80723
+ positionals.push(token);
80724
+ continue;
80725
+ }
80726
+ const hit = findDefinition(token, byName);
80727
+ if (!hit) {
80728
+ throw new ArgvParseError(`Unknown option: ${token.split("=", 1)[0]}`);
80729
+ }
80730
+ if (hit.definition.kind === "boolean") {
80731
+ assignOption(options, hit.definition, hit.joined === undefined ? true : parseBoolean(hit.joined, hit.name), hit.name);
80732
+ continue;
80733
+ }
80734
+ let value = hit.joined;
80735
+ if (value === undefined) {
80736
+ const next = argv[index + 1];
80737
+ if (next === "--") {
80738
+ value = argv[index + 2];
80739
+ if (value === undefined) {
80740
+ throw new ArgvParseError(`Option ${hit.name} requires a value`);
80741
+ }
80742
+ index += 2;
80743
+ } else {
80744
+ if (next === undefined || next.startsWith("-")) {
80745
+ throw new ArgvParseError(`Option ${hit.name} requires a value; use ${hit.name}=<value> for values beginning with "-"`);
80746
+ }
80747
+ value = next;
80748
+ index += 1;
80749
+ }
80750
+ }
80751
+ assignOption(options, hit.definition, value, hit.name);
80752
+ }
80753
+ return { options, positionals };
80754
+ }
80755
+ function stringOption(parsed, key2) {
80756
+ const value = parsed.options[key2];
80757
+ return typeof value === "string" ? value : undefined;
80758
+ }
80759
+ function stringOptions(parsed, key2) {
80760
+ const value = parsed.options[key2];
80761
+ return Array.isArray(value) ? value : [];
80762
+ }
80763
+ function booleanOption(parsed, key2) {
80764
+ return parsed.options[key2] === true;
79249
80765
  }
79250
80766
 
79251
80767
  // src/cli/task.ts
@@ -79253,13 +80769,16 @@ var VALUE_FLAGS = [
79253
80769
  ["--agent", "agentId"],
79254
80770
  ["--message", "message"],
79255
80771
  ["--title", "title"],
79256
- ["--model", "model"]
80772
+ ["--model", "model"],
80773
+ ["--timeout", "timeout"]
79257
80774
  ];
80775
+ var BOOLEAN_FLAGS = ["--json", "--wait"];
79258
80776
  var TASK_OPTION_NAMES = new Set([
79259
80777
  ...VALUE_FLAGS.map(([flag]) => flag),
79260
- "--json"
80778
+ ...BOOLEAN_FLAGS
79261
80779
  ]);
79262
80780
  var HELP_FLAGS = new Set(["--help", "-h"]);
80781
+ var LIST_LIMIT_MAX = 200;
79263
80782
  function missingValueError(flag) {
79264
80783
  if (flag === "--message" || flag === "--agent") {
79265
80784
  return new Error(`${flag} is required and must not be blank.`);
@@ -79275,14 +80794,17 @@ function parseCreateArgs(args) {
79275
80794
  for (let index = 0;index < args.length; index += 1) {
79276
80795
  const arg = args[index];
79277
80796
  if (HELP_FLAGS.has(arg)) {
79278
- return { help: true, options };
80797
+ return { help: true, options: finishCreateArgs(options) };
79279
80798
  }
79280
- if (arg === "--json") {
80799
+ if (arg === "--json" || arg === "--wait") {
79281
80800
  if (seen.has(arg)) {
79282
80801
  throw new Error(`Duplicate task create option: ${arg}`);
79283
80802
  }
79284
80803
  seen.add(arg);
79285
- options.json = true;
80804
+ if (arg === "--json")
80805
+ options.json = true;
80806
+ else
80807
+ options.wait = true;
79286
80808
  continue;
79287
80809
  }
79288
80810
  let matched = false;
@@ -79302,7 +80824,7 @@ function parseCreateArgs(args) {
79302
80824
  throw missingValueError(flag);
79303
80825
  index += 1;
79304
80826
  } else if (HELP_FLAGS.has(value) && (option === "agentId" || option === "model")) {
79305
- return { help: true, options };
80827
+ return { help: true, options: finishCreateArgs(options) };
79306
80828
  } else if (TASK_OPTION_NAMES.has(value)) {
79307
80829
  throw flagLikeValueError(flag, value);
79308
80830
  }
@@ -79326,7 +80848,38 @@ function parseCreateArgs(args) {
79326
80848
  throw new Error(`Unknown task create argument: ${arg}`);
79327
80849
  }
79328
80850
  }
79329
- return { help: false, options };
80851
+ return { help: false, options: finishCreateArgs(options) };
80852
+ }
80853
+ function finishCreateArgs(raw) {
80854
+ const { timeout, ...rest2 } = raw;
80855
+ if (timeout === undefined)
80856
+ return rest2;
80857
+ const seconds = Number(timeout.trim());
80858
+ if (!Number.isFinite(seconds) || seconds <= 0) {
80859
+ throw new Error("--timeout must be a positive number of seconds.");
80860
+ }
80861
+ if (!rest2.wait) {
80862
+ throw new Error("--timeout only applies with --wait.");
80863
+ }
80864
+ return { ...rest2, timeoutSeconds: seconds };
80865
+ }
80866
+ function positiveInteger(value, flag, max2) {
80867
+ if (value === undefined)
80868
+ return;
80869
+ const parsed = Number(value.trim());
80870
+ if (!Number.isInteger(parsed) || parsed <= 0) {
80871
+ throw new Error(`${flag} must be a positive whole number.`);
80872
+ }
80873
+ if (max2 !== undefined && parsed > max2) {
80874
+ throw new Error(`${flag} must be between 1 and ${max2}.`);
80875
+ }
80876
+ return parsed;
80877
+ }
80878
+ function requiredTaskId(positionals, usage) {
80879
+ if (positionals.length !== 1 || !positionals[0]?.trim()) {
80880
+ throw new Error(`Usage: ${usage}`);
80881
+ }
80882
+ return positionals[0].trim();
79330
80883
  }
79331
80884
  async function runTask(cwd2, sub, args) {
79332
80885
  switch (sub) {
@@ -79339,6 +80892,56 @@ async function runTask(cwd2, sub, args) {
79339
80892
  await runTaskCreate(cwd2, parsed.options);
79340
80893
  return;
79341
80894
  }
80895
+ case "list": {
80896
+ if (args.some((arg) => HELP_FLAGS.has(arg))) {
80897
+ printHelp4();
80898
+ return;
80899
+ }
80900
+ const parsed = parseArgv(args, [
80901
+ { key: "agent", names: ["--agent"], kind: "value" },
80902
+ { key: "limit", names: ["--limit"], kind: "value" },
80903
+ { key: "json", names: ["--json"], kind: "boolean" }
80904
+ ]);
80905
+ if (parsed.positionals.length > 0) {
80906
+ throw new Error("Usage: brainbase task list [--agent <id>] [--limit <n>] [--json]");
80907
+ }
80908
+ await runTaskList({
80909
+ agentId: stringOption(parsed, "agent"),
80910
+ limit: positiveInteger(stringOption(parsed, "limit"), "--limit", LIST_LIMIT_MAX),
80911
+ json: booleanOption(parsed, "json")
80912
+ });
80913
+ return;
80914
+ }
80915
+ case "get": {
80916
+ if (args.some((arg) => HELP_FLAGS.has(arg))) {
80917
+ printHelp4();
80918
+ return;
80919
+ }
80920
+ const parsed = parseArgv(args, [
80921
+ { key: "json", names: ["--json"], kind: "boolean" }
80922
+ ]);
80923
+ const taskId = requiredTaskId(parsed.positionals, "brainbase task get <task-id> [--json]");
80924
+ await runTaskGet(taskId, { json: booleanOption(parsed, "json") });
80925
+ return;
80926
+ }
80927
+ case "logs": {
80928
+ if (args.some((arg) => HELP_FLAGS.has(arg))) {
80929
+ printHelp4();
80930
+ return;
80931
+ }
80932
+ const parsed = parseArgv(args, [
80933
+ { key: "limit", names: ["--limit"], kind: "value" },
80934
+ { key: "json", names: ["--json"], kind: "boolean" },
80935
+ { key: "follow", names: ["--follow", "-f"], kind: "boolean" }
80936
+ ]);
80937
+ const taskId = requiredTaskId(parsed.positionals, "brainbase task logs <task-id> [--limit <n>] [--json] [--follow]");
80938
+ await runTaskLogs(taskId, {
80939
+ limit: positiveInteger(stringOption(parsed, "limit"), "--limit"),
80940
+ json: booleanOption(parsed, "json"),
80941
+ follow: booleanOption(parsed, "follow")
80942
+ });
80943
+ return;
80944
+ }
79342
80945
  case undefined:
79343
80946
  case "help":
79344
80947
  case "-h":
@@ -79355,18 +80958,220 @@ async function runTask(cwd2, sub, args) {
79355
80958
  function printHelp4() {
79356
80959
  const out = [];
79357
80960
  out.push("");
79358
- out.push(` ${import_picocolors52.default.bold("brainbase task")} ${import_picocolors52.default.dim("<sub> [options]")}`);
80961
+ out.push(` ${import_picocolors56.default.bold("brainbase task")} ${import_picocolors56.default.dim("<sub> [options]")}`);
80962
+ out.push("");
80963
+ out.push(` ${import_picocolors56.default.cyan("create")} ${import_picocolors56.default.dim("--message <text>")} ${import_picocolors56.default.dim("create a task and start its first run")}`);
80964
+ out.push(` ${import_picocolors56.default.cyan("list")} ${import_picocolors56.default.dim("recent tasks you can reach (--json for scripts)")}`);
80965
+ out.push(` ${import_picocolors56.default.cyan("get")} ${import_picocolors56.default.dim("<task-id>")} ${import_picocolors56.default.dim("one task: status, agent, machine, eval verdicts")}`);
80966
+ out.push(` ${import_picocolors56.default.cyan("logs")} ${import_picocolors56.default.dim("<task-id> [--follow]")} ${import_picocolors56.default.dim("one line per event in the task's transcript, or tail it live")}`);
80967
+ out.push("");
80968
+ out.push(` ${import_picocolors56.default.bold("create flags")}`);
80969
+ out.push(` ${import_picocolors56.default.dim("--message <text>")} required first user message`);
80970
+ out.push(` ${import_picocolors56.default.dim("--agent <id>")} override the claimed agent in brainbase.agent.yaml`);
80971
+ out.push(` ${import_picocolors56.default.dim("--title <text>")} optional task title`);
80972
+ out.push(` ${import_picocolors56.default.dim("--model <id>")} optional model override`);
80973
+ out.push(` ${import_picocolors56.default.dim("--wait")} block until the task finishes; exit 1 unless it succeeded`);
80974
+ out.push(` ${import_picocolors56.default.dim("--timeout <secs>")} give up waiting after <secs> and exit 1 (needs --wait)`);
80975
+ out.push("");
80976
+ out.push(` ${import_picocolors56.default.bold("list flags")}`);
80977
+ out.push(` ${import_picocolors56.default.dim("--agent <id>")} only this agent's tasks`);
80978
+ out.push(` ${import_picocolors56.default.dim("--limit <n>")} cap the page (1-200, server default 50)`);
79359
80979
  out.push("");
79360
- out.push(` ${import_picocolors52.default.cyan("create")} ${import_picocolors52.default.dim("--message <text>")} ${import_picocolors52.default.dim("create a task and start its first run")}`);
80980
+ out.push(` ${import_picocolors56.default.bold("logs flags")}`);
80981
+ out.push(` ${import_picocolors56.default.dim("--limit <n>")} stop after <n> events instead of the whole transcript; with --follow, the first page size`);
80982
+ out.push(` ${import_picocolors56.default.dim("--follow, -f")} stay attached and print events as they land, until the task finishes`);
79361
80983
  out.push("");
79362
- out.push(` ${import_picocolors52.default.bold("create flags")}`);
79363
- out.push(` ${import_picocolors52.default.dim("--message <text>")} required first user message`);
79364
- out.push(` ${import_picocolors52.default.dim("--agent <id>")} override the claimed agent in brainbase.agent.yaml`);
79365
- out.push(` ${import_picocolors52.default.dim("--title <text>")} optional task title`);
79366
- out.push(` ${import_picocolors52.default.dim("--model <id>")} optional model override`);
79367
- out.push(` ${import_picocolors52.default.dim("--json")} print task_id, agent_id, and status as JSON`);
80984
+ out.push(` ${import_picocolors56.default.bold("every subcommand")}`);
80985
+ out.push(` ${import_picocolors56.default.dim("--json")} machine-readable output create prints task_id, agent_id and status; list, get and logs print the raw records`);
80986
+ out.push(` ${import_picocolors56.default.dim(" logs --follow --json prints one record per line, since a live stream has no end to close on")}`);
79368
80987
  out.push("");
79369
- out.push(` ${import_picocolors52.default.dim("Flag-like values:")} use ${import_picocolors52.default.cyan("--flag=value")} or ${import_picocolors52.default.cyan("--flag -- <value>")}`);
80988
+ out.push(` ${import_picocolors56.default.dim("Exit codes with --wait:")} ${import_picocolors56.default.dim("0 succeeded · 1 failed, needs input, or timed out · 130 interrupted")}`);
80989
+ out.push(` ${import_picocolors56.default.dim("Exit codes with --follow:")} ${import_picocolors56.default.dim("0 task reached a terminal state · 1 stream refused, or unreachable for 60s · 130 interrupted (Ctrl-C) · 143 terminated")}`);
80990
+ out.push("");
80991
+ out.push(` ${import_picocolors56.default.dim("Flag-like values:")} use ${import_picocolors56.default.cyan("--flag=value")} or ${import_picocolors56.default.cyan("--flag -- <value>")}`);
80992
+ out.push("");
80993
+ console.log(out.join(`
80994
+ `));
80995
+ }
80996
+
80997
+ // src/cli/machine.ts
80998
+ var import_picocolors59 = __toESM(require_picocolors(), 1);
80999
+
81000
+ // src/cli/machine-list.ts
81001
+ var import_picocolors57 = __toESM(require_picocolors(), 1);
81002
+ async function runMachineList(args = {}) {
81003
+ if (!args.json)
81004
+ banner("machine list — sandboxes you can reach");
81005
+ const machines = await masApi.listMachines({
81006
+ includeDead: Boolean(args.all),
81007
+ limit: args.limit
81008
+ });
81009
+ if (args.json) {
81010
+ console.log(JSON.stringify(machines, null, 2));
81011
+ return;
81012
+ }
81013
+ console.log(formatMachineList(machines, Boolean(args.all)));
81014
+ }
81015
+ function formatMachineList(machines, includedDead) {
81016
+ const lines = [""];
81017
+ if (machines.length === 0) {
81018
+ lines.push(` ${import_picocolors57.default.dim(includedDead ? "No machines." : "No live machines. Pass --all to include torn-down ones.")}`, "");
81019
+ return lines.join(`
81020
+ `);
81021
+ }
81022
+ const rows = machines.map((machine) => ({
81023
+ id: machine.id,
81024
+ kind: machine.kind,
81025
+ status: machine.destroyed_at ? "dead" : machine.status,
81026
+ size: machine.machine_size ?? "",
81027
+ age: age(machine.created_at),
81028
+ dead: Boolean(machine.destroyed_at)
81029
+ }));
81030
+ const width = (pick3) => Math.max(...rows.map((row) => pick3(row).length), 0);
81031
+ const idWidth = width((row) => row.id);
81032
+ const kindWidth = width((row) => row.kind);
81033
+ const statusWidth = width((row) => row.status);
81034
+ const sizeWidth = width((row) => row.size);
81035
+ for (const row of rows) {
81036
+ const status = row.status.padEnd(statusWidth);
81037
+ const cells = [
81038
+ row.id.padEnd(idWidth),
81039
+ row.kind.padEnd(kindWidth),
81040
+ row.dead ? status : statusTint(row.status, status),
81041
+ row.size.padEnd(sizeWidth),
81042
+ import_picocolors57.default.dim(row.age)
81043
+ ].join(" ");
81044
+ lines.push(` ${row.dead ? import_picocolors57.default.dim(cells) : cells}`);
81045
+ }
81046
+ lines.push("", ` ${import_picocolors57.default.dim("tear one down with")} ${import_picocolors57.default.cyan("brainbase machine rm <id>")}`, "");
81047
+ return lines.join(`
81048
+ `);
81049
+ }
81050
+ function statusTint(status, padded) {
81051
+ switch (status) {
81052
+ case "running":
81053
+ return import_picocolors57.default.green(padded);
81054
+ case "stopped":
81055
+ case "starting":
81056
+ return import_picocolors57.default.yellow(padded);
81057
+ default:
81058
+ return padded;
81059
+ }
81060
+ }
81061
+ function age(createdAt) {
81062
+ if (!createdAt)
81063
+ return "";
81064
+ const started = new Date(createdAt).getTime();
81065
+ if (Number.isNaN(started))
81066
+ return "";
81067
+ const minutes = Math.floor((Date.now() - started) / 60000);
81068
+ if (minutes < 1)
81069
+ return "just now";
81070
+ if (minutes < 60)
81071
+ return `${minutes}m`;
81072
+ const hours = Math.floor(minutes / 60);
81073
+ if (hours < 24)
81074
+ return `${hours}h`;
81075
+ return `${Math.floor(hours / 24)}d`;
81076
+ }
81077
+
81078
+ // src/cli/machine-rm.ts
81079
+ var import_picocolors58 = __toESM(require_picocolors(), 1);
81080
+ async function runMachineRm(args, dependencies = {}) {
81081
+ const setExitCode = dependencies.setExitCode ?? ((code) => process.exitCode = code);
81082
+ const machineId = args.machineId?.trim();
81083
+ if (!machineId) {
81084
+ throw new Error("A machine id is required: `brainbase machine rm <id>`. List them with `brainbase machine ls`.");
81085
+ }
81086
+ if (!args.json)
81087
+ banner(`machine rm — ${machineId}`);
81088
+ const confirmTeardown = dependencies.confirmTeardown ?? promptForTeardown;
81089
+ if (!await confirmTeardown({ ...args, machineId })) {
81090
+ if (args.json) {
81091
+ console.log(JSON.stringify({ id: machineId, torn_down: false, outcome: "declined" }, null, 2));
81092
+ } else {
81093
+ console.log(` ${import_picocolors58.default.dim("Left alone.")}`);
81094
+ }
81095
+ return;
81096
+ }
81097
+ const machine = await masApi.deleteMachine(machineId);
81098
+ if (!machine.destroyed_at)
81099
+ setExitCode(1);
81100
+ if (args.json) {
81101
+ console.log(JSON.stringify(machine, null, 2));
81102
+ return;
81103
+ }
81104
+ console.log(formatMachineRm(machine));
81105
+ }
81106
+ async function promptForTeardown(args) {
81107
+ if (autoProceed(args.yes))
81108
+ return true;
81109
+ if (!args.json) {
81110
+ console.log(`
81111
+ ${import_picocolors58.default.red("This destroys the sandbox.")} ${import_picocolors58.default.dim("Anything not committed out of it is lost. The machine row is kept for cost history.")}
81112
+ `);
81113
+ }
81114
+ const answer = await se({
81115
+ message: `Tear down ${args.machineId}?`,
81116
+ initialValue: false
81117
+ });
81118
+ return ensureNotCancelled(answer);
81119
+ }
81120
+ function formatMachineRm(machine) {
81121
+ const lines = [""];
81122
+ if (machine.destroyed_at) {
81123
+ lines.push(` ${import_picocolors58.default.green("✓")} ${machine.kind} sandbox ${machine.id} is torn down.`, ` ${import_picocolors58.default.dim(`destroyed_at ${machine.destroyed_at}`)}`);
81124
+ } else {
81125
+ lines.push(` ${import_picocolors58.default.yellow("!")} ${machine.id} came back without a teardown timestamp (status ${machine.status}).`, ` ${import_picocolors58.default.dim("Re-check with `brainbase machine ls --all` before assuming it stopped billing.")}`);
81126
+ }
81127
+ lines.push("");
81128
+ return lines.join(`
81129
+ `);
81130
+ }
81131
+
81132
+ // src/cli/machine.ts
81133
+ async function runMachine(sub, args, opts) {
81134
+ if (args.some((arg) => arg === "--help" || arg === "-h")) {
81135
+ printHelp5();
81136
+ return;
81137
+ }
81138
+ switch (sub) {
81139
+ case "ls":
81140
+ case "list":
81141
+ await runMachineList({ all: opts.all, limit: opts.limit, json: opts.json });
81142
+ return;
81143
+ case "rm":
81144
+ await runMachineRm({
81145
+ machineId: args[0],
81146
+ yes: opts.yes,
81147
+ json: opts.json
81148
+ });
81149
+ return;
81150
+ case undefined:
81151
+ case "help":
81152
+ case "-h":
81153
+ case "--help":
81154
+ printHelp5();
81155
+ return;
81156
+ default:
81157
+ console.error(`Unknown machine subcommand: ${sub}
81158
+ `);
81159
+ printHelp5();
81160
+ process.exit(1);
81161
+ }
81162
+ }
81163
+ function printHelp5() {
81164
+ const out = [];
81165
+ out.push("");
81166
+ out.push(` ${import_picocolors59.default.bold("brainbase machine")} ${import_picocolors59.default.dim("<sub> [options]")}`);
81167
+ out.push("");
81168
+ out.push(` ${import_picocolors59.default.cyan("ls")} ${import_picocolors59.default.dim("list your sandboxes — live ones only unless --all")}`);
81169
+ out.push(` ${import_picocolors59.default.cyan("rm")} ${import_picocolors59.default.dim("<id>")} ${import_picocolors59.default.dim("tear a sandbox down (terminal; the row is kept for cost history)")}`);
81170
+ out.push("");
81171
+ out.push(` ${import_picocolors59.default.dim("--all")} ${import_picocolors59.default.dim("for ls: include machines already torn down")}`);
81172
+ out.push(` ${import_picocolors59.default.dim("--limit <n>")} ${import_picocolors59.default.dim("for ls: how many to fetch (1-200, default 50)")}`);
81173
+ out.push(` ${import_picocolors59.default.dim("--yes, -y")} ${import_picocolors59.default.dim("for rm: skip the confirmation")}`);
81174
+ out.push(` ${import_picocolors59.default.dim("--json")} ${import_picocolors59.default.dim("machine-readable output")}`);
79370
81175
  out.push("");
79371
81176
  console.log(out.join(`
79372
81177
  `));
@@ -79377,7 +81182,7 @@ import {
79377
81182
  execFileSync as execFileSync3,
79378
81183
  spawn as spawn5
79379
81184
  } from "node:child_process";
79380
- import crypto10 from "node:crypto";
81185
+ import crypto11 from "node:crypto";
79381
81186
  import fs86 from "node:fs";
79382
81187
  import os19 from "node:os";
79383
81188
  import path94 from "node:path";
@@ -79387,7 +81192,7 @@ import {
79387
81192
  execFileSync as execFileSync2,
79388
81193
  spawn as spawn4
79389
81194
  } from "node:child_process";
79390
- import crypto6 from "node:crypto";
81195
+ import crypto7 from "node:crypto";
79391
81196
  import fs81 from "node:fs";
79392
81197
  import os16 from "node:os";
79393
81198
  import path89 from "node:path";
@@ -79976,7 +81781,7 @@ function openRegularFileNoFollow(filePath, label, root) {
79976
81781
  return { fd, stat };
79977
81782
  }
79978
81783
  async function sha256OfDescriptor(fd) {
79979
- const hash = crypto6.createHash("sha256");
81784
+ const hash = crypto7.createHash("sha256");
79980
81785
  const stream = fs81.createReadStream("", {
79981
81786
  fd,
79982
81787
  autoClose: false,
@@ -80224,7 +82029,7 @@ async function downloadInputReference(stagingRoot, input, context) {
80224
82029
  fs81.mkdirSync(path89.dirname(destination), { recursive: true, mode: 448 });
80225
82030
  assertNoSymlinkTraversal(stagingRoot, relative);
80226
82031
  assertWritableDestination(stagingRoot, relative);
80227
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.download`;
82032
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.download`;
80228
82033
  const controller = new AbortController;
80229
82034
  const timer = setTimeout(() => controller.abort(), remainingMs);
80230
82035
  let descriptor;
@@ -80256,7 +82061,7 @@ async function downloadInputReference(stagingRoot, input, context) {
80256
82061
  let actual;
80257
82062
  let size2 = 0;
80258
82063
  try {
80259
- const hash = crypto6.createHash("sha256");
82064
+ const hash = crypto7.createHash("sha256");
80260
82065
  while (true) {
80261
82066
  const { done, value } = await reader.read();
80262
82067
  if (done)
@@ -80441,9 +82246,9 @@ function findZipMembers(archivePath, requested, context) {
80441
82246
  }
80442
82247
  async function writeVerifiedArchiveMember(source, destination, material, context) {
80443
82248
  fs81.mkdirSync(path89.dirname(destination), { recursive: true, mode: 448 });
80444
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
82249
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
80445
82250
  const descriptor = fs81.openSync(temporary, "wx", 384);
80446
- const hash = crypto6.createHash("sha256");
82251
+ const hash = crypto7.createHash("sha256");
80447
82252
  let size2 = 0;
80448
82253
  try {
80449
82254
  for await (const value of source) {
@@ -80632,7 +82437,7 @@ async function extractArchiveMembers(archivePath, outputRoot, materials, context
80632
82437
  }
80633
82438
  async function atomicCopy(source, destination, mode, sourceRoot) {
80634
82439
  fs81.mkdirSync(path89.dirname(destination), { recursive: true });
80635
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
82440
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
80636
82441
  const opened = openRegularFileNoFollow(source, `copy source ${source}`, sourceRoot);
80637
82442
  try {
80638
82443
  await pipeline2(fs81.createReadStream("", {
@@ -81040,7 +82845,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81040
82845
  const reachesPhaseDeadline = command.timeout_ms === undefined || command.timeout_ms >= remainingMs;
81041
82846
  const timeoutMs2 = reachesPhaseDeadline ? remainingMs : command.timeout_ms;
81042
82847
  const started = Date.now();
81043
- const commandMarker = crypto6.randomBytes(32).toString("hex");
82848
+ const commandMarker = crypto7.randomBytes(32).toString("hex");
81044
82849
  return await new Promise((resolve, reject2) => {
81045
82850
  const child = spawn4(command.argv[0], command.argv.slice(1), {
81046
82851
  cwd: cwd2,
@@ -81077,7 +82882,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81077
82882
  return error2;
81078
82883
  return new BenchmarkCommandExecutionError(error2.code, error2.message, sanitizedOutput(stdout), sanitizedOutput(stderr), error2.durationMs);
81079
82884
  };
81080
- const fail = (error2) => {
82885
+ const fail2 = (error2) => {
81081
82886
  if (settled)
81082
82887
  return;
81083
82888
  settled = true;
@@ -81096,7 +82901,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81096
82901
  captured += chunk2.length;
81097
82902
  context.remainingOutputBytes -= chunk2.length;
81098
82903
  if (captured > spec.budget.max_output_bytes || context.remainingOutputBytes < 0) {
81099
- fail(new BenchmarkPhaseError("output_limit_exceeded", `command output exceeded budget: ${command.id}`));
82904
+ fail2(new BenchmarkPhaseError("output_limit_exceeded", `command output exceeded budget: ${command.id}`));
81100
82905
  return;
81101
82906
  }
81102
82907
  target.push(chunk2);
@@ -81119,14 +82924,14 @@ async function runCommand(command, root, spec, context, options = {}) {
81119
82924
  child.stdout?.on("data", (chunk2) => capture(stdout, chunk2));
81120
82925
  child.stderr?.on("data", (chunk2) => capture(stderr, chunk2));
81121
82926
  child.on("error", (error2) => {
81122
- fail(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
82927
+ fail2(new BenchmarkCommandExecutionError("command_start_failed", `failed to start ${command.id}: ${error2.message}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
81123
82928
  });
81124
82929
  timer = setTimeout(() => {
81125
82930
  if (reachesPhaseDeadline) {
81126
- fail(new BenchmarkPhaseError("phase_timeout", "phase budget expired"));
82931
+ fail2(new BenchmarkPhaseError("phase_timeout", "phase budget expired"));
81127
82932
  return;
81128
82933
  }
81129
- fail(new BenchmarkCommandExecutionError("command_timeout", `command timed out: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
82934
+ fail2(new BenchmarkCommandExecutionError("command_timeout", `command timed out: ${command.id}`, Buffer.concat(stdout), Buffer.concat(stderr), Date.now() - started));
81130
82935
  }, timeoutMs2);
81131
82936
  child.on("exit", (code, signal) => {
81132
82937
  if (settled)
@@ -81149,7 +82954,7 @@ async function runCommand(command, root, spec, context, options = {}) {
81149
82954
  async function writeLog(root, name, data, spec) {
81150
82955
  const destination = path89.join(root, name);
81151
82956
  fs81.mkdirSync(path89.dirname(destination), { recursive: true });
81152
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
82957
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
81153
82958
  try {
81154
82959
  fs81.writeFileSync(temporary, redactCommandOutput(data, spec), {
81155
82960
  flag: "wx",
@@ -81163,7 +82968,7 @@ async function writeLog(root, name, data, spec) {
81163
82968
  }
81164
82969
  function writeBufferAtomic(destination, data) {
81165
82970
  fs81.mkdirSync(path89.dirname(destination), { recursive: true });
81166
- const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
82971
+ const temporary = `${destination}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
81167
82972
  try {
81168
82973
  fs81.writeFileSync(temporary, data, { flag: "wx", mode: 384 });
81169
82974
  fs81.renameSync(temporary, destination);
@@ -82006,7 +83811,7 @@ async function executeEvaluate(spec, context) {
82006
83811
  if (spec.capture_workspace_archive) {
82007
83812
  const regularFiles = manifest.filter((entry) => entry.kind !== "symlink").map((entry) => entry.path);
82008
83813
  const archive = path89.join(spec.logs_root, "candidate-workspace.tar.gz");
82009
- const temporary = `${archive}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
83814
+ const temporary = `${archive}.${process.pid}.${crypto7.randomBytes(6).toString("hex")}.tmp`;
82010
83815
  try {
82011
83816
  await pack({ rootDir: spec.workspace_root, outFile: temporary, files: regularFiles });
82012
83817
  fs81.renameSync(temporary, archive);
@@ -82459,7 +84264,7 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
82459
84264
  }
82460
84265
 
82461
84266
  // src/cli/benchmark-control.ts
82462
- var import_picocolors53 = __toESM(require_picocolors(), 1);
84267
+ var import_picocolors60 = __toESM(require_picocolors(), 1);
82463
84268
  import fs85 from "node:fs";
82464
84269
  import path93 from "node:path";
82465
84270
 
@@ -82957,10 +84762,10 @@ class BenchmarkApiClient {
82957
84762
  }
82958
84763
  });
82959
84764
  }
82960
- exportManifest(benchmarkId, format, revisionId) {
84765
+ exportManifest(benchmarkId, format2, revisionId) {
82961
84766
  return this.request(`/${encodeURIComponent(benchmarkId)}/export`, {
82962
84767
  mode: "binary",
82963
- query: { format, revision_id: revisionId }
84768
+ query: { format: format2, revision_id: revisionId }
82964
84769
  });
82965
84770
  }
82966
84771
  exportBundle(benchmarkId, revisionId) {
@@ -83158,7 +84963,7 @@ class BenchmarkApiClient {
83158
84963
 
83159
84964
  // src/core/benchmark-authoring.ts
83160
84965
  var import_yaml7 = __toESM(require_dist(), 1);
83161
- import crypto7 from "node:crypto";
84966
+ import crypto8 from "node:crypto";
83162
84967
  import fs82 from "node:fs";
83163
84968
  import path90 from "node:path";
83164
84969
 
@@ -83957,7 +85762,7 @@ function renderYaml(value) {
83957
85762
  return import_yaml7.default.stringify(stableValue(value), { indent: 2, lineWidth: 0 });
83958
85763
  }
83959
85764
  function digest(data) {
83960
- return crypto7.createHash("sha256").update(data).digest("hex");
85765
+ return crypto8.createHash("sha256").update(data).digest("hex");
83961
85766
  }
83962
85767
  function assertRegularFile(filePath, label) {
83963
85768
  let stat;
@@ -84327,7 +86132,7 @@ function sameDestinationObjectIdentity(target, identity2) {
84327
86132
  }
84328
86133
  }
84329
86134
  function acquireDestinationWriteLock(root) {
84330
- const digest2 = crypto7.createHash("sha256").update(root).digest("hex").slice(0, 20);
86135
+ const digest2 = crypto8.createHash("sha256").update(root).digest("hex").slice(0, 20);
84331
86136
  const lockPath = path90.join(path90.dirname(root), `.brainbase-benchmark-write-lock-${digest2}`);
84332
86137
  let descriptor;
84333
86138
  try {
@@ -84413,7 +86218,7 @@ function closeDestinationIdentity(identity2) {
84413
86218
  throw cleanupError;
84414
86219
  }
84415
86220
  function createDirectoryClaim(target, identity2) {
84416
- const claimPath = path90.join(target, `.brainbase-benchmark-write-${crypto7.randomUUID()}`);
86221
+ const claimPath = path90.join(target, `.brainbase-benchmark-write-${crypto8.randomUUID()}`);
84417
86222
  try {
84418
86223
  fs82.writeFileSync(claimPath, "", { flag: "wx", mode: 384 });
84419
86224
  } catch (error2) {
@@ -84797,145 +86602,6 @@ function scaffoldBenchmarkDirectory(rootDir, options) {
84797
86602
  return writeBenchmarkDirectory(rootDir, project);
84798
86603
  }
84799
86604
 
84800
- // src/core/argv.ts
84801
- class ArgvParseError extends Error {
84802
- code = "invalid_arguments";
84803
- constructor(message) {
84804
- super(message);
84805
- this.name = "ArgvParseError";
84806
- }
84807
- }
84808
- function parseBoolean(value, name) {
84809
- switch (value.trim().toLowerCase()) {
84810
- case "":
84811
- case "0":
84812
- case "false":
84813
- case "no":
84814
- case "off":
84815
- return false;
84816
- case "1":
84817
- case "true":
84818
- case "yes":
84819
- case "on":
84820
- return true;
84821
- default:
84822
- throw new ArgvParseError(`Unrecognised value for ${name}: ${value}`);
84823
- }
84824
- }
84825
- function validateDefinitions(definitions) {
84826
- const names = new Map;
84827
- const keys2 = new Set;
84828
- for (const definition of definitions) {
84829
- if (!definition.key) {
84830
- throw new Error("argv option keys must not be empty");
84831
- }
84832
- if (keys2.has(definition.key)) {
84833
- throw new Error(`duplicate argv option key: ${definition.key}`);
84834
- }
84835
- keys2.add(definition.key);
84836
- for (const name of definition.names) {
84837
- if (!name.startsWith("-") || name === "-") {
84838
- throw new Error(`invalid argv option name: ${name}`);
84839
- }
84840
- if (name.includes("=")) {
84841
- throw new Error(`argv option names must not contain "=": ${name}`);
84842
- }
84843
- if (names.has(name)) {
84844
- throw new Error(`duplicate argv option name: ${name}`);
84845
- }
84846
- names.set(name, definition);
84847
- }
84848
- }
84849
- return names;
84850
- }
84851
- function findDefinition(token, definitions) {
84852
- const exact = definitions.get(token);
84853
- if (exact)
84854
- return { definition: exact, name: token };
84855
- const equals = token.indexOf("=");
84856
- if (equals < 1)
84857
- return null;
84858
- const name = token.slice(0, equals);
84859
- const definition = definitions.get(name);
84860
- return definition ? { definition, name, joined: token.slice(equals + 1) } : null;
84861
- }
84862
- function assignOption(options, definition, value, name) {
84863
- const current = options[definition.key];
84864
- if (definition.multiple) {
84865
- if (typeof value !== "string") {
84866
- throw new Error(`boolean argv option cannot be repeated: ${name}`);
84867
- }
84868
- options[definition.key] = [
84869
- ...Array.isArray(current) ? current : [],
84870
- value
84871
- ];
84872
- return;
84873
- }
84874
- if (current !== undefined) {
84875
- throw new ArgvParseError(`Option ${name} may only be provided once`);
84876
- }
84877
- options[definition.key] = value;
84878
- }
84879
- function parseArgv(argv, definitions) {
84880
- const byName = validateDefinitions(definitions);
84881
- const options = Object.create(null);
84882
- const positionals = [];
84883
- let optionsEnabled = true;
84884
- for (let index = 0;index < argv.length; index += 1) {
84885
- const token = argv[index];
84886
- if (!optionsEnabled) {
84887
- positionals.push(token);
84888
- continue;
84889
- }
84890
- if (token === "--") {
84891
- optionsEnabled = false;
84892
- continue;
84893
- }
84894
- if (!token.startsWith("-") || token === "-") {
84895
- positionals.push(token);
84896
- continue;
84897
- }
84898
- const hit = findDefinition(token, byName);
84899
- if (!hit) {
84900
- throw new ArgvParseError(`Unknown option: ${token.split("=", 1)[0]}`);
84901
- }
84902
- if (hit.definition.kind === "boolean") {
84903
- assignOption(options, hit.definition, hit.joined === undefined ? true : parseBoolean(hit.joined, hit.name), hit.name);
84904
- continue;
84905
- }
84906
- let value = hit.joined;
84907
- if (value === undefined) {
84908
- const next = argv[index + 1];
84909
- if (next === "--") {
84910
- value = argv[index + 2];
84911
- if (value === undefined) {
84912
- throw new ArgvParseError(`Option ${hit.name} requires a value`);
84913
- }
84914
- index += 2;
84915
- } else {
84916
- if (next === undefined || next.startsWith("-")) {
84917
- throw new ArgvParseError(`Option ${hit.name} requires a value; use ${hit.name}=<value> for values beginning with "-"`);
84918
- }
84919
- value = next;
84920
- index += 1;
84921
- }
84922
- }
84923
- assignOption(options, hit.definition, value, hit.name);
84924
- }
84925
- return { options, positionals };
84926
- }
84927
- function stringOption(parsed, key2) {
84928
- const value = parsed.options[key2];
84929
- return typeof value === "string" ? value : undefined;
84930
- }
84931
- function stringOptions(parsed, key2) {
84932
- const value = parsed.options[key2];
84933
- return Array.isArray(value) ? value : [];
84934
- }
84935
- function booleanOption(parsed, key2) {
84936
- return parsed.options[key2] === true;
84937
- }
84938
-
84939
86605
  // src/cli/benchmark-control-options.ts
84940
86606
  var DEFINITIONS = [
84941
86607
  { key: "agent", names: ["--agent", "-a"], kind: "value" },
@@ -85112,13 +86778,13 @@ function kebab(value) {
85112
86778
  }
85113
86779
 
85114
86780
  // src/cli/benchmark-control-io.ts
85115
- import crypto9 from "node:crypto";
86781
+ import crypto10 from "node:crypto";
85116
86782
  import fs84 from "node:fs";
85117
86783
  import os18 from "node:os";
85118
86784
  import path92 from "node:path";
85119
86785
 
85120
86786
  // src/core/benchmark-bundle.ts
85121
- import crypto8 from "node:crypto";
86787
+ import crypto9 from "node:crypto";
85122
86788
  import fs83 from "node:fs";
85123
86789
  import os17 from "node:os";
85124
86790
  import path91 from "node:path";
@@ -85136,7 +86802,7 @@ var MANIFEST_NAMES = [
85136
86802
  ];
85137
86803
  var MANIFEST_NAME_SET = new Set(MANIFEST_NAMES);
85138
86804
  function sha2562(data) {
85139
- return crypto8.createHash("sha256").update(data).digest("hex");
86805
+ return crypto9.createHash("sha256").update(data).digest("hex");
85140
86806
  }
85141
86807
  function normalizeArchivePath(raw, directory) {
85142
86808
  const candidate = directory ? raw.replace(/\/+$/, "") : raw;
@@ -85267,7 +86933,7 @@ async function writeBenchmarkBundle(outputPath, input) {
85267
86933
  validateMemberTree(portableMembers);
85268
86934
  const output = path91.resolve(outputPath);
85269
86935
  const staging = fs83.mkdtempSync(path91.join(os17.tmpdir(), "brainbase-benchmark-bundle-"));
85270
- const temporaryOutput = `${output}.${process.pid}.${crypto8.randomUUID()}.tmp`;
86936
+ const temporaryOutput = `${output}.${process.pid}.${crypto9.randomUUID()}.tmp`;
85271
86937
  try {
85272
86938
  const manifestData = Buffer.from(`${canonicalBenchmarkJson(project.manifest)}
85273
86939
  `, "utf8");
@@ -85530,7 +87196,7 @@ function writeBinaryOutput(cwd2, output, bytes, fallbackName, force) {
85530
87196
  const target = path92.resolve(cwd2, output ?? safeExportFilename(undefined, fallbackName));
85531
87197
  fs84.mkdirSync(path92.dirname(target), { recursive: true });
85532
87198
  assertSafeOutputDestination(target, force);
85533
- const temporary = `${target}.${process.pid}.${crypto9.randomUUID()}.tmp`;
87199
+ const temporary = `${target}.${process.pid}.${crypto10.randomUUID()}.tmp`;
85534
87200
  const descriptor = fs84.openSync(temporary, "wx", 384);
85535
87201
  let closed = false;
85536
87202
  try {
@@ -85804,20 +87470,20 @@ async function emitCursorPages(output, fetchPage, options) {
85804
87470
  function commandHelp() {
85805
87471
  return [
85806
87472
  "",
85807
- ` ${import_picocolors53.default.bold("brainbase benchmark")} ${import_picocolors53.default.dim("<command> [options]")}`,
87473
+ ` ${import_picocolors60.default.bold("brainbase benchmark")} ${import_picocolors60.default.dim("<command> [options]")}`,
85808
87474
  "",
85809
- ` ${import_picocolors53.default.cyan("init | list | show | create | validate | pull | push")}`,
85810
- ` ${import_picocolors53.default.cyan("publish | revisions | import | export | archive | restore")}`,
85811
- ` ${import_picocolors53.default.cyan("run plan|start|watch|list|show|cancel")}`,
85812
- ` ${import_picocolors53.default.cyan("results | diagnoses | attempt | artifacts | export-results")}`,
85813
- ` ${import_picocolors53.default.cyan("history | baseline show|set|clear")}`,
87475
+ ` ${import_picocolors60.default.cyan("init | list | show | create | validate | pull | push")}`,
87476
+ ` ${import_picocolors60.default.cyan("publish | revisions | import | export | archive | restore")}`,
87477
+ ` ${import_picocolors60.default.cyan("run plan|start|watch|list|show|cancel")}`,
87478
+ ` ${import_picocolors60.default.cyan("results | diagnoses | attempt | artifacts | export-results")}`,
87479
+ ` ${import_picocolors60.default.cyan("history | baseline show|set|clear")}`,
85814
87480
  "",
85815
- ` ${import_picocolors53.default.dim("Use --agent to override brainbase.agent.yaml, and --json or --jsonl for automation.")}`,
87481
+ ` ${import_picocolors60.default.dim("Use --agent to override brainbase.agent.yaml, and --json or --jsonl for automation.")}`,
85816
87482
  "",
85817
- ` ${import_picocolors53.default.bold("benchmark runtime (machine-only)")}`,
85818
- ` ${import_picocolors53.default.cyan("hydrate")} ${import_picocolors53.default.dim("--spec <path> --result <path> --json")}`,
85819
- ` ${import_picocolors53.default.cyan("evaluate")} ${import_picocolors53.default.dim("--spec <path> --result <path> --json")}`,
85820
- ` ${import_picocolors53.default.cyan("capabilities")} ${import_picocolors53.default.dim("--json")}`,
87483
+ ` ${import_picocolors60.default.bold("benchmark runtime (machine-only)")}`,
87484
+ ` ${import_picocolors60.default.cyan("hydrate")} ${import_picocolors60.default.dim("--spec <path> --result <path> --json")}`,
87485
+ ` ${import_picocolors60.default.cyan("evaluate")} ${import_picocolors60.default.dim("--spec <path> --result <path> --json")}`,
87486
+ ` ${import_picocolors60.default.cyan("capabilities")} ${import_picocolors60.default.dim("--json")}`,
85821
87487
  ""
85822
87488
  ].join(`
85823
87489
  `);
@@ -86149,19 +87815,19 @@ async function runExport(cwd2, argv, deps) {
86149
87815
  ]);
86150
87816
  expectPositionals(options.positionals, 1, 1, "brainbase benchmark export <benchmark-id> --format <yaml|json|bundle>");
86151
87817
  const benchmarkId = options.positionals[0];
86152
- const format = optionalNonblank(options.parsed, "format") ?? "yaml";
86153
- if (!["yaml", "json", "bundle"].includes(format)) {
87818
+ const format2 = optionalNonblank(options.parsed, "format") ?? "yaml";
87819
+ if (!["yaml", "json", "bundle"].includes(format2)) {
86154
87820
  throw usageError("--format must be yaml, json, or bundle");
86155
87821
  }
86156
87822
  const { client, output } = context(cwd2, "benchmark export", options, deps);
86157
87823
  const revision = optionalNonblank(options.parsed, "revision");
86158
- const response = format === "bundle" ? await client.exportBundle(benchmarkId, revision) : await client.exportManifest(benchmarkId, format, revision);
86159
- const extension2 = format === "bundle" ? "tar.gz" : format;
87824
+ const response = format2 === "bundle" ? await client.exportBundle(benchmarkId, revision) : await client.exportManifest(benchmarkId, format2, revision);
87825
+ const extension2 = format2 === "bundle" ? "tar.gz" : format2;
86160
87826
  const fallback = safeExportFilename(response.filename, `benchmark-${benchmarkId}.${extension2}`);
86161
87827
  const written = writeBinaryOutput(cwd2, optionalNonblank(options.parsed, "output"), response.bytes, fallback, bool(options.parsed, "force"));
86162
87828
  output.result({
86163
87829
  benchmark_id: benchmarkId,
86164
- format,
87830
+ format: format2,
86165
87831
  path: written,
86166
87832
  size_bytes: response.bytes.byteLength
86167
87833
  });
@@ -86971,7 +88637,7 @@ function terminatePhase(child) {
86971
88637
  }
86972
88638
  }
86973
88639
  function createAnonymousSpecFd(bytes) {
86974
- const temporary = path94.join(os19.tmpdir(), `brainbase-benchmark-spec-${process.pid}-${crypto10.randomBytes(12).toString("hex")}`);
88640
+ const temporary = path94.join(os19.tmpdir(), `brainbase-benchmark-spec-${process.pid}-${crypto11.randomBytes(12).toString("hex")}`);
86975
88641
  fs86.writeFileSync(temporary, bytes, { flag: "wx", mode: 384 });
86976
88642
  try {
86977
88643
  const fd = fs86.openSync(temporary, "r");
@@ -87015,7 +88681,7 @@ async function runSupervisedPhase(phase, parsed, write) {
87015
88681
  `);
87016
88682
  return 1;
87017
88683
  }
87018
- const childToken = crypto10.randomBytes(32).toString("hex");
88684
+ const childToken = crypto11.randomBytes(32).toString("hex");
87019
88685
  let specFd;
87020
88686
  try {
87021
88687
  specFd = createAnonymousSpecFd(specBytes);
@@ -87200,148 +88866,161 @@ var SUBCOMMAND_OWNED_FLAGS = {
87200
88866
  function help() {
87201
88867
  const out = [];
87202
88868
  out.push("");
87203
- out.push(` ${brandTint("◆")} ${import_picocolors54.default.bold("brainbase")} ${import_picocolors54.default.dim(`v${VERSION}`)}`);
87204
- out.push(` ${import_picocolors54.default.dim("connect your local agent to the brainbase platform")}`);
88869
+ out.push(` ${brandTint("◆")} ${import_picocolors61.default.bold("brainbase")} ${import_picocolors61.default.dim(`v${VERSION}`)}`);
88870
+ out.push(` ${import_picocolors61.default.dim("connect your local agent to the brainbase platform")}`);
87205
88871
  out.push("");
87206
88872
  out.push(divider("USAGE"));
87207
88873
  out.push("");
87208
- out.push(` ${import_picocolors54.default.bold("brainbase")} ${import_picocolors54.default.dim("<command> [options]")}`);
88874
+ out.push(` ${import_picocolors61.default.bold("brainbase")} ${import_picocolors61.default.dim("<command> [options]")}`);
87209
88875
  out.push("");
87210
88876
  out.push(divider("AUTH"));
87211
88877
  out.push("");
87212
- out.push(` ${import_picocolors54.default.cyan("login")} ${import_picocolors54.default.dim(" open the web app and connect this device")}`);
87213
- out.push(` ${import_picocolors54.default.cyan("logout")} ${import_picocolors54.default.dim(" clear the local session")}`);
87214
- out.push(` ${import_picocolors54.default.cyan("whoami")} ${import_picocolors54.default.dim("[--json]")} ${import_picocolors54.default.dim(" show which credential is in use and what it covers")}`);
88878
+ out.push(` ${import_picocolors61.default.cyan("login")} ${import_picocolors61.default.dim(" open the web app and connect this device")}`);
88879
+ out.push(` ${import_picocolors61.default.cyan("logout")} ${import_picocolors61.default.dim(" clear the local session")}`);
88880
+ out.push(` ${import_picocolors61.default.cyan("whoami")} ${import_picocolors61.default.dim("[--json]")} ${import_picocolors61.default.dim(" show which credential is in use and what it covers")}`);
87215
88881
  out.push("");
87216
88882
  out.push(divider("DISCOVERY"));
87217
88883
  out.push("");
87218
- out.push(` ${import_picocolors54.default.cyan("team list")} ${import_picocolors54.default.dim("show the teams you can create agents in")}`);
87219
- out.push(` ${import_picocolors54.default.cyan("agent list")} ${import_picocolors54.default.dim("show a team's agents and their ids")}`);
88884
+ out.push(` ${import_picocolors61.default.cyan("team list")} ${import_picocolors61.default.dim("show the teams you can create agents in")}`);
88885
+ out.push(` ${import_picocolors61.default.cyan("agent list")} ${import_picocolors61.default.dim("show a team's agents and their ids")}`);
87220
88886
  out.push("");
87221
88887
  out.push(divider("LINKED AGENT"));
87222
88888
  out.push("");
87223
- out.push(` ${import_picocolors54.default.cyan("agent init")} ${import_picocolors54.default.dim("write a starter brainbase.agent.yaml here — offline, no login needed")}`);
87224
- out.push(` ${import_picocolors54.default.cyan("agent create")} ${import_picocolors54.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
87225
- out.push(` ${import_picocolors54.default.cyan("agent pull")} ${import_picocolors54.default.dim("[<id>]")} ${import_picocolors54.default.dim("bring cloud changes into this folder (--force to override; --run-entrypoint to also execute the agent entrypoint)")}`);
87226
- out.push(` ${import_picocolors54.default.cyan("agent push")} ${import_picocolors54.default.dim("send local changes to the cloud (--force to overwrite cloud-side conflicts with local)")}`);
87227
- out.push(` ${import_picocolors54.default.cyan("agent unpack")} ${import_picocolors54.default.dim("install the claimed agent into a harness layout")}`);
87228
- out.push(` ${import_picocolors54.default.cyan("link")} ${import_picocolors54.default.dim("attach this folder to an existing agent")}`);
87229
- out.push(` ${import_picocolors54.default.cyan("agent status")} ${import_picocolors54.default.dim("show what would pull and what would push")}`);
87230
- out.push(` ${import_picocolors54.default.cyan("agent connections")} ${import_picocolors54.default.dim("show which integrations this agent is wired to (--json for CI)")}`);
87231
- out.push(` ${import_picocolors54.default.cyan("agent connect")} ${import_picocolors54.default.dim("<name>")} ${import_picocolors54.default.dim("connect slack or meeting from the terminal")}`);
87232
- out.push(` ${import_picocolors54.default.cyan("agent disconnect")} ${import_picocolors54.default.dim("<name>")} ${import_picocolors54.default.dim("revoke a slack or meeting install")}`);
87233
- out.push(` ${import_picocolors54.default.cyan("agent env")} ${import_picocolors54.default.dim("print export lines for `eval $(brainbase agent env)`")}`);
87234
- out.push(` ${import_picocolors54.default.cyan("run")} ${import_picocolors54.default.dim("<cmd> [args...]")} ${import_picocolors54.default.dim("run <cmd> with secrets.env loaded into env")}`);
87235
- out.push(` ${import_picocolors54.default.cyan("status")} ${import_picocolors54.default.dim("show what this folder is linked to")}`);
87236
- out.push(` ${import_picocolors54.default.cyan("unlink")} ${import_picocolors54.default.dim("disconnect this folder")}`);
88889
+ out.push(` ${import_picocolors61.default.cyan("agent init")} ${import_picocolors61.default.dim("write a starter brainbase.agent.yaml here — offline, no login needed")}`);
88890
+ out.push(` ${import_picocolors61.default.cyan("agent create")} ${import_picocolors61.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
88891
+ out.push(` ${import_picocolors61.default.cyan("agent pull")} ${import_picocolors61.default.dim("[<id>]")} ${import_picocolors61.default.dim("bring cloud changes into this folder (--force to override; --run-entrypoint to also execute the agent entrypoint)")}`);
88892
+ out.push(` ${import_picocolors61.default.cyan("agent push")} ${import_picocolors61.default.dim("send local changes to the cloud (--force to overwrite cloud-side conflicts with local)")}`);
88893
+ out.push(` ${import_picocolors61.default.cyan("agent unpack")} ${import_picocolors61.default.dim("install the claimed agent into a harness layout")}`);
88894
+ out.push(` ${import_picocolors61.default.cyan("link")} ${import_picocolors61.default.dim("attach this folder to an existing agent")}`);
88895
+ out.push(` ${import_picocolors61.default.cyan("agent status")} ${import_picocolors61.default.dim("show what would pull and what would push")}`);
88896
+ out.push(` ${import_picocolors61.default.cyan("agent connections")} ${import_picocolors61.default.dim("show which integrations this agent is wired to (--json for CI)")}`);
88897
+ out.push(` ${import_picocolors61.default.cyan("agent connect")} ${import_picocolors61.default.dim("<name>")} ${import_picocolors61.default.dim("connect slack or meeting from the terminal")}`);
88898
+ out.push(` ${import_picocolors61.default.cyan("agent disconnect")} ${import_picocolors61.default.dim("<name>")} ${import_picocolors61.default.dim("revoke a slack or meeting install")}`);
88899
+ out.push(` ${import_picocolors61.default.cyan("agent env")} ${import_picocolors61.default.dim("print export lines for `eval $(brainbase agent env)`")}`);
88900
+ out.push(` ${import_picocolors61.default.cyan("run")} ${import_picocolors61.default.dim("<cmd> [args...]")} ${import_picocolors61.default.dim("run <cmd> with secrets.env loaded into env")}`);
88901
+ out.push(` ${import_picocolors61.default.cyan("status")} ${import_picocolors61.default.dim("show what this folder is linked to")}`);
88902
+ out.push(` ${import_picocolors61.default.cyan("unlink")} ${import_picocolors61.default.dim("disconnect this folder")}`);
87237
88903
  out.push("");
87238
88904
  out.push(divider("TASKS"));
87239
88905
  out.push("");
87240
- out.push(` ${import_picocolors54.default.cyan("task create")} ${import_picocolors54.default.dim("--message <text>")} ${import_picocolors54.default.dim("create a managed task and start its first run")}`);
88906
+ out.push(` ${import_picocolors61.default.cyan("task create")} ${import_picocolors61.default.dim("--message <text>")} ${import_picocolors61.default.dim("create a managed task and start its first run (--wait to block on it)")}`);
88907
+ out.push(` ${import_picocolors61.default.cyan("task list")} ${import_picocolors61.default.dim("recent tasks you can reach")}`);
88908
+ out.push(` ${import_picocolors61.default.cyan("task get")} ${import_picocolors61.default.dim("<task-id>")} ${import_picocolors61.default.dim("one task: status, agent, machine, eval verdicts")}`);
88909
+ out.push(` ${import_picocolors61.default.cyan("task logs")} ${import_picocolors61.default.dim("<task-id> [--follow]")} ${import_picocolors61.default.dim("one line per event in the task's transcript, or tail it live")}`);
88910
+ out.push("");
88911
+ out.push(divider("SANDBOXES"));
88912
+ out.push("");
88913
+ out.push(` ${import_picocolors61.default.cyan("machine ls")} ${import_picocolors61.default.dim("[--all]")} ${import_picocolors61.default.dim("list your sandboxes — live ones only unless --all")}`);
88914
+ out.push(` ${import_picocolors61.default.cyan("machine rm")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("tear a sandbox down (it bills until you do)")}`);
87241
88915
  out.push("");
87242
88916
  out.push(divider("BENCHMARKS"));
87243
88917
  out.push("");
87244
- out.push(` ${import_picocolors54.default.cyan("benchmark list")} ${import_picocolors54.default.dim("list benchmarks for the linked agent")}`);
87245
- out.push(` ${import_picocolors54.default.cyan("benchmark init")} ${import_picocolors54.default.dim("[directory]")} ${import_picocolors54.default.dim("scaffold a local benchmark")}`);
87246
- out.push(` ${import_picocolors54.default.cyan("benchmark create")} ${import_picocolors54.default.dim("[path|-]")} ${import_picocolors54.default.dim("create a benchmark draft")}`);
87247
- out.push(` ${import_picocolors54.default.cyan("benchmark run")} ${import_picocolors54.default.dim("<benchmark> --yes")} ${import_picocolors54.default.dim("plan and start a benchmark run")}`);
87248
- out.push(` ${import_picocolors54.default.cyan("benchmark results")} ${import_picocolors54.default.dim("<run>")} ${import_picocolors54.default.dim("inspect normalized benchmark results")}`);
88918
+ out.push(` ${import_picocolors61.default.cyan("benchmark list")} ${import_picocolors61.default.dim("list benchmarks for the linked agent")}`);
88919
+ out.push(` ${import_picocolors61.default.cyan("benchmark init")} ${import_picocolors61.default.dim("[directory]")} ${import_picocolors61.default.dim("scaffold a local benchmark")}`);
88920
+ out.push(` ${import_picocolors61.default.cyan("benchmark create")} ${import_picocolors61.default.dim("[path|-]")} ${import_picocolors61.default.dim("create a benchmark draft")}`);
88921
+ out.push(` ${import_picocolors61.default.cyan("benchmark run")} ${import_picocolors61.default.dim("<benchmark> --yes")} ${import_picocolors61.default.dim("plan and start a benchmark run")}`);
88922
+ out.push(` ${import_picocolors61.default.cyan("benchmark results")} ${import_picocolors61.default.dim("<run>")} ${import_picocolors61.default.dim("inspect normalized benchmark results")}`);
87249
88923
  out.push("");
87250
88924
  out.push(divider("BENCHMARK RUNTIME"));
87251
88925
  out.push("");
87252
- out.push(` ${import_picocolors54.default.cyan("benchmark hydrate")} ${import_picocolors54.default.dim("--spec <path> --result <path> --json")}`);
87253
- out.push(` ${import_picocolors54.default.cyan("benchmark evaluate")} ${import_picocolors54.default.dim("--spec <path> --result <path> --json")}`);
87254
- out.push(` ${import_picocolors54.default.cyan("benchmark capabilities")} ${import_picocolors54.default.dim("--json")}`);
88926
+ out.push(` ${import_picocolors61.default.cyan("benchmark hydrate")} ${import_picocolors61.default.dim("--spec <path> --result <path> --json")}`);
88927
+ out.push(` ${import_picocolors61.default.cyan("benchmark evaluate")} ${import_picocolors61.default.dim("--spec <path> --result <path> --json")}`);
88928
+ out.push(` ${import_picocolors61.default.cyan("benchmark capabilities")} ${import_picocolors61.default.dim("--json")}`);
87255
88929
  out.push("");
87256
88930
  out.push(divider("ORCHESTRATIONS"));
87257
88931
  out.push("");
87258
- out.push(` ${import_picocolors54.default.cyan("orchestration create")} ${import_picocolors54.default.dim("claim a local orchestration manifest and create it in the cloud")}`);
87259
- out.push(` ${import_picocolors54.default.cyan("orchestration list")} ${import_picocolors54.default.dim("list orchestrations under a team")}`);
87260
- out.push(` ${import_picocolors54.default.cyan("orchestration pull")} ${import_picocolors54.default.dim("<id>")} ${import_picocolors54.default.dim("recursively fetch an orchestration + every member agent")}`);
87261
- out.push(` ${import_picocolors54.default.cyan("orchestration push")} ${import_picocolors54.default.dim("recursively push each member, then update the graph")}`);
87262
- out.push(` ${import_picocolors54.default.cyan("orchestration status")} ${import_picocolors54.default.dim("show what would push and what would pull")}`);
88932
+ out.push(` ${import_picocolors61.default.cyan("orchestration create")} ${import_picocolors61.default.dim("claim a local orchestration manifest and create it in the cloud")}`);
88933
+ out.push(` ${import_picocolors61.default.cyan("orchestration list")} ${import_picocolors61.default.dim("list orchestrations under a team")}`);
88934
+ out.push(` ${import_picocolors61.default.cyan("orchestration pull")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("recursively fetch an orchestration + every member agent")}`);
88935
+ out.push(` ${import_picocolors61.default.cyan("orchestration push")} ${import_picocolors61.default.dim("recursively push each member, then update the graph")}`);
88936
+ out.push(` ${import_picocolors61.default.cyan("orchestration status")} ${import_picocolors61.default.dim("show what would push and what would pull")}`);
87263
88937
  out.push("");
87264
88938
  out.push(divider("TEMPLATES"));
87265
88939
  out.push("");
87266
- out.push(` ${import_picocolors54.default.cyan("template pack")} ${import_picocolors54.default.dim("bundle the current agent into a template")}`);
87267
- out.push(` ${import_picocolors54.default.cyan("template publish")} ${import_picocolors54.default.dim("upload a template to the registry")}`);
87268
- out.push(` ${import_picocolors54.default.cyan("template search")} ${import_picocolors54.default.dim("[query]")} ${import_picocolors54.default.dim("search the registry")}`);
87269
- out.push(` ${import_picocolors54.default.cyan("template info")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("show registry details for a template")}`);
87270
- out.push(` ${import_picocolors54.default.cyan("template onboard")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("install (or refresh) a template")}`);
87271
- out.push(` ${import_picocolors54.default.cyan("template list")} ${import_picocolors54.default.dim("show installed templates")}`);
87272
- out.push(` ${import_picocolors54.default.cyan("template remove")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("uninstall a template")}`);
88940
+ out.push(` ${import_picocolors61.default.cyan("template pack")} ${import_picocolors61.default.dim("bundle the current agent into a template")}`);
88941
+ out.push(` ${import_picocolors61.default.cyan("template publish")} ${import_picocolors61.default.dim("upload a template to the registry")}`);
88942
+ out.push(` ${import_picocolors61.default.cyan("template search")} ${import_picocolors61.default.dim("[query]")} ${import_picocolors61.default.dim("search the registry")}`);
88943
+ out.push(` ${import_picocolors61.default.cyan("template info")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("show registry details for a template")}`);
88944
+ out.push(` ${import_picocolors61.default.cyan("template onboard")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("install (or refresh) a template")}`);
88945
+ out.push(` ${import_picocolors61.default.cyan("template list")} ${import_picocolors61.default.dim("show installed templates")}`);
88946
+ out.push(` ${import_picocolors61.default.cyan("template remove")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("uninstall a template")}`);
87273
88947
  out.push("");
87274
88948
  out.push(divider("SKILLS"));
87275
88949
  out.push("");
87276
- out.push(` ${import_picocolors54.default.cyan("skill add")} ${import_picocolors54.default.dim("<source>")} ${import_picocolors54.default.dim("install a skill (github / git / brainbase)")}`);
87277
- out.push(` ${import_picocolors54.default.cyan("skill list")} ${import_picocolors54.default.dim("show locally installed skills + their source")}`);
87278
- out.push(` ${import_picocolors54.default.cyan("skill update")} ${import_picocolors54.default.dim("<slug>")} ${import_picocolors54.default.dim("re-fetch a skill from its recorded source")}`);
87279
- out.push(` ${import_picocolors54.default.cyan("skill remove")} ${import_picocolors54.default.dim("<slug>")} ${import_picocolors54.default.dim("uninstall a skill")}`);
87280
- out.push(` ${import_picocolors54.default.cyan("skill search")} ${import_picocolors54.default.dim("[query]")} ${import_picocolors54.default.dim("search the brainbase skill registry")}`);
87281
- out.push(` ${import_picocolors54.default.cyan("skill info")} ${import_picocolors54.default.dim("<creator/slug>")} ${import_picocolors54.default.dim("show registry details for a skill")}`);
87282
- out.push(` ${import_picocolors54.default.cyan("skill publish")} ${import_picocolors54.default.dim("[dir]")} ${import_picocolors54.default.dim("publish a SKILL.md folder (defaults to .)")}`);
88950
+ out.push(` ${import_picocolors61.default.cyan("skill add")} ${import_picocolors61.default.dim("<source>")} ${import_picocolors61.default.dim("install a skill (github / git / brainbase)")}`);
88951
+ out.push(` ${import_picocolors61.default.cyan("skill list")} ${import_picocolors61.default.dim("show locally installed skills + their source")}`);
88952
+ out.push(` ${import_picocolors61.default.cyan("skill update")} ${import_picocolors61.default.dim("<slug>")} ${import_picocolors61.default.dim("re-fetch a skill from its recorded source")}`);
88953
+ out.push(` ${import_picocolors61.default.cyan("skill remove")} ${import_picocolors61.default.dim("<slug>")} ${import_picocolors61.default.dim("uninstall a skill")}`);
88954
+ out.push(` ${import_picocolors61.default.cyan("skill search")} ${import_picocolors61.default.dim("[query]")} ${import_picocolors61.default.dim("search the brainbase skill registry")}`);
88955
+ out.push(` ${import_picocolors61.default.cyan("skill info")} ${import_picocolors61.default.dim("<creator/slug>")} ${import_picocolors61.default.dim("show registry details for a skill")}`);
88956
+ out.push(` ${import_picocolors61.default.cyan("skill publish")} ${import_picocolors61.default.dim("[dir]")} ${import_picocolors61.default.dim("publish a SKILL.md folder (defaults to .)")}`);
87283
88957
  out.push("");
87284
88958
  out.push(divider("CLI TOKENS"));
87285
88959
  out.push("");
87286
- out.push(` ${import_picocolors54.default.cyan("token create")} ${import_picocolors54.default.dim("issue a long-lived CLI key for CI / scripts")}`);
87287
- out.push(` ${import_picocolors54.default.cyan("token list")} ${import_picocolors54.default.dim("show your tokens")}`);
87288
- out.push(` ${import_picocolors54.default.cyan("token rename")} ${import_picocolors54.default.dim("<id>")} ${import_picocolors54.default.dim("relabel a token")}`);
87289
- out.push(` ${import_picocolors54.default.cyan("token revoke")} ${import_picocolors54.default.dim("<id>")} ${import_picocolors54.default.dim("revoke a token")}`);
88960
+ out.push(` ${import_picocolors61.default.cyan("token create")} ${import_picocolors61.default.dim("issue a long-lived CLI key for CI / scripts")}`);
88961
+ out.push(` ${import_picocolors61.default.cyan("token list")} ${import_picocolors61.default.dim("show your tokens")}`);
88962
+ out.push(` ${import_picocolors61.default.cyan("token rename")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("relabel a token")}`);
88963
+ out.push(` ${import_picocolors61.default.cyan("token revoke")} ${import_picocolors61.default.dim("<id>")} ${import_picocolors61.default.dim("revoke a token")}`);
87290
88964
  out.push("");
87291
88965
  out.push(divider("MCP"));
87292
88966
  out.push("");
87293
- out.push(` ${import_picocolors54.default.cyan("mcp check")} ${import_picocolors54.default.dim("[--json]")} ${import_picocolors54.default.dim("verify MCP server connectivity through the brainbase proxy (runs at sandbox bootstrap)")}`);
87294
- out.push(` ${import_picocolors54.default.cyan("mcp list")} ${import_picocolors54.default.dim("[--json]")} ${import_picocolors54.default.dim("show configured servers with OAuth state and expiry")}`);
88967
+ out.push(` ${import_picocolors61.default.cyan("mcp check")} ${import_picocolors61.default.dim("[--json]")} ${import_picocolors61.default.dim("verify MCP server connectivity through the brainbase proxy (runs at sandbox bootstrap)")}`);
88968
+ out.push(` ${import_picocolors61.default.cyan("mcp list")} ${import_picocolors61.default.dim("[--json]")} ${import_picocolors61.default.dim("show configured servers with OAuth state and expiry")}`);
87295
88969
  out.push("");
87296
88970
  out.push(divider("FLAGS"));
87297
88971
  out.push("");
87298
- out.push(` ${import_picocolors54.default.dim("--harness <id>")} force harness for onboard / sync (e.g. claude-code)`);
87299
- out.push(` ${import_picocolors54.default.dim("--scope <s>")} force scope: global | project`);
87300
- out.push(` ${import_picocolors54.default.dim("--yes, -y")} skip confirmations / auto-overwrite`);
87301
- out.push(` ${import_picocolors54.default.dim("--agent <id>")} for link/task create: use this agent id explicitly`);
87302
- out.push(` ${import_picocolors54.default.dim("--message <text>")} for task create: required first user message`);
87303
- out.push(` ${import_picocolors54.default.dim("--title <text>")} for task create: optional task title`);
87304
- out.push(` ${import_picocolors54.default.dim("--model <id>")} for task create: optional model override`);
87305
- out.push(` ${import_picocolors54.default.dim("--org <id-or-slug>")} pick the organization (team/agent list, agent create, orchestration create/list)`);
87306
- out.push(` ${import_picocolors54.default.dim("--team <id>")} pick the team, same commands (works without --org)`);
87307
- out.push(` ${import_picocolors54.default.dim("--json")} machine-readable output for supported commands`);
87308
- out.push(` ${import_picocolors54.default.dim("--no-tracking")} for link: skip routing LLM traffic through brainbase`);
87309
- out.push(` ${import_picocolors54.default.dim("--track")} for agent create: enable tracking non-interactively (off without a TTY)`);
87310
- out.push(` ${import_picocolors54.default.dim("--shell <sh|fish>")} for agent env: pick output format (auto-detected from $SHELL)`);
87311
- out.push(` ${import_picocolors54.default.dim("--bot-token <t>")} for agent connect slack (or BRAINBASE_SLACK_BOT_TOKEN, or stdin)`);
87312
- out.push(` ${import_picocolors54.default.dim("--signing-secret <s>")} for agent connect slack (or BRAINBASE_SLACK_SIGNING_SECRET, or stdin)`);
87313
- out.push(` ${import_picocolors54.default.dim("--bot-name <name>")} for agent connect meeting: the bot's display name`);
87314
- out.push(` ${import_picocolors54.default.dim("--full")} for agent init: write a commented template covering every block`);
87315
- out.push(` ${import_picocolors54.default.dim("--minimal")} for agent init: write the starter manifest (the default)`);
87316
- out.push(` ${import_picocolors54.default.dim("--all")} for template list: include installs from other folders`);
87317
- out.push(` ${import_picocolors54.default.dim("--web <url>")} for login: web app URL (default https://app.brainbaselabs.com)`);
88972
+ out.push(` ${import_picocolors61.default.dim("--harness <id>")} force harness for onboard / sync (e.g. claude-code)`);
88973
+ out.push(` ${import_picocolors61.default.dim("--scope <s>")} force scope: global | project`);
88974
+ out.push(` ${import_picocolors61.default.dim("--yes, -y")} skip confirmations / auto-overwrite`);
88975
+ out.push(` ${" ".repeat("--yes, -y".length)} required for destructive commands with no terminal (pipes, CI)`);
88976
+ out.push(` ${import_picocolors61.default.dim("--agent <id>")} for link/task create: use this agent id explicitly (task list: filter to it)`);
88977
+ out.push(` ${import_picocolors61.default.dim("--message <text>")} for task create: required first user message`);
88978
+ out.push(` ${import_picocolors61.default.dim("--title <text>")} for task create: optional task title`);
88979
+ out.push(` ${import_picocolors61.default.dim("--model <id>")} for task create: optional model override`);
88980
+ out.push(` ${import_picocolors61.default.dim("--wait")} for task create: block until the task finishes; exit 1 unless it succeeded`);
88981
+ out.push(` ${import_picocolors61.default.dim("--timeout <secs>")} for task create --wait: give up after <secs> and exit 1`);
88982
+ out.push(` ${import_picocolors61.default.dim("--limit <n>")} for task list (1-200), task logs (events to print; with --follow, the first page size) and machine ls`);
88983
+ out.push(` ${import_picocolors61.default.dim("--follow, -f")} for task logs: stay attached and print events as they land`);
88984
+ out.push(` ${import_picocolors61.default.dim("--org <id-or-slug>")} pick the organization (team/agent list, agent create, orchestration create/list)`);
88985
+ out.push(` ${import_picocolors61.default.dim("--team <id>")} pick the team, same commands (works without --org)`);
88986
+ out.push(` ${import_picocolors61.default.dim("--json")} machine-readable output for supported commands`);
88987
+ out.push(` ${import_picocolors61.default.dim("--no-tracking")} for link: skip routing LLM traffic through brainbase`);
88988
+ out.push(` ${import_picocolors61.default.dim("--track")} for agent create: enable tracking non-interactively (off without a TTY)`);
88989
+ out.push(` ${import_picocolors61.default.dim("--shell <sh|fish>")} for agent env: pick output format (auto-detected from $SHELL)`);
88990
+ out.push(` ${import_picocolors61.default.dim("--bot-token <t>")} for agent connect slack (or BRAINBASE_SLACK_BOT_TOKEN, or stdin)`);
88991
+ out.push(` ${import_picocolors61.default.dim("--signing-secret <s>")} for agent connect slack (or BRAINBASE_SLACK_SIGNING_SECRET, or stdin)`);
88992
+ out.push(` ${import_picocolors61.default.dim("--bot-name <name>")} for agent connect meeting: the bot's display name`);
88993
+ out.push(` ${import_picocolors61.default.dim("--full")} for agent init: write a commented template covering every block`);
88994
+ out.push(` ${import_picocolors61.default.dim("--minimal")} for agent init: write the starter manifest (the default)`);
88995
+ out.push(` ${import_picocolors61.default.dim("--all")} for template list: include installs from other folders; for machine ls: include torn-down machines`);
88996
+ out.push(` ${import_picocolors61.default.dim("--web <url>")} for login: web app URL (default https://app.brainbaselabs.com)`);
87318
88997
  out.push("");
87319
88998
  out.push(divider("ENV"));
87320
88999
  out.push("");
87321
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_HOME")} override the local config dir (default ~/.brainbase)`);
87322
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_DEBUG")} print full stack traces on error (any value; unset to disable)`);
87323
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_WEB_URL")} override the web app URL used by login`);
87324
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_CONTROL_PLANE_URL")} override the MAS host (/v2/cli; task create uses /v2/tasks)`);
87325
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_API_URL")} legacy KLS host override (uses /api/cli; proxy/registry fallback)`);
87326
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_PROXY_URL")} override the model-proxy URL used by harness traffic (default https://api.v1.brainbaselabs.com)`);
87327
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_REGISTRY_URL")} override the registry API URL (default https://api.v1.brainbaselabs.com)`);
87328
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT; the only PAT control-plane commands accept (token.json is not read there)`);
87329
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
87330
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
87331
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
89000
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_HOME")} override the local config dir (default ~/.brainbase)`);
89001
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_DEBUG")} print full stack traces on error (any value; unset to disable)`);
89002
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_WEB_URL")} override the web app URL used by login`);
89003
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_CONTROL_PLANE_URL")} override the MAS host (/v2/cli; task create uses /v2/tasks)`);
89004
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_API_URL")} legacy KLS host override (uses /api/cli; proxy/registry fallback)`);
89005
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_PROXY_URL")} override the model-proxy URL used by harness traffic (default https://api.v1.brainbaselabs.com)`);
89006
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_REGISTRY_URL")} override the registry API URL (default https://api.v1.brainbaselabs.com)`);
89007
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT; the only PAT control-plane commands accept (token.json is not read there)`);
89008
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
89009
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
89010
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
87332
89011
  out.push("");
87333
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_MEMORY_MCP_URL")} override the built-in memory MCP host`);
87334
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_BROWSER_MCP_URL")} override the built-in browser MCP host`);
87335
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_SLACK_MCP_URL")} override the built-in Slack MCP host`);
87336
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_MEETING_MCP_URL")} override the built-in meeting MCP host`);
87337
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_GITHUB_MCP_URL")} override the built-in GitHub MCP host`);
87338
- out.push(` ${import_picocolors54.default.dim("BRAINBASE_ORCHESTRATION_MCP_URL")} override the built-in orchestration MCP host`);
89012
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_MEMORY_MCP_URL")} override the built-in memory MCP host`);
89013
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_BROWSER_MCP_URL")} override the built-in browser MCP host`);
89014
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_SLACK_MCP_URL")} override the built-in Slack MCP host`);
89015
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_MEETING_MCP_URL")} override the built-in meeting MCP host`);
89016
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_GITHUB_MCP_URL")} override the built-in GitHub MCP host`);
89017
+ out.push(` ${import_picocolors61.default.dim("BRAINBASE_ORCHESTRATION_MCP_URL")} override the built-in orchestration MCP host`);
87339
89018
  out.push("");
87340
89019
  out.push(divider("HARNESSES"));
87341
89020
  out.push("");
87342
- out.push(` ${import_picocolors54.default.dim("•")} ${import_picocolors54.default.bold("claude-code")} ${import_picocolors54.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
87343
- out.push(` ${import_picocolors54.default.dim("•")} ${import_picocolors54.default.bold("codex")} ${import_picocolors54.default.dim("skills, mcps, commands, playbooks, instructions, files")}`);
87344
- out.push(` ${import_picocolors54.default.dim("•")} ${import_picocolors54.default.bold("kafka")} ${import_picocolors54.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
89021
+ out.push(` ${import_picocolors61.default.dim("•")} ${import_picocolors61.default.bold("claude-code")} ${import_picocolors61.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
89022
+ out.push(` ${import_picocolors61.default.dim("•")} ${import_picocolors61.default.bold("codex")} ${import_picocolors61.default.dim("skills, mcps, commands, playbooks, instructions, files")}`);
89023
+ out.push(` ${import_picocolors61.default.dim("•")} ${import_picocolors61.default.bold("kafka")} ${import_picocolors61.default.dim("skills, mcps, agents, commands, playbooks, instructions, files")}`);
87345
89024
  out.push("");
87346
89025
  console.log(out.join(`
87347
89026
  `));
@@ -87495,13 +89174,13 @@ async function requireAuth(cmd) {
87495
89174
  if (STORED_PAT_COMMANDS.has(cmd) && readToken())
87496
89175
  return;
87497
89176
  console.error("");
87498
- console.error(` ${brandTint("◆")} ${import_picocolors54.default.bold("brainbase")}`);
89177
+ console.error(` ${brandTint("◆")} ${import_picocolors61.default.bold("brainbase")}`);
87499
89178
  console.error("");
87500
- console.error(` ${import_picocolors54.default.red("✗")} You need to sign in to use ${import_picocolors54.default.bold("brainbase " + cmd)}.`);
89179
+ console.error(` ${import_picocolors61.default.red("✗")} You need to sign in to use ${import_picocolors61.default.bold("brainbase " + cmd)}.`);
87501
89180
  if (status.reason)
87502
- console.error(` ${import_picocolors54.default.dim(status.reason)}`);
89181
+ console.error(` ${import_picocolors61.default.dim(status.reason)}`);
87503
89182
  console.error("");
87504
- console.error(` Run ${import_picocolors54.default.cyan("brainbase login")} to connect this device.`);
89183
+ console.error(` Run ${import_picocolors61.default.cyan("brainbase login")} to connect this device.`);
87505
89184
  console.error("");
87506
89185
  process14.exit(1);
87507
89186
  }
@@ -87682,6 +89361,17 @@ async function main() {
87682
89361
  await runTask(cwd2, sub, argv);
87683
89362
  break;
87684
89363
  }
89364
+ case "machine":
89365
+ case "machines": {
89366
+ const sub = argv.shift();
89367
+ await runMachine(sub, argv, {
89368
+ all,
89369
+ limit: limitFlag,
89370
+ yes,
89371
+ json: jsonFlag
89372
+ });
89373
+ break;
89374
+ }
87685
89375
  case "benchmark": {
87686
89376
  const sub = argv.shift();
87687
89377
  process14.exitCode = await runBenchmark(sub, argv, undefined, cwd2);
@@ -87696,6 +89386,7 @@ async function main() {
87696
89386
  orgId: orgIdFlag,
87697
89387
  teamId: teamIdFlag,
87698
89388
  graphOnly: graphOnlyFlag,
89389
+ force: forceFlag,
87699
89390
  name: nameFlag,
87700
89391
  from: fromFlags,
87701
89392
  to: toFlags,
@@ -87726,10 +89417,10 @@ async function main() {
87726
89417
  process14.exit(1);
87727
89418
  }
87728
89419
  } catch (err) {
87729
- console.error(import_picocolors54.default.red(`
89420
+ console.error(import_picocolors61.default.red(`
87730
89421
  ${err.message}`));
87731
89422
  if (err instanceof ApiError && err.status === 401) {
87732
- console.error(` Run ${import_picocolors54.default.cyan("brainbase login")} to connect this device.`);
89423
+ console.error(` Run ${import_picocolors61.default.cyan("brainbase login")} to connect this device.`);
87733
89424
  }
87734
89425
  if (process14.env.BRAINBASE_DEBUG)
87735
89426
  console.error(err.stack);