@brainbase-labs/cli 0.13.1 → 0.13.3

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 +97 -78
  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.1",
29452
+ version: "0.13.3",
29453
29453
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
29454
29454
  type: "module",
29455
29455
  bin: {
@@ -48912,6 +48912,24 @@ function defaultSkillSlug(source) {
48912
48912
  return "skill";
48913
48913
  }
48914
48914
  }
48915
+ function looseSkillComponentSlug(raw) {
48916
+ const scoped = raw.match(/^registry:([a-z0-9_-]+)\/([a-z0-9_-]+)/i);
48917
+ if (scoped)
48918
+ return `${scoped[1]}--${scoped[2]}`;
48919
+ const unscoped = raw.match(/^registry:([a-z0-9_-]+)(?:@|$)/i);
48920
+ if (unscoped)
48921
+ return unscoped[1];
48922
+ if (raw.startsWith("./") || raw.startsWith("../") || raw.startsWith("/")) {
48923
+ return path32.basename(raw);
48924
+ }
48925
+ try {
48926
+ const source = parseSkillSource(raw);
48927
+ if (source.type !== "inline" && source.type !== "local") {
48928
+ return defaultSkillSlug(source);
48929
+ }
48930
+ } catch {}
48931
+ return null;
48932
+ }
48915
48933
  function describeSource(source) {
48916
48934
  switch (source.type) {
48917
48935
  case "inline":
@@ -51336,6 +51354,23 @@ function backfillPlaybookIds(playbooks, cloudComponents) {
51336
51354
  });
51337
51355
  return { playbooks: changed ? next : playbooks, changed };
51338
51356
  }
51357
+ function stripPlaybookFrontmatter(raw) {
51358
+ const m3 = /^---\s*\n([\s\S]*?)\n---[ \t]*(?:\n|$)/.exec(raw);
51359
+ if (!m3)
51360
+ return { frontmatter: {}, body: raw };
51361
+ let fm = {};
51362
+ try {
51363
+ const parsed = import_yaml2.default.parse(m3[1] ?? "");
51364
+ if (parsed && typeof parsed === "object")
51365
+ fm = parsed;
51366
+ } catch {
51367
+ return { frontmatter: {}, body: raw };
51368
+ }
51369
+ return { frontmatter: fm, body: raw.slice(m3[0].length).replace(/^\n+/, "") };
51370
+ }
51371
+ function normalizeInstructionBody(body) {
51372
+ return body.trim();
51373
+ }
51339
51374
 
51340
51375
  // src/core/link.ts
51341
51376
  var LINK_DIR = ".brainbase";
@@ -52549,7 +52584,6 @@ import path48 from "node:path";
52549
52584
  import fs45 from "node:fs";
52550
52585
  import os12 from "node:os";
52551
52586
  var import_picocolors25 = __toESM(require_picocolors(), 1);
52552
- var import_yaml3 = __toESM(require_dist(), 1);
52553
52587
 
52554
52588
  // src/core/agent-diff.ts
52555
52589
  import path46 from "node:path";
@@ -52561,6 +52595,9 @@ function compKey(type, slug) {
52561
52595
  function hashString(s3) {
52562
52596
  return crypto4.createHash("sha256").update(s3).digest("hex");
52563
52597
  }
52598
+ function jsonStringAsciiSafe(s3) {
52599
+ return JSON.stringify(s3).replace(/[\u007f-\uffff]/g, (c2) => "\\u" + c2.charCodeAt(0).toString(16).padStart(4, "0"));
52600
+ }
52564
52601
  function canonicalJson(value) {
52565
52602
  if (value === null)
52566
52603
  return "null";
@@ -52572,14 +52609,14 @@ function canonicalJson(value) {
52572
52609
  return String(value);
52573
52610
  }
52574
52611
  if (typeof value === "string")
52575
- return JSON.stringify(value);
52612
+ return jsonStringAsciiSafe(value);
52576
52613
  if (Array.isArray(value)) {
52577
52614
  return "[" + value.map(canonicalJson).join(",") + "]";
52578
52615
  }
52579
52616
  if (typeof value === "object") {
52580
52617
  const obj = value;
52581
52618
  const keys2 = Object.keys(obj).sort();
52582
- return "{" + keys2.map((k3) => `${JSON.stringify(k3)}:${canonicalJson(obj[k3])}`).join(",") + "}";
52619
+ return "{" + keys2.map((k3) => `${jsonStringAsciiSafe(k3)}:${canonicalJson(obj[k3])}`).join(",") + "}";
52583
52620
  }
52584
52621
  throw new Error(`Unsupported value in canonicalJson: ${typeof value}`);
52585
52622
  }
@@ -52649,7 +52686,7 @@ function readLocalComponents(cwd2, manifest) {
52649
52686
  out.push({
52650
52687
  type: "instruction",
52651
52688
  slug: "agent-instructions",
52652
- hash: componentHashFromFileHashes([hashString(instr)])
52689
+ hash: componentHashFromFileHashes([hashString(normalizeInstructionBody(instr))])
52653
52690
  });
52654
52691
  }
52655
52692
  for (const entry of manifest.skills) {
@@ -52787,6 +52824,38 @@ function threeWayDiff(input) {
52787
52824
  }
52788
52825
  return rows;
52789
52826
  }
52827
+ function partitionPushRows(rows, force) {
52828
+ const toSend = [];
52829
+ const conflicts = [];
52830
+ const upstreamOnly = [];
52831
+ for (const r2 of rows) {
52832
+ switch (r2.status) {
52833
+ case "modified-local":
52834
+ case "added-only-local":
52835
+ case "added-local":
52836
+ case "removed-local":
52837
+ toSend.push(r2);
52838
+ break;
52839
+ case "modified-both":
52840
+ if (force)
52841
+ toSend.push(r2);
52842
+ else
52843
+ conflicts.push(r2);
52844
+ break;
52845
+ case "modified-cloud":
52846
+ case "added-cloud":
52847
+ case "removed-cloud":
52848
+ upstreamOnly.push(r2);
52849
+ break;
52850
+ case "in-sync":
52851
+ break;
52852
+ default: {
52853
+ const _exhaustive = r2.status;
52854
+ }
52855
+ }
52856
+ }
52857
+ return { toSend, conflicts, upstreamOnly };
52858
+ }
52790
52859
  function diffAgentMeta(manifestMeta, lockMeta, cloudMeta) {
52791
52860
  const eq = (a3, b4) => {
52792
52861
  if (!a3 && !b4)
@@ -53221,7 +53290,7 @@ function materializeInstructions(cwd2, cloud, toInstall, keepLocal, existingMani
53221
53290
  const targetRel = existingManifest?.instructions?.file ?? DEFAULT_INSTRUCTIONS_FILE;
53222
53291
  const target = path48.resolve(cwd2, targetRel);
53223
53292
  ensureDir(path48.dirname(target));
53224
- fs45.writeFileSync(target, body, "utf8");
53293
+ fs45.writeFileSync(target, normalizeInstructionBody(body), "utf8");
53225
53294
  }
53226
53295
  }
53227
53296
  function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifest) {
@@ -53236,7 +53305,7 @@ function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifes
53236
53305
  const raw = c2.files[0]?.content ?? "";
53237
53306
  if (!raw.trim())
53238
53307
  continue;
53239
- const { body } = stripFrontmatter(raw);
53308
+ const { body } = stripPlaybookFrontmatter(raw);
53240
53309
  const existing = existingManifest?.playbooks?.find((p2) => slugifyPlaybookTitle(p2.title) === c2.slug);
53241
53310
  if (existing?.content?.text !== undefined)
53242
53311
  continue;
@@ -53246,30 +53315,9 @@ function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifes
53246
53315
  fs45.writeFileSync(target, body, "utf8");
53247
53316
  }
53248
53317
  }
53249
- function stripFrontmatter(raw) {
53250
- const m3 = /^---\s*\n([\s\S]*?)\n---\s*\n?/.exec(raw);
53251
- if (!m3)
53252
- return { frontmatter: {}, body: raw };
53253
- let fm = {};
53254
- try {
53255
- const parsed = import_yaml3.default.parse(m3[1] ?? "");
53256
- if (parsed && typeof parsed === "object")
53257
- fm = parsed;
53258
- } catch {
53259
- return { frontmatter: {}, body: raw };
53260
- }
53261
- return { frontmatter: fm, body: raw.slice(m3[0].length) };
53262
- }
53263
53318
  function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
53264
53319
  const skills = cloud.components.filter((c2) => c2.type === "skill").map((c2) => {
53265
- const localDecl = prev?.skills.find((s3) => {
53266
- try {
53267
- const parsed = parseSourceLoose(s3.source);
53268
- return parsed?.slug === c2.slug;
53269
- } catch {
53270
- return false;
53271
- }
53272
- });
53320
+ const localDecl = prev?.skills.find((s3) => looseSkillComponentSlug(s3.source) === c2.slug);
53273
53321
  if (localDecl)
53274
53322
  return { source: localDecl.source };
53275
53323
  const meta = c2.meta ?? {};
@@ -53297,7 +53345,7 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
53297
53345
  }
53298
53346
  const playbooks = cloud.components.filter((c2) => c2.type === "playbook").map((c2) => {
53299
53347
  const raw = c2.files[0]?.content ?? "";
53300
- const { frontmatter } = stripFrontmatter(raw);
53348
+ const { frontmatter } = stripPlaybookFrontmatter(raw);
53301
53349
  const local = prev?.playbooks?.find((pb) => slugifyPlaybookTitle(pb.title) === c2.slug);
53302
53350
  const title = local?.title ?? (typeof frontmatter.title === "string" && frontmatter.title || c2.slug);
53303
53351
  const description = local?.description ?? (typeof frontmatter.description === "string" ? frontmatter.description : undefined);
@@ -53366,21 +53414,6 @@ function materializeEntrypoint(cwd2, cloudEntrypoint, prev) {
53366
53414
  fs45.chmodSync(target, 493);
53367
53415
  } catch {}
53368
53416
  }
53369
- function parseSourceLoose(raw) {
53370
- const m3 = raw.match(/^registry:[a-z0-9_-]+\/([a-z0-9_-]+)/i);
53371
- if (m3)
53372
- return { slug: m3[1] };
53373
- if (raw.startsWith("./") || raw.startsWith("../") || raw.startsWith("/")) {
53374
- return { slug: path48.basename(raw) };
53375
- }
53376
- try {
53377
- const core = parseSkillSource(raw);
53378
- if (core.type !== "inline" && core.type !== "local") {
53379
- return { slug: defaultSkillSlug(core) };
53380
- }
53381
- } catch {}
53382
- return null;
53383
- }
53384
53417
  function buildLockComponents(input) {
53385
53418
  const prevByKey = new Map((input.prevLock?.components ?? []).map((c2) => [`${c2.type}/${c2.slug}`, c2]));
53386
53419
  const localHashByKey = new Map;
@@ -53398,10 +53431,7 @@ function buildLockComponents(input) {
53398
53431
  continue;
53399
53432
  }
53400
53433
  }
53401
- const sourceDecl = m3.type === "skill" ? input.localManifest.skills.find((s3) => {
53402
- const parsed = parseSourceLoose(s3.source);
53403
- return parsed?.slug === m3.slug;
53404
- })?.source : undefined;
53434
+ const sourceDecl = m3.type === "skill" ? input.localManifest.skills.find((s3) => looseSkillComponentSlug(s3.source) === m3.slug)?.source : undefined;
53405
53435
  const localHash = localHashByKey.get(key2);
53406
53436
  out.push({
53407
53437
  type: m3.type,
@@ -53562,10 +53592,11 @@ async function buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions)
53562
53592
  }
53563
53593
  if (body.trim()) {
53564
53594
  const fileName = manifest.instructions.file ?? "instructions.md";
53565
- const fileHash2 = hashString(body);
53595
+ const wireBody = normalizeInstructionBody(body);
53596
+ const fileHash2 = hashString(wireBody);
53566
53597
  const file = {
53567
53598
  path: fileName,
53568
- content: body,
53599
+ content: wireBody,
53569
53600
  hash: fileHash2
53570
53601
  };
53571
53602
  out.push({
@@ -53878,26 +53909,8 @@ async function runAgentPush(cwd2, args) {
53878
53909
  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.`);
53879
53910
  return;
53880
53911
  }
53881
- const toSend = [];
53882
- const conflicts = [];
53883
- const upstreamOnly = [];
53884
- for (const r2 of rows) {
53885
- switch (r2.status) {
53886
- case "modified-local":
53887
- case "added-only-local":
53888
- case "removed-local":
53889
- toSend.push(r2);
53890
- break;
53891
- case "modified-both":
53892
- conflicts.push(r2);
53893
- break;
53894
- case "modified-cloud":
53895
- case "added-cloud":
53896
- case "removed-cloud":
53897
- upstreamOnly.push(r2);
53898
- break;
53899
- }
53900
- }
53912
+ const { toSend, conflicts, upstreamOnly } = partitionPushRows(rows, !!args.force);
53913
+ const forcedOverrides = args.force ? rows.filter((r2) => r2.status === "modified-both") : [];
53901
53914
  if (!meta.localChanged && !entrypointChanged && toSend.length === 0 && conflicts.length === 0) {
53902
53915
  f2.info("Nothing to push — local is in sync with the cloud.");
53903
53916
  return;
@@ -53907,9 +53920,15 @@ async function runAgentPush(cwd2, args) {
53907
53920
  for (const r2 of conflicts) {
53908
53921
  console.error(` ${import_picocolors27.default.red("!")} ${fmtType(r2.type)} ${import_picocolors27.default.bold(r2.slug)}`);
53909
53922
  }
53910
- f2.info(`Run ${import_picocolors27.default.cyan("brainbase agent pull")} first to reconcile, then push again.`);
53923
+ 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.`);
53911
53924
  return;
53912
53925
  }
53926
+ if (forcedOverrides.length > 0) {
53927
+ f2.warn(`${import_picocolors27.default.yellow("--force")}: overwriting ${forcedOverrides.length} cloud change${forcedOverrides.length === 1 ? "" : "s"} with your local version (cloud edits discarded):`);
53928
+ for (const r2 of forcedOverrides) {
53929
+ console.warn(` ${import_picocolors27.default.yellow("⤒")} ${fmtType(r2.type)} ${import_picocolors27.default.bold(r2.slug)}`);
53930
+ }
53931
+ }
53913
53932
  if (upstreamOnly.length > 0 && !args.yes) {
53914
53933
  f2.warn(`Cloud has ${upstreamOnly.length} change${upstreamOnly.length === 1 ? "" : "s"} you don't have locally:`);
53915
53934
  for (const r2 of upstreamOnly) {
@@ -54979,7 +54998,7 @@ async function runAgent(cwd2, sub, args, opts) {
54979
54998
  });
54980
54999
  return;
54981
55000
  case "push":
54982
- await runAgentPush(cwd2, { yes: opts.yes });
55001
+ await runAgentPush(cwd2, { yes: opts.yes, force: opts.force });
54983
55002
  return;
54984
55003
  case "unpack":
54985
55004
  await runAgentUnpack(cwd2, {
@@ -55014,7 +55033,7 @@ function printHelp() {
55014
55033
  out.push("");
55015
55034
  out.push(` ${import_picocolors32.default.cyan("create")} ${import_picocolors32.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
55016
55035
  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")}`);
55017
- out.push(` ${import_picocolors32.default.cyan("push")} ${import_picocolors32.default.dim("send local changes to the cloud — instructions, playbooks, skills, MCPs, entrypoint")}`);
55036
+ 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)")}`);
55018
55037
  out.push(` ${import_picocolors32.default.cyan("unpack")} ${import_picocolors32.default.dim("install the claimed agent into a harness layout (--harness to override)")}`);
55019
55038
  out.push(` ${import_picocolors32.default.cyan("status")} ${import_picocolors32.default.dim("show what would push and what would pull")}`);
55020
55039
  out.push(` ${import_picocolors32.default.cyan("env")} ${import_picocolors32.default.dim('print export statements — use with `eval "$(brainbase agent env)"`')}`);
@@ -55034,7 +55053,7 @@ var import_picocolors33 = __toESM(require_picocolors(), 1);
55034
55053
  // src/core/orchestration-manifest.ts
55035
55054
  import path51 from "node:path";
55036
55055
  import fs47 from "node:fs";
55037
- var import_yaml4 = __toESM(require_dist(), 1);
55056
+ var import_yaml3 = __toESM(require_dist(), 1);
55038
55057
  var ORCH_MANIFEST_FILE = "brainbase-orchestration.yaml";
55039
55058
  var ORCH_MEMBERS_DIR = "agents";
55040
55059
  var OrchMetaSchema = exports_external.object({
@@ -55086,7 +55105,7 @@ function readOrchManifest(cwd2) {
55086
55105
  const raw = fs47.readFileSync(p2, "utf8");
55087
55106
  let parsed;
55088
55107
  try {
55089
- parsed = import_yaml4.default.parse(raw);
55108
+ parsed = import_yaml3.default.parse(raw);
55090
55109
  } catch (err) {
55091
55110
  throw new Error(`${ORCH_MANIFEST_FILE} is not valid YAML: ${err.message}`);
55092
55111
  }
@@ -55097,7 +55116,7 @@ function readOrchManifest(cwd2) {
55097
55116
  return result2.data;
55098
55117
  }
55099
55118
  function writeOrchManifest(cwd2, manifest) {
55100
- const doc = new import_yaml4.default.Document;
55119
+ const doc = new import_yaml3.default.Document;
55101
55120
  doc.contents = manifest;
55102
55121
  doc.commentBefore = ` brainbase-orchestration.yaml — declarative orchestration manifest.
55103
55122
  ` + ` Committed to source control. Edit by hand, then
@@ -56952,7 +56971,7 @@ function help() {
56952
56971
  out.push("");
56953
56972
  out.push(` ${import_picocolors41.default.cyan("agent create")} ${import_picocolors41.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
56954
56973
  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)")}`);
56955
- out.push(` ${import_picocolors41.default.cyan("agent push")} ${import_picocolors41.default.dim("send local changes to the cloud instructions, playbooks, skills, MCPs, entrypoint")}`);
56974
+ 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)")}`);
56956
56975
  out.push(` ${import_picocolors41.default.cyan("agent unpack")} ${import_picocolors41.default.dim("install the claimed agent into a harness layout")}`);
56957
56976
  out.push(` ${import_picocolors41.default.cyan("link")} ${import_picocolors41.default.dim("attach this folder to an existing agent")}`);
56958
56977
  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.1",
3
+ "version": "0.13.3",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {