@brainbase-labs/cli 0.10.0 → 0.11.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 (2) hide show
  1. package/dist/index.js +130 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -29449,7 +29449,7 @@ function padStart(s, n) {
29449
29449
  // package.json
29450
29450
  var package_default = {
29451
29451
  name: "@brainbase-labs/cli",
29452
- version: "0.10.0",
29452
+ version: "0.11.0",
29453
29453
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
29454
29454
  type: "module",
29455
29455
  bin: {
@@ -50413,6 +50413,7 @@ async function runSkillPublish(cwd2, args) {
50413
50413
  writeSkillMarker(skillDir, newSource);
50414
50414
  $e(`${import_picocolors16.default.bold(creator + "/" + pkgSlug)}@${version} published.`);
50415
50415
  f2.info(import_picocolors16.default.dim(`id: ${result2.id}`));
50416
+ f2.info(import_picocolors16.default.dim(`To use it on an agent: add ${import_picocolors16.default.cyan(`source: registry:${creator}/${pkgSlug}`)} under ${import_picocolors16.default.cyan("skills:")} in brainbase.agent.yaml, then ${import_picocolors16.default.cyan("brainbase agent push")}. Agents already linking it pick up ${version} on their next push.`));
50416
50417
  } finally {
50417
50418
  try {
50418
50419
  fs37.rmSync(tmp, { recursive: true, force: true });
@@ -50544,6 +50545,9 @@ async function runSkillUpdate(cwd2, args) {
50544
50545
  }
50545
50546
  writeSkillMarker(target.dir, marker.source);
50546
50547
  $e(`${import_picocolors17.default.bold(args.slug)} updated.`);
50548
+ if (marker.source.type === "brainbase") {
50549
+ f2.info(import_picocolors17.default.dim(`This refreshed your local copy only. Agents using this skill update on their next ${import_picocolors17.default.cyan("brainbase agent push")} (run it in the agent's folder).`));
50550
+ }
50547
50551
  }
50548
50552
 
50549
50553
  // src/cli/skill-search.ts
@@ -52649,7 +52653,8 @@ function threeWayDiff(input) {
52649
52653
  } else if (present.local && !present.cloud && present.lock) {
52650
52654
  status = "removed-cloud";
52651
52655
  } else {
52652
- const localChanged = localHash !== null && lockHash !== undefined && localHash !== lockHash;
52656
+ const sourceChanged = !!local && local.hash === null && !!local.declaredSource && lock?.source !== undefined && lock.source !== local.declaredSource;
52657
+ const localChanged = sourceChanged || localHash !== null && lockHash !== undefined && localHash !== lockHash;
52653
52658
  const cloudChanged = cloudHash !== undefined && lockHash !== undefined && cloudHash !== lockHash;
52654
52659
  if (localChanged && cloudChanged)
52655
52660
  status = "modified-both";
@@ -52787,6 +52792,14 @@ function diffSecrets(local, cloud) {
52787
52792
  };
52788
52793
  }
52789
52794
 
52795
+ // src/core/entrypoint-gate.ts
52796
+ function entrypointExecutionAllowed(opts) {
52797
+ if (opts.flag)
52798
+ return true;
52799
+ const v3 = (opts.env.BRAINBASE_RUN_ENTRYPOINT ?? "").trim().toLowerCase();
52800
+ return v3 === "1" || v3 === "true";
52801
+ }
52802
+
52790
52803
  // src/cli/agent-pull.ts
52791
52804
  async function runAgentPull(cwd2, args) {
52792
52805
  banner("agent pull — bring cloud changes into this folder");
@@ -53002,7 +53015,7 @@ async function runAgentPull(cwd2, args) {
53002
53015
  };
53003
53016
  writeSyncState(cwd2, newState);
53004
53017
  await pullSecrets(cwd2, agentId);
53005
- await runEntrypointIfPresent(cwd2, yaml);
53018
+ await runEntrypointIfPresent(cwd2, yaml, entrypointExecutionAllowed({ flag: args.runEntrypoint, env: process.env }));
53006
53019
  $e(`Pulled ${cloudAgent.name} at revision ${cloud.revision}.`);
53007
53020
  } finally {
53008
53021
  try {
@@ -53321,7 +53334,7 @@ function buildLinkFromAgent(agent, harness, prev) {
53321
53334
  tracking: prev?.tracking
53322
53335
  };
53323
53336
  }
53324
- async function runEntrypointIfPresent(cwd2, manifest) {
53337
+ async function runEntrypointIfPresent(cwd2, manifest, execute) {
53325
53338
  if (!manifest.entrypoint)
53326
53339
  return;
53327
53340
  const body = resolveEntrypoint(cwd2, manifest);
@@ -53335,6 +53348,10 @@ async function runEntrypointIfPresent(cwd2, manifest) {
53335
53348
  try {
53336
53349
  fs45.chmodSync(scriptPath, 493);
53337
53350
  } catch {}
53351
+ if (!execute) {
53352
+ f2.info(`Agent has an entrypoint — written to ${import_picocolors25.default.dim(path48.relative(cwd2, scriptPath))}, not executed. ` + `Run it with ${import_picocolors25.default.cyan("brainbase run bash .brainbase/entrypoint.sh")} or re-pull with ${import_picocolors25.default.cyan("--run-entrypoint")}. Sandboxes run it automatically.`);
53353
+ return;
53354
+ }
53338
53355
  f2.info(`Running entrypoint ${import_picocolors25.default.dim(`(${path48.relative(cwd2, scriptPath)})`)}`);
53339
53356
  const secrets = readLocalSecrets(cwd2);
53340
53357
  const env3 = { ...process.env, ...secrets };
@@ -53413,7 +53430,7 @@ var import_picocolors27 = __toESM(require_picocolors(), 1);
53413
53430
 
53414
53431
  // src/core/agent-outgoing.ts
53415
53432
  var import_picocolors26 = __toESM(require_picocolors(), 1);
53416
- async function buildOutgoingComponents(cwd2, manifest, cloud) {
53433
+ async function buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions) {
53417
53434
  const out = [];
53418
53435
  if (manifest.instructions) {
53419
53436
  if (manifest.instructions.text !== undefined && manifest.instructions.file !== undefined) {
@@ -53460,6 +53477,7 @@ async function buildOutgoingComponents(cwd2, manifest, cloud) {
53460
53477
  }
53461
53478
  const slug = registrySkillComponentSlug(parsed);
53462
53479
  const cloudMatch = cloud?.components.find((c2) => c2.type === "skill" && c2.slug === slug);
53480
+ const version = parsed.version ?? resolvedVersions?.get(slug);
53463
53481
  out.push({
53464
53482
  type: "skill",
53465
53483
  slug,
@@ -53467,7 +53485,7 @@ async function buildOutgoingComponents(cwd2, manifest, cloud) {
53467
53485
  files: [],
53468
53486
  meta: {
53469
53487
  name: parsed.creator ? `${parsed.creator}/${parsed.slug}` : parsed.slug,
53470
- ...parsed.version ? { version: parsed.version } : {}
53488
+ ...version ? { version } : {}
53471
53489
  }
53472
53490
  });
53473
53491
  }
@@ -53552,6 +53570,62 @@ function yamlScalar(s3) {
53552
53570
  return JSON.stringify(s3);
53553
53571
  }
53554
53572
 
53573
+ // src/core/registry-skill-updates.ts
53574
+ function parseSemver2(v3) {
53575
+ const m3 = /^(\d+)\.(\d+)\.(\d+)$/.exec(v3);
53576
+ if (!m3)
53577
+ return null;
53578
+ return [Number(m3[1]), Number(m3[2]), Number(m3[3])];
53579
+ }
53580
+ function compareDirection(current, latest) {
53581
+ if (!current)
53582
+ return "unknown";
53583
+ const a3 = parseSemver2(current);
53584
+ const b4 = parseSemver2(latest);
53585
+ if (!a3 || !b4)
53586
+ return "unknown";
53587
+ for (let i = 0;i < 3; i++) {
53588
+ if (b4[i] > a3[i])
53589
+ return "upgrade";
53590
+ if (b4[i] < a3[i])
53591
+ return "downgrade";
53592
+ }
53593
+ return "unknown";
53594
+ }
53595
+ function planRegistrySkillUpdates(skills, cloudComponents, latestByName) {
53596
+ const out = [];
53597
+ for (const entry of skills) {
53598
+ let parsed;
53599
+ try {
53600
+ parsed = parseSkillSource2(entry.source);
53601
+ } catch {
53602
+ continue;
53603
+ }
53604
+ if (parsed.kind !== "registry" || parsed.version || !parsed.creator) {
53605
+ continue;
53606
+ }
53607
+ const name = `${parsed.creator}/${parsed.slug}`;
53608
+ const latest = latestByName.get(name);
53609
+ if (!latest)
53610
+ continue;
53611
+ const componentSlug = registrySkillComponentSlug(parsed);
53612
+ const cloud = cloudComponents.find((c2) => c2.type === "skill" && c2.slug === componentSlug);
53613
+ if (!cloud)
53614
+ continue;
53615
+ const current = cloud.meta?.["version"];
53616
+ if (current === latest)
53617
+ continue;
53618
+ out.push({
53619
+ componentSlug,
53620
+ name,
53621
+ current,
53622
+ latest,
53623
+ direction: compareDirection(current, latest)
53624
+ });
53625
+ }
53626
+ return out;
53627
+ }
53628
+
53555
53629
  // src/cli/agent-push.ts
53556
53630
  async function runAgentPush(cwd2, args) {
53557
53631
  banner("agent push — send your local changes to the cloud");
@@ -53590,6 +53664,37 @@ async function runAgentPush(cwd2, args) {
53590
53664
  lock: lock?.components ?? [],
53591
53665
  cloud: cloud.components
53592
53666
  });
53667
+ const unpinned = new Map;
53668
+ for (const entry of manifest.skills) {
53669
+ try {
53670
+ const parsed = parseSkillSource2(entry.source);
53671
+ if (parsed.kind === "registry" && !parsed.version && parsed.creator) {
53672
+ unpinned.set(`${parsed.creator}/${parsed.slug}`, {
53673
+ creator: parsed.creator,
53674
+ slug: parsed.slug
53675
+ });
53676
+ }
53677
+ } catch {}
53678
+ }
53679
+ const latestByName = new Map;
53680
+ if (unpinned.size > 0) {
53681
+ await Promise.all([...unpinned.entries()].map(async ([name, ref]) => {
53682
+ try {
53683
+ const pkg = await skillsApi.getPackage(ref.creator, ref.slug);
53684
+ if (pkg.latest_version?.version) {
53685
+ latestByName.set(name, pkg.latest_version.version);
53686
+ }
53687
+ } catch {}
53688
+ }));
53689
+ }
53690
+ const skillUpdates = planRegistrySkillUpdates(manifest.skills, cloud.components, latestByName);
53691
+ const resolvedVersions = new Map(skillUpdates.map((u2) => [u2.componentSlug, u2.latest]));
53692
+ for (const u2 of skillUpdates) {
53693
+ const row = rows.find((r2) => r2.key === compKey("skill", u2.componentSlug));
53694
+ if (row && (row.status === "in-sync" || row.status === "modified-cloud" || row.status === "added-local")) {
53695
+ row.status = "modified-local";
53696
+ }
53697
+ }
53593
53698
  let cloudMeta = lock?.agentMeta;
53594
53699
  if (!cloudMeta) {
53595
53700
  try {
@@ -53673,6 +53778,16 @@ async function runAgentPush(cwd2, args) {
53673
53778
  }
53674
53779
  f2.info(`If you push now, your push targets revision ${cloud.revision} and may race. Consider \`brainbase agent pull\` first.`);
53675
53780
  }
53781
+ const sendKeys = new Set(toSend.map((r2) => r2.key));
53782
+ for (const u2 of skillUpdates) {
53783
+ if (!sendKeys.has(compKey("skill", u2.componentSlug)))
53784
+ continue;
53785
+ if (u2.direction === "downgrade") {
53786
+ f2.warn(`Skill ${import_picocolors27.default.bold(u2.name)}: registry latest is ${import_picocolors27.default.bold(u2.latest)}, below the agent's ${import_picocolors27.default.dim(u2.current)} — the agent's version was likely yanked. Converging on latest.`);
53787
+ } else {
53788
+ f2.info(`Skill ${import_picocolors27.default.bold(u2.name)} → ${import_picocolors27.default.bold(u2.latest)} (registry latest${u2.current ? `, agent has ${u2.current}` : ""}).`);
53789
+ }
53790
+ }
53676
53791
  const resultRows = [];
53677
53792
  if (meta.localChanged) {
53678
53793
  resultRows.push({
@@ -53727,7 +53842,7 @@ async function runAgentPush(cwd2, args) {
53727
53842
  return handleApiError3(err);
53728
53843
  }
53729
53844
  }
53730
- const outgoing = await buildOutgoingComponents(cwd2, manifest, cloud);
53845
+ const outgoing = await buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions);
53731
53846
  if (outgoing === null)
53732
53847
  return;
53733
53848
  const pushSpinner = de();
@@ -54708,7 +54823,8 @@ async function runAgent(cwd2, sub, args, opts) {
54708
54823
  yes: opts.yes,
54709
54824
  scope: opts.scope,
54710
54825
  agentIdArg: args[0],
54711
- force: opts.force
54826
+ force: opts.force,
54827
+ runEntrypoint: opts.runEntrypoint
54712
54828
  });
54713
54829
  return;
54714
54830
  case "push":
@@ -54746,7 +54862,7 @@ function printHelp() {
54746
54862
  out.push(` ${import_picocolors32.default.bold("brainbase agent")} ${import_picocolors32.default.dim("<sub> [options]")}`);
54747
54863
  out.push("");
54748
54864
  out.push(` ${import_picocolors32.default.cyan("create")} ${import_picocolors32.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
54749
- out.push(` ${import_picocolors32.default.cyan("pull")} ${import_picocolors32.default.dim("[<id>]")} ${import_picocolors32.default.dim("apply cloud changes into this folder — pass <id> to switch (--force to override)")}`);
54865
+ out.push(` ${import_picocolors32.default.cyan("pull")} ${import_picocolors32.default.dim("[<id>]")} ${import_picocolors32.default.dim("apply cloud changes into this folder — pass <id> to switch (--force to override); --run-entrypoint to also execute the agent entrypoint")}`);
54750
54866
  out.push(` ${import_picocolors32.default.cyan("push")} ${import_picocolors32.default.dim("send local changes to the cloud")}`);
54751
54867
  out.push(` ${import_picocolors32.default.cyan("unpack")} ${import_picocolors32.default.dim("install the claimed agent into a harness layout (--harness to override)")}`);
54752
54868
  out.push(` ${import_picocolors32.default.cyan("status")} ${import_picocolors32.default.dim("show what would push and what would pull")}`);
@@ -56679,7 +56795,7 @@ function help() {
56679
56795
  out.push(divider("LINKED AGENT"));
56680
56796
  out.push("");
56681
56797
  out.push(` ${import_picocolors41.default.cyan("agent create")} ${import_picocolors41.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
56682
- out.push(` ${import_picocolors41.default.cyan("agent pull")} ${import_picocolors41.default.dim("[<id>]")} ${import_picocolors41.default.dim("bring cloud changes into this folder (--force to override)")}`);
56798
+ out.push(` ${import_picocolors41.default.cyan("agent pull")} ${import_picocolors41.default.dim("[<id>]")} ${import_picocolors41.default.dim("bring cloud changes into this folder (--force to override; --run-entrypoint to also execute the agent entrypoint)")}`);
56683
56799
  out.push(` ${import_picocolors41.default.cyan("agent push")} ${import_picocolors41.default.dim("send local changes to the cloud")}`);
56684
56800
  out.push(` ${import_picocolors41.default.cyan("agent unpack")} ${import_picocolors41.default.dim("install the claimed agent into a harness layout")}`);
56685
56801
  out.push(` ${import_picocolors41.default.cyan("link")} ${import_picocolors41.default.dim("attach this folder to an existing agent")}`);
@@ -56743,6 +56859,7 @@ function help() {
56743
56859
  out.push(` ${import_picocolors41.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT (overrides token.json)`);
56744
56860
  out.push(` ${import_picocolors41.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
56745
56861
  out.push(` ${import_picocolors41.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
56862
+ out.push(` ${import_picocolors41.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
56746
56863
  out.push("");
56747
56864
  out.push(divider("HARNESSES"));
56748
56865
  out.push("");
@@ -56847,6 +56964,7 @@ async function main() {
56847
56964
  const noTracking = hasFlag2(argv, "--no-tracking");
56848
56965
  const track = hasFlag2(argv, "--track");
56849
56966
  const forceFlag = hasFlag2(argv, "--force");
56967
+ const runEntrypointFlag = hasFlag2(argv, "--run-entrypoint");
56850
56968
  const graphOnlyFlag = hasFlag2(argv, "--graph-only");
56851
56969
  const nameFlag = getFlag(argv, "--name");
56852
56970
  const skillVersionFlag = getFlag(argv, "--skill-version");
@@ -56938,7 +57056,8 @@ async function main() {
56938
57056
  teamId: teamIdFlag,
56939
57057
  noTracking,
56940
57058
  track,
56941
- force: forceFlag
57059
+ force: forceFlag,
57060
+ runEntrypoint: runEntrypointFlag
56942
57061
  });
56943
57062
  break;
56944
57063
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {