@brainbase-labs/cli 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +109 -51
  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.13.0",
29452
+ version: "0.13.2",
29453
29453
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
29454
29454
  type: "module",
29455
29455
  bin: {
@@ -51314,6 +51314,45 @@ function assignPlaybookSlugs(titles) {
51314
51314
  return slug;
51315
51315
  });
51316
51316
  }
51317
+ function backfillPlaybookIds(playbooks, cloudComponents) {
51318
+ const idBySlug = new Map;
51319
+ for (const c2 of cloudComponents) {
51320
+ if (c2.type !== "playbook")
51321
+ continue;
51322
+ const id = c2.meta?.playbook_id;
51323
+ if (typeof id === "string" && id)
51324
+ idBySlug.set(c2.slug, id);
51325
+ }
51326
+ const slugs = assignPlaybookSlugs(playbooks.map((e2) => e2.title));
51327
+ let changed = false;
51328
+ const next = playbooks.map((entry, i) => {
51329
+ if (entry.id)
51330
+ return entry;
51331
+ const id = idBySlug.get(slugs[i]);
51332
+ if (!id)
51333
+ return entry;
51334
+ changed = true;
51335
+ return { id, ...entry };
51336
+ });
51337
+ return { playbooks: changed ? next : playbooks, changed };
51338
+ }
51339
+ function stripPlaybookFrontmatter(raw) {
51340
+ const m3 = /^---\s*\n([\s\S]*?)\n---[ \t]*(?:\n|$)/.exec(raw);
51341
+ if (!m3)
51342
+ return { frontmatter: {}, body: raw };
51343
+ let fm = {};
51344
+ try {
51345
+ const parsed = import_yaml2.default.parse(m3[1] ?? "");
51346
+ if (parsed && typeof parsed === "object")
51347
+ fm = parsed;
51348
+ } catch {
51349
+ return { frontmatter: {}, body: raw };
51350
+ }
51351
+ return { frontmatter: fm, body: raw.slice(m3[0].length).replace(/^\n+/, "") };
51352
+ }
51353
+ function normalizeInstructionBody(body) {
51354
+ return body.trim();
51355
+ }
51317
51356
 
51318
51357
  // src/core/link.ts
51319
51358
  var LINK_DIR = ".brainbase";
@@ -52527,7 +52566,6 @@ import path48 from "node:path";
52527
52566
  import fs45 from "node:fs";
52528
52567
  import os12 from "node:os";
52529
52568
  var import_picocolors25 = __toESM(require_picocolors(), 1);
52530
- var import_yaml3 = __toESM(require_dist(), 1);
52531
52569
 
52532
52570
  // src/core/agent-diff.ts
52533
52571
  import path46 from "node:path";
@@ -52539,6 +52577,9 @@ function compKey(type, slug) {
52539
52577
  function hashString(s3) {
52540
52578
  return crypto4.createHash("sha256").update(s3).digest("hex");
52541
52579
  }
52580
+ function jsonStringAsciiSafe(s3) {
52581
+ return JSON.stringify(s3).replace(/[\u007f-\uffff]/g, (c2) => "\\u" + c2.charCodeAt(0).toString(16).padStart(4, "0"));
52582
+ }
52542
52583
  function canonicalJson(value) {
52543
52584
  if (value === null)
52544
52585
  return "null";
@@ -52550,14 +52591,14 @@ function canonicalJson(value) {
52550
52591
  return String(value);
52551
52592
  }
52552
52593
  if (typeof value === "string")
52553
- return JSON.stringify(value);
52594
+ return jsonStringAsciiSafe(value);
52554
52595
  if (Array.isArray(value)) {
52555
52596
  return "[" + value.map(canonicalJson).join(",") + "]";
52556
52597
  }
52557
52598
  if (typeof value === "object") {
52558
52599
  const obj = value;
52559
52600
  const keys2 = Object.keys(obj).sort();
52560
- return "{" + keys2.map((k3) => `${JSON.stringify(k3)}:${canonicalJson(obj[k3])}`).join(",") + "}";
52601
+ return "{" + keys2.map((k3) => `${jsonStringAsciiSafe(k3)}:${canonicalJson(obj[k3])}`).join(",") + "}";
52561
52602
  }
52562
52603
  throw new Error(`Unsupported value in canonicalJson: ${typeof value}`);
52563
52604
  }
@@ -52627,7 +52668,7 @@ function readLocalComponents(cwd2, manifest) {
52627
52668
  out.push({
52628
52669
  type: "instruction",
52629
52670
  slug: "agent-instructions",
52630
- hash: componentHashFromFileHashes([hashString(instr)])
52671
+ hash: componentHashFromFileHashes([hashString(normalizeInstructionBody(instr))])
52631
52672
  });
52632
52673
  }
52633
52674
  for (const entry of manifest.skills) {
@@ -52765,6 +52806,38 @@ function threeWayDiff(input) {
52765
52806
  }
52766
52807
  return rows;
52767
52808
  }
52809
+ function partitionPushRows(rows, force) {
52810
+ const toSend = [];
52811
+ const conflicts = [];
52812
+ const upstreamOnly = [];
52813
+ for (const r2 of rows) {
52814
+ switch (r2.status) {
52815
+ case "modified-local":
52816
+ case "added-only-local":
52817
+ case "added-local":
52818
+ case "removed-local":
52819
+ toSend.push(r2);
52820
+ break;
52821
+ case "modified-both":
52822
+ if (force)
52823
+ toSend.push(r2);
52824
+ else
52825
+ conflicts.push(r2);
52826
+ break;
52827
+ case "modified-cloud":
52828
+ case "added-cloud":
52829
+ case "removed-cloud":
52830
+ upstreamOnly.push(r2);
52831
+ break;
52832
+ case "in-sync":
52833
+ break;
52834
+ default: {
52835
+ const _exhaustive = r2.status;
52836
+ }
52837
+ }
52838
+ }
52839
+ return { toSend, conflicts, upstreamOnly };
52840
+ }
52768
52841
  function diffAgentMeta(manifestMeta, lockMeta, cloudMeta) {
52769
52842
  const eq = (a3, b4) => {
52770
52843
  if (!a3 && !b4)
@@ -53199,7 +53272,7 @@ function materializeInstructions(cwd2, cloud, toInstall, keepLocal, existingMani
53199
53272
  const targetRel = existingManifest?.instructions?.file ?? DEFAULT_INSTRUCTIONS_FILE;
53200
53273
  const target = path48.resolve(cwd2, targetRel);
53201
53274
  ensureDir(path48.dirname(target));
53202
- fs45.writeFileSync(target, body, "utf8");
53275
+ fs45.writeFileSync(target, normalizeInstructionBody(body), "utf8");
53203
53276
  }
53204
53277
  }
53205
53278
  function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifest) {
@@ -53214,7 +53287,7 @@ function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifes
53214
53287
  const raw = c2.files[0]?.content ?? "";
53215
53288
  if (!raw.trim())
53216
53289
  continue;
53217
- const { body } = stripFrontmatter(raw);
53290
+ const { body } = stripPlaybookFrontmatter(raw);
53218
53291
  const existing = existingManifest?.playbooks?.find((p2) => slugifyPlaybookTitle(p2.title) === c2.slug);
53219
53292
  if (existing?.content?.text !== undefined)
53220
53293
  continue;
@@ -53224,20 +53297,6 @@ function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifes
53224
53297
  fs45.writeFileSync(target, body, "utf8");
53225
53298
  }
53226
53299
  }
53227
- function stripFrontmatter(raw) {
53228
- const m3 = /^---\s*\n([\s\S]*?)\n---\s*\n?/.exec(raw);
53229
- if (!m3)
53230
- return { frontmatter: {}, body: raw };
53231
- let fm = {};
53232
- try {
53233
- const parsed = import_yaml3.default.parse(m3[1] ?? "");
53234
- if (parsed && typeof parsed === "object")
53235
- fm = parsed;
53236
- } catch {
53237
- return { frontmatter: {}, body: raw };
53238
- }
53239
- return { frontmatter: fm, body: raw.slice(m3[0].length) };
53240
- }
53241
53300
  function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
53242
53301
  const skills = cloud.components.filter((c2) => c2.type === "skill").map((c2) => {
53243
53302
  const localDecl = prev?.skills.find((s3) => {
@@ -53275,7 +53334,7 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
53275
53334
  }
53276
53335
  const playbooks = cloud.components.filter((c2) => c2.type === "playbook").map((c2) => {
53277
53336
  const raw = c2.files[0]?.content ?? "";
53278
- const { frontmatter } = stripFrontmatter(raw);
53337
+ const { frontmatter } = stripPlaybookFrontmatter(raw);
53279
53338
  const local = prev?.playbooks?.find((pb) => slugifyPlaybookTitle(pb.title) === c2.slug);
53280
53339
  const title = local?.title ?? (typeof frontmatter.title === "string" && frontmatter.title || c2.slug);
53281
53340
  const description = local?.description ?? (typeof frontmatter.description === "string" ? frontmatter.description : undefined);
@@ -53540,10 +53599,11 @@ async function buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions)
53540
53599
  }
53541
53600
  if (body.trim()) {
53542
53601
  const fileName = manifest.instructions.file ?? "instructions.md";
53543
- const fileHash2 = hashString(body);
53602
+ const wireBody = normalizeInstructionBody(body);
53603
+ const fileHash2 = hashString(wireBody);
53544
53604
  const file = {
53545
53605
  path: fileName,
53546
- content: body,
53606
+ content: wireBody,
53547
53607
  hash: fileHash2
53548
53608
  };
53549
53609
  out.push({
@@ -53856,26 +53916,8 @@ async function runAgentPush(cwd2, args) {
53856
53916
  f2.error(`Playbook ${preIdSchemaSlugs.length === 1 ? "entry" : "entries"} ${preIdSchemaSlugs.map((s3) => import_picocolors27.default.bold(s3)).join(", ")} in ${import_picocolors27.default.bold("brainbase.agent.yaml")} ${preIdSchemaSlugs.length === 1 ? "is" : "are"} missing ${import_picocolors27.default.cyan("id:")}. Run ${import_picocolors27.default.bold("brainbase agent pull")} first to sync playbook ids, then push.`);
53857
53917
  return;
53858
53918
  }
53859
- const toSend = [];
53860
- const conflicts = [];
53861
- const upstreamOnly = [];
53862
- for (const r2 of rows) {
53863
- switch (r2.status) {
53864
- case "modified-local":
53865
- case "added-only-local":
53866
- case "removed-local":
53867
- toSend.push(r2);
53868
- break;
53869
- case "modified-both":
53870
- conflicts.push(r2);
53871
- break;
53872
- case "modified-cloud":
53873
- case "added-cloud":
53874
- case "removed-cloud":
53875
- upstreamOnly.push(r2);
53876
- break;
53877
- }
53878
- }
53919
+ const { toSend, conflicts, upstreamOnly } = partitionPushRows(rows, !!args.force);
53920
+ const forcedOverrides = args.force ? rows.filter((r2) => r2.status === "modified-both") : [];
53879
53921
  if (!meta.localChanged && !entrypointChanged && toSend.length === 0 && conflicts.length === 0) {
53880
53922
  f2.info("Nothing to push — local is in sync with the cloud.");
53881
53923
  return;
@@ -53885,9 +53927,15 @@ async function runAgentPush(cwd2, args) {
53885
53927
  for (const r2 of conflicts) {
53886
53928
  console.error(` ${import_picocolors27.default.red("!")} ${fmtType(r2.type)} ${import_picocolors27.default.bold(r2.slug)}`);
53887
53929
  }
53888
- f2.info(`Run ${import_picocolors27.default.cyan("brainbase agent pull")} first to reconcile, then push again.`);
53930
+ f2.info(`Run ${import_picocolors27.default.cyan("brainbase agent pull")} first to reconcile, then push again — or ${import_picocolors27.default.cyan("brainbase agent push --force")} to overwrite the cloud with your local version.`);
53889
53931
  return;
53890
53932
  }
53933
+ if (forcedOverrides.length > 0) {
53934
+ f2.warn(`${import_picocolors27.default.yellow("--force")}: overwriting ${forcedOverrides.length} cloud change${forcedOverrides.length === 1 ? "" : "s"} with your local version (cloud edits discarded):`);
53935
+ for (const r2 of forcedOverrides) {
53936
+ console.warn(` ${import_picocolors27.default.yellow("⤒")} ${fmtType(r2.type)} ${import_picocolors27.default.bold(r2.slug)}`);
53937
+ }
53938
+ }
53891
53939
  if (upstreamOnly.length > 0 && !args.yes) {
53892
53940
  f2.warn(`Cloud has ${upstreamOnly.length} change${upstreamOnly.length === 1 ? "" : "s"} you don't have locally:`);
53893
53941
  for (const r2 of upstreamOnly) {
@@ -53981,6 +54029,11 @@ async function runAgentPush(cwd2, args) {
53981
54029
  }
53982
54030
  return handleApiError3(err);
53983
54031
  }
54032
+ const backfill = backfillPlaybookIds(manifest.playbooks ?? [], updatedCloud.components);
54033
+ if (backfill.changed) {
54034
+ manifest.playbooks = backfill.playbooks;
54035
+ writeManifest(cwd2, manifest);
54036
+ }
53984
54037
  const existing = readLink(cwd2);
53985
54038
  if (existing) {
53986
54039
  writeLink(cwd2, {
@@ -54542,6 +54595,11 @@ async function runAgentCreate(cwd2, args) {
54542
54595
  }
54543
54596
  }
54544
54597
  if (updatedCloud) {
54598
+ const backfill = backfillPlaybookIds(manifest.playbooks ?? [], updatedCloud.components);
54599
+ if (backfill.changed) {
54600
+ manifest.playbooks = backfill.playbooks;
54601
+ writeManifest(cwd2, manifest);
54602
+ }
54545
54603
  const localHashByKey = new Map;
54546
54604
  for (const lc of readLocalComponents(cwd2, manifest)) {
54547
54605
  if (lc.hash)
@@ -54947,7 +55005,7 @@ async function runAgent(cwd2, sub, args, opts) {
54947
55005
  });
54948
55006
  return;
54949
55007
  case "push":
54950
- await runAgentPush(cwd2, { yes: opts.yes });
55008
+ await runAgentPush(cwd2, { yes: opts.yes, force: opts.force });
54951
55009
  return;
54952
55010
  case "unpack":
54953
55011
  await runAgentUnpack(cwd2, {
@@ -54982,7 +55040,7 @@ function printHelp() {
54982
55040
  out.push("");
54983
55041
  out.push(` ${import_picocolors32.default.cyan("create")} ${import_picocolors32.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
54984
55042
  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")}`);
54985
- out.push(` ${import_picocolors32.default.cyan("push")} ${import_picocolors32.default.dim("send local changes to the cloud — instructions, playbooks, skills, MCPs, entrypoint")}`);
55043
+ out.push(` ${import_picocolors32.default.cyan("push")} ${import_picocolors32.default.dim("send local changes to the cloud — instructions, playbooks, skills, MCPs, entrypoint (--force to overwrite cloud-side conflicts with local)")}`);
54986
55044
  out.push(` ${import_picocolors32.default.cyan("unpack")} ${import_picocolors32.default.dim("install the claimed agent into a harness layout (--harness to override)")}`);
54987
55045
  out.push(` ${import_picocolors32.default.cyan("status")} ${import_picocolors32.default.dim("show what would push and what would pull")}`);
54988
55046
  out.push(` ${import_picocolors32.default.cyan("env")} ${import_picocolors32.default.dim('print export statements — use with `eval "$(brainbase agent env)"`')}`);
@@ -55002,7 +55060,7 @@ var import_picocolors33 = __toESM(require_picocolors(), 1);
55002
55060
  // src/core/orchestration-manifest.ts
55003
55061
  import path51 from "node:path";
55004
55062
  import fs47 from "node:fs";
55005
- var import_yaml4 = __toESM(require_dist(), 1);
55063
+ var import_yaml3 = __toESM(require_dist(), 1);
55006
55064
  var ORCH_MANIFEST_FILE = "brainbase-orchestration.yaml";
55007
55065
  var ORCH_MEMBERS_DIR = "agents";
55008
55066
  var OrchMetaSchema = exports_external.object({
@@ -55054,7 +55112,7 @@ function readOrchManifest(cwd2) {
55054
55112
  const raw = fs47.readFileSync(p2, "utf8");
55055
55113
  let parsed;
55056
55114
  try {
55057
- parsed = import_yaml4.default.parse(raw);
55115
+ parsed = import_yaml3.default.parse(raw);
55058
55116
  } catch (err) {
55059
55117
  throw new Error(`${ORCH_MANIFEST_FILE} is not valid YAML: ${err.message}`);
55060
55118
  }
@@ -55065,7 +55123,7 @@ function readOrchManifest(cwd2) {
55065
55123
  return result2.data;
55066
55124
  }
55067
55125
  function writeOrchManifest(cwd2, manifest) {
55068
- const doc = new import_yaml4.default.Document;
55126
+ const doc = new import_yaml3.default.Document;
55069
55127
  doc.contents = manifest;
55070
55128
  doc.commentBefore = ` brainbase-orchestration.yaml — declarative orchestration manifest.
55071
55129
  ` + ` Committed to source control. Edit by hand, then
@@ -56920,7 +56978,7 @@ function help() {
56920
56978
  out.push("");
56921
56979
  out.push(` ${import_picocolors41.default.cyan("agent create")} ${import_picocolors41.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
56922
56980
  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)")}`);
56923
- out.push(` ${import_picocolors41.default.cyan("agent push")} ${import_picocolors41.default.dim("send local changes to the cloud instructions, playbooks, skills, MCPs, entrypoint")}`);
56981
+ out.push(` ${import_picocolors41.default.cyan("agent push")} ${import_picocolors41.default.dim("send local changes to the cloud (--force to overwrite cloud-side conflicts with local)")}`);
56924
56982
  out.push(` ${import_picocolors41.default.cyan("agent unpack")} ${import_picocolors41.default.dim("install the claimed agent into a harness layout")}`);
56925
56983
  out.push(` ${import_picocolors41.default.cyan("link")} ${import_picocolors41.default.dim("attach this folder to an existing agent")}`);
56926
56984
  out.push(` ${import_picocolors41.default.cyan("agent status")} ${import_picocolors41.default.dim("show what would pull and what would push")}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {