@braid-cloud/cli 0.1.16 → 0.1.18

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.
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var init_esm_shims = __esm({
20
20
 
21
21
  // src/lib/braid-workspace.ts
22
22
  import { existsSync, readFileSync } from "fs";
23
- import { dirname as dirname2, join as join2 } from "path";
23
+ import { dirname as dirname3, join as join2 } from "path";
24
24
  import process3 from "process";
25
25
  function readPackageName(packagePath) {
26
26
  if (!existsSync(packagePath)) {
@@ -44,7 +44,7 @@ function findBraidWorkspaceRoot(startDir = process3.cwd()) {
44
44
  if (isBraidWorkspaceRoot(currentDir)) {
45
45
  return currentDir;
46
46
  }
47
- const parentDir = dirname2(currentDir);
47
+ const parentDir = dirname3(currentDir);
48
48
  if (parentDir === currentDir) {
49
49
  return void 0;
50
50
  }
@@ -107,7 +107,7 @@ __export(config_exports, {
107
107
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
108
108
  import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
109
109
  import { homedir as homedir2 } from "os";
110
- import { dirname as dirname3, join as join3, parse } from "path";
110
+ import { dirname as dirname4, join as join3, parse } from "path";
111
111
  import process4 from "process";
112
112
  import { Data as Data2, Effect as Effect3, pipe as pipe3 } from "effect";
113
113
  var CONFIG_DIR, CONFIG_FILE, PROJECT_CONFIG_FILENAME, USER_CONFIG_FILENAME, CONVEX_CLOUD_SUFFIX_REGEX, LOCAL_CONVEX_PORT_REGEX, LOCAL_SERVER_ENV_FILES, LOOPBACK_HOSTS, NEWLINE_REGEX, TRAILING_SLASHES, ConfigReadError, ConfigWriteError, findConfigFile, findProjectConfigFile, findUserConfigFile, stripQuotes, parseDotenv, normalizeBaseUrl, resolveConvexSiteUrl, resolveLocalServerUrlFromEnv, resolveLocalServerUrlFromFiles, resolveLocalServerUrl, loadProjectConfig, loadUserConfig, resolveUserConfigWritePath, resolveProjectConfigWritePath, saveUserConfig, saveProjectConfig, isValidServerUrl, resolveServerUrlFromConfig, applyConfigSource, applyEnvOverrides, createDefaultMergedConfig, loadMergedConfig, loadConfig, saveConfig, getApiKey, setApiKey, getServerUrl, clearApiKey, getDemoContext, setDemoContext, clearDemoContext, loadConfigAsync, loadProjectConfigAsync, loadUserConfigAsync, loadMergedConfigAsync, findProjectConfigFileAsync, findUserConfigFileAsync, saveConfigAsync, saveUserConfigAsync, saveProjectConfigAsync, getApiKeyAsync, setApiKeyAsync, persistApiKeyAsync, getServerUrlAsync, clearApiKeyAsync, getDemoContextAsync, setDemoContextAsync, clearDemoContextAsync;
@@ -256,7 +256,7 @@ var init_config = __esm({
256
256
  return pipe3(
257
257
  Effect3.tryPromise({
258
258
  try: async () => {
259
- await mkdir2(dirname3(targetPath), { recursive: true, mode: 448 });
259
+ await mkdir2(dirname4(targetPath), { recursive: true, mode: 448 });
260
260
  await writeFile2(targetPath, JSON.stringify(config, null, 2), {
261
261
  encoding: "utf-8",
262
262
  mode: 384
@@ -272,7 +272,7 @@ var init_config = __esm({
272
272
  return pipe3(
273
273
  Effect3.tryPromise({
274
274
  try: async () => {
275
- await mkdir2(dirname3(targetPath), { recursive: true, mode: 448 });
275
+ await mkdir2(dirname4(targetPath), { recursive: true, mode: 448 });
276
276
  await writeFile2(targetPath, JSON.stringify(config, null, 2), {
277
277
  encoding: "utf-8",
278
278
  mode: 384
@@ -392,7 +392,7 @@ var init_config = __esm({
392
392
  saveConfig = (config) => pipe3(
393
393
  Effect3.tryPromise({
394
394
  try: async () => {
395
- await mkdir2(dirname3(CONFIG_FILE), { recursive: true, mode: 448 });
395
+ await mkdir2(dirname4(CONFIG_FILE), { recursive: true, mode: 448 });
396
396
  await writeFile2(CONFIG_FILE, JSON.stringify(config, null, 2), {
397
397
  encoding: "utf-8",
398
398
  mode: 384
@@ -478,8 +478,13 @@ var init_config = __esm({
478
478
  saveProjectConfigAsync = (config, startDir) => Effect3.runPromise(saveProjectConfig(config, startDir));
479
479
  getApiKeyAsync = () => Effect3.runPromise(getApiKey());
480
480
  setApiKeyAsync = (apiKey) => Effect3.runPromise(setApiKey(apiKey));
481
- persistApiKeyAsync = async (apiKey) => {
482
- await setApiKeyAsync(apiKey);
481
+ persistApiKeyAsync = async (apiKey, serverUrl) => {
482
+ const config = await loadConfigAsync();
483
+ await saveConfigAsync({
484
+ ...config,
485
+ apiKey,
486
+ ...serverUrl ? { serverUrl } : {}
487
+ });
483
488
  const existingUserConfig = await loadUserConfigAsync();
484
489
  if (existingUserConfig?.token) {
485
490
  await saveUserConfigAsync({ ...existingUserConfig, token: apiKey });
@@ -510,7 +515,8 @@ var init_api = __esm({
510
515
  const parsed = new URL(serverUrl);
511
516
  const hostname2 = parsed.hostname;
512
517
  const isBraidHost = hostname2 === "braid.cloud" || hostname2.endsWith(".braid.cloud");
513
- if (parsed.protocol === "https:" && isBraidHost) {
518
+ const isConvexSite = hostname2.endsWith(".convex.site");
519
+ if (parsed.protocol === "https:" && (isBraidHost || isConvexSite)) {
514
520
  return true;
515
521
  }
516
522
  if (parsed.protocol === "http:" && isLocalHost(hostname2)) {
@@ -1941,7 +1947,7 @@ var writeAgentsForPlatformAsync = (platform2, specs, installPath) => Effect.runP
1941
1947
  init_esm_shims();
1942
1948
  import { access, constants } from "fs/promises";
1943
1949
  import { homedir } from "os";
1944
- import { join } from "path";
1950
+ import { dirname as dirname2, join } from "path";
1945
1951
  import process2 from "process";
1946
1952
  import { Effect as Effect2, pipe as pipe2 } from "effect";
1947
1953
  var home = homedir();
@@ -2585,7 +2591,7 @@ var DeviceAuthExpiredError = class extends Error {
2585
2591
  this.name = "DeviceAuthExpiredError";
2586
2592
  }
2587
2593
  };
2588
- var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
2594
+ var sleep = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
2589
2595
  var normalizeBaseUrl2 = (rawUrl) => {
2590
2596
  const trimmed = rawUrl.replace(TRAILING_SLASHES2, "");
2591
2597
  try {
@@ -2989,7 +2995,7 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2989
2995
  pollSpinner.stop("Authentication failed");
2990
2996
  handlePollingError(error);
2991
2997
  }
2992
- await persistApiKeyAsync(session.sessionToken);
2998
+ await persistApiKeyAsync(session.sessionToken, authConfig.convexSiteUrl);
2993
2999
  if (options.scope !== false) {
2994
3000
  await configureDefaultScopeAsync(authConfig.convexSiteUrl);
2995
3001
  }
@@ -3288,7 +3294,7 @@ import {
3288
3294
  writeFile as writeFile3
3289
3295
  } from "fs/promises";
3290
3296
  import { homedir as homedir3 } from "os";
3291
- import { basename, dirname as dirname4, join as join5, resolve as resolve2 } from "path";
3297
+ import { basename, dirname as dirname5, join as join5, resolve as resolve2 } from "path";
3292
3298
  var BRAID_CONFIG_DIR = join5(homedir3(), ".config", "braid");
3293
3299
  var DEFAULT_STORE_ROOT = join5(BRAID_CONFIG_DIR, "store", "skills");
3294
3300
  var DEFAULT_DISABLED_ROOT = join5(BRAID_CONFIG_DIR, ".disabled");
@@ -3397,7 +3403,7 @@ var enableBundleAsync = async (originalPath, options = {}) => {
3397
3403
  if (!record) {
3398
3404
  throw new Error(`No disabled bundle found for ${originalPath}`);
3399
3405
  }
3400
- await mkdir3(dirname4(record.originalPath), { recursive: true });
3406
+ await mkdir3(dirname5(record.originalPath), { recursive: true });
3401
3407
  await rm(record.originalPath, { recursive: true, force: true });
3402
3408
  await rename(record.payloadPath, record.originalPath);
3403
3409
  await rm(record.disabledPath, { recursive: true, force: true });
@@ -3414,9 +3420,9 @@ init_lockfile();
3414
3420
  // src/lib/metadata.ts
3415
3421
  init_esm_shims();
3416
3422
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
3417
- import { join as join7 } from "path";
3423
+ import { dirname as dirname6, join as join7 } from "path";
3418
3424
  import { Data as Data5, Effect as Effect6, pipe as pipe6 } from "effect";
3419
- var METADATA_FILENAME2 = ".braidskills-metadata.json";
3425
+ var METADATA_FILENAME2 = ".braid-metadata.json";
3420
3426
  var MetadataReadError = class extends Data5.TaggedError("MetadataReadError") {
3421
3427
  };
3422
3428
  var MetadataWriteError = class extends Data5.TaggedError("MetadataWriteError") {
@@ -3432,7 +3438,7 @@ var normalizeInstalledSkill = (skill) => ({
3432
3438
  var normalizeMetadata = (metadata) => ({
3433
3439
  skills: Array.isArray(metadata?.skills) ? metadata.skills.map(normalizeInstalledSkill) : []
3434
3440
  });
3435
- var getMetadataPath = (skillsDir) => join7(skillsDir, METADATA_FILENAME2);
3441
+ var getMetadataPath = (skillsDir) => join7(dirname6(skillsDir), METADATA_FILENAME2);
3436
3442
  var readMetadata = (skillsDir) => {
3437
3443
  const metadataPath = getMetadataPath(skillsDir);
3438
3444
  return pipe6(
@@ -3449,10 +3455,26 @@ var readMetadata = (skillsDir) => {
3449
3455
  Effect6.orElseSucceed(() => normalizeMetadata(void 0))
3450
3456
  );
3451
3457
  };
3458
+ var readRawJson = async (path2) => {
3459
+ try {
3460
+ const content = await readFile4(path2, "utf-8");
3461
+ const parsed = JSON.parse(content);
3462
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
3463
+ return parsed;
3464
+ }
3465
+ } catch (_) {
3466
+ return {};
3467
+ }
3468
+ return {};
3469
+ };
3452
3470
  var writeMetadata = (skillsDir, metadata) => {
3453
3471
  const metadataPath = getMetadataPath(skillsDir);
3454
3472
  return Effect6.tryPromise({
3455
- try: () => writeFile5(metadataPath, JSON.stringify(metadata, null, 2), "utf-8"),
3473
+ try: async () => {
3474
+ const existing = await readRawJson(metadataPath);
3475
+ const merged = { ...existing, skills: metadata.skills };
3476
+ await writeFile5(metadataPath, JSON.stringify(merged, null, 2), "utf-8");
3477
+ },
3456
3478
  catch: (e) => new MetadataWriteError({ path: metadataPath, cause: e })
3457
3479
  });
3458
3480
  };
@@ -3497,7 +3519,7 @@ var removeFromMetadataAsync = (skillsDir, skillName) => Effect6.runPromise(remov
3497
3519
  // src/lib/rule-writer.ts
3498
3520
  init_esm_shims();
3499
3521
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
3500
- import { dirname as dirname5, resolve as resolve3, sep as sep2 } from "path";
3522
+ import { dirname as dirname7, resolve as resolve3, sep as sep2 } from "path";
3501
3523
  import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
3502
3524
  var RuleWriteError = class extends Data6.TaggedError("RuleWriteError") {
3503
3525
  };
@@ -3595,7 +3617,7 @@ ${rule.content}`)
3595
3617
  Effect7.asVoid
3596
3618
  );
3597
3619
  var writeAppendSingleRules = (filePath, rules2) => pipe7(
3598
- createDirectory(dirname5(filePath)),
3620
+ createDirectory(dirname7(filePath)),
3599
3621
  Effect7.flatMap(
3600
3622
  () => pipe7(
3601
3623
  readTextFile(filePath),
@@ -3658,7 +3680,7 @@ var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect7.runPromise(w
3658
3680
  init_esm_shims();
3659
3681
  import { createHash as createHash2 } from "crypto";
3660
3682
  import { chmod, mkdir as mkdir5, rm as rm2, symlink, writeFile as writeFile7 } from "fs/promises";
3661
- import { dirname as dirname6, join as join8, resolve as resolve4, sep as sep3 } from "path";
3683
+ import { dirname as dirname8, join as join8, resolve as resolve4, sep as sep3 } from "path";
3662
3684
  import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
3663
3685
  var WriteError = class extends Data7.TaggedError("WriteError") {
3664
3686
  };
@@ -3739,7 +3761,7 @@ var setExecutableIfScript = (fullPath, file, agentId) => isScriptFile(file.path)
3739
3761
  var writeSkillFile = (basePath, file, agentId) => pipe8(
3740
3762
  assertWithinBase2(basePath, file.path),
3741
3763
  Effect8.flatMap((fullPath) => {
3742
- const dir = dirname6(fullPath);
3764
+ const dir = dirname8(fullPath);
3743
3765
  return pipe8(
3744
3766
  createDirectory2(dir, fullPath),
3745
3767
  Effect8.flatMap(() => writeFileContent(fullPath, file, agentId)),
@@ -3786,7 +3808,7 @@ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
3786
3808
  disabledRoot: options.disabledRoot
3787
3809
  }
3788
3810
  );
3789
- await mkdir5(dirname6(destinationPath), { recursive: true });
3811
+ await mkdir5(dirname8(destinationPath), { recursive: true });
3790
3812
  await rm2(destinationPath, { recursive: true, force: true });
3791
3813
  await symlink(
3792
3814
  storedBundlePath,
@@ -4531,17 +4553,17 @@ init_config();
4531
4553
 
4532
4554
  // src/lib/manage-actions.ts
4533
4555
  init_esm_shims();
4534
- import { rm as rm4 } from "fs/promises";
4535
- import { basename as basename2, dirname as dirname8, join as join10, relative } from "path";
4556
+ import { rm as rm5 } from "fs/promises";
4557
+ import { basename as basename2, dirname as dirname10, join as join11, relative } from "path";
4536
4558
 
4537
4559
  // src/lib/hook-writer.ts
4538
4560
  init_esm_shims();
4539
4561
  import { mkdir as mkdir6, readFile as readFile6, rm as rm3, writeFile as writeFile8 } from "fs/promises";
4540
- import { dirname as dirname7, join as join9 } from "path";
4562
+ import { dirname as dirname9, join as join9 } from "path";
4541
4563
  import { Data as Data8, Effect as Effect9 } from "effect";
4542
4564
  var HookConfigWriteError = class extends Data8.TaggedError("HookConfigWriteError") {
4543
4565
  };
4544
- var HOOK_METADATA_FILENAME = ".braid-hooks-metadata.json";
4566
+ var HOOK_METADATA_FILENAME = ".braid-metadata.json";
4545
4567
  function stableStringify(value) {
4546
4568
  return JSON.stringify(value, (_key, currentValue) => {
4547
4569
  if (currentValue && typeof currentValue === "object" && !Array.isArray(currentValue)) {
@@ -4582,7 +4604,7 @@ async function readJsonFile(path2) {
4582
4604
  }
4583
4605
  }
4584
4606
  async function writeJsonFile(path2, value) {
4585
- await mkdir6(dirname7(path2), { recursive: true, mode: 448 });
4607
+ await mkdir6(dirname9(path2), { recursive: true, mode: 448 });
4586
4608
  await writeFile8(path2, `${JSON.stringify(value, null, 2)}
4587
4609
  `, {
4588
4610
  encoding: "utf-8",
@@ -4681,7 +4703,7 @@ function addHookHandlers(config, hook) {
4681
4703
  return writeMatcherGroups(config, hook.event, groups);
4682
4704
  }
4683
4705
  function getHookMetadataPath(settingsPath) {
4684
- return join9(dirname7(settingsPath), HOOK_METADATA_FILENAME);
4706
+ return join9(dirname9(settingsPath), HOOK_METADATA_FILENAME);
4685
4707
  }
4686
4708
  async function readHookMetadata(settingsPath) {
4687
4709
  const metadataPath = getHookMetadataPath(settingsPath);
@@ -4690,9 +4712,9 @@ async function readHookMetadata(settingsPath) {
4690
4712
  return { hooks };
4691
4713
  }
4692
4714
  async function writeHookMetadata(settingsPath, metadata) {
4693
- await writeJsonFile(getHookMetadataPath(settingsPath), {
4694
- hooks: metadata.hooks
4695
- });
4715
+ const metadataPath = getHookMetadataPath(settingsPath);
4716
+ const existing = await readJsonFile(metadataPath);
4717
+ await writeJsonFile(metadataPath, { ...existing, hooks: metadata.hooks });
4696
4718
  }
4697
4719
  function buildMarketplaceHookSource(sourceSlug) {
4698
4720
  return {
@@ -4790,7 +4812,7 @@ async function listInstalledMarketplaceHookSources(settingsPath) {
4790
4812
  );
4791
4813
  }
4792
4814
  function buildMarketplaceHookBundlePath(settingsPath, sourceSlug) {
4793
- return join9(dirname7(settingsPath), ".braid-hooks", `${sourceSlug}.json`);
4815
+ return join9(dirname9(settingsPath), ".braid-hooks", `${sourceSlug}.json`);
4794
4816
  }
4795
4817
  function buildMarketplaceHookBundle(hooks, options) {
4796
4818
  return {
@@ -4987,7 +5009,7 @@ var requestJson = (url, options) => pipe9(
4987
5009
  const payload = await response.json().catch(() => null);
4988
5010
  if (!response.ok) {
4989
5011
  throw new MarketplaceApiError({
4990
- message: payload?.error ?? payload?.message ?? "Request failed",
5012
+ message: payload?.error ?? payload?.message ?? `Request failed (HTTP ${response.status} from ${new URL(url).origin})`,
4991
5013
  status: response.status,
4992
5014
  code: payload?.code ?? "REQUEST_ERROR"
4993
5015
  });
@@ -5042,6 +5064,8 @@ var fetchMarketplaceInstallManifestAsync = (slug, options) => Effect10.runPromis
5042
5064
 
5043
5065
  // src/lib/marketplace-installer.ts
5044
5066
  init_esm_shims();
5067
+ import { rm as rm4 } from "fs/promises";
5068
+ import { join as join10 } from "path";
5045
5069
  function parseAgentIds(value) {
5046
5070
  if (!value) {
5047
5071
  return [];
@@ -5184,6 +5208,58 @@ async function installHooksForAgent(args) {
5184
5208
  args.targets.push(`${args.agent.name} hooks -> ${hookConfigPath}`);
5185
5209
  return true;
5186
5210
  }
5211
+ async function pruneStaleMarketplaceSkills(installPath, slug, currentSkillNames) {
5212
+ const metadata = await readMetadataAsync(installPath);
5213
+ const stale = metadata.skills.filter(
5214
+ (skill) => skill.source.type === "marketplace" && skill.source.slug === slug && !currentSkillNames.has(skill.name)
5215
+ );
5216
+ for (const skill of stale) {
5217
+ const skillPath = join10(installPath, skill.name);
5218
+ await rm4(skillPath, { recursive: true, force: true });
5219
+ await removeFromMetadataAsync(installPath, skill.name);
5220
+ }
5221
+ }
5222
+ async function installAllForAgent(args) {
5223
+ await installSkillsForAgent({
5224
+ agent: args.agent,
5225
+ manifest: args.manifest,
5226
+ slug: args.slug,
5227
+ skills: args.skills,
5228
+ global: args.global,
5229
+ installedSkills: args.installedSkills,
5230
+ targets: args.targets
5231
+ });
5232
+ await installRulesForAgent({
5233
+ agent: args.agent,
5234
+ rules: args.rules,
5235
+ global: args.global,
5236
+ targets: args.targets
5237
+ });
5238
+ await installAgentsForAgent({
5239
+ agent: args.agent,
5240
+ agents: args.agents,
5241
+ global: args.global,
5242
+ targets: args.targets
5243
+ });
5244
+ if (args.installHooks) {
5245
+ await installHooksForAgent({
5246
+ agent: args.agent,
5247
+ hooks: args.hooks,
5248
+ slug: args.slug,
5249
+ manifest: args.manifest,
5250
+ global: args.global,
5251
+ targets: args.targets
5252
+ });
5253
+ }
5254
+ const installPath = resolveInstallPath(args.agent, { global: args.global });
5255
+ if (installPath) {
5256
+ await pruneStaleMarketplaceSkills(
5257
+ installPath,
5258
+ args.slug,
5259
+ args.installedSkills
5260
+ );
5261
+ }
5262
+ }
5187
5263
  async function installMarketplaceSkillSet(options) {
5188
5264
  if (options.manifest.blocked) {
5189
5265
  throw new Error(
@@ -5195,12 +5271,8 @@ async function installMarketplaceSkillSet(options) {
5195
5271
  const hooks = options.manifest.manifest?.hooks ?? [];
5196
5272
  const agents2 = options.manifest.manifest?.agents ?? [];
5197
5273
  const workflows = options.manifest.manifest?.workflows ?? [];
5198
- if (hooks.length > 0 && !options.allowHooks) {
5199
- throw new Error(
5200
- "Marketplace packs with hooks require explicit opt-in. Re-run with --allow-hooks after reviewing the hook commands."
5201
- );
5202
- }
5203
- if (resolvedSkills.length === 0 && rules2.length === 0 && hooks.length === 0 && agents2.length === 0 && workflows.length === 0) {
5274
+ const installHooks = hooks.length > 0 && options.allowHooks === true;
5275
+ if (resolvedSkills.length === 0 && rules2.length === 0 && (!installHooks || hooks.length === 0) && agents2.length === 0 && workflows.length === 0) {
5204
5276
  throw new Error("No installable content found in manifest");
5205
5277
  }
5206
5278
  const targetAgents = await resolveTargetAgents(options.agents);
@@ -5212,33 +5284,17 @@ async function installMarketplaceSkillSet(options) {
5212
5284
  const targets = [];
5213
5285
  const installedSkills = /* @__PURE__ */ new Set();
5214
5286
  for (const agent of targetAgents) {
5215
- await installSkillsForAgent({
5287
+ await installAllForAgent({
5216
5288
  agent,
5217
5289
  manifest: options.manifest,
5218
5290
  slug: options.slug,
5219
5291
  skills: resolvedSkills,
5220
- global: options.global,
5221
- installedSkills,
5222
- targets
5223
- });
5224
- await installRulesForAgent({
5225
- agent,
5226
5292
  rules: rules2,
5227
- global: options.global,
5228
- targets
5229
- });
5230
- await installAgentsForAgent({
5231
- agent,
5232
5293
  agents: agents2,
5233
- global: options.global,
5234
- targets
5235
- });
5236
- await installHooksForAgent({
5237
- agent,
5238
5294
  hooks,
5239
- slug: options.slug,
5240
- manifest: options.manifest,
5295
+ installHooks,
5241
5296
  global: options.global,
5297
+ installedSkills,
5242
5298
  targets
5243
5299
  });
5244
5300
  }
@@ -5257,12 +5313,12 @@ function isMarketplaceHookBundlePath(originalPath) {
5257
5313
  return getHookBundleRoot(originalPath) !== null;
5258
5314
  }
5259
5315
  function getHookBundleRoot(originalPath) {
5260
- let currentPath = dirname8(originalPath);
5316
+ let currentPath = dirname10(originalPath);
5261
5317
  while (true) {
5262
5318
  if (basename2(currentPath) === ".braid-hooks") {
5263
- return dirname8(currentPath);
5319
+ return dirname10(currentPath);
5264
5320
  }
5265
- const parentPath = dirname8(currentPath);
5321
+ const parentPath = dirname10(currentPath);
5266
5322
  if (parentPath === currentPath) {
5267
5323
  return null;
5268
5324
  }
@@ -5274,7 +5330,7 @@ function getHookBundleRelativePath(originalPath) {
5274
5330
  if (!bundleRoot) {
5275
5331
  throw new Error(`Invalid marketplace hook bundle path: ${originalPath}`);
5276
5332
  }
5277
- return relative(join10(bundleRoot, ".braid-hooks"), originalPath);
5333
+ return relative(join11(bundleRoot, ".braid-hooks"), originalPath);
5278
5334
  }
5279
5335
  function getHookSourceSlug(originalPath) {
5280
5336
  const relativePath = getHookBundleRelativePath(originalPath);
@@ -5293,7 +5349,7 @@ function getHookSettingsPath(originalPath) {
5293
5349
  if (!bundleRoot) {
5294
5350
  throw new Error(`Invalid marketplace hook bundle path: ${originalPath}`);
5295
5351
  }
5296
- return join10(bundleRoot, "settings.json");
5352
+ return join11(bundleRoot, "settings.json");
5297
5353
  }
5298
5354
  async function disableManagedBundleAsync(options) {
5299
5355
  if (options.surface === "hooks") {
@@ -5350,7 +5406,7 @@ async function removeManagedBundleAsync(options) {
5350
5406
  }
5351
5407
  );
5352
5408
  await removeMarketplaceHooksBySourceAsync(
5353
- join10(options.installRoot, "settings.json"),
5409
+ join11(options.installRoot, "settings.json"),
5354
5410
  options.bundleName
5355
5411
  );
5356
5412
  if (disabledRecord2) {
@@ -5364,7 +5420,7 @@ async function removeManagedBundleAsync(options) {
5364
5420
  disabledRoot: options.disabledRoot
5365
5421
  }
5366
5422
  );
5367
- await rm4(options.originalPath, { recursive: true, force: true });
5423
+ await rm5(options.originalPath, { recursive: true, force: true });
5368
5424
  if (disabledRecord) {
5369
5425
  await removePathAsync(disabledRecord.disabledPath);
5370
5426
  }
@@ -5388,7 +5444,7 @@ async function installLibraryPackAsync(options) {
5388
5444
  // src/lib/manage-inventory.ts
5389
5445
  init_esm_shims();
5390
5446
  import { access as access2, constants as constants2, lstat, readdir as readdir2 } from "fs/promises";
5391
- import { basename as basename3, delimiter, dirname as dirname9, join as join11, resolve as resolve5 } from "path";
5447
+ import { basename as basename3, delimiter, dirname as dirname11, join as join12, resolve as resolve5 } from "path";
5392
5448
  var MANAGE_PROVIDER_COMMANDS = {
5393
5449
  amp: ["amp"],
5394
5450
  "claude-code": ["claude"],
@@ -5436,7 +5492,7 @@ function getCommandExtensions() {
5436
5492
  function getCommandCandidates(command, pathValue) {
5437
5493
  return pathValue.split(delimiter).flatMap(
5438
5494
  (segment) => segment ? getCommandExtensions().map(
5439
- (extension) => join11(segment, `${command}${extension}`)
5495
+ (extension) => join12(segment, `${command}${extension}`)
5440
5496
  ) : []
5441
5497
  );
5442
5498
  }
@@ -5552,9 +5608,9 @@ async function listSurfaceEntries(rootPath, surface) {
5552
5608
  }
5553
5609
  return entry.isDirectory() || entry.isFile() || entry.isSymbolicLink();
5554
5610
  }).map((entry) => ({
5555
- currentPath: join11(rootPath, entry.name),
5611
+ currentPath: join12(rootPath, entry.name),
5556
5612
  name: entry.name,
5557
- originalPath: join11(rootPath, entry.name)
5613
+ originalPath: join12(rootPath, entry.name)
5558
5614
  }));
5559
5615
  }
5560
5616
  function toBundleKey(path2) {
@@ -5603,7 +5659,7 @@ async function collectSkillBundles(args) {
5603
5659
  });
5604
5660
  }
5605
5661
  for (const metadataEntry of metadata.skills) {
5606
- const originalPath = join11(args.rootPath, metadataEntry.name);
5662
+ const originalPath = join12(args.rootPath, metadataEntry.name);
5607
5663
  const bundleKey = toBundleKey(originalPath);
5608
5664
  if (bundles.has(bundleKey)) {
5609
5665
  continue;
@@ -5684,7 +5740,7 @@ async function collectHookBundles(args) {
5684
5740
  return [];
5685
5741
  }
5686
5742
  const settingsPath = args.settingsPath;
5687
- const installRoot = dirname9(settingsPath);
5743
+ const installRoot = dirname11(settingsPath);
5688
5744
  const sources = await listInstalledMarketplaceHookSourcesAsync(settingsPath);
5689
5745
  const disabledRecords = getDisabledRecordsForSurface({
5690
5746
  agent: args.agent,
@@ -5788,7 +5844,7 @@ async function collectScopeInventory(args) {
5788
5844
  },
5789
5845
  hooks: {
5790
5846
  bundles: hooks,
5791
- rootPath: hookSettingsPath ? dirname9(hookSettingsPath) : null,
5847
+ rootPath: hookSettingsPath ? dirname11(hookSettingsPath) : null,
5792
5848
  surface: "hooks"
5793
5849
  },
5794
5850
  rules: {
@@ -6246,7 +6302,10 @@ async function startManageServer(options = {}) {
6246
6302
  pendingRequest.expiresIn,
6247
6303
  MANAGE_AUTH_TIMEOUT_SECONDS
6248
6304
  );
6249
- await persistApiKeyAsync(session.sessionToken);
6305
+ await persistApiKeyAsync(
6306
+ session.sessionToken,
6307
+ pendingRequest.convexSiteUrl
6308
+ );
6250
6309
  return {
6251
6310
  authenticated: true,
6252
6311
  expiresAt: session.expiresAt,
@@ -6389,39 +6448,288 @@ async function manageCommand(options) {
6389
6448
 
6390
6449
  // src/commands/marketplace.ts
6391
6450
  init_esm_shims();
6451
+ import { rm as rm6 } from "fs/promises";
6452
+ import { join as join13, resolve as resolve7 } from "path";
6453
+ import process11 from "process";
6392
6454
  init_tui();
6393
- async function marketplaceLibraryCommand(options) {
6394
- const items = await fetchMarketplaceLibraryAsync({
6395
- server: options.server,
6396
- apiKey: options.apiKey
6455
+ async function selectInstallScope(options) {
6456
+ if (options.global != null) {
6457
+ return options.global;
6458
+ }
6459
+ if (options.yes) {
6460
+ return false;
6461
+ }
6462
+ const result = await select({
6463
+ message: "Install location:",
6464
+ options: [
6465
+ { value: false, label: "Project", hint: "local to this directory" },
6466
+ { value: true, label: "Global", hint: "available everywhere" }
6467
+ ],
6468
+ initialValue: false
6397
6469
  });
6398
- if (items.length === 0) {
6399
- log.info("No marketplace packs in your library yet.");
6400
- return;
6470
+ if (isCancel(result)) {
6471
+ cancel("Install cancelled.");
6472
+ process11.exit(0);
6401
6473
  }
6402
- for (const item of items) {
6403
- const scan = item.scanVerdict ?? "clean";
6404
- const commit = item.commitSha ?? "unknown";
6405
- log.info(
6406
- `${item.slug} | ${item.title} | scan=${scan} | commit=${commit} | install=braid marketplace install ${item.slug}`
6474
+ return result;
6475
+ }
6476
+ async function selectInstallAgents(options, global) {
6477
+ const detected = await detectAgentsAsync();
6478
+ const available = detected.map((d) => getAgentById(d.id)).filter((a) => a != null);
6479
+ if (available.length === 0) {
6480
+ throw new Error(
6481
+ "No compatible agents found. Use --agents to specify targets."
6407
6482
  );
6408
6483
  }
6484
+ if (options.agents) {
6485
+ const ids = options.agents.split(",").map((s) => s.trim());
6486
+ const matched = ids.map((id) => getAgentById(id)).filter((a) => a != null);
6487
+ if (matched.length === 0) {
6488
+ throw new Error(`No agents matched: ${options.agents}`);
6489
+ }
6490
+ return matched;
6491
+ }
6492
+ if (options.yes || available.length === 1) {
6493
+ return available.length === 1 ? available : available;
6494
+ }
6495
+ const selected = await multiselect({
6496
+ message: "Select agents to install to:",
6497
+ options: available.map((agent) => ({
6498
+ value: agent,
6499
+ label: agent.name,
6500
+ hint: global ? agent.globalPath : agent.projectPath
6501
+ })),
6502
+ initialValues: [],
6503
+ required: true
6504
+ });
6505
+ if (isCancel(selected)) {
6506
+ cancel("Install cancelled.");
6507
+ process11.exit(0);
6508
+ }
6509
+ return selected;
6409
6510
  }
6410
- async function marketplaceInstallCommand(slug, options) {
6511
+ async function confirmHooksOptIn(hookCount, options) {
6512
+ if (options.allowHooks) {
6513
+ return true;
6514
+ }
6515
+ if (options.yes) {
6516
+ return false;
6517
+ }
6518
+ const result = await confirm({
6519
+ message: `This pack includes ${hookCount} hook${hookCount === 1 ? "" : "s"} that run shell commands. Allow hooks?`,
6520
+ initialValue: false
6521
+ });
6522
+ if (isCancel(result)) {
6523
+ cancel("Install cancelled.");
6524
+ process11.exit(0);
6525
+ }
6526
+ return result;
6527
+ }
6528
+ async function marketplaceLibraryCommand(options) {
6529
+ try {
6530
+ const items = await fetchMarketplaceLibraryAsync({
6531
+ server: options.server,
6532
+ apiKey: options.apiKey
6533
+ });
6534
+ if (items.length === 0) {
6535
+ log.info("No marketplace packs in your library yet.");
6536
+ return;
6537
+ }
6538
+ for (const item of items) {
6539
+ const scan = item.scanVerdict ?? "clean";
6540
+ const commit = item.commitSha ?? "unknown";
6541
+ log.info(
6542
+ `${item.slug} | ${item.title} | scan=${scan} | commit=${commit} | install=braid marketplace install ${item.slug}`
6543
+ );
6544
+ }
6545
+ } catch (error) {
6546
+ log.error(
6547
+ error instanceof Error ? error.message : "Failed to fetch library"
6548
+ );
6549
+ process11.exit(1);
6550
+ }
6551
+ }
6552
+ async function resolveAndInstallPack(slug, options, overrides) {
6411
6553
  const manifest = await fetchMarketplaceInstallManifestAsync(slug, {
6412
6554
  server: options.server,
6413
6555
  apiKey: options.apiKey
6414
6556
  });
6415
- const installResult = await installMarketplaceSkillSet({
6557
+ const global = overrides?.global ?? await selectInstallScope(options);
6558
+ const hooks = manifest.manifest?.hooks ?? [];
6559
+ if (hooks.length > 0) {
6560
+ const allowed = await confirmHooksOptIn(hooks.length, options);
6561
+ if (!allowed) {
6562
+ log.warn("Hooks skipped. Re-run with --allow-hooks to include them.");
6563
+ }
6564
+ options.allowHooks = allowed;
6565
+ }
6566
+ const agents2 = overrides?.agents ?? (await selectInstallAgents(options, global)).map((a) => a.id).join(",");
6567
+ await installMarketplaceSkillSet({
6416
6568
  slug,
6417
6569
  manifest,
6418
- agents: options.agents,
6419
- global: options.global,
6570
+ agents: agents2,
6571
+ global,
6420
6572
  allowHooks: options.allowHooks
6421
6573
  });
6422
- log.success(`Installed marketplace pack ${slug}`);
6423
- for (const target of installResult.targets) {
6424
- log.info(` ${target}`);
6574
+ }
6575
+ async function marketplaceInstallCommand(slug, options) {
6576
+ try {
6577
+ await resolveAndInstallPack(slug, options);
6578
+ log.success(`Installed marketplace pack ${slug}`);
6579
+ } catch (error) {
6580
+ log.error(
6581
+ error instanceof Error ? error.message : "Failed to install pack"
6582
+ );
6583
+ process11.exit(1);
6584
+ }
6585
+ }
6586
+ function addSkillToPack(packMap, slug, versionId, skillName, agent, installPath) {
6587
+ let pack = packMap.get(slug);
6588
+ if (!pack) {
6589
+ pack = { slug, versionId, skills: [], agents: [] };
6590
+ packMap.set(slug, pack);
6591
+ }
6592
+ if (!pack.skills.includes(skillName)) {
6593
+ pack.skills.push(skillName);
6594
+ }
6595
+ if (!pack.agents.some((a) => a.agent.id === agent.id)) {
6596
+ pack.agents.push({ agent, installPath });
6597
+ }
6598
+ }
6599
+ async function findInstalledMarketplacePacks(options) {
6600
+ const detected = await detectAgentsAsync();
6601
+ const agents2 = detected.map((d) => getAgentById(d.id)).filter((a) => a != null);
6602
+ const packMap = /* @__PURE__ */ new Map();
6603
+ for (const agent of agents2) {
6604
+ const installPath = resolveInstallPath(agent, {
6605
+ global: options.global ?? false
6606
+ });
6607
+ if (!installPath) {
6608
+ continue;
6609
+ }
6610
+ const metadata = await readMetadataAsync(installPath);
6611
+ for (const skill of metadata.skills) {
6612
+ if (skill.source.type !== "marketplace") {
6613
+ continue;
6614
+ }
6615
+ addSkillToPack(
6616
+ packMap,
6617
+ skill.source.slug,
6618
+ skill.source.versionId ?? skill.version,
6619
+ skill.name,
6620
+ agent,
6621
+ installPath
6622
+ );
6623
+ }
6624
+ }
6625
+ return [...packMap.values()];
6626
+ }
6627
+ async function marketplaceUpdateCommand(slug, options) {
6628
+ try {
6629
+ const installed = await findInstalledMarketplacePacks(options);
6630
+ if (installed.length === 0) {
6631
+ log.info("No marketplace packs installed.");
6632
+ return;
6633
+ }
6634
+ const toUpdate = slug ? installed.filter((p) => p.slug === slug) : installed;
6635
+ if (toUpdate.length === 0) {
6636
+ log.error(`Pack "${slug}" is not installed.`);
6637
+ process11.exit(1);
6638
+ }
6639
+ let updated = 0;
6640
+ for (const pack of toUpdate) {
6641
+ const agentIds = pack.agents.map((a) => a.agent.id).join(",");
6642
+ const isGlobal = options.global ?? false;
6643
+ await resolveAndInstallPack(pack.slug, options, {
6644
+ agents: agentIds,
6645
+ global: isGlobal
6646
+ });
6647
+ log.success(`Updated ${pack.slug}`);
6648
+ updated++;
6649
+ }
6650
+ if (updated === 0) {
6651
+ log.info("All packs are up to date.");
6652
+ }
6653
+ } catch (error) {
6654
+ log.error(
6655
+ error instanceof Error ? error.message : "Failed to update packs"
6656
+ );
6657
+ process11.exit(1);
6658
+ }
6659
+ }
6660
+ async function selectPacksToRemove(installed, slug, options) {
6661
+ if (slug) {
6662
+ const matched = installed.filter((p) => p.slug === slug);
6663
+ if (matched.length === 0) {
6664
+ throw new Error(`Pack "${slug}" is not installed.`);
6665
+ }
6666
+ return matched;
6667
+ }
6668
+ if (options.yes) {
6669
+ return installed;
6670
+ }
6671
+ const selected = await multiselect({
6672
+ message: "Select packs to remove:",
6673
+ options: installed.map((p) => ({
6674
+ value: p,
6675
+ label: p.slug,
6676
+ hint: `${p.skills.length} skill${p.skills.length === 1 ? "" : "s"}`
6677
+ })),
6678
+ initialValues: [],
6679
+ required: true
6680
+ });
6681
+ if (isCancel(selected)) {
6682
+ cancel("Remove cancelled.");
6683
+ process11.exit(0);
6684
+ }
6685
+ return selected;
6686
+ }
6687
+ async function removePackFiles(pack, options) {
6688
+ for (const { agent, installPath } of pack.agents) {
6689
+ for (const skillName of pack.skills) {
6690
+ const skillPath = resolve7(join13(installPath, skillName));
6691
+ if (!skillPath.startsWith(`${resolve7(installPath)}/`)) {
6692
+ continue;
6693
+ }
6694
+ await rm6(skillPath, { recursive: true, force: true });
6695
+ await removeFromMetadataAsync(installPath, skillName);
6696
+ }
6697
+ const hookConfigPath = resolveHookConfigPath(agent, {
6698
+ global: options.global ?? false
6699
+ });
6700
+ if (hookConfigPath) {
6701
+ await removeMarketplaceHooksBySourceAsync(hookConfigPath, pack.slug);
6702
+ }
6703
+ }
6704
+ }
6705
+ async function marketplaceRemoveCommand(slug, options) {
6706
+ try {
6707
+ const installed = await findInstalledMarketplacePacks(options);
6708
+ if (installed.length === 0) {
6709
+ log.info("No marketplace packs installed.");
6710
+ return;
6711
+ }
6712
+ const toRemove = await selectPacksToRemove(installed, slug, options);
6713
+ if (!options.yes) {
6714
+ const slugList = toRemove.map((p) => p.slug).join(", ");
6715
+ const confirmed = await confirm({
6716
+ message: `Remove ${toRemove.length} pack${toRemove.length === 1 ? "" : "s"} (${slugList})?`,
6717
+ initialValue: false
6718
+ });
6719
+ if (isCancel(confirmed) || !confirmed) {
6720
+ cancel("Remove cancelled.");
6721
+ process11.exit(0);
6722
+ }
6723
+ }
6724
+ for (const pack of toRemove) {
6725
+ await removePackFiles(pack, options);
6726
+ log.success(`Removed ${pack.slug}`);
6727
+ }
6728
+ } catch (error) {
6729
+ log.error(
6730
+ error instanceof Error ? error.message : "Failed to remove packs"
6731
+ );
6732
+ process11.exit(1);
6425
6733
  }
6426
6734
  }
6427
6735
 
@@ -6429,15 +6737,15 @@ async function marketplaceInstallCommand(slug, options) {
6429
6737
  init_esm_shims();
6430
6738
  init_api();
6431
6739
  init_tui();
6432
- import process11 from "process";
6740
+ import process12 from "process";
6433
6741
  var writeJson4 = (value) => {
6434
- process11.stdout.write(`${JSON.stringify(value, null, 2)}
6742
+ process12.stdout.write(`${JSON.stringify(value, null, 2)}
6435
6743
  `);
6436
6744
  };
6437
6745
  var exitWithError3 = (error) => {
6438
6746
  const message = error instanceof Error ? error.message : String(error);
6439
6747
  log.error(message);
6440
- process11.exit(1);
6748
+ process12.exit(1);
6441
6749
  };
6442
6750
  var fail2 = (message) => {
6443
6751
  throw new Error(message);
@@ -6536,9 +6844,9 @@ async function profilesSetDefaultCommand(options) {
6536
6844
  init_esm_shims();
6537
6845
  init_api();
6538
6846
  init_tui();
6539
- import process12 from "process";
6847
+ import process13 from "process";
6540
6848
  var writeJson5 = (value) => {
6541
- process12.stdout.write(`${JSON.stringify(value, null, 2)}
6849
+ process13.stdout.write(`${JSON.stringify(value, null, 2)}
6542
6850
  `);
6543
6851
  };
6544
6852
  var fail3 = (message) => {
@@ -6566,7 +6874,7 @@ var run3 = async (command, args, options) => {
6566
6874
  var exitWithError4 = (error) => {
6567
6875
  const message = error instanceof Error ? error.message : String(error);
6568
6876
  log.error(message);
6569
- process12.exit(1);
6877
+ process13.exit(1);
6570
6878
  };
6571
6879
  async function projectsGetCommand(options) {
6572
6880
  try {
@@ -6620,9 +6928,9 @@ async function projectsRemoveCommand(options) {
6620
6928
  init_esm_shims();
6621
6929
  init_api();
6622
6930
  init_tui();
6623
- import process13 from "process";
6931
+ import process14 from "process";
6624
6932
  var writeJson6 = (value) => {
6625
- process13.stdout.write(`${JSON.stringify(value, null, 2)}
6933
+ process14.stdout.write(`${JSON.stringify(value, null, 2)}
6626
6934
  `);
6627
6935
  };
6628
6936
  var parseCsv3 = (input) => {
@@ -6635,7 +6943,7 @@ var parseCsv3 = (input) => {
6635
6943
  var exitWithError5 = (error) => {
6636
6944
  const message = error instanceof Error ? error.message : String(error);
6637
6945
  log.error(message);
6638
- process13.exit(1);
6946
+ process14.exit(1);
6639
6947
  };
6640
6948
  var fail4 = (message) => {
6641
6949
  throw new Error(message);
@@ -6722,9 +7030,9 @@ async function referencesReorderCommand(options) {
6722
7030
 
6723
7031
  // src/commands/remove.ts
6724
7032
  init_esm_shims();
6725
- import { rm as rm5 } from "fs/promises";
6726
- import { join as join12, resolve as resolve7 } from "path";
6727
- import process14 from "process";
7033
+ import { rm as rm7 } from "fs/promises";
7034
+ import { join as join14, resolve as resolve8 } from "path";
7035
+ import process15 from "process";
6728
7036
  init_tui();
6729
7037
  async function collectInstalledSkills(detectedAgents, options) {
6730
7038
  const skillsToRemove = [];
@@ -6745,7 +7053,7 @@ async function collectInstalledSkills(detectedAgents, options) {
6745
7053
  name: skill.name,
6746
7054
  agentName: agent.name,
6747
7055
  installPath,
6748
- skillPath: join12(installPath, skill.name)
7056
+ skillPath: join14(installPath, skill.name)
6749
7057
  });
6750
7058
  }
6751
7059
  }
@@ -6757,7 +7065,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
6757
7065
  if (selected.length === 0) {
6758
7066
  log.error(`Skill '${options.skill}' not found.`);
6759
7067
  log.info("Run 'braid list' to see installed skills.");
6760
- process14.exit(1);
7068
+ process15.exit(1);
6761
7069
  }
6762
7070
  return selected;
6763
7071
  }
@@ -6776,7 +7084,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
6776
7084
  });
6777
7085
  if (isCancel(result)) {
6778
7086
  cancel("Remove cancelled.");
6779
- process14.exit(0);
7087
+ process15.exit(0);
6780
7088
  }
6781
7089
  return result;
6782
7090
  }
@@ -6790,20 +7098,20 @@ async function confirmRemoval(selectedCount, options) {
6790
7098
  });
6791
7099
  if (isCancel(confirmed) || !confirmed) {
6792
7100
  cancel("Remove cancelled.");
6793
- process14.exit(0);
7101
+ process15.exit(0);
6794
7102
  }
6795
7103
  }
6796
7104
  async function removeSkill(skill, removeSpinner) {
6797
7105
  removeSpinner.start(`Removing ${skill.name} from ${skill.agentName}...`);
6798
7106
  try {
6799
- const resolvedSkillPath = resolve7(skill.skillPath);
6800
- const resolvedInstallPath = resolve7(skill.installPath);
7107
+ const resolvedSkillPath = resolve8(skill.skillPath);
7108
+ const resolvedInstallPath = resolve8(skill.installPath);
6801
7109
  if (!resolvedSkillPath.startsWith(`${resolvedInstallPath}/`)) {
6802
7110
  removeSpinner.stop(`Unsafe path for ${skill.name}`);
6803
7111
  log.warn(" Skill path escapes install directory, skipping.");
6804
7112
  return false;
6805
7113
  }
6806
- await rm5(resolvedSkillPath, { recursive: true, force: true });
7114
+ await rm7(resolvedSkillPath, { recursive: true, force: true });
6807
7115
  await removeFromMetadataAsync(skill.installPath, skill.name);
6808
7116
  removeSpinner.stop(`Removed ${skill.name} from ${skill.agentName}`);
6809
7117
  return true;
@@ -6854,7 +7162,7 @@ async function removeCommand(options) {
6854
7162
  removeSpinner.stop("Remove failed");
6855
7163
  const message = error instanceof Error ? error.message : String(error);
6856
7164
  log.error(message);
6857
- process14.exit(1);
7165
+ process15.exit(1);
6858
7166
  }
6859
7167
  }
6860
7168
 
@@ -7052,7 +7360,7 @@ async function rollbackCommand(source, options) {
7052
7360
  init_esm_shims();
7053
7361
  init_api();
7054
7362
  init_tui();
7055
- import process15 from "process";
7363
+ import process16 from "process";
7056
7364
  var parseCsv4 = (input) => {
7057
7365
  if (!input) {
7058
7366
  return void 0;
@@ -7061,13 +7369,13 @@ var parseCsv4 = (input) => {
7061
7369
  return values.length > 0 ? values : void 0;
7062
7370
  };
7063
7371
  var writeJson7 = (value) => {
7064
- process15.stdout.write(`${JSON.stringify(value, null, 2)}
7372
+ process16.stdout.write(`${JSON.stringify(value, null, 2)}
7065
7373
  `);
7066
7374
  };
7067
7375
  var exitWithError6 = (error) => {
7068
7376
  const message = error instanceof Error ? error.message : String(error);
7069
7377
  log.error(message);
7070
- process15.exit(1);
7378
+ process16.exit(1);
7071
7379
  };
7072
7380
  var fail5 = (message) => {
7073
7381
  throw new Error(message);
@@ -7246,7 +7554,7 @@ async function rulesSyncNowCommand(options) {
7246
7554
 
7247
7555
  // src/commands/scaffold.ts
7248
7556
  init_esm_shims();
7249
- import process16 from "process";
7557
+ import process17 from "process";
7250
7558
 
7251
7559
  // src/lib/scaffold.ts
7252
7560
  init_esm_shims();
@@ -7256,11 +7564,11 @@ import {
7256
7564
  mkdir as mkdir7,
7257
7565
  readdir as readdir3,
7258
7566
  readFile as readFile8,
7259
- rm as rm6,
7567
+ rm as rm8,
7260
7568
  stat,
7261
7569
  writeFile as writeFile9
7262
7570
  } from "fs/promises";
7263
- import { dirname as dirname10, join as join13 } from "path";
7571
+ import { dirname as dirname12, join as join15 } from "path";
7264
7572
  var BRAID_PACK_FILENAME = "braid-pack.json";
7265
7573
  var DEFAULT_TIMEOUT_MS = 3e5;
7266
7574
  var MAX_DURATION_MS = 18e5;
@@ -7392,7 +7700,7 @@ var directoryContainsMatchingFile = async (absoluteDirectoryPath, matcher) => {
7392
7700
  );
7393
7701
  }
7394
7702
  for (const entry of entries) {
7395
- const absoluteEntryPath = join13(absoluteDirectoryPath, entry.name);
7703
+ const absoluteEntryPath = join15(absoluteDirectoryPath, entry.name);
7396
7704
  if (entry.isDirectory()) {
7397
7705
  if (await directoryContainsMatchingFile(absoluteEntryPath, matcher)) {
7398
7706
  return true;
@@ -7448,19 +7756,19 @@ var directoryContainsHookArtifact = async (absoluteDirectoryPath) => directoryCo
7448
7756
  }
7449
7757
  );
7450
7758
  var hasUnmanifestedPackContent = async (cwd) => {
7451
- if (await manifestExists(join13(cwd, "SKILL.md"))) {
7759
+ if (await manifestExists(join15(cwd, "SKILL.md"))) {
7452
7760
  return true;
7453
7761
  }
7454
- if (await directoryContainsSkillArtifact(join13(cwd, "skills"))) {
7762
+ if (await directoryContainsSkillArtifact(join15(cwd, "skills"))) {
7455
7763
  return true;
7456
7764
  }
7457
- if (await directoryContainsAgentArtifact(join13(cwd, "agents"))) {
7765
+ if (await directoryContainsAgentArtifact(join15(cwd, "agents"))) {
7458
7766
  return true;
7459
7767
  }
7460
- if (await directoryContainsHookArtifact(join13(cwd, "hooks"))) {
7768
+ if (await directoryContainsHookArtifact(join15(cwd, "hooks"))) {
7461
7769
  return true;
7462
7770
  }
7463
- return directoryContainsWorkflowArtifact(join13(cwd, "workflows"));
7771
+ return directoryContainsWorkflowArtifact(join15(cwd, "workflows"));
7464
7772
  };
7465
7773
  var parseManifestEntries = (manifest, key, parser) => {
7466
7774
  const value = manifest[key];
@@ -7986,7 +8294,7 @@ var writeTextFile3 = async (absolutePath, relativePath, content) => {
7986
8294
  var restoreManifest = async (absoluteManifestPath, previousContent) => {
7987
8295
  try {
7988
8296
  if (previousContent === void 0) {
7989
- await rm6(absoluteManifestPath, { force: true });
8297
+ await rm8(absoluteManifestPath, { force: true });
7990
8298
  return;
7991
8299
  }
7992
8300
  await writeFile9(absoluteManifestPath, previousContent, "utf-8");
@@ -8010,8 +8318,8 @@ var prepareScaffoldOperation = async (normalizedInput) => {
8010
8318
  normalizedInput.type,
8011
8319
  normalizedInput.name
8012
8320
  );
8013
- const absoluteManifestPath = join13(normalizedInput.cwd, BRAID_PACK_FILENAME);
8014
- const absoluteArtifactPath = join13(normalizedInput.cwd, filePath);
8321
+ const absoluteManifestPath = join15(normalizedInput.cwd, BRAID_PACK_FILENAME);
8322
+ const absoluteArtifactPath = join15(normalizedInput.cwd, filePath);
8015
8323
  const manifestKey = MANIFEST_KEY_BY_TYPE[normalizedInput.type];
8016
8324
  const previousManifestContent = context.state === "manifest-pack" ? await readManifestFile(absoluteManifestPath) : void 0;
8017
8325
  const existingEntry = context.manifest[manifestKey].find(
@@ -8063,7 +8371,7 @@ var writeArtifactWithRollback = async ({
8063
8371
  previousManifestContent
8064
8372
  }) => {
8065
8373
  try {
8066
- await mkdir7(dirname10(absoluteArtifactPath), { recursive: true });
8374
+ await mkdir7(dirname12(absoluteArtifactPath), { recursive: true });
8067
8375
  await writeTextFile3(absoluteArtifactPath, filePath, artifactContent);
8068
8376
  } catch (error) {
8069
8377
  const artifactError = error instanceof ScaffoldError ? error : toIoError(filePath, error);
@@ -8090,10 +8398,10 @@ var ScaffoldError = class extends Error {
8090
8398
  }
8091
8399
  };
8092
8400
  var inspectScaffoldDirectory = async (cwd) => {
8093
- const manifestPath = join13(cwd, BRAID_PACK_FILENAME);
8401
+ const manifestPath = join15(cwd, BRAID_PACK_FILENAME);
8094
8402
  const hasManifest = await manifestExists(manifestPath);
8095
8403
  if (!hasManifest) {
8096
- if (await manifestExists(join13(cwd, "SKILL.md"))) {
8404
+ if (await manifestExists(join15(cwd, "SKILL.md"))) {
8097
8405
  throw new ScaffoldError(
8098
8406
  "invalid_repo_state",
8099
8407
  "Invalid repo state: root SKILL.md requires migration to braid-pack.json before scaffolding."
@@ -8206,11 +8514,11 @@ var normalizeReferenceList = (referenceLabel, availableNamesLabel, values, avail
8206
8514
  };
8207
8515
  var exitCancelled2 = () => {
8208
8516
  cancel("Scaffold cancelled.");
8209
- process16.exit(0);
8517
+ process17.exit(0);
8210
8518
  };
8211
8519
  var exitWithError7 = (message) => {
8212
8520
  log.error(message);
8213
- process16.exit(1);
8521
+ process17.exit(1);
8214
8522
  };
8215
8523
  var requirePromptValue = (value) => {
8216
8524
  if (isCancel(value)) {
@@ -8339,7 +8647,7 @@ var resolveOptionalReference = async (label, flagValue, options, availableNames)
8339
8647
  return selected || void 0;
8340
8648
  };
8341
8649
  var buildScaffoldInput = async (options) => {
8342
- const context = await inspectScaffoldDirectory(process16.cwd());
8650
+ const context = await inspectScaffoldDirectory(process17.cwd());
8343
8651
  const availableSkillNames = context.manifest.skills.map(
8344
8652
  (entry) => entry.name
8345
8653
  );
@@ -8393,7 +8701,7 @@ var buildScaffoldInput = async (options) => {
8393
8701
  availableWorkflowNames
8394
8702
  ) : void 0;
8395
8703
  return {
8396
- cwd: process16.cwd(),
8704
+ cwd: process17.cwd(),
8397
8705
  type,
8398
8706
  name,
8399
8707
  title,
@@ -8485,8 +8793,8 @@ init_scope();
8485
8793
 
8486
8794
  // src/commands/update.ts
8487
8795
  init_esm_shims();
8488
- import { rm as rm7 } from "fs/promises";
8489
- import { join as join14, resolve as resolve8 } from "path";
8796
+ import { rm as rm9 } from "fs/promises";
8797
+ import { join as join16, resolve as resolve9 } from "path";
8490
8798
  import {
8491
8799
  cancel as cancel2,
8492
8800
  isCancel as isCancel2,
@@ -8631,12 +8939,12 @@ var areSameSourceProjects = (left, right) => {
8631
8939
  };
8632
8940
  var isSameInstalledSource = (source, target) => source.type === target.type && source.name === target.name && areSameSourceProjects(source.orgProjects, target.orgProjects) && areSameSourceProjects(source.personalProjects, target.personalProjects);
8633
8941
  var removeInstalledBundle = async (installPath, bundleName) => {
8634
- const resolvedInstallPath = resolve8(installPath);
8635
- const resolvedBundlePath = resolve8(join14(installPath, bundleName));
8942
+ const resolvedInstallPath = resolve9(installPath);
8943
+ const resolvedBundlePath = resolve9(join16(installPath, bundleName));
8636
8944
  if (!resolvedBundlePath.startsWith(`${resolvedInstallPath}/`)) {
8637
8945
  throw new Error(`Unsafe bundle path for ${bundleName}`);
8638
8946
  }
8639
- await rm7(resolvedBundlePath, { recursive: true, force: true });
8947
+ await rm9(resolvedBundlePath, { recursive: true, force: true });
8640
8948
  const disabledRecord = await findDisabledBundleByOriginalPathAsync(
8641
8949
  resolvedBundlePath,
8642
8950
  {
@@ -8875,10 +9183,16 @@ async function updateCommand(options) {
8875
9183
  }
8876
9184
 
8877
9185
  // src/index.ts
9186
+ init_tui();
8878
9187
  var require2 = createRequire(import.meta.url);
8879
9188
  var { version: PACKAGE_VERSION } = require2("../package.json");
9189
+ var COMMANDER_ERROR_PREFIX = /^error:\s*/i;
8880
9190
  var program = new Command();
8881
- program.name("braid").description("Install and manage braid prompt artifacts locally").version(PACKAGE_VERSION);
9191
+ program.name("braid").description("Install and manage braid prompt artifacts locally").version(PACKAGE_VERSION).configureOutput({
9192
+ outputError: (str) => {
9193
+ log.error(str.replace(COMMANDER_ERROR_PREFIX, "").trim());
9194
+ }
9195
+ });
8882
9196
  var auth = program.command("auth").description("Configure API key for braid authentication");
8883
9197
  auth.command("login", { isDefault: true }).description("Authenticate with braid via browser login or API key").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option(
8884
9198
  "--token <token>",
@@ -8954,6 +9268,11 @@ marketplace.command("install").description("Install a marketplace pack by slug")
8954
9268
  "--allow-hooks",
8955
9269
  "Allow marketplace packs to install executable Claude Code hooks"
8956
9270
  ).option("-y, --yes", "Skip confirmation prompts").action((slug, options) => marketplaceInstallCommand(slug, options));
9271
+ marketplace.command("update").description("Update installed marketplace packs to their latest version").argument("[slug]", "Pack slug (updates all if omitted)").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("-g, --global", "Update packs in global agent directories").option(
9272
+ "--allow-hooks",
9273
+ "Allow marketplace packs to install executable Claude Code hooks"
9274
+ ).option("-y, --yes", "Skip confirmation prompts").action((slug, options) => marketplaceUpdateCommand(slug, options));
9275
+ marketplace.command("remove").description("Remove installed marketplace packs").argument("[slug]", "Pack slug (prompts for selection if omitted)").option("-g, --global", "Remove from global agent directories").option("-y, --yes", "Skip confirmation prompts").action((slug, options) => marketplaceRemoveCommand(slug, options));
8957
9276
  var projects = program.command("projects").description("Discover available projects from braid");
8958
9277
  projects.command("list").description("List available personal and org projects").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(projectsListCommand);
8959
9278
  projects.command("get").description("Get a project by id").requiredOption("--id <id>", "Project ID").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(projectsGetCommand);