@brainbase-labs/cli 0.4.0 → 0.4.1

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 +57 -79
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -49938,59 +49938,52 @@ import path40 from "node:path";
49938
49938
  import os9 from "node:os";
49939
49939
  import fs37 from "node:fs";
49940
49940
  var import_picocolors16 = __toESM(require_picocolors(), 1);
49941
- function parseRef3(input) {
49942
- const at2 = input.indexOf("@");
49943
- if (at2 < 0)
49944
- return { name: input };
49945
- return { name: input.slice(0, at2), version: input.slice(at2 + 1) };
49941
+ function parseName(input) {
49942
+ if (!/^[a-z0-9_-]+\/[a-z0-9_-]+$/i.test(input))
49943
+ return null;
49944
+ const [creator, slug] = input.toLowerCase().split("/");
49945
+ return { creator, slug };
49946
49946
  }
49947
49947
  async function runSkillPublish(cwd2, args) {
49948
49948
  banner("skill publish — send a skill to the registry");
49949
- const candidates = collectSkillCandidates(cwd2);
49950
- if (candidates.length === 0) {
49951
- f2.error("No skills found. Add one with `brainbase skill add <source>` or write one locally first.");
49949
+ const skillDir = path40.resolve(cwd2, args.dir ?? ".");
49950
+ if (!exists(skillDir) || !fs37.statSync(skillDir).isDirectory()) {
49951
+ f2.error(`Not a directory: ${import_picocolors16.default.bold(skillDir)}`);
49952
49952
  return;
49953
49953
  }
49954
- let chosen;
49955
- let suggestedName;
49956
- let suggestedVersion;
49957
- if (args.ref) {
49958
- const { name: name2, version: version2 } = parseRef3(args.ref);
49959
- suggestedName = name2;
49960
- suggestedVersion = version2;
49961
- const slug = name2.split("/").pop();
49962
- const matches = candidates.filter((c2) => c2.slug === slug);
49963
- if (matches.length === 0) {
49964
- f2.error(`No local skill named ${import_picocolors16.default.bold(slug)} to publish.`);
49965
- return;
49966
- }
49967
- chosen = matches[0];
49968
- } else {
49969
- const pick = await ie({
49970
- message: "Which skill do you want to publish?",
49971
- options: candidates.map((c2, i) => ({
49972
- value: String(i),
49973
- label: `${import_picocolors16.default.bold(c2.slug.padEnd(24))} ${import_picocolors16.default.dim(c2.harness + "/" + c2.scope)}`
49974
- }))
49975
- });
49976
- chosen = candidates[Number(ensureNotCancelled(pick))];
49954
+ if (!exists(path40.join(skillDir, "SKILL.md"))) {
49955
+ 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.`);
49956
+ return;
49977
49957
  }
49978
- const existingMarker = readSkillMarker(chosen.dir);
49958
+ const folderSlug = path40.basename(skillDir).toLowerCase();
49959
+ const existingMarker = readSkillMarker(skillDir);
49960
+ let suggestedName;
49979
49961
  if (existingMarker && existingMarker.source.type === "brainbase") {
49980
- suggestedName ??= `${existingMarker.source.creator}/${existingMarker.source.slug}`;
49962
+ suggestedName = `${existingMarker.source.creator}/${existingMarker.source.slug}`;
49963
+ }
49964
+ if (existingMarker && (existingMarker.source.type === "github" || existingMarker.source.type === "git")) {
49965
+ f2.warn(`This skill was installed from ${existingMarker.source.type}. Publishing will fork it under your name.`);
49966
+ }
49967
+ let name = args.name ?? suggestedName;
49968
+ if (name && !parseName(name)) {
49969
+ f2.error(`Invalid --name "${name}". Use creator/slug.`);
49970
+ return;
49981
49971
  }
49982
- let name = suggestedName;
49983
49972
  if (!name) {
49984
49973
  const ans = await te({
49985
49974
  message: "Publish as (creator/slug)",
49986
- placeholder: `gokhan/${chosen.slug}`,
49987
- initialValue: `gokhan/${chosen.slug}`,
49988
- validate: (v3) => /^[a-z0-9_-]+\/[a-z0-9_-]+$/i.test(v3 ?? "") ? undefined : "Use creator/slug"
49975
+ placeholder: `gokhan/${folderSlug}`,
49976
+ initialValue: `gokhan/${folderSlug}`,
49977
+ validate: (v3) => parseName(v3 ?? "") ? undefined : "Use creator/slug"
49989
49978
  });
49990
49979
  name = ensureNotCancelled(ans).toLowerCase();
49991
49980
  }
49992
- const [creator, pkgSlug] = name.toLowerCase().split("/");
49993
- let version = suggestedVersion;
49981
+ const { creator, slug: pkgSlug } = parseName(name);
49982
+ let version = args.version;
49983
+ if (version && !/^\d+\.\d+\.\d+$/.test(version)) {
49984
+ f2.error(`Invalid --skill-version "${version}". Use MAJOR.MINOR.PATCH.`);
49985
+ return;
49986
+ }
49994
49987
  if (!version) {
49995
49988
  const ans = await te({
49996
49989
  message: "Version",
@@ -50043,7 +50036,7 @@ async function runSkillPublish(cwd2, args) {
50043
50036
  owner_user_id: owner.type === "user" ? owner.userId : undefined,
50044
50037
  owner_team_id: owner.type === "team" ? owner.teamId : undefined,
50045
50038
  visibility,
50046
- description: readSkillDescription(chosen.dir)
50039
+ description: readSkillDescription(skillDir)
50047
50040
  });
50048
50041
  } catch (err) {
50049
50042
  if (err instanceof ApiError && err.status === 409) {} else {
@@ -50055,20 +50048,20 @@ async function runSkillPublish(cwd2, args) {
50055
50048
  const tmp = fs37.mkdtempSync(path40.join(os9.tmpdir(), "bb-skill-pub-"));
50056
50049
  const tar = path40.join(tmp, "skill.tgz");
50057
50050
  try {
50058
- const files = collectSkillFiles(chosen.dir).filter((f4) => f4 !== SKILL_MARKER_FILE);
50051
+ const files = collectSkillFiles(skillDir).filter((f4) => f4 !== SKILL_MARKER_FILE);
50059
50052
  if (files.length === 0) {
50060
50053
  f2.error("Skill folder is empty.");
50061
50054
  return;
50062
50055
  }
50063
50056
  const buildSp = de();
50064
50057
  buildSp.start("Building skill bundle…");
50065
- await pack({ rootDir: chosen.dir, outFile: tar, files });
50058
+ await pack({ rootDir: skillDir, outFile: tar, files });
50066
50059
  const sha = await sha256OfFile(tar);
50067
50060
  const size = fs37.statSync(tar).size;
50068
50061
  buildSp.stop(`Bundle ready (${(size / 1024).toFixed(1)} KB).`);
50069
50062
  if (!args.yes) {
50070
50063
  const ok = await se({
50071
- message: `Publish ${import_picocolors16.default.bold(pkgSlug)}@${version} as ${import_picocolors16.default.bold(creator + "/" + pkgSlug)}?`,
50064
+ message: `Publish ${import_picocolors16.default.bold(skillDir)} as ${import_picocolors16.default.bold(creator + "/" + pkgSlug)}@${version}?`,
50072
50065
  initialValue: true
50073
50066
  });
50074
50067
  if (!ensureNotCancelled(ok)) {
@@ -50076,12 +50069,13 @@ async function runSkillPublish(cwd2, args) {
50076
50069
  return;
50077
50070
  }
50078
50071
  }
50079
- const manifest = buildSkillManifest(chosen.dir, {
50072
+ const sourceHarness = args.harness ?? "claude-code";
50073
+ const manifest = buildSkillManifest(skillDir, {
50080
50074
  name: `${creator}/${pkgSlug}`,
50081
50075
  version,
50082
- description: readSkillDescription(chosen.dir),
50083
- sourceHarness: chosen.harness,
50084
- agents: [chosen.harness]
50076
+ description: readSkillDescription(skillDir),
50077
+ sourceHarness,
50078
+ agents: args.harness ? [args.harness] : []
50085
50079
  });
50086
50080
  const upSp = de();
50087
50081
  upSp.start("Uploading…");
@@ -50092,7 +50086,7 @@ async function runSkillPublish(cwd2, args) {
50092
50086
  bundlePath: tar,
50093
50087
  bundleSha256: sha,
50094
50088
  version,
50095
- sourceHarness: chosen.harness
50089
+ sourceHarness
50096
50090
  });
50097
50091
  upSp.stop("Uploaded.");
50098
50092
  } catch (err) {
@@ -50105,7 +50099,7 @@ async function runSkillPublish(cwd2, args) {
50105
50099
  slug: pkgSlug,
50106
50100
  version
50107
50101
  };
50108
- writeSkillMarker(chosen.dir, newSource);
50102
+ writeSkillMarker(skillDir, newSource);
50109
50103
  $e(`${import_picocolors16.default.bold(creator + "/" + pkgSlug)}@${version} published.`);
50110
50104
  f2.info(import_picocolors16.default.dim(`id: ${result.id}`));
50111
50105
  } finally {
@@ -50114,30 +50108,6 @@ async function runSkillPublish(cwd2, args) {
50114
50108
  } catch {}
50115
50109
  }
50116
50110
  }
50117
- function collectSkillCandidates(cwd2) {
50118
- const out = [];
50119
- const sources = [
50120
- { harness: "claude-code", scope: "global", root: scopePaths(cwd2, "global").skills },
50121
- { harness: "claude-code", scope: "project", root: scopePaths(cwd2, "project").skills },
50122
- { harness: "codex", scope: "global", root: scopePaths2(cwd2, "global").skills },
50123
- { harness: "codex", scope: "project", root: scopePaths2(cwd2, "project").skills }
50124
- ];
50125
- for (const s3 of sources) {
50126
- if (!exists(s3.root))
50127
- continue;
50128
- for (const slug of fs37.readdirSync(s3.root)) {
50129
- const dir = path40.join(s3.root, slug);
50130
- if (!exists(path40.join(dir, "SKILL.md")))
50131
- continue;
50132
- const marker = readSkillMarker(dir);
50133
- if (marker && (marker.source.type === "github" || marker.source.type === "git")) {
50134
- continue;
50135
- }
50136
- out.push({ harness: s3.harness, scope: s3.scope, slug, dir });
50137
- }
50138
- }
50139
- return out;
50140
- }
50141
50111
  function collectSkillFiles(skillDir) {
50142
50112
  const out = [];
50143
50113
  function walk(dir, rel) {
@@ -50289,7 +50259,7 @@ async function runSkillSearch(args) {
50289
50259
 
50290
50260
  // src/cli/skill-info.ts
50291
50261
  var import_picocolors19 = __toESM(require_picocolors(), 1);
50292
- function parseRef4(input) {
50262
+ function parseRef3(input) {
50293
50263
  const at2 = input.indexOf("@");
50294
50264
  if (at2 < 0)
50295
50265
  return { name: input };
@@ -50297,7 +50267,7 @@ function parseRef4(input) {
50297
50267
  }
50298
50268
  async function runSkillInfo(args) {
50299
50269
  banner(`skill info — ${import_picocolors19.default.bold(args.ref)}`);
50300
- const { name } = parseRef4(args.ref);
50270
+ const { name } = parseRef3(args.ref);
50301
50271
  const [creator, slug] = name.split("/");
50302
50272
  if (!creator || !slug) {
50303
50273
  console.error("Invalid ref. Expected creator/slug.");
@@ -50395,8 +50365,11 @@ async function runSkill(cwd2, sub, rest, args) {
50395
50365
  }
50396
50366
  case "publish":
50397
50367
  await runSkillPublish(cwd2, {
50398
- ref: rest[0],
50368
+ dir: rest[0],
50369
+ name: args.name,
50370
+ version: args.skillVersion,
50399
50371
  visibility: args.visibility,
50372
+ harness: args.harness,
50400
50373
  yes: args.yes
50401
50374
  });
50402
50375
  return;
@@ -50424,7 +50397,7 @@ function printSkillHelp() {
50424
50397
  out.push(` ${import_picocolors20.default.cyan("remove")} ${import_picocolors20.default.dim("<slug>")} ${import_picocolors20.default.dim("uninstall a skill")}`);
50425
50398
  out.push(` ${import_picocolors20.default.cyan("search")} ${import_picocolors20.default.dim("[query]")} ${import_picocolors20.default.dim("search the brainbase skill registry")}`);
50426
50399
  out.push(` ${import_picocolors20.default.cyan("info")} ${import_picocolors20.default.dim("<creator/slug>")} ${import_picocolors20.default.dim("show registry details for a skill")}`);
50427
- out.push(` ${import_picocolors20.default.cyan("publish")} ${import_picocolors20.default.dim("[creator/slug][@v]")} ${import_picocolors20.default.dim("publish a local skill to the registry")}`);
50400
+ out.push(` ${import_picocolors20.default.cyan("publish")} ${import_picocolors20.default.dim("[dir]")} ${import_picocolors20.default.dim("publish the SKILL.md folder (defaults to .)")}`);
50428
50401
  out.push("");
50429
50402
  out.push(` ${import_picocolors20.default.dim("Source forms accepted by `add`:")}`);
50430
50403
  out.push(` ${import_picocolors20.default.dim("owner/repo github short-form")}`);
@@ -50440,6 +50413,8 @@ function printSkillHelp() {
50440
50413
  out.push(` ${import_picocolors20.default.dim("--category <s>")} for search`);
50441
50414
  out.push(` ${import_picocolors20.default.dim("--agent <h>")} for search (filter by harness)`);
50442
50415
  out.push(` ${import_picocolors20.default.dim("--visibility <v>")} for publish: public | unlisted | private`);
50416
+ out.push(` ${import_picocolors20.default.dim("--name <c/s>")} for publish: creator/slug (skip prompt)`);
50417
+ out.push(` ${import_picocolors20.default.dim("--skill-version <v>")} for publish: MAJOR.MINOR.PATCH (skip prompt)`);
50443
50418
  out.push("");
50444
50419
  console.log(out.join(`
50445
50420
  `));
@@ -55033,7 +55008,7 @@ function help() {
55033
55008
  out.push(` ${import_picocolors38.default.cyan("skill remove")} ${import_picocolors38.default.dim("<slug>")} ${import_picocolors38.default.dim("uninstall a skill")}`);
55034
55009
  out.push(` ${import_picocolors38.default.cyan("skill search")} ${import_picocolors38.default.dim("[query]")} ${import_picocolors38.default.dim("search the brainbase skill registry")}`);
55035
55010
  out.push(` ${import_picocolors38.default.cyan("skill info")} ${import_picocolors38.default.dim("<creator/slug>")} ${import_picocolors38.default.dim("show registry details for a skill")}`);
55036
- out.push(` ${import_picocolors38.default.cyan("skill publish")} ${import_picocolors38.default.dim("[creator/slug][@v]")} ${import_picocolors38.default.dim("publish a local skill to the registry")}`);
55011
+ out.push(` ${import_picocolors38.default.cyan("skill publish")} ${import_picocolors38.default.dim("[dir]")} ${import_picocolors38.default.dim("publish a SKILL.md folder (defaults to .)")}`);
55037
55012
  out.push("");
55038
55013
  out.push(divider("CLI TOKENS"));
55039
55014
  out.push("");
@@ -55154,6 +55129,7 @@ async function main() {
55154
55129
  const noTracking = hasFlag2(argv, "--no-tracking");
55155
55130
  const graphOnlyFlag = hasFlag2(argv, "--graph-only");
55156
55131
  const nameFlag = getFlag(argv, "--name");
55132
+ const skillVersionFlag = getFlag(argv, "--skill-version");
55157
55133
  const taglineFlag = getFlag(argv, "--tagline");
55158
55134
  const orgIdFlag = getFlag(argv, "--org");
55159
55135
  const teamIdFlag = getFlag(argv, "--team");
@@ -55197,7 +55173,9 @@ async function main() {
55197
55173
  as: asSlug,
55198
55174
  category,
55199
55175
  agent: agentFlag,
55200
- page
55176
+ page,
55177
+ name: nameFlag,
55178
+ skillVersion: skillVersionFlag
55201
55179
  });
55202
55180
  break;
55203
55181
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {