@brainbase-labs/cli 0.11.0 → 0.13.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 +201 -77
  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.11.0",
29452
+ version: "0.13.0",
29453
29453
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
29454
29454
  type: "module",
29455
29455
  bin: {
@@ -43741,6 +43741,33 @@ function removeInstalledRecord(name, scope, cwd2) {
43741
43741
  // src/core/registry-remote.ts
43742
43742
  import fs22 from "node:fs";
43743
43743
 
43744
+ // src/core/api-error-message.ts
43745
+ function apiErrorMessage(body, status) {
43746
+ if (body && typeof body === "object") {
43747
+ const obj = body;
43748
+ for (const key2 of ["error", "message", "detail"]) {
43749
+ const v2 = obj[key2];
43750
+ if (typeof v2 === "string" && v2.trim())
43751
+ return v2.trim();
43752
+ if (Array.isArray(v2)) {
43753
+ const msgs = v2.map((item) => {
43754
+ if (typeof item === "string" && item.trim())
43755
+ return item.trim();
43756
+ if (item && typeof item === "object" && typeof item.msg === "string") {
43757
+ return item.msg;
43758
+ }
43759
+ return null;
43760
+ }).filter((m2) => !!m2);
43761
+ if (msgs.length)
43762
+ return msgs.join("; ");
43763
+ }
43764
+ }
43765
+ } else if (typeof body === "string" && body.trim()) {
43766
+ return body.trim();
43767
+ }
43768
+ return `HTTP ${status}`;
43769
+ }
43770
+
43744
43771
  // src/core/api.ts
43745
43772
  var DEFAULT_API_BASE = "https://kafka-llm-service.onrender.com";
43746
43773
 
@@ -43800,8 +43827,7 @@ async function request(pathname, init = {}) {
43800
43827
  body = text2 ? JSON.parse(text2) : null;
43801
43828
  } catch {}
43802
43829
  if (!res.ok) {
43803
- const msg = (body && typeof body === "object" && "error" in body ? String(body.error) : null) ?? (body && typeof body === "object" && "message" in body ? String(body.message) : null) ?? `HTTP ${res.status}`;
43804
- throw new ApiError(msg, res.status, body);
43830
+ throw new ApiError(apiErrorMessage(body, res.status), res.status, body);
43805
43831
  }
43806
43832
  return body;
43807
43833
  }
@@ -51126,8 +51152,10 @@ var PlaybookContentSchema = exports_external.object({
51126
51152
  message: "playbook content must set either `file` or `text`"
51127
51153
  });
51128
51154
  var PlaybookSchema = exports_external.object({
51155
+ id: exports_external.string().optional(),
51129
51156
  title: exports_external.string().min(1),
51130
51157
  description: exports_external.string().optional(),
51158
+ icon: exports_external.string().optional(),
51131
51159
  content: PlaybookContentSchema
51132
51160
  });
51133
51161
  var SkillEntrySchema = exports_external.object({
@@ -51145,7 +51173,8 @@ var McpEntrySchema = exports_external.object({
51145
51173
  var CapabilitiesSchema = exports_external.object({
51146
51174
  memory: exports_external.boolean().optional(),
51147
51175
  browser: exports_external.boolean().optional(),
51148
- slack: exports_external.boolean().optional()
51176
+ slack: exports_external.boolean().optional(),
51177
+ meeting: exports_external.boolean().optional()
51149
51178
  });
51150
51179
  var AgentManifestSchema = exports_external.object({
51151
51180
  schema: exports_external.literal(1),
@@ -51252,7 +51281,38 @@ function resolvePlaybookContent(cwd2, entry) {
51252
51281
  return null;
51253
51282
  }
51254
51283
  function slugifyPlaybookTitle(title) {
51255
- return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "playbook";
51284
+ return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/^-+|-+$/g, "") || "playbook";
51285
+ }
51286
+ function playbookYamlScalar(value) {
51287
+ const safe = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, " ").trim();
51288
+ return `"${safe}"`;
51289
+ }
51290
+ function assemblePlaybookWireBody(entry, body) {
51291
+ const lines = ["---", `title: ${playbookYamlScalar(entry.title)}`];
51292
+ if (entry.description) {
51293
+ lines.push(`description: ${playbookYamlScalar(entry.description)}`);
51294
+ }
51295
+ if (entry.icon) {
51296
+ lines.push(`icon: ${playbookYamlScalar(entry.icon)}`);
51297
+ }
51298
+ lines.push("---", "", "");
51299
+ return lines.join(`
51300
+ `) + body.replace(/^\n+/, "").trimEnd() + `
51301
+ `;
51302
+ }
51303
+ function assignPlaybookSlugs(titles) {
51304
+ const used = new Set;
51305
+ return titles.map((t) => {
51306
+ const base2 = slugifyPlaybookTitle(t);
51307
+ let slug = base2;
51308
+ let n = 1;
51309
+ while (used.has(slug)) {
51310
+ n += 1;
51311
+ slug = `${base2}-${n}`;
51312
+ }
51313
+ used.add(slug);
51314
+ return slug;
51315
+ });
51256
51316
  }
51257
51317
 
51258
51318
  // src/core/link.ts
@@ -51301,7 +51361,7 @@ var SyncedComponentSchema = exports_external.object({
51301
51361
  });
51302
51362
  var AgentMetaSnapshotSchema = exports_external.object({
51303
51363
  name: exports_external.string(),
51304
- tagline: exports_external.string().optional(),
51364
+ tagline: exports_external.string().nullish().transform((v3) => v3 ?? undefined),
51305
51365
  entrypoint: exports_external.string().optional()
51306
51366
  });
51307
51367
  var SyncStateSchema = exports_external.object({
@@ -52043,6 +52103,39 @@ function buildSlackMcpComponent(scope) {
52043
52103
  };
52044
52104
  }
52045
52105
 
52106
+ // src/core/meeting-mcp.ts
52107
+ var DEFAULT_MEETING_MCP_BASE = "https://meeting.mcp.brainbaselabs.com";
52108
+ var MEETING_MCP_SLUG = "brainbase-meeting";
52109
+ function meetingMcpBaseUrl() {
52110
+ const envOverride = process.env.BRAINBASE_MEETING_MCP_URL;
52111
+ if (envOverride)
52112
+ return envOverride.replace(/\/+$/, "");
52113
+ return DEFAULT_MEETING_MCP_BASE;
52114
+ }
52115
+ function meetingMcpUrl() {
52116
+ return `${meetingMcpBaseUrl()}/t/\${BRAINBASE_THREAD_ID}/mcp`;
52117
+ }
52118
+ function meetingMcpPayload() {
52119
+ return {
52120
+ url: meetingMcpUrl(),
52121
+ headers: {
52122
+ Authorization: "Bearer ${BRAINBASE_TOKEN}"
52123
+ }
52124
+ };
52125
+ }
52126
+ function buildMeetingMcpComponent(scope) {
52127
+ return {
52128
+ type: "mcp",
52129
+ slug: MEETING_MCP_SLUG,
52130
+ scope,
52131
+ rootDir: "",
52132
+ description: "This agent's Meeting connector (join calls, read transcripts, manage " + "recordings and notes). Scoped to this agent only.",
52133
+ meta: { mcp: meetingMcpPayload() },
52134
+ payload: meetingMcpPayload(),
52135
+ checksum: "builtin:brainbase-meeting:v1"
52136
+ };
52137
+ }
52138
+
52046
52139
  // src/core/browser-mcp.ts
52047
52140
  var DEFAULT_BROWSER_MCP_BASE = "https://brainbase-browser-mcp.onrender.com";
52048
52141
  var BROWSER_MCP_SLUG = "brainbase-browser";
@@ -52081,7 +52174,8 @@ function capabilitiesFromAgent(agent) {
52081
52174
  return {
52082
52175
  memory: agent.memory_enabled !== false,
52083
52176
  browser: agent.browser_enabled !== false,
52084
- slack: agent.slack_connected === true
52177
+ slack: agent.slack_connected === true,
52178
+ meeting: agent.meeting_connected === true
52085
52179
  };
52086
52180
  }
52087
52181
  function capabilitiesFromManifest(manifest) {
@@ -52089,7 +52183,8 @@ function capabilitiesFromManifest(manifest) {
52089
52183
  return {
52090
52184
  memory: c2.memory !== false,
52091
52185
  browser: c2.browser !== false,
52092
- slack: c2.slack === true
52186
+ slack: c2.slack === true,
52187
+ meeting: c2.meeting === true
52093
52188
  };
52094
52189
  }
52095
52190
  function resolveBuiltinMcps(input) {
@@ -52098,7 +52193,8 @@ function resolveBuiltinMcps(input) {
52098
52193
  { slug: ORCHESTRATION_MCP_SLUG, enabled: true, build: buildOrchestrationMcpComponent },
52099
52194
  { slug: MEMORY_MCP_SLUG, enabled: caps.memory, build: buildMemoryMcpComponent },
52100
52195
  { slug: BROWSER_MCP_SLUG, enabled: caps.browser, build: buildBrowserMcpComponent },
52101
- { slug: SLACK_MCP_SLUG, enabled: caps.slack, build: buildSlackMcpComponent }
52196
+ { slug: SLACK_MCP_SLUG, enabled: caps.slack, build: buildSlackMcpComponent },
52197
+ { slug: MEETING_MCP_SLUG, enabled: caps.meeting, build: buildMeetingMcpComponent }
52102
52198
  ];
52103
52199
  const install = [];
52104
52200
  const removeSlugs = [];
@@ -52578,34 +52674,25 @@ function readLocalComponents(cwd2, manifest) {
52578
52674
  hash: hashMcpEntry(entry)
52579
52675
  });
52580
52676
  }
52581
- for (const entry of manifest.playbooks ?? []) {
52677
+ const pbEntries = manifest.playbooks ?? [];
52678
+ const pbSlugs = assignPlaybookSlugs(pbEntries.map((e2) => e2.title));
52679
+ for (let i = 0;i < pbEntries.length; i++) {
52680
+ const entry = pbEntries[i];
52681
+ const slug = pbSlugs[i];
52582
52682
  const body = resolvePlaybookContent(cwd2, entry);
52583
52683
  if (body === null) {
52584
- out.push({
52585
- type: "playbook",
52586
- slug: slugifyPlaybookTitle(entry.title),
52587
- hash: null
52588
- });
52684
+ out.push({ type: "playbook", slug, hash: null });
52589
52685
  continue;
52590
52686
  }
52591
- const wireBody = /^---\s*\n/.test(body) ? body : `---
52592
- title: ${jsonOrPlain(entry.title)}` + (entry.description ? `
52593
- description: ${jsonOrPlain(entry.description)}` : "") + `
52594
- ---
52595
- ${body.replace(/^\n+/, "")}`;
52687
+ const wireBody = assemblePlaybookWireBody(entry, body);
52596
52688
  out.push({
52597
52689
  type: "playbook",
52598
- slug: slugifyPlaybookTitle(entry.title),
52690
+ slug,
52599
52691
  hash: componentHashFromFileHashes([hashString(wireBody)])
52600
52692
  });
52601
52693
  }
52602
52694
  return out;
52603
52695
  }
52604
- function jsonOrPlain(s3) {
52605
- if (!/[\n":#&*!|>'%@`{}[\]]/.test(s3) && s3.trim() === s3 && s3 !== "")
52606
- return s3;
52607
- return JSON.stringify(s3);
52608
- }
52609
52696
  function threeWayDiff(input) {
52610
52697
  const lockMap = new Map;
52611
52698
  for (const c2 of input.lock)
@@ -53128,7 +53215,7 @@ function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifes
53128
53215
  if (!raw.trim())
53129
53216
  continue;
53130
53217
  const { body } = stripFrontmatter(raw);
53131
- const existing = existingManifest?.playbooks?.find((p2) => slugifyForCompare(p2.title) === c2.slug);
53218
+ const existing = existingManifest?.playbooks?.find((p2) => slugifyPlaybookTitle(p2.title) === c2.slug);
53132
53219
  if (existing?.content?.text !== undefined)
53133
53220
  continue;
53134
53221
  const targetRel = existing?.content?.file ?? path48.join(DEFAULT_PLAYBOOKS_DIR, `${c2.slug}.md`);
@@ -53137,9 +53224,6 @@ function materializePlaybooks(cwd2, cloud, toInstall, keepLocal, existingManifes
53137
53224
  fs45.writeFileSync(target, body, "utf8");
53138
53225
  }
53139
53226
  }
53140
- function slugifyForCompare(s3) {
53141
- return s3.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "playbook";
53142
- }
53143
53227
  function stripFrontmatter(raw) {
53144
53228
  const m3 = /^---\s*\n([\s\S]*?)\n---\s*\n?/.exec(raw);
53145
53229
  if (!m3)
@@ -53192,15 +53276,18 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
53192
53276
  const playbooks = cloud.components.filter((c2) => c2.type === "playbook").map((c2) => {
53193
53277
  const raw = c2.files[0]?.content ?? "";
53194
53278
  const { frontmatter } = stripFrontmatter(raw);
53195
- const local = prev?.playbooks?.find((pb) => slugifyForCompare(pb.title) === c2.slug);
53279
+ const local = prev?.playbooks?.find((pb) => slugifyPlaybookTitle(pb.title) === c2.slug);
53196
53280
  const title = local?.title ?? (typeof frontmatter.title === "string" && frontmatter.title || c2.slug);
53197
53281
  const description = local?.description ?? (typeof frontmatter.description === "string" ? frontmatter.description : undefined);
53198
53282
  const content = local?.content?.text !== undefined ? { text: local.content.text } : {
53199
53283
  file: local?.content?.file ?? path48.join(DEFAULT_PLAYBOOKS_DIR, `${c2.slug}.md`)
53200
53284
  };
53285
+ const pbMeta = c2.meta ?? {};
53201
53286
  return {
53287
+ ...typeof pbMeta.playbook_id === "string" && pbMeta.playbook_id ? { id: pbMeta.playbook_id } : {},
53202
53288
  title,
53203
53289
  ...description ? { description } : {},
53290
+ ...typeof pbMeta.icon === "string" && pbMeta.icon ? { icon: pbMeta.icon } : {},
53204
53291
  content
53205
53292
  };
53206
53293
  });
@@ -53235,7 +53322,12 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
53235
53322
  playbooks,
53236
53323
  skills,
53237
53324
  mcp,
53238
- capabilities: { memory: caps.memory, browser: caps.browser, slack: caps.slack }
53325
+ capabilities: {
53326
+ memory: caps.memory,
53327
+ browser: caps.browser,
53328
+ slack: caps.slack,
53329
+ meeting: caps.meeting
53330
+ }
53239
53331
  };
53240
53332
  }
53241
53333
  function materializeEntrypoint(cwd2, cloudEntrypoint, prev) {
@@ -53489,8 +53581,11 @@ async function buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions)
53489
53581
  }
53490
53582
  });
53491
53583
  }
53492
- const seenPlaybookSlugs = new Set;
53493
- for (const entry of manifest.playbooks ?? []) {
53584
+ const playbookEntries = manifest.playbooks ?? [];
53585
+ const playbookSlugs = assignPlaybookSlugs(playbookEntries.map((e2) => e2.title));
53586
+ for (let i = 0;i < playbookEntries.length; i++) {
53587
+ const entry = playbookEntries[i];
53588
+ const slug = playbookSlugs[i];
53494
53589
  if (entry.content.text !== undefined && entry.content.file !== undefined) {
53495
53590
  f2.error(`Playbook ${import_picocolors26.default.bold(entry.title)} sets both ${import_picocolors26.default.cyan("text")} and ${import_picocolors26.default.cyan("file")} — pick one.`);
53496
53591
  return null;
@@ -53504,24 +53599,25 @@ async function buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions)
53504
53599
  }
53505
53600
  return null;
53506
53601
  }
53507
- const slug = slugifyPlaybookTitle(entry.title);
53508
- if (seenPlaybookSlugs.has(slug)) {
53509
- f2.error(`Two playbooks resolve to the same slug ${import_picocolors26.default.bold(slug)} (from title ${import_picocolors26.default.bold(entry.title)}). Pick distinct titles.`);
53510
- return null;
53511
- }
53512
- seenPlaybookSlugs.add(slug);
53513
- const wireBody = assemblePlaybookBody(entry, body);
53602
+ const wireBody = assemblePlaybookWireBody(entry, body);
53514
53603
  const fileName = `${slug}.md`;
53515
53604
  const fileHash2 = hashString(wireBody);
53605
+ const file = {
53606
+ path: fileName,
53607
+ content: wireBody,
53608
+ hash: fileHash2
53609
+ };
53516
53610
  out.push({
53517
53611
  type: "playbook",
53518
53612
  slug,
53519
53613
  description: entry.description,
53520
53614
  hash: componentHashFromFileHashes([fileHash2]),
53521
- files: [{ path: fileName, content: wireBody, hash: fileHash2 }],
53615
+ files: [file],
53522
53616
  meta: {
53523
53617
  title: entry.title,
53524
- ...entry.description ? { description: entry.description } : {}
53618
+ ...entry.description ? { description: entry.description } : {},
53619
+ ...entry.icon ? { icon: entry.icon } : {},
53620
+ ...entry.id ? { playbook_id: entry.id } : {}
53525
53621
  }
53526
53622
  });
53527
53623
  }
@@ -53552,23 +53648,6 @@ async function buildOutgoingComponents(cwd2, manifest, cloud, resolvedVersions)
53552
53648
  }
53553
53649
  return out;
53554
53650
  }
53555
- function assemblePlaybookBody(entry, body) {
53556
- if (/^---\s*\n/.test(body))
53557
- return body;
53558
- const lines = ["---", `title: ${yamlScalar(entry.title)}`];
53559
- if (entry.description) {
53560
- lines.push(`description: ${yamlScalar(entry.description)}`);
53561
- }
53562
- lines.push("---", "");
53563
- return lines.join(`
53564
- `) + body.replace(/^\n+/, "");
53565
- }
53566
- function yamlScalar(s3) {
53567
- if (!/[\n":#&*!|>'%@`{}[\]]/.test(s3) && s3.trim() === s3 && s3 !== "") {
53568
- return s3;
53569
- }
53570
- return JSON.stringify(s3);
53571
- }
53572
53651
 
53573
53652
  // src/core/registry-skill-updates.ts
53574
53653
  function parseSemver2(v3) {
@@ -53664,29 +53743,56 @@ async function runAgentPush(cwd2, args) {
53664
53743
  lock: lock?.components ?? [],
53665
53744
  cloud: cloud.components
53666
53745
  });
53667
- const unpinned = new Map;
53746
+ const registryRefs = new Map;
53668
53747
  for (const entry of manifest.skills) {
53669
53748
  try {
53670
53749
  const parsed = parseSkillSource2(entry.source);
53671
- if (parsed.kind === "registry" && !parsed.version && parsed.creator) {
53672
- unpinned.set(`${parsed.creator}/${parsed.slug}`, {
53750
+ if (parsed.kind === "registry" && parsed.creator) {
53751
+ registryRefs.set(`${parsed.creator}/${parsed.slug}`, {
53673
53752
  creator: parsed.creator,
53674
- slug: parsed.slug
53753
+ slug: parsed.slug,
53754
+ version: parsed.version
53675
53755
  });
53676
53756
  }
53677
53757
  } catch {}
53678
53758
  }
53679
53759
  const latestByName = new Map;
53680
- if (unpinned.size > 0) {
53681
- await Promise.all([...unpinned.entries()].map(async ([name, ref]) => {
53760
+ const notInRegistry = new Set;
53761
+ if (registryRefs.size > 0) {
53762
+ await Promise.all([...registryRefs.entries()].map(async ([name, ref]) => {
53682
53763
  try {
53683
53764
  const pkg = await skillsApi.getPackage(ref.creator, ref.slug);
53684
53765
  if (pkg.latest_version?.version) {
53685
53766
  latestByName.set(name, pkg.latest_version.version);
53686
53767
  }
53687
- } catch {}
53768
+ } catch (err) {
53769
+ if (err instanceof ApiError && err.status === 404) {
53770
+ notInRegistry.add(name);
53771
+ }
53772
+ }
53688
53773
  }));
53689
53774
  }
53775
+ const unresolvable = [...notInRegistry].filter((name) => {
53776
+ const ref = registryRefs.get(name);
53777
+ const componentSlug = registrySkillComponentSlug({
53778
+ kind: "registry",
53779
+ creator: ref.creator,
53780
+ slug: ref.slug
53781
+ });
53782
+ return !cloud.components.some((c2) => c2.type === "skill" && c2.slug === componentSlug);
53783
+ });
53784
+ if (unresolvable.length > 0) {
53785
+ for (const name of unresolvable) {
53786
+ f2.error(`Skill ${import_picocolors27.default.bold(name)} isn't in the registry (not published, or private to another owner).`);
53787
+ }
53788
+ const noun = unresolvable.length === 1 ? "it" : "each one";
53789
+ f2.info(`Publish ${noun} first, then push again:`);
53790
+ for (const name of unresolvable) {
53791
+ const version = registryRefs.get(name)?.version ?? "0.1.0";
53792
+ f2.info(` ${import_picocolors27.default.cyan(`brainbase skill publish ./path/to/skill --name ${name} --skill-version ${version} --yes`)}`);
53793
+ }
53794
+ return;
53795
+ }
53690
53796
  const skillUpdates = planRegistrySkillUpdates(manifest.skills, cloud.components, latestByName);
53691
53797
  const resolvedVersions = new Map(skillUpdates.map((u2) => [u2.componentSlug, u2.latest]));
53692
53798
  for (const u2 of skillUpdates) {
@@ -53723,7 +53829,7 @@ async function runAgentPush(cwd2, args) {
53723
53829
  const entrypointChanged = resolvedEntrypoint !== undefined && (resolvedEntrypoint ?? "").trim() !== (lock?.agentMeta?.entrypoint ?? "").trim();
53724
53830
  for (const r2 of rows) {
53725
53831
  if (r2.type !== "instruction" && r2.type !== "skill" && r2.type !== "mcp" && r2.type !== "playbook") {
53726
- f2.error(`Component ${fmtType(r2.type)} ${import_picocolors27.default.bold(r2.slug)} can't be pushed yet — the server accepts instructions, skills, mcps, and playbooks in this version.`);
53832
+ f2.error(`Component ${fmtType(r2.type)} ${import_picocolors27.default.bold(r2.slug)} can't be pushed yet — the server accepts instructions, skills, and mcps in this version.`);
53727
53833
  return;
53728
53834
  }
53729
53835
  }
@@ -53739,6 +53845,17 @@ async function runAgentPush(cwd2, args) {
53739
53845
  return;
53740
53846
  }
53741
53847
  }
53848
+ const localPlaybooks = manifest.playbooks ?? [];
53849
+ const localPlaybookSlugs = assignPlaybookSlugs(localPlaybooks.map((entry) => entry.title));
53850
+ const idlessLocalSlugs = new Set(localPlaybooks.flatMap((entry, i) => entry.id ? [] : [localPlaybookSlugs[i]]));
53851
+ const preIdSchemaSlugs = cloud.components.filter((c2) => c2.type === "playbook").filter((c2) => {
53852
+ const id = c2.meta?.playbook_id;
53853
+ return typeof id === "string" && id && !localPlaybooks.some((p2) => p2.id === id) && idlessLocalSlugs.has(c2.slug);
53854
+ }).map((c2) => c2.slug);
53855
+ if (preIdSchemaSlugs.length > 0) {
53856
+ 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
+ return;
53858
+ }
53742
53859
  const toSend = [];
53743
53860
  const conflicts = [];
53744
53861
  const upstreamOnly = [];
@@ -53851,7 +53968,8 @@ async function runAgentPush(cwd2, args) {
53851
53968
  try {
53852
53969
  updatedCloud = await api.pushAgentManifest(agentId, {
53853
53970
  components: outgoing,
53854
- base_revision: cloud.revision
53971
+ base_revision: cloud.revision,
53972
+ reconcile_playbooks: true
53855
53973
  });
53856
53974
  pushSpinner.stop(`Pushed. New revision ${updatedCloud.revision}.`);
53857
53975
  } catch (err) {
@@ -54398,7 +54516,7 @@ async function runAgentCreate(cwd2, args) {
54398
54516
  writeLink(cwd2, link2);
54399
54517
  manifest = readManifest(cwd2);
54400
54518
  let updatedCloud = null;
54401
- const hasContent = !!manifest.instructions || manifest.skills.length > 0 || (manifest.mcp ?? []).length > 0;
54519
+ const hasContent = !!manifest.instructions || manifest.skills.length > 0 || (manifest.mcp ?? []).length > 0 || (manifest.playbooks ?? []).length > 0;
54402
54520
  if (hasContent) {
54403
54521
  const outgoing = await buildOutgoingComponents(cwd2, manifest, null);
54404
54522
  if (outgoing === null) {
@@ -54409,7 +54527,8 @@ async function runAgentCreate(cwd2, args) {
54409
54527
  try {
54410
54528
  updatedCloud = await api.pushAgentManifest(agent.id, {
54411
54529
  components: outgoing,
54412
- base_revision: 0
54530
+ base_revision: 0,
54531
+ reconcile_playbooks: true
54413
54532
  });
54414
54533
  pushSpinner.stop(`Pushed at revision ${updatedCloud.revision}.`);
54415
54534
  } catch (err) {
@@ -54768,14 +54887,14 @@ function copyDirRecursive(src, dest) {
54768
54887
  }
54769
54888
  }
54770
54889
  function assembleFrontmatter(title, description) {
54771
- const lines = ["---", `title: ${yamlScalar2(title)}`];
54890
+ const lines = ["---", `title: ${yamlScalar(title)}`];
54772
54891
  if (description)
54773
- lines.push(`description: ${yamlScalar2(description)}`);
54892
+ lines.push(`description: ${yamlScalar(description)}`);
54774
54893
  lines.push("---", "");
54775
54894
  return lines.join(`
54776
54895
  `);
54777
54896
  }
54778
- function yamlScalar2(s3) {
54897
+ function yamlScalar(s3) {
54779
54898
  if (!/[\n":#&*!|>'%@`{}[\]]/.test(s3) && s3.trim() === s3 && s3 !== "")
54780
54899
  return s3;
54781
54900
  return JSON.stringify(s3);
@@ -54863,7 +54982,7 @@ function printHelp() {
54863
54982
  out.push("");
54864
54983
  out.push(` ${import_picocolors32.default.cyan("create")} ${import_picocolors32.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
54865
54984
  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")}`);
54866
- out.push(` ${import_picocolors32.default.cyan("push")} ${import_picocolors32.default.dim("send local changes to the cloud")}`);
54985
+ out.push(` ${import_picocolors32.default.cyan("push")} ${import_picocolors32.default.dim("send local changes to the cloud — instructions, playbooks, skills, MCPs, entrypoint")}`);
54867
54986
  out.push(` ${import_picocolors32.default.cyan("unpack")} ${import_picocolors32.default.dim("install the claimed agent into a harness layout (--harness to override)")}`);
54868
54987
  out.push(` ${import_picocolors32.default.cyan("status")} ${import_picocolors32.default.dim("show what would push and what would pull")}`);
54869
54988
  out.push(` ${import_picocolors32.default.cyan("env")} ${import_picocolors32.default.dim('print export statements — use with `eval "$(brainbase agent env)"`')}`);
@@ -55239,7 +55358,12 @@ function buildManifestFromCloud(cloud, agent) {
55239
55358
  playbooks: [],
55240
55359
  skills,
55241
55360
  mcp,
55242
- capabilities: { memory: caps.memory, browser: caps.browser, slack: caps.slack }
55361
+ capabilities: {
55362
+ memory: caps.memory,
55363
+ browser: caps.browser,
55364
+ slack: caps.slack,
55365
+ meeting: caps.meeting
55366
+ }
55243
55367
  };
55244
55368
  }
55245
55369
  async function pullAgentSecrets(cwd2, agentId) {
@@ -56796,7 +56920,7 @@ function help() {
56796
56920
  out.push("");
56797
56921
  out.push(` ${import_picocolors41.default.cyan("agent create")} ${import_picocolors41.default.dim("claim an unclaimed brainbase.agent.yaml and create the cloud agent")}`);
56798
56922
  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)")}`);
56799
- out.push(` ${import_picocolors41.default.cyan("agent push")} ${import_picocolors41.default.dim("send local changes to the cloud")}`);
56923
+ out.push(` ${import_picocolors41.default.cyan("agent push")} ${import_picocolors41.default.dim("send local changes to the cloud — instructions, playbooks, skills, MCPs, entrypoint")}`);
56800
56924
  out.push(` ${import_picocolors41.default.cyan("agent unpack")} ${import_picocolors41.default.dim("install the claimed agent into a harness layout")}`);
56801
56925
  out.push(` ${import_picocolors41.default.cyan("link")} ${import_picocolors41.default.dim("attach this folder to an existing agent")}`);
56802
56926
  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.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {