@braid-cloud/cli 0.1.10 → 0.1.12

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
@@ -55,28 +55,28 @@ __export(config_exports, {
55
55
  setApiKeyAsync: () => setApiKeyAsync
56
56
  });
57
57
  import { existsSync } from "fs";
58
- import { mkdir, readFile, writeFile } from "fs/promises";
59
- import { homedir } from "os";
60
- import { dirname, join, parse } from "path";
61
- import process2 from "process";
62
- import { Data, Effect, pipe } from "effect";
58
+ import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
59
+ import { homedir as homedir2 } from "os";
60
+ import { dirname as dirname2, join as join2, parse } from "path";
61
+ import process3 from "process";
62
+ import { Data as Data2, Effect as Effect3, pipe as pipe3 } from "effect";
63
63
  var CONFIG_DIR, CONFIG_FILE, PROJECT_CONFIG_FILENAME, USER_CONFIG_FILENAME, ConfigReadError, ConfigWriteError, findConfigFile, findProjectConfigFile, findUserConfigFile, loadProjectConfig, loadUserConfig, resolveUserConfigWritePath, resolveProjectConfigWritePath, saveUserConfig, saveProjectConfig, isValidServerUrl, resolveServerUrlFromConfig, applyConfigSource, applyEnvOverrides, createDefaultMergedConfig, loadMergedConfig, loadConfig, saveConfig, getApiKey, setApiKey, getServerUrl, clearApiKey, loadConfigAsync, loadProjectConfigAsync, loadUserConfigAsync, loadMergedConfigAsync, findProjectConfigFileAsync, findUserConfigFileAsync, saveConfigAsync, saveUserConfigAsync, saveProjectConfigAsync, getApiKeyAsync, setApiKeyAsync, getServerUrlAsync, clearApiKeyAsync;
64
64
  var init_config = __esm({
65
65
  "src/lib/config.ts"() {
66
66
  "use strict";
67
67
  init_esm_shims();
68
- CONFIG_DIR = join(homedir(), ".config", "braid");
69
- CONFIG_FILE = join(CONFIG_DIR, "config.json");
68
+ CONFIG_DIR = join2(homedir2(), ".config", "braid");
69
+ CONFIG_FILE = join2(CONFIG_DIR, "config.json");
70
70
  PROJECT_CONFIG_FILENAME = "braid.json";
71
71
  USER_CONFIG_FILENAME = "braid.user.json";
72
- ConfigReadError = class extends Data.TaggedError("ConfigReadError") {
72
+ ConfigReadError = class extends Data2.TaggedError("ConfigReadError") {
73
73
  };
74
- ConfigWriteError = class extends Data.TaggedError("ConfigWriteError") {
74
+ ConfigWriteError = class extends Data2.TaggedError("ConfigWriteError") {
75
75
  };
76
- findConfigFile = (filename, startDir = process2.cwd()) => {
76
+ findConfigFile = (filename, startDir = process3.cwd()) => {
77
77
  let currentDir = startDir;
78
78
  while (true) {
79
- const configPath = join(currentDir, filename);
79
+ const configPath = join2(currentDir, filename);
80
80
  if (existsSync(configPath)) {
81
81
  return configPath;
82
82
  }
@@ -87,9 +87,9 @@ var init_config = __esm({
87
87
  currentDir = parsed.dir;
88
88
  }
89
89
  };
90
- findProjectConfigFile = (startDir = process2.cwd()) => findConfigFile(PROJECT_CONFIG_FILENAME, startDir);
91
- findUserConfigFile = (startDir = process2.cwd()) => findConfigFile(USER_CONFIG_FILENAME, startDir);
92
- loadProjectConfig = () => Effect.tryPromise({
90
+ findProjectConfigFile = (startDir = process3.cwd()) => findConfigFile(PROJECT_CONFIG_FILENAME, startDir);
91
+ findUserConfigFile = (startDir = process3.cwd()) => findConfigFile(USER_CONFIG_FILENAME, startDir);
92
+ loadProjectConfig = () => Effect3.tryPromise({
93
93
  try: async () => {
94
94
  const configPath = await findProjectConfigFile();
95
95
  if (!configPath) {
@@ -99,8 +99,8 @@ var init_config = __esm({
99
99
  return JSON.parse(content);
100
100
  },
101
101
  catch: () => void 0
102
- }).pipe(Effect.orElseSucceed(() => void 0));
103
- loadUserConfig = () => Effect.tryPromise({
102
+ }).pipe(Effect3.orElseSucceed(() => void 0));
103
+ loadUserConfig = () => Effect3.tryPromise({
104
104
  try: async () => {
105
105
  const configPath = await findUserConfigFile();
106
106
  if (!configPath) {
@@ -110,16 +110,16 @@ var init_config = __esm({
110
110
  return JSON.parse(content);
111
111
  },
112
112
  catch: () => void 0
113
- }).pipe(Effect.orElseSucceed(() => void 0));
114
- resolveUserConfigWritePath = (startDir = process2.cwd()) => findUserConfigFile(startDir) ?? join(startDir, USER_CONFIG_FILENAME);
115
- resolveProjectConfigWritePath = (startDir = process2.cwd()) => findProjectConfigFile(startDir) ?? join(startDir, PROJECT_CONFIG_FILENAME);
116
- saveUserConfig = (config, startDir = process2.cwd()) => {
113
+ }).pipe(Effect3.orElseSucceed(() => void 0));
114
+ resolveUserConfigWritePath = (startDir = process3.cwd()) => findUserConfigFile(startDir) ?? join2(startDir, USER_CONFIG_FILENAME);
115
+ resolveProjectConfigWritePath = (startDir = process3.cwd()) => findProjectConfigFile(startDir) ?? join2(startDir, PROJECT_CONFIG_FILENAME);
116
+ saveUserConfig = (config, startDir = process3.cwd()) => {
117
117
  const targetPath = resolveUserConfigWritePath(startDir);
118
- return pipe(
119
- Effect.tryPromise({
118
+ return pipe3(
119
+ Effect3.tryPromise({
120
120
  try: async () => {
121
- await mkdir(dirname(targetPath), { recursive: true, mode: 448 });
122
- await writeFile(targetPath, JSON.stringify(config, null, 2), {
121
+ await mkdir2(dirname2(targetPath), { recursive: true, mode: 448 });
122
+ await writeFile2(targetPath, JSON.stringify(config, null, 2), {
123
123
  encoding: "utf-8",
124
124
  mode: 384
125
125
  });
@@ -129,13 +129,13 @@ var init_config = __esm({
129
129
  })
130
130
  );
131
131
  };
132
- saveProjectConfig = (config, startDir = process2.cwd()) => {
132
+ saveProjectConfig = (config, startDir = process3.cwd()) => {
133
133
  const targetPath = resolveProjectConfigWritePath(startDir);
134
- return pipe(
135
- Effect.tryPromise({
134
+ return pipe3(
135
+ Effect3.tryPromise({
136
136
  try: async () => {
137
- await mkdir(dirname(targetPath), { recursive: true, mode: 448 });
138
- await writeFile(targetPath, JSON.stringify(config, null, 2), {
137
+ await mkdir2(dirname2(targetPath), { recursive: true, mode: 448 });
138
+ await writeFile2(targetPath, JSON.stringify(config, null, 2), {
139
139
  encoding: "utf-8",
140
140
  mode: 384
141
141
  });
@@ -206,10 +206,10 @@ var init_config = __esm({
206
206
  }
207
207
  };
208
208
  applyEnvOverrides = (merged) => {
209
- if (process2.env.BRAID_API_KEY) {
210
- merged.token = process2.env.BRAID_API_KEY;
209
+ if (process3.env.BRAID_API_KEY) {
210
+ merged.token = process3.env.BRAID_API_KEY;
211
211
  }
212
- const envServerUrl = process2.env.BRAID_SKILLS_SERVER_URL ?? process2.env.BRAID_SERVER_URL;
212
+ const envServerUrl = process3.env.BRAID_SKILLS_SERVER_URL ?? process3.env.BRAID_SERVER_URL;
213
213
  if (envServerUrl && isValidServerUrl(envServerUrl)) {
214
214
  merged.serverUrl = envServerUrl;
215
215
  }
@@ -219,15 +219,15 @@ var init_config = __esm({
219
219
  includeUserGlobal: true,
220
220
  includeOrgGlobal: true
221
221
  });
222
- loadMergedConfig = () => pipe(
223
- Effect.all({
222
+ loadMergedConfig = () => pipe3(
223
+ Effect3.all({
224
224
  projectConfig: loadProjectConfig(),
225
225
  userConfig: loadUserConfig(),
226
226
  globalConfig: loadConfig().pipe(
227
- Effect.orElseSucceed(() => ({}))
227
+ Effect3.orElseSucceed(() => ({}))
228
228
  )
229
229
  }),
230
- Effect.map(({ projectConfig, userConfig, globalConfig }) => {
230
+ Effect3.map(({ projectConfig, userConfig, globalConfig }) => {
231
231
  const merged = createDefaultMergedConfig();
232
232
  applyConfigSource(merged, projectConfig);
233
233
  applyConfigSource(merged, userConfig);
@@ -238,24 +238,24 @@ var init_config = __esm({
238
238
  return merged;
239
239
  })
240
240
  );
241
- loadConfig = () => pipe(
242
- Effect.tryPromise({
241
+ loadConfig = () => pipe3(
242
+ Effect3.tryPromise({
243
243
  try: () => readFile(CONFIG_FILE, "utf-8"),
244
244
  catch: (e) => new ConfigReadError({ path: CONFIG_FILE, cause: e })
245
245
  }),
246
- Effect.flatMap(
247
- (content) => Effect.try({
246
+ Effect3.flatMap(
247
+ (content) => Effect3.try({
248
248
  try: () => JSON.parse(content),
249
249
  catch: () => ({})
250
250
  })
251
251
  ),
252
- Effect.orElseSucceed(() => ({}))
252
+ Effect3.orElseSucceed(() => ({}))
253
253
  );
254
- saveConfig = (config) => pipe(
255
- Effect.tryPromise({
254
+ saveConfig = (config) => pipe3(
255
+ Effect3.tryPromise({
256
256
  try: async () => {
257
- await mkdir(dirname(CONFIG_FILE), { recursive: true, mode: 448 });
258
- await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), {
257
+ await mkdir2(dirname2(CONFIG_FILE), { recursive: true, mode: 448 });
258
+ await writeFile2(CONFIG_FILE, JSON.stringify(config, null, 2), {
259
259
  encoding: "utf-8",
260
260
  mode: 384
261
261
  });
@@ -263,33 +263,33 @@ var init_config = __esm({
263
263
  catch: (e) => new ConfigWriteError({ path: CONFIG_FILE, cause: e })
264
264
  })
265
265
  );
266
- getApiKey = () => pipe(
267
- Effect.succeed(process2.env.BRAID_API_KEY),
268
- Effect.flatMap(
269
- (envKey) => envKey ? Effect.succeed(envKey) : pipe(
266
+ getApiKey = () => pipe3(
267
+ Effect3.succeed(process3.env.BRAID_API_KEY),
268
+ Effect3.flatMap(
269
+ (envKey) => envKey ? Effect3.succeed(envKey) : pipe3(
270
270
  loadUserConfig(),
271
- Effect.flatMap(
272
- (userConfig) => userConfig?.token ? Effect.succeed(userConfig.token) : pipe(
271
+ Effect3.flatMap(
272
+ (userConfig) => userConfig?.token ? Effect3.succeed(userConfig.token) : pipe3(
273
273
  loadConfig(),
274
- Effect.map((config) => config.apiKey)
274
+ Effect3.map((config) => config.apiKey)
275
275
  )
276
276
  )
277
277
  )
278
278
  )
279
279
  );
280
- setApiKey = (apiKey) => pipe(
280
+ setApiKey = (apiKey) => pipe3(
281
281
  loadConfig(),
282
- Effect.flatMap((config) => saveConfig({ ...config, apiKey }))
282
+ Effect3.flatMap((config) => saveConfig({ ...config, apiKey }))
283
283
  );
284
- getServerUrl = () => pipe(
285
- Effect.succeed(process2.env.BRAID_SERVER_URL),
286
- Effect.flatMap(
287
- (envUrl) => envUrl ? Effect.succeed(envUrl) : pipe(
284
+ getServerUrl = () => pipe3(
285
+ Effect3.succeed(process3.env.BRAID_SERVER_URL),
286
+ Effect3.flatMap(
287
+ (envUrl) => envUrl ? Effect3.succeed(envUrl) : pipe3(
288
288
  loadUserConfig(),
289
- Effect.flatMap(
290
- (userConfig) => userConfig?.serverUrl ? Effect.succeed(userConfig.serverUrl) : pipe(
289
+ Effect3.flatMap(
290
+ (userConfig) => userConfig?.serverUrl ? Effect3.succeed(userConfig.serverUrl) : pipe3(
291
291
  loadConfig(),
292
- Effect.map(
292
+ Effect3.map(
293
293
  (config) => config.serverUrl ?? "https://braid.cloud"
294
294
  )
295
295
  )
@@ -297,55 +297,74 @@ var init_config = __esm({
297
297
  )
298
298
  )
299
299
  );
300
- clearApiKey = () => pipe(
300
+ clearApiKey = () => pipe3(
301
301
  loadConfig(),
302
- Effect.flatMap((config) => {
302
+ Effect3.flatMap((config) => {
303
303
  const { apiKey: _, ...rest } = config;
304
304
  return saveConfig(rest);
305
305
  })
306
306
  );
307
- loadConfigAsync = () => Effect.runPromise(loadConfig());
308
- loadProjectConfigAsync = () => Effect.runPromise(loadProjectConfig());
309
- loadUserConfigAsync = () => Effect.runPromise(loadUserConfig());
310
- loadMergedConfigAsync = () => Effect.runPromise(loadMergedConfig());
307
+ loadConfigAsync = () => Effect3.runPromise(loadConfig());
308
+ loadProjectConfigAsync = () => Effect3.runPromise(loadProjectConfig());
309
+ loadUserConfigAsync = () => Effect3.runPromise(loadUserConfig());
310
+ loadMergedConfigAsync = () => Effect3.runPromise(loadMergedConfig());
311
311
  findProjectConfigFileAsync = (startDir) => findProjectConfigFile(startDir);
312
312
  findUserConfigFileAsync = (startDir) => findUserConfigFile(startDir);
313
- saveConfigAsync = (config) => Effect.runPromise(saveConfig(config));
314
- saveUserConfigAsync = (config, startDir) => Effect.runPromise(saveUserConfig(config, startDir));
315
- saveProjectConfigAsync = (config, startDir) => Effect.runPromise(saveProjectConfig(config, startDir));
316
- getApiKeyAsync = () => Effect.runPromise(getApiKey());
317
- setApiKeyAsync = (apiKey) => Effect.runPromise(setApiKey(apiKey));
318
- getServerUrlAsync = () => Effect.runPromise(getServerUrl());
319
- clearApiKeyAsync = () => Effect.runPromise(clearApiKey());
313
+ saveConfigAsync = (config) => Effect3.runPromise(saveConfig(config));
314
+ saveUserConfigAsync = (config, startDir) => Effect3.runPromise(saveUserConfig(config, startDir));
315
+ saveProjectConfigAsync = (config, startDir) => Effect3.runPromise(saveProjectConfig(config, startDir));
316
+ getApiKeyAsync = () => Effect3.runPromise(getApiKey());
317
+ setApiKeyAsync = (apiKey) => Effect3.runPromise(setApiKey(apiKey));
318
+ getServerUrlAsync = () => Effect3.runPromise(getServerUrl());
319
+ clearApiKeyAsync = () => Effect3.runPromise(clearApiKey());
320
320
  }
321
321
  });
322
322
 
323
323
  // src/lib/api.ts
324
- import { Data as Data2, Effect as Effect2, pipe as pipe2 } from "effect";
325
- var TRAILING_SLASH_REGEX, ApiError, AuthenticationError, NetworkError, resolveApiKey, resolveServerUrl, parseResponse, buildApiUrl, buildExportUrl, executeApiRequest, parseScopeOptionsResponse, fetchScopeOptions, buildRuleOptionsUrl, fetchRuleOptions, fetchSkills, runLifecycleCommand, DEFAULT_PUBLIC_SERVER_URL, NotFoundError, RateLimitedError, handlePublicApiResponse, fetchPublicMetadata, fetchPublicExport, fetchPublicMetadataAsync, fetchPublicExportAsync, validateApiKey, fetchSkillsAsync, validateApiKeyAsync, fetchScopeOptionsAsync, fetchRuleOptionsAsync, runLifecycleCommandAsync;
324
+ import { Data as Data3, Effect as Effect4, pipe as pipe4 } from "effect";
325
+ var TRAILING_SLASH_REGEX, ALLOW_UNTRUSTED_SERVER_ENV, isTruthy, isLocalHost, isTrustedApiServerUrl, ApiError, AuthenticationError, NetworkError, resolveApiKey, resolveServerUrl, parseResponse, buildApiUrl, buildExportUrl, executeApiRequest, parseScopeOptionsResponse, fetchScopeOptions, buildRuleOptionsUrl, fetchRuleOptions, fetchSkills, runLifecycleCommand, DEFAULT_PUBLIC_SERVER_URL, NotFoundError, RateLimitedError, handlePublicApiResponse, fetchPublicMetadata, fetchPublicExport, fetchPublicMetadataAsync, fetchPublicExportAsync, validateApiKey, fetchSkillsAsync, validateApiKeyAsync, fetchScopeOptionsAsync, fetchRuleOptionsAsync, runLifecycleCommandAsync;
326
326
  var init_api = __esm({
327
327
  "src/lib/api.ts"() {
328
328
  "use strict";
329
329
  init_esm_shims();
330
330
  init_config();
331
331
  TRAILING_SLASH_REGEX = /\/$/;
332
- ApiError = class extends Data2.TaggedError("ApiError") {
332
+ ALLOW_UNTRUSTED_SERVER_ENV = "BRAID_ALLOW_UNTRUSTED_SERVER_URL";
333
+ isTruthy = (value) => value === "1" || value === "true" || value === "yes";
334
+ isLocalHost = (hostname2) => hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1";
335
+ isTrustedApiServerUrl = (serverUrl) => {
336
+ try {
337
+ const parsed = new URL(serverUrl);
338
+ const hostname2 = parsed.hostname;
339
+ const isBraidHost = hostname2 === "braid.cloud" || hostname2.endsWith(".braid.cloud");
340
+ if (parsed.protocol === "https:" && isBraidHost) {
341
+ return true;
342
+ }
343
+ if (parsed.protocol === "http:" && isLocalHost(hostname2)) {
344
+ return true;
345
+ }
346
+ return false;
347
+ } catch {
348
+ return false;
349
+ }
350
+ };
351
+ ApiError = class extends Data3.TaggedError("ApiError") {
333
352
  };
334
- AuthenticationError = class extends Data2.TaggedError("AuthenticationError") {
353
+ AuthenticationError = class extends Data3.TaggedError("AuthenticationError") {
335
354
  };
336
- NetworkError = class extends Data2.TaggedError("NetworkError") {
355
+ NetworkError = class extends Data3.TaggedError("NetworkError") {
337
356
  };
338
357
  resolveApiKey = (optionsApiKey) => {
339
358
  if (optionsApiKey) {
340
- return Effect2.succeed(optionsApiKey);
359
+ return Effect4.succeed(optionsApiKey);
341
360
  }
342
- return pipe2(
343
- Effect2.tryPromise({
361
+ return pipe4(
362
+ Effect4.tryPromise({
344
363
  try: () => getApiKeyAsync(),
345
364
  catch: () => new NetworkError({ message: "Failed to read config" })
346
365
  }),
347
- Effect2.flatMap(
348
- (key) => key ? Effect2.succeed(key) : Effect2.fail(
366
+ Effect4.flatMap(
367
+ (key) => key ? Effect4.succeed(key) : Effect4.fail(
349
368
  new AuthenticationError({
350
369
  message: 'No API key configured. Run "braid auth" to authenticate.'
351
370
  })
@@ -355,24 +374,43 @@ var init_api = __esm({
355
374
  };
356
375
  resolveServerUrl = (optionsServerUrl) => {
357
376
  if (optionsServerUrl) {
358
- return Effect2.succeed(optionsServerUrl);
377
+ if (!(isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(optionsServerUrl))) {
378
+ return Effect4.fail(
379
+ new NetworkError({
380
+ message: `Untrusted server URL '${optionsServerUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
381
+ })
382
+ );
383
+ }
384
+ return Effect4.succeed(optionsServerUrl);
359
385
  }
360
- return Effect2.tryPromise({
361
- try: () => getServerUrlAsync(),
362
- catch: () => new NetworkError({ message: "Failed to read config" })
363
- });
386
+ return pipe4(
387
+ Effect4.tryPromise({
388
+ try: () => getServerUrlAsync(),
389
+ catch: () => new NetworkError({ message: "Failed to read config" })
390
+ }),
391
+ Effect4.flatMap((serverUrl) => {
392
+ if (isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(serverUrl)) {
393
+ return Effect4.succeed(serverUrl);
394
+ }
395
+ return Effect4.fail(
396
+ new NetworkError({
397
+ message: `Untrusted server URL '${serverUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
398
+ })
399
+ );
400
+ })
401
+ );
364
402
  };
365
403
  parseResponse = (response, json) => {
366
404
  if (!response.ok) {
367
405
  const errorResponse = json;
368
406
  if (response.status === 401) {
369
- return Effect2.fail(
407
+ return Effect4.fail(
370
408
  new AuthenticationError({
371
409
  message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
372
410
  })
373
411
  );
374
412
  }
375
- return Effect2.fail(
413
+ return Effect4.fail(
376
414
  new ApiError({
377
415
  message: errorResponse.error || "API request failed",
378
416
  code: errorResponse.code || "UNKNOWN_ERROR",
@@ -380,7 +418,7 @@ var init_api = __esm({
380
418
  })
381
419
  );
382
420
  }
383
- return Effect2.succeed(json);
421
+ return Effect4.succeed(json);
384
422
  };
385
423
  buildApiUrl = (serverUrl, path2) => {
386
424
  const baseUrl = serverUrl.replace(TRAILING_SLASH_REGEX, "");
@@ -420,8 +458,8 @@ var init_api = __esm({
420
458
  }
421
459
  return url;
422
460
  };
423
- executeApiRequest = (url, apiKey, serverUrl) => pipe2(
424
- Effect2.tryPromise({
461
+ executeApiRequest = (url, apiKey, serverUrl) => pipe4(
462
+ Effect4.tryPromise({
425
463
  try: () => fetch(url.toString(), {
426
464
  method: "GET",
427
465
  headers: {
@@ -434,13 +472,13 @@ var init_api = __esm({
434
472
  cause: e
435
473
  })
436
474
  }),
437
- Effect2.flatMap(
438
- (response) => pipe2(
439
- Effect2.tryPromise({
475
+ Effect4.flatMap(
476
+ (response) => pipe4(
477
+ Effect4.tryPromise({
440
478
  try: () => response.json(),
441
479
  catch: () => new NetworkError({ message: "Failed to parse API response" })
442
480
  }),
443
- Effect2.flatMap((json) => parseResponse(response, json))
481
+ Effect4.flatMap((json) => parseResponse(response, json))
444
482
  )
445
483
  )
446
484
  );
@@ -448,13 +486,13 @@ var init_api = __esm({
448
486
  if (!response.ok) {
449
487
  const errorResponse = json;
450
488
  if (response.status === 401) {
451
- return Effect2.fail(
489
+ return Effect4.fail(
452
490
  new AuthenticationError({
453
491
  message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
454
492
  })
455
493
  );
456
494
  }
457
- return Effect2.fail(
495
+ return Effect4.fail(
458
496
  new ApiError({
459
497
  message: errorResponse.error || "API request failed",
460
498
  code: errorResponse.code || "UNKNOWN_ERROR",
@@ -462,17 +500,17 @@ var init_api = __esm({
462
500
  })
463
501
  );
464
502
  }
465
- return Effect2.succeed(json);
503
+ return Effect4.succeed(json);
466
504
  };
467
- fetchScopeOptions = (options = {}) => pipe2(
468
- Effect2.all({
505
+ fetchScopeOptions = (options = {}) => pipe4(
506
+ Effect4.all({
469
507
  apiKey: resolveApiKey(options.apiKey),
470
508
  serverUrl: resolveServerUrl(options.serverUrl)
471
509
  }),
472
- Effect2.flatMap(({ apiKey, serverUrl }) => {
510
+ Effect4.flatMap(({ apiKey, serverUrl }) => {
473
511
  const url = buildApiUrl(serverUrl, "/api/skills/scope-options");
474
- return pipe2(
475
- Effect2.tryPromise({
512
+ return pipe4(
513
+ Effect4.tryPromise({
476
514
  try: () => fetch(url.toString(), {
477
515
  method: "GET",
478
516
  headers: {
@@ -485,13 +523,13 @@ var init_api = __esm({
485
523
  cause: e
486
524
  })
487
525
  }),
488
- Effect2.flatMap(
489
- (response) => pipe2(
490
- Effect2.tryPromise({
526
+ Effect4.flatMap(
527
+ (response) => pipe4(
528
+ Effect4.tryPromise({
491
529
  try: () => response.json(),
492
530
  catch: () => new NetworkError({ message: "Failed to parse API response" })
493
531
  }),
494
- Effect2.flatMap((json) => parseScopeOptionsResponse(response, json))
532
+ Effect4.flatMap((json) => parseScopeOptionsResponse(response, json))
495
533
  )
496
534
  )
497
535
  );
@@ -522,14 +560,14 @@ var init_api = __esm({
522
560
  }
523
561
  return url;
524
562
  };
525
- fetchRuleOptions = (options = {}) => pipe2(
526
- Effect2.all({
563
+ fetchRuleOptions = (options = {}) => pipe4(
564
+ Effect4.all({
527
565
  apiKey: resolveApiKey(options.apiKey),
528
566
  serverUrl: resolveServerUrl(options.serverUrl)
529
567
  }),
530
- Effect2.flatMap(
531
- ({ apiKey, serverUrl }) => pipe2(
532
- Effect2.tryPromise({
568
+ Effect4.flatMap(
569
+ ({ apiKey, serverUrl }) => pipe4(
570
+ Effect4.tryPromise({
533
571
  try: () => fetch(buildRuleOptionsUrl(serverUrl, options).toString(), {
534
572
  method: "GET",
535
573
  headers: {
@@ -542,17 +580,17 @@ var init_api = __esm({
542
580
  cause: e
543
581
  })
544
582
  }),
545
- Effect2.flatMap(
546
- (response) => pipe2(
547
- Effect2.tryPromise({
583
+ Effect4.flatMap(
584
+ (response) => pipe4(
585
+ Effect4.tryPromise({
548
586
  try: () => response.json(),
549
587
  catch: () => new NetworkError({ message: "Failed to parse API response" })
550
588
  }),
551
- Effect2.flatMap((json) => {
589
+ Effect4.flatMap((json) => {
552
590
  if (!response.ok) {
553
591
  return parseResponse(response, json).pipe(
554
- Effect2.flatMap(
555
- () => Effect2.fail(
592
+ Effect4.flatMap(
593
+ () => Effect4.fail(
556
594
  new ApiError({
557
595
  message: "API request failed",
558
596
  code: "UNKNOWN_ERROR",
@@ -562,30 +600,30 @@ var init_api = __esm({
562
600
  )
563
601
  );
564
602
  }
565
- return Effect2.succeed(json);
603
+ return Effect4.succeed(json);
566
604
  })
567
605
  )
568
606
  )
569
607
  )
570
608
  )
571
609
  );
572
- fetchSkills = (options) => pipe2(
573
- Effect2.all({
610
+ fetchSkills = (options) => pipe4(
611
+ Effect4.all({
574
612
  apiKey: resolveApiKey(options.apiKey),
575
613
  serverUrl: resolveServerUrl(options.serverUrl)
576
614
  }),
577
- Effect2.flatMap(({ apiKey, serverUrl }) => {
615
+ Effect4.flatMap(({ apiKey, serverUrl }) => {
578
616
  const url = buildExportUrl(serverUrl, options);
579
617
  return executeApiRequest(url, apiKey, serverUrl);
580
618
  })
581
619
  );
582
- runLifecycleCommand = (request, options = {}) => pipe2(
583
- Effect2.all({
620
+ runLifecycleCommand = (request, options = {}) => pipe4(
621
+ Effect4.all({
584
622
  apiKey: resolveApiKey(options.apiKey),
585
623
  serverUrl: resolveServerUrl(options.serverUrl)
586
624
  }),
587
- Effect2.flatMap(
588
- ({ apiKey, serverUrl }) => Effect2.tryPromise({
625
+ Effect4.flatMap(
626
+ ({ apiKey, serverUrl }) => Effect4.tryPromise({
589
627
  try: async () => {
590
628
  const response = await fetch(
591
629
  buildApiUrl(serverUrl, "/api/lifecycle").toString(),
@@ -627,23 +665,23 @@ var init_api = __esm({
627
665
  )
628
666
  );
629
667
  DEFAULT_PUBLIC_SERVER_URL = "https://braid.cloud";
630
- NotFoundError = class extends Data2.TaggedError("NotFoundError") {
668
+ NotFoundError = class extends Data3.TaggedError("NotFoundError") {
631
669
  };
632
- RateLimitedError = class extends Data2.TaggedError("RateLimitedError") {
670
+ RateLimitedError = class extends Data3.TaggedError("RateLimitedError") {
633
671
  };
634
672
  handlePublicApiResponse = (response, json) => {
635
673
  if (response.ok) {
636
- return Effect2.succeed(json);
674
+ return Effect4.succeed(json);
637
675
  }
638
676
  if (response.status === 404) {
639
- return Effect2.fail(
677
+ return Effect4.fail(
640
678
  new NotFoundError({ message: "Public content not found" })
641
679
  );
642
680
  }
643
681
  if (response.status === 429) {
644
682
  const retryAfter = response.headers.get("Retry-After");
645
683
  const retryMs = retryAfter ? Number(retryAfter) * 1e3 : null;
646
- return Effect2.fail(
684
+ return Effect4.fail(
647
685
  new RateLimitedError({
648
686
  message: "Rate limited. Please try again later.",
649
687
  ...retryMs !== null ? { retryAfterMs: retryMs } : {}
@@ -651,7 +689,7 @@ var init_api = __esm({
651
689
  );
652
690
  }
653
691
  const errorResponse = json;
654
- return Effect2.fail(
692
+ return Effect4.fail(
655
693
  new ApiError({
656
694
  message: errorResponse.error || "API request failed",
657
695
  code: errorResponse.code || "UNKNOWN_ERROR",
@@ -665,21 +703,21 @@ var init_api = __esm({
665
703
  ""
666
704
  );
667
705
  const url = `${baseUrl}/api/public/@${handle}/${slug}`;
668
- return pipe2(
669
- Effect2.tryPromise({
706
+ return pipe4(
707
+ Effect4.tryPromise({
670
708
  try: () => fetch(url, { method: "GET" }),
671
709
  catch: (e) => new NetworkError({
672
710
  message: `Failed to connect to ${baseUrl}`,
673
711
  cause: e
674
712
  })
675
713
  }),
676
- Effect2.flatMap(
677
- (response) => pipe2(
678
- Effect2.tryPromise({
714
+ Effect4.flatMap(
715
+ (response) => pipe4(
716
+ Effect4.tryPromise({
679
717
  try: () => response.json(),
680
718
  catch: () => new NetworkError({ message: "Failed to parse API response" })
681
719
  }),
682
- Effect2.flatMap(
720
+ Effect4.flatMap(
683
721
  (json) => handlePublicApiResponse(response, json)
684
722
  )
685
723
  )
@@ -695,60 +733,73 @@ var init_api = __esm({
695
733
  if (ruleIds && ruleIds.length > 0) {
696
734
  url.searchParams.set("ruleIds", ruleIds.join(","));
697
735
  }
698
- return pipe2(
699
- Effect2.tryPromise({
736
+ return pipe4(
737
+ Effect4.tryPromise({
700
738
  try: () => fetch(url.toString(), { method: "GET" }),
701
739
  catch: (e) => new NetworkError({
702
740
  message: `Failed to connect to ${baseUrl}`,
703
741
  cause: e
704
742
  })
705
743
  }),
706
- Effect2.flatMap(
707
- (response) => pipe2(
708
- Effect2.tryPromise({
744
+ Effect4.flatMap(
745
+ (response) => pipe4(
746
+ Effect4.tryPromise({
709
747
  try: () => response.json(),
710
748
  catch: () => new NetworkError({ message: "Failed to parse API response" })
711
749
  }),
712
- Effect2.flatMap(
750
+ Effect4.flatMap(
713
751
  (json) => handlePublicApiResponse(response, json)
714
752
  )
715
753
  )
716
754
  )
717
755
  );
718
756
  };
719
- fetchPublicMetadataAsync = (handle, slug, serverUrl) => Effect2.runPromise(fetchPublicMetadata(handle, slug, serverUrl));
720
- fetchPublicExportAsync = (handle, slug, ruleIds, serverUrl) => Effect2.runPromise(fetchPublicExport(handle, slug, ruleIds, serverUrl));
721
- validateApiKey = (apiKey, serverUrl) => pipe2(
722
- Effect2.tryPromise({
723
- try: async () => {
757
+ fetchPublicMetadataAsync = (handle, slug, serverUrl) => Effect4.runPromise(fetchPublicMetadata(handle, slug, serverUrl));
758
+ fetchPublicExportAsync = (handle, slug, ruleIds, serverUrl) => Effect4.runPromise(fetchPublicExport(handle, slug, ruleIds, serverUrl));
759
+ validateApiKey = (apiKey, serverUrl) => pipe4(
760
+ Effect4.try({
761
+ try: () => {
724
762
  const baseUrl = serverUrl ?? "https://braid.cloud";
725
- const url = buildApiUrl(baseUrl, "/api/skills/export");
726
- url.searchParams.set("profile", "default");
727
- const response = await fetch(url.toString(), {
728
- method: "GET",
729
- headers: {
730
- Authorization: `Bearer ${apiKey}`,
731
- "Content-Type": "application/json"
732
- }
733
- });
734
- return response.status !== 401;
763
+ if (!(isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(baseUrl))) {
764
+ throw new NetworkError({
765
+ message: `Untrusted server URL '${baseUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
766
+ });
767
+ }
768
+ return baseUrl;
735
769
  },
736
- catch: (e) => new NetworkError({
737
- message: `Failed to connect to ${serverUrl ?? "https://braid.cloud"}`,
738
- cause: e
770
+ catch: (e) => e instanceof NetworkError ? e : new NetworkError({ message: "Invalid server URL" })
771
+ }),
772
+ Effect4.flatMap(
773
+ (baseUrl) => Effect4.tryPromise({
774
+ try: async () => {
775
+ const url = buildApiUrl(baseUrl, "/api/skills/export");
776
+ url.searchParams.set("profile", "default");
777
+ const response = await fetch(url.toString(), {
778
+ method: "GET",
779
+ headers: {
780
+ Authorization: `Bearer ${apiKey}`,
781
+ "Content-Type": "application/json"
782
+ }
783
+ });
784
+ return response.status !== 401;
785
+ },
786
+ catch: (e) => new NetworkError({
787
+ message: `Failed to connect to ${baseUrl}`,
788
+ cause: e
789
+ })
739
790
  })
740
- })
791
+ )
741
792
  );
742
- fetchSkillsAsync = (options) => Effect2.runPromise(fetchSkills(options));
743
- validateApiKeyAsync = (apiKey, serverUrl) => Effect2.runPromise(validateApiKey(apiKey, serverUrl));
744
- fetchScopeOptionsAsync = (options = {}) => Effect2.runPromise(fetchScopeOptions(options));
745
- fetchRuleOptionsAsync = (options = {}) => Effect2.runPromise(fetchRuleOptions(options));
746
- runLifecycleCommandAsync = (request, options = {}) => Effect2.runPromise(runLifecycleCommand(request, options));
793
+ fetchSkillsAsync = (options) => Effect4.runPromise(fetchSkills(options));
794
+ validateApiKeyAsync = (apiKey, serverUrl) => Effect4.runPromise(validateApiKey(apiKey, serverUrl));
795
+ fetchScopeOptionsAsync = (options = {}) => Effect4.runPromise(fetchScopeOptions(options));
796
+ fetchRuleOptionsAsync = (options = {}) => Effect4.runPromise(fetchRuleOptions(options));
797
+ runLifecycleCommandAsync = (request, options = {}) => Effect4.runPromise(runLifecycleCommand(request, options));
747
798
  }
748
799
  });
749
800
 
750
801
  // src/lib/tui.ts
751
- import process3 from "process";
802
+ import process4 from "process";
752
803
  import {
753
804
  cancel as clackCancel,
754
805
  confirm as clackConfirm,
@@ -773,19 +824,19 @@ function spinner() {
773
824
  return {
774
825
  start: (message) => {
775
826
  if (message) {
776
- process3.stdout.write(`${message}
827
+ process4.stdout.write(`${message}
777
828
  `);
778
829
  }
779
830
  },
780
831
  stop: (message) => {
781
832
  if (message) {
782
- process3.stdout.write(`${message}
833
+ process4.stdout.write(`${message}
783
834
  `);
784
835
  }
785
836
  },
786
837
  message: (message) => {
787
838
  if (message) {
788
- process3.stdout.write(`${message}
839
+ process4.stdout.write(`${message}
789
840
  `);
790
841
  }
791
842
  }
@@ -795,7 +846,7 @@ function intro(message) {
795
846
  if (isTTY) {
796
847
  clackIntro(message);
797
848
  } else {
798
- process3.stdout.write(`
849
+ process4.stdout.write(`
799
850
  ${message}
800
851
  `);
801
852
  }
@@ -804,7 +855,7 @@ function outro(message) {
804
855
  if (isTTY) {
805
856
  clackOutro(message);
806
857
  } else {
807
- process3.stdout.write(`${message}
858
+ process4.stdout.write(`${message}
808
859
 
809
860
  `);
810
861
  }
@@ -814,7 +865,7 @@ var init_tui = __esm({
814
865
  "src/lib/tui.ts"() {
815
866
  "use strict";
816
867
  init_esm_shims();
817
- isTTY = Boolean(process3.stdout.isTTY);
868
+ isTTY = Boolean(process4.stdout.isTTY);
818
869
  cancel = clackCancel;
819
870
  confirm = clackConfirm;
820
871
  isCancel = clackIsCancel;
@@ -925,10 +976,10 @@ var scope_exports = {};
925
976
  __export(scope_exports, {
926
977
  scopeCommand: () => scopeCommand
927
978
  });
928
- import process4 from "process";
979
+ import process5 from "process";
929
980
  function exitCancelled(message) {
930
981
  cancel(message);
931
- process4.exit(0);
982
+ process5.exit(0);
932
983
  }
933
984
  async function selectTargetFile(options) {
934
985
  if (options.file) {
@@ -1276,7 +1327,7 @@ async function scopeCommand(options) {
1276
1327
  } catch (error) {
1277
1328
  loadSpinner.stop("Failed to load scope options");
1278
1329
  log.error(error instanceof Error ? error.message : String(error));
1279
- process4.exit(1);
1330
+ process5.exit(1);
1280
1331
  }
1281
1332
  }
1282
1333
  var init_scope = __esm({
@@ -1296,303 +1347,256 @@ init_esm_shims();
1296
1347
  import { createRequire } from "module";
1297
1348
  import { Command } from "commander";
1298
1349
 
1299
- // src/commands/auth.ts
1350
+ // src/commands/agents.ts
1300
1351
  init_esm_shims();
1301
- init_api();
1302
- init_config();
1303
- init_tui();
1304
- import process5 from "process";
1305
- async function configureDefaultScopeAsync(serverUrl) {
1306
- const shouldConfigureScope = await confirm({
1307
- message: "Configure organization and scope defaults now?",
1308
- initialValue: true
1309
- });
1310
- if (isCancel(shouldConfigureScope) || !shouldConfigureScope) {
1311
- return;
1312
- }
1313
- const { scopeCommand: scopeCommand2 } = await Promise.resolve().then(() => (init_scope(), scope_exports));
1314
- const config = await loadMergedConfigAsync();
1315
- await scopeCommand2({
1316
- file: "user",
1317
- server: serverUrl,
1318
- ...config.token ? { apiKey: config.token } : {}
1319
- });
1320
- }
1321
- async function authCommand(options) {
1322
- intro("braid auth");
1323
- const config = await loadMergedConfigAsync();
1324
- if (config.token) {
1325
- const shouldReplace = await confirm({
1326
- message: "An API key is already configured. Replace it?",
1327
- initialValue: false
1328
- });
1329
- if (isCancel(shouldReplace) || !shouldReplace) {
1330
- outro("Auth cancelled.");
1331
- return;
1332
- }
1352
+
1353
+ // src/lib/agent-writer.ts
1354
+ init_esm_shims();
1355
+ import { mkdir, writeFile } from "fs/promises";
1356
+ import { dirname, resolve, sep } from "path";
1357
+ import { Data, Effect, pipe } from "effect";
1358
+
1359
+ // src/lib/agent-adapters.ts
1360
+ init_esm_shims();
1361
+ var NATIVE_AGENT_IDS = /* @__PURE__ */ new Set(["claude-code", "opencode"]);
1362
+ var MAPPED_AGENT_IDS = /* @__PURE__ */ new Set([
1363
+ "cursor",
1364
+ "windsurf",
1365
+ "cline",
1366
+ "roo",
1367
+ "codex"
1368
+ ]);
1369
+ var resolveProfile = (agent) => {
1370
+ if (NATIVE_AGENT_IDS.has(agent.id)) {
1371
+ return {
1372
+ tier: "native",
1373
+ supportsGlobalInstall: Boolean(agent.agentsGlobalPath),
1374
+ supportsProjectInstall: Boolean(agent.agentsProjectPath),
1375
+ notes: "Native subagent/frontmatter format supported."
1376
+ };
1333
1377
  }
1334
- const apiKey = options.token ? options.token : await password({
1335
- message: "Enter your braid API key:",
1336
- validate: (value) => {
1337
- if (!value) {
1338
- return "API key is required";
1339
- }
1340
- if (!value.startsWith("br_")) {
1341
- return "API key should start with 'br_'";
1342
- }
1343
- return void 0;
1344
- }
1345
- });
1346
- if (isCancel(apiKey)) {
1347
- cancel("Auth cancelled.");
1348
- process5.exit(0);
1378
+ if (MAPPED_AGENT_IDS.has(agent.id)) {
1379
+ return {
1380
+ tier: "mapped",
1381
+ supportsGlobalInstall: Boolean(agent.globalPath),
1382
+ supportsProjectInstall: Boolean(agent.projectPath),
1383
+ notes: "No native agent file format confirmed; install as compatibility artifacts."
1384
+ };
1349
1385
  }
1350
- const authSpinner = spinner();
1351
- let authSpinnerActive = true;
1352
- const stopAuthSpinner = (message) => {
1353
- if (!authSpinnerActive) {
1354
- return;
1355
- }
1356
- authSpinner.stop(message);
1357
- authSpinnerActive = false;
1386
+ return {
1387
+ tier: "compat",
1388
+ supportsGlobalInstall: Boolean(agent.globalPath),
1389
+ supportsProjectInstall: Boolean(agent.projectPath),
1390
+ notes: "Install compatibility output only, with capability warning surfaced to user."
1358
1391
  };
1359
- authSpinner.start("Validating API key...");
1360
- try {
1361
- const serverUrl = options.server ?? "https://braid.cloud";
1362
- const isValid = await validateApiKeyAsync(apiKey, serverUrl);
1363
- if (!isValid) {
1364
- stopAuthSpinner("Invalid API key");
1365
- log.error(
1366
- "The API key could not be validated. Please check your key and try again."
1367
- );
1368
- process5.exit(1);
1369
- }
1370
- await setApiKeyAsync(apiKey);
1371
- const existingUserConfig = await loadUserConfigAsync();
1372
- if (existingUserConfig?.token) {
1373
- await saveUserConfigAsync({ ...existingUserConfig, token: apiKey });
1392
+ };
1393
+
1394
+ // src/lib/agent-writer.ts
1395
+ var AgentWriteError = class extends Data.TaggedError("AgentWriteError") {
1396
+ };
1397
+ var isObjectRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1398
+ var slugify = (value) => value.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1399
+ var escapeYaml = (value) => value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1400
+ var toYamlLines = (key, value, indent = 0) => {
1401
+ const prefix = " ".repeat(indent);
1402
+ const nestedPrefix = " ".repeat(indent + 2);
1403
+ if (typeof value === "string") {
1404
+ return [`${prefix}${key}: "${escapeYaml(value)}"`];
1405
+ }
1406
+ if (typeof value === "number" || typeof value === "boolean") {
1407
+ return [`${prefix}${key}: ${value}`];
1408
+ }
1409
+ if (Array.isArray(value)) {
1410
+ if (value.length === 0) {
1411
+ return [`${prefix}${key}: []`];
1374
1412
  }
1375
- stopAuthSpinner("API key validated and saved");
1376
- if (options.scope !== false) {
1377
- await configureDefaultScopeAsync(serverUrl);
1413
+ return [
1414
+ `${prefix}${key}:`,
1415
+ ...value.map((item) => {
1416
+ if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
1417
+ return `${nestedPrefix}- ${typeof item === "string" ? `"${escapeYaml(item)}"` : item}`;
1418
+ }
1419
+ if (isObjectRecord(item)) {
1420
+ const nested = Object.entries(item).flatMap(
1421
+ ([nestedKey, nestedValue]) => toYamlLines(nestedKey, nestedValue, indent + 4)
1422
+ );
1423
+ return `${nestedPrefix}-
1424
+ ${nested.join("\n")}`;
1425
+ }
1426
+ return `${nestedPrefix}- null`;
1427
+ })
1428
+ ];
1429
+ }
1430
+ if (isObjectRecord(value)) {
1431
+ const entries = Object.entries(value);
1432
+ if (entries.length === 0) {
1433
+ return [`${prefix}${key}: {}`];
1378
1434
  }
1379
- log.success(`Config saved to ${CONFIG_FILE}`);
1380
- outro(
1381
- "You're authenticated! Run 'braid install --profile <name>' to install skills."
1382
- );
1383
- } catch (error) {
1384
- stopAuthSpinner("Validation failed");
1385
- const message = error instanceof Error ? error.message : String(error);
1386
- log.error(`Failed to validate API key: ${message}`);
1387
- process5.exit(1);
1435
+ return [
1436
+ `${prefix}${key}:`,
1437
+ ...entries.flatMap(
1438
+ ([nestedKey, nestedValue]) => toYamlLines(nestedKey, nestedValue, indent + 2)
1439
+ )
1440
+ ];
1388
1441
  }
1389
- }
1390
- async function displayTokenSource(masked) {
1391
- if (process5.env.BRAID_API_KEY) {
1392
- log.info(`Authenticated with key: ${masked}`);
1393
- log.info("Source: BRAID_API_KEY environment variable");
1394
- return;
1442
+ return [];
1443
+ };
1444
+ var buildClaudeMarkdown = (spec) => {
1445
+ const lines = ["---"];
1446
+ lines.push(...toYamlLines("name", spec.name));
1447
+ lines.push(...toYamlLines("description", spec.description));
1448
+ if (spec.model) {
1449
+ lines.push(...toYamlLines("model", spec.model));
1395
1450
  }
1396
- const userConfigPath = await findUserConfigFileAsync();
1397
- log.info(`Authenticated with key: ${masked}`);
1398
- log.info(`Source: ${userConfigPath ?? CONFIG_FILE}`);
1399
- }
1400
- async function displayProjectConfig() {
1401
- const projectConfigPath = await findProjectConfigFileAsync();
1402
- if (projectConfigPath) {
1403
- log.info(`Project config: ${projectConfigPath}`);
1451
+ if (spec.tools !== void 0) {
1452
+ lines.push(...toYamlLines("tools", spec.tools));
1404
1453
  }
1405
- }
1406
- function displayResolvedSettings(config) {
1407
- const hasOrgProjects = config.orgProjects && config.orgProjects.length > 0;
1408
- const hasPersonalProjects = config.personalProjects && config.personalProjects.length > 0;
1409
- if (!(config.profile || hasOrgProjects || hasPersonalProjects)) {
1410
- return;
1454
+ if (spec.permission !== void 0) {
1455
+ lines.push(...toYamlLines("permissionMode", spec.permission));
1411
1456
  }
1412
- log.info("");
1413
- if (config.profile) {
1414
- log.info(`Default profile: ${config.profile}`);
1457
+ if (spec.steps !== void 0) {
1458
+ lines.push(...toYamlLines("maxTurns", spec.steps));
1415
1459
  }
1416
- if (hasOrgProjects) {
1417
- log.info(`Org projects: ${config.orgProjects?.join(", ")}`);
1460
+ if (spec.temperature !== void 0) {
1461
+ lines.push(...toYamlLines("temperature", spec.temperature));
1418
1462
  }
1419
- if (hasPersonalProjects) {
1420
- log.info(`Personal projects: ${config.personalProjects?.join(", ")}`);
1463
+ if (spec.linkedSkillNames && spec.linkedSkillNames.length > 0) {
1464
+ lines.push(...toYamlLines("skills", spec.linkedSkillNames));
1421
1465
  }
1422
- if (config.serverUrl !== "https://braid.cloud") {
1423
- log.info(`Server: ${config.serverUrl}`);
1466
+ if (spec.additional) {
1467
+ for (const [key, value] of Object.entries(spec.additional)) {
1468
+ lines.push(...toYamlLines(key, value));
1469
+ }
1424
1470
  }
1425
- }
1426
- async function authStatusCommand() {
1427
- const config = await loadMergedConfigAsync();
1428
- if (!config.token) {
1429
- log.warn("Not authenticated. Run 'braid auth' to configure your API key.");
1430
- log.info(
1431
- `Or create a ${USER_CONFIG_FILENAME} file with: { "token": "br_xxx" }`
1432
- );
1433
- return;
1471
+ lines.push("---", "", spec.prompt.trim(), "");
1472
+ return lines.join("\n");
1473
+ };
1474
+ var buildOpenCodeMarkdown = (spec) => {
1475
+ const lines = ["---"];
1476
+ lines.push(...toYamlLines("description", spec.description));
1477
+ if (spec.mode) {
1478
+ lines.push(...toYamlLines("mode", spec.mode));
1434
1479
  }
1435
- const masked = `${config.token.slice(0, 7)}...${config.token.slice(-4)}`;
1436
- await displayTokenSource(masked);
1437
- await displayProjectConfig();
1438
- displayResolvedSettings(config);
1439
- }
1440
- async function authLogoutCommand() {
1441
- const { clearApiKeyAsync: clearApiKeyAsync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1442
- await clearApiKeyAsync2();
1443
- log.success("Logged out. API key removed from config.");
1444
- }
1445
-
1446
- // src/commands/discover.ts
1447
- init_esm_shims();
1448
- init_api();
1449
- init_config();
1450
- init_tui();
1451
- import process6 from "process";
1452
- var parseCsv = (input) => {
1453
- if (!input) {
1454
- return void 0;
1480
+ if (spec.model) {
1481
+ lines.push(...toYamlLines("model", spec.model));
1455
1482
  }
1456
- const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
1457
- return values.length > 0 ? values : void 0;
1458
- };
1459
- var writeJson = (value) => {
1460
- process6.stdout.write(`${JSON.stringify(value, null, 2)}
1461
- `);
1462
- };
1463
- var exitWithError = (error) => {
1464
- const message = error instanceof Error ? error.message : String(error);
1465
- log.error(message);
1466
- process6.exit(1);
1467
- };
1468
- var applyCommonOptions = (base, options) => ({
1469
- ...base,
1470
- ...options.server ? { serverUrl: options.server } : {},
1471
- ...options.apiKey ? { apiKey: options.apiKey } : {}
1472
- });
1473
- var applyOptionalString = (target, key, value) => {
1474
- if (value) {
1475
- target[key] = value;
1483
+ if (spec.tools !== void 0) {
1484
+ lines.push(...toYamlLines("tools", spec.tools));
1476
1485
  }
1477
- };
1478
- var applyOptionalArray = (target, key, value) => {
1479
- if (value && value.length > 0) {
1480
- target[key] = value;
1486
+ if (spec.permission !== void 0) {
1487
+ lines.push(...toYamlLines("permission", spec.permission));
1488
+ }
1489
+ if (spec.steps !== void 0) {
1490
+ lines.push(...toYamlLines("steps", spec.steps));
1481
1491
  }
1492
+ if (spec.temperature !== void 0) {
1493
+ lines.push(...toYamlLines("temperature", spec.temperature));
1494
+ }
1495
+ if (spec.topP !== void 0) {
1496
+ lines.push(...toYamlLines("top_p", spec.topP));
1497
+ }
1498
+ if (spec.hidden !== void 0) {
1499
+ lines.push(...toYamlLines("hidden", spec.hidden));
1500
+ }
1501
+ if (spec.color) {
1502
+ lines.push(...toYamlLines("color", spec.color));
1503
+ }
1504
+ if (spec.additional) {
1505
+ for (const [key, value] of Object.entries(spec.additional)) {
1506
+ lines.push(...toYamlLines(key, value));
1507
+ }
1508
+ }
1509
+ lines.push("---", "", spec.prompt.trim(), "");
1510
+ return lines.join("\n");
1482
1511
  };
1483
- var applyOptionalBoolean = (target, key, value) => {
1484
- if (value !== void 0) {
1485
- target[key] = value;
1512
+ var buildCompatMarkdown = (spec, source) => [
1513
+ "# Compatibility Agent",
1514
+ "",
1515
+ `Source: ${source}`,
1516
+ `Name: ${spec.name}`,
1517
+ "",
1518
+ spec.prompt.trim(),
1519
+ ""
1520
+ ].join("\n");
1521
+ var assertWithinBase = (basePath, filename) => {
1522
+ const resolvedBase = resolve(basePath);
1523
+ const resolvedPath = resolve(basePath, filename);
1524
+ if (resolvedPath !== resolvedBase && !resolvedPath.startsWith(`${resolvedBase}${sep}`)) {
1525
+ throw new Error(`Path traversal detected: ${filename}`);
1486
1526
  }
1527
+ return resolvedPath;
1487
1528
  };
1488
- async function projectsListCommand(options) {
1489
- try {
1490
- const result = await fetchScopeOptionsAsync(
1491
- applyCommonOptions({}, options)
1492
- );
1493
- if (options.json) {
1494
- writeJson(result);
1495
- return;
1529
+ var writeSingleAgent = (installPath, platform2, spec) => Effect.tryPromise({
1530
+ try: async () => {
1531
+ const profile = resolveProfile(platform2);
1532
+ const fileStem = slugify(spec.name) || "agent";
1533
+ const filename = `${fileStem}.md`;
1534
+ const fullPath = assertWithinBase(installPath, filename);
1535
+ await mkdir(dirname(fullPath), { recursive: true });
1536
+ const warnings = [];
1537
+ let content;
1538
+ if (platform2.id === "claude-code") {
1539
+ if (spec.mode === "primary") {
1540
+ warnings.push(
1541
+ "Claude adapter does not support primary mode; using subagent semantics."
1542
+ );
1543
+ }
1544
+ content = buildClaudeMarkdown(spec);
1545
+ } else if (platform2.id === "opencode") {
1546
+ content = buildOpenCodeMarkdown(spec);
1547
+ } else {
1548
+ warnings.push(
1549
+ `Platform ${platform2.id} uses ${profile.tier} adapter with compatibility output.`
1550
+ );
1551
+ content = buildCompatMarkdown(spec, platform2.name);
1496
1552
  }
1497
- log.info("Personal projects:");
1498
- if (result.personalProjects.length === 0) {
1499
- log.info(" (none)");
1500
- }
1501
- for (const project of result.personalProjects) {
1502
- log.info(` ${project.id} ${project.name}`);
1503
- }
1504
- for (const orgProjects of result.orgProjects) {
1505
- log.info(`
1506
- Organization: ${orgProjects.orgName} (${orgProjects.orgId})`);
1507
- if (orgProjects.projects.length === 0) {
1508
- log.info(" (none)");
1509
- continue;
1510
- }
1511
- for (const project of orgProjects.projects) {
1512
- log.info(` ${project.id} ${project.name}`);
1513
- }
1514
- }
1515
- } catch (error) {
1516
- exitWithError(error);
1517
- }
1518
- }
1519
- var buildRulesRequest = async (options) => {
1520
- const config = await loadMergedConfigAsync();
1521
- const orgProjects = parseCsv(options.orgProjects) ?? config.orgProjects;
1522
- const personalProjects = parseCsv(options.personalProjects) ?? config.personalProjects;
1523
- const request = applyCommonOptions({}, options);
1524
- applyOptionalString(request, "orgId", options.orgId ?? config.org);
1525
- applyOptionalArray(request, "orgProjects", orgProjects);
1526
- applyOptionalArray(request, "personalProjects", personalProjects);
1527
- applyOptionalBoolean(request, "includeUserGlobal", options.includeUserGlobal);
1528
- applyOptionalBoolean(request, "includeOrgGlobal", options.includeOrgGlobal);
1529
- return request;
1530
- };
1531
- async function rulesListCommand(options) {
1532
- try {
1533
- const result = await fetchRuleOptionsAsync(
1534
- await buildRulesRequest(options)
1535
- );
1536
- if (options.json) {
1537
- writeJson(result);
1538
- return;
1539
- }
1540
- if (result.rules.length === 0) {
1541
- log.info("No rules found.");
1542
- return;
1543
- }
1544
- for (const rule of result.rules) {
1545
- log.info(`${rule.id} ${rule.title}`);
1546
- }
1547
- } catch (error) {
1548
- exitWithError(error);
1549
- }
1550
- }
1551
- var buildSkillsRequest = async (options) => {
1552
- const config = await loadMergedConfigAsync();
1553
- const orgProjects = parseCsv(options.orgProjects) ?? config.orgProjects;
1554
- const personalProjects = parseCsv(options.personalProjects) ?? config.personalProjects;
1555
- const request = applyCommonOptions({}, options);
1556
- applyOptionalString(request, "profile", options.profile ?? config.profile);
1557
- applyOptionalArray(request, "orgProjects", orgProjects);
1558
- applyOptionalArray(request, "personalProjects", personalProjects);
1559
- applyOptionalBoolean(request, "includeUserGlobal", options.includeUserGlobal);
1560
- applyOptionalBoolean(request, "includeOrgGlobal", options.includeOrgGlobal);
1561
- return request;
1562
- };
1563
- async function skillsListCommand(options) {
1564
- try {
1565
- const result = await fetchSkillsAsync(await buildSkillsRequest(options));
1566
- if (options.json) {
1567
- writeJson(result);
1568
- return;
1569
- }
1570
- if (result.skills.length === 0) {
1571
- log.info("No skills found.");
1572
- return;
1573
- }
1574
- for (const skill of result.skills) {
1575
- log.info(`${skill.name}`);
1576
- }
1577
- } catch (error) {
1578
- exitWithError(error);
1579
- }
1580
- }
1581
-
1582
- // src/commands/install.ts
1583
- init_esm_shims();
1553
+ await writeFile(fullPath, content, "utf-8");
1554
+ return { written: fullPath, warnings };
1555
+ },
1556
+ catch: (cause) => new AgentWriteError({ path: installPath, operation: "write", cause })
1557
+ });
1558
+ var writeAgentsForPlatform = (platform2, specs, installPath) => pipe(
1559
+ Effect.forEach(
1560
+ specs,
1561
+ (spec) => pipe(
1562
+ writeSingleAgent(installPath, platform2, spec),
1563
+ Effect.map((result) => ({
1564
+ success: true,
1565
+ written: result.written,
1566
+ warnings: result.warnings
1567
+ })),
1568
+ Effect.catchAll(
1569
+ (error) => Effect.succeed({
1570
+ success: false,
1571
+ error: error.cause instanceof Error ? error.cause.message : String(error.cause),
1572
+ specName: spec.name
1573
+ })
1574
+ )
1575
+ ),
1576
+ { concurrency: "unbounded" }
1577
+ ),
1578
+ Effect.map((results) => {
1579
+ const written = results.filter((r) => r.success).map((r) => r.written).sort();
1580
+ const warnings = results.filter((r) => r.success).flatMap((r) => r.warnings);
1581
+ const errors = results.filter(
1582
+ (r) => !r.success
1583
+ ).map((r) => ({ agent: r.specName, error: r.error }));
1584
+ return { written, warnings, errors };
1585
+ })
1586
+ );
1587
+ var writeAgentsForPlatformAsync = (platform2, specs, installPath) => Effect.runPromise(writeAgentsForPlatform(platform2, specs, installPath));
1584
1588
 
1585
1589
  // src/lib/agents.ts
1586
1590
  init_esm_shims();
1587
1591
  import { access, constants } from "fs/promises";
1588
- import { homedir as homedir2 } from "os";
1589
- import { join as join2 } from "path";
1590
- import process7 from "process";
1591
- import { Effect as Effect3, pipe as pipe3 } from "effect";
1592
- var home = homedir2();
1592
+ import { homedir } from "os";
1593
+ import { join } from "path";
1594
+ import process2 from "process";
1595
+ import { Effect as Effect2, pipe as pipe2 } from "effect";
1596
+ var home = homedir();
1593
1597
  var vscodeExtSettingsPath = (extensionId, filename) => {
1594
- if (process7.platform === "darwin") {
1595
- return join2(
1598
+ if (process2.platform === "darwin") {
1599
+ return join(
1596
1600
  home,
1597
1601
  "Library",
1598
1602
  "Application Support",
@@ -1604,9 +1608,9 @@ var vscodeExtSettingsPath = (extensionId, filename) => {
1604
1608
  filename
1605
1609
  );
1606
1610
  }
1607
- if (process7.platform === "win32") {
1608
- const appData = process7.env.APPDATA ?? join2(home, "AppData", "Roaming");
1609
- return join2(
1611
+ if (process2.platform === "win32") {
1612
+ const appData = process2.env.APPDATA ?? join(home, "AppData", "Roaming");
1613
+ return join(
1610
1614
  appData,
1611
1615
  "Code",
1612
1616
  "User",
@@ -1616,7 +1620,7 @@ var vscodeExtSettingsPath = (extensionId, filename) => {
1616
1620
  filename
1617
1621
  );
1618
1622
  }
1619
- return join2(
1623
+ return join(
1620
1624
  home,
1621
1625
  ".config",
1622
1626
  "Code",
@@ -1628,8 +1632,8 @@ var vscodeExtSettingsPath = (extensionId, filename) => {
1628
1632
  );
1629
1633
  };
1630
1634
  var claudeDesktopConfigPath = () => {
1631
- if (process7.platform === "darwin") {
1632
- return join2(
1635
+ if (process2.platform === "darwin") {
1636
+ return join(
1633
1637
  home,
1634
1638
  "Library",
1635
1639
  "Application Support",
@@ -1637,46 +1641,48 @@ var claudeDesktopConfigPath = () => {
1637
1641
  "claude_desktop_config.json"
1638
1642
  );
1639
1643
  }
1640
- if (process7.platform === "win32") {
1641
- const appData = process7.env.APPDATA ?? join2(home, "AppData", "Roaming");
1642
- return join2(appData, "Claude", "claude_desktop_config.json");
1644
+ if (process2.platform === "win32") {
1645
+ const appData = process2.env.APPDATA ?? join(home, "AppData", "Roaming");
1646
+ return join(appData, "Claude", "claude_desktop_config.json");
1643
1647
  }
1644
- return join2(home, ".config", "Claude", "claude_desktop_config.json");
1648
+ return join(home, ".config", "Claude", "claude_desktop_config.json");
1645
1649
  };
1646
1650
  var AGENTS = [
1647
1651
  {
1648
1652
  id: "amp",
1649
1653
  name: "Amp",
1650
1654
  projectPath: ".agents/skills",
1651
- globalPath: join2(home, ".config", "agents", "skills"),
1655
+ globalPath: join(home, ".config", "agents", "skills"),
1652
1656
  mcpProjectConfigPath: ".amp/mcp.json",
1653
- mcpGlobalConfigPath: join2(home, ".amp", "mcp.json")
1657
+ mcpGlobalConfigPath: join(home, ".amp", "mcp.json")
1654
1658
  },
1655
1659
  {
1656
1660
  id: "kimi-cli",
1657
1661
  name: "Kimi Code CLI",
1658
1662
  projectPath: ".agents/skills",
1659
- globalPath: join2(home, ".config", "agents", "skills"),
1663
+ globalPath: join(home, ".config", "agents", "skills"),
1660
1664
  mcpProjectConfigPath: ".agents/mcp.json",
1661
- mcpGlobalConfigPath: join2(home, ".config", "agents", "mcp.json")
1665
+ mcpGlobalConfigPath: join(home, ".config", "agents", "mcp.json")
1662
1666
  },
1663
1667
  {
1664
1668
  id: "antigravity",
1665
1669
  name: "Antigravity",
1666
1670
  projectPath: ".agent/skills",
1667
- globalPath: join2(home, ".gemini", "antigravity", "global_skills"),
1671
+ globalPath: join(home, ".gemini", "antigravity", "global_skills"),
1668
1672
  mcpProjectConfigPath: ".agent/mcp.json",
1669
- mcpGlobalConfigPath: join2(home, ".gemini", "antigravity", "mcp.json")
1673
+ mcpGlobalConfigPath: join(home, ".gemini", "antigravity", "mcp.json")
1670
1674
  },
1671
1675
  {
1672
1676
  id: "claude-code",
1673
1677
  name: "Claude Code",
1674
1678
  projectPath: ".claude/skills",
1675
- globalPath: join2(home, ".claude", "skills"),
1679
+ globalPath: join(home, ".claude", "skills"),
1680
+ agentsProjectPath: ".claude/agents",
1681
+ agentsGlobalPath: join(home, ".claude", "agents"),
1676
1682
  projectMarkerPath: ".claude",
1677
- globalMarkerPath: join2(home, ".claude"),
1683
+ globalMarkerPath: join(home, ".claude"),
1678
1684
  rulesProjectPath: ".claude/rules",
1679
- rulesGlobalPath: join2(home, ".claude", "rules"),
1685
+ rulesGlobalPath: join(home, ".claude", "rules"),
1680
1686
  ruleFormat: "markdown-dir",
1681
1687
  mcpProjectConfigPath: ".mcp.json",
1682
1688
  mcpEntryStyle: "typed-stdio"
@@ -1692,15 +1698,15 @@ var AGENTS = [
1692
1698
  id: "moltbot",
1693
1699
  name: "Moltbot",
1694
1700
  projectPath: "skills",
1695
- globalPath: join2(home, ".moltbot", "skills"),
1701
+ globalPath: join(home, ".moltbot", "skills"),
1696
1702
  mcpProjectConfigPath: "mcp.json",
1697
- mcpGlobalConfigPath: join2(home, ".moltbot", "mcp.json")
1703
+ mcpGlobalConfigPath: join(home, ".moltbot", "mcp.json")
1698
1704
  },
1699
1705
  {
1700
1706
  id: "cline",
1701
1707
  name: "Cline",
1702
1708
  projectPath: ".cline/skills",
1703
- globalPath: join2(home, ".cline", "skills"),
1709
+ globalPath: join(home, ".cline", "skills"),
1704
1710
  rulesProjectPath: ".clinerules",
1705
1711
  ruleFormat: "append-single",
1706
1712
  mcpGlobalConfigPath: vscodeExtSettingsPath(
@@ -1713,73 +1719,73 @@ var AGENTS = [
1713
1719
  id: "codebuddy",
1714
1720
  name: "CodeBuddy",
1715
1721
  projectPath: ".codebuddy/skills",
1716
- globalPath: join2(home, ".codebuddy", "skills"),
1722
+ globalPath: join(home, ".codebuddy", "skills"),
1717
1723
  mcpProjectConfigPath: ".codebuddy/mcp.json",
1718
- mcpGlobalConfigPath: join2(home, ".codebuddy", "mcp.json")
1724
+ mcpGlobalConfigPath: join(home, ".codebuddy", "mcp.json")
1719
1725
  },
1720
1726
  {
1721
1727
  id: "codex",
1722
1728
  name: "Codex",
1723
1729
  projectPath: ".codex/skills",
1724
- globalPath: join2(home, ".codex", "skills"),
1730
+ globalPath: join(home, ".codex", "skills"),
1725
1731
  mcpProjectConfigPath: ".codex/mcp.json",
1726
- mcpGlobalConfigPath: join2(home, ".codex", "mcp.json")
1732
+ mcpGlobalConfigPath: join(home, ".codex", "mcp.json")
1727
1733
  },
1728
1734
  {
1729
1735
  id: "command-code",
1730
1736
  name: "Command Code",
1731
1737
  projectPath: ".commandcode/skills",
1732
- globalPath: join2(home, ".commandcode", "skills"),
1738
+ globalPath: join(home, ".commandcode", "skills"),
1733
1739
  mcpProjectConfigPath: ".commandcode/mcp.json",
1734
- mcpGlobalConfigPath: join2(home, ".commandcode", "mcp.json")
1740
+ mcpGlobalConfigPath: join(home, ".commandcode", "mcp.json")
1735
1741
  },
1736
1742
  {
1737
1743
  id: "continue",
1738
1744
  name: "Continue",
1739
1745
  projectPath: ".continue/skills",
1740
- globalPath: join2(home, ".continue", "skills"),
1746
+ globalPath: join(home, ".continue", "skills"),
1741
1747
  mcpProjectConfigPath: ".continue/mcp.json",
1742
- mcpGlobalConfigPath: join2(home, ".continue", "mcp.json")
1748
+ mcpGlobalConfigPath: join(home, ".continue", "mcp.json")
1743
1749
  },
1744
1750
  {
1745
1751
  id: "crush",
1746
1752
  name: "Crush",
1747
1753
  projectPath: ".crush/skills",
1748
- globalPath: join2(home, ".config", "crush", "skills"),
1754
+ globalPath: join(home, ".config", "crush", "skills"),
1749
1755
  mcpProjectConfigPath: ".crush/mcp.json",
1750
- mcpGlobalConfigPath: join2(home, ".config", "crush", "mcp.json")
1756
+ mcpGlobalConfigPath: join(home, ".config", "crush", "mcp.json")
1751
1757
  },
1752
1758
  {
1753
1759
  id: "cursor",
1754
1760
  name: "Cursor",
1755
1761
  projectPath: ".cursor/skills",
1756
- globalPath: join2(home, ".cursor", "skills"),
1762
+ globalPath: join(home, ".cursor", "skills"),
1757
1763
  rulesProjectPath: ".cursor/rules",
1758
1764
  ruleFormat: "mdc",
1759
1765
  mcpProjectConfigPath: ".cursor/mcp.json",
1760
- mcpGlobalConfigPath: join2(home, ".cursor", "mcp.json")
1766
+ mcpGlobalConfigPath: join(home, ".cursor", "mcp.json")
1761
1767
  },
1762
1768
  {
1763
1769
  id: "droid",
1764
1770
  name: "Droid",
1765
1771
  projectPath: ".factory/skills",
1766
- globalPath: join2(home, ".factory", "skills"),
1772
+ globalPath: join(home, ".factory", "skills"),
1767
1773
  mcpProjectConfigPath: ".factory/mcp.json",
1768
- mcpGlobalConfigPath: join2(home, ".factory", "mcp.json")
1774
+ mcpGlobalConfigPath: join(home, ".factory", "mcp.json")
1769
1775
  },
1770
1776
  {
1771
1777
  id: "gemini-cli",
1772
1778
  name: "Gemini CLI",
1773
1779
  projectPath: ".gemini/skills",
1774
- globalPath: join2(home, ".gemini", "skills"),
1780
+ globalPath: join(home, ".gemini", "skills"),
1775
1781
  mcpProjectConfigPath: ".gemini/mcp.json",
1776
- mcpGlobalConfigPath: join2(home, ".gemini", "mcp.json")
1782
+ mcpGlobalConfigPath: join(home, ".gemini", "mcp.json")
1777
1783
  },
1778
1784
  {
1779
1785
  id: "github-copilot",
1780
1786
  name: "GitHub Copilot",
1781
1787
  projectPath: ".github/skills",
1782
- globalPath: join2(home, ".copilot", "skills"),
1788
+ globalPath: join(home, ".copilot", "skills"),
1783
1789
  rulesProjectPath: ".github/copilot-instructions.md",
1784
1790
  ruleFormat: "append-single",
1785
1791
  mcpProjectConfigPath: ".vscode/mcp.json",
@@ -1790,22 +1796,22 @@ var AGENTS = [
1790
1796
  id: "goose",
1791
1797
  name: "Goose",
1792
1798
  projectPath: ".goose/skills",
1793
- globalPath: join2(home, ".config", "goose", "skills"),
1794
- mcpGlobalConfigPath: join2(home, ".config", "goose", "mcp.json")
1799
+ globalPath: join(home, ".config", "goose", "skills"),
1800
+ mcpGlobalConfigPath: join(home, ".config", "goose", "mcp.json")
1795
1801
  },
1796
1802
  {
1797
1803
  id: "junie",
1798
1804
  name: "Junie",
1799
1805
  projectPath: ".junie/skills",
1800
- globalPath: join2(home, ".junie", "skills"),
1806
+ globalPath: join(home, ".junie", "skills"),
1801
1807
  mcpProjectConfigPath: ".junie/mcp.json",
1802
- mcpGlobalConfigPath: join2(home, ".junie", "mcp.json")
1808
+ mcpGlobalConfigPath: join(home, ".junie", "mcp.json")
1803
1809
  },
1804
1810
  {
1805
1811
  id: "kilo",
1806
1812
  name: "Kilo Code",
1807
1813
  projectPath: ".kilocode/skills",
1808
- globalPath: join2(home, ".kilocode", "skills"),
1814
+ globalPath: join(home, ".kilocode", "skills"),
1809
1815
  mcpGlobalConfigPath: vscodeExtSettingsPath(
1810
1816
  "kilocode.kilo-code",
1811
1817
  "mcp_settings.json"
@@ -1816,81 +1822,83 @@ var AGENTS = [
1816
1822
  id: "kiro-cli",
1817
1823
  name: "Kiro CLI",
1818
1824
  projectPath: ".kiro/skills",
1819
- globalPath: join2(home, ".kiro", "skills"),
1825
+ globalPath: join(home, ".kiro", "skills"),
1820
1826
  mcpProjectConfigPath: ".kiro/mcp.json",
1821
- mcpGlobalConfigPath: join2(home, ".kiro", "mcp.json")
1827
+ mcpGlobalConfigPath: join(home, ".kiro", "mcp.json")
1822
1828
  },
1823
1829
  {
1824
1830
  id: "kode",
1825
1831
  name: "Kode",
1826
1832
  projectPath: ".kode/skills",
1827
- globalPath: join2(home, ".kode", "skills"),
1833
+ globalPath: join(home, ".kode", "skills"),
1828
1834
  mcpProjectConfigPath: ".kode/mcp.json",
1829
- mcpGlobalConfigPath: join2(home, ".kode", "mcp.json")
1835
+ mcpGlobalConfigPath: join(home, ".kode", "mcp.json")
1830
1836
  },
1831
1837
  {
1832
1838
  id: "mcpjam",
1833
1839
  name: "MCPJam",
1834
1840
  projectPath: ".mcpjam/skills",
1835
- globalPath: join2(home, ".mcpjam", "skills"),
1841
+ globalPath: join(home, ".mcpjam", "skills"),
1836
1842
  mcpProjectConfigPath: ".mcpjam/mcp.json",
1837
- mcpGlobalConfigPath: join2(home, ".mcpjam", "mcp.json")
1843
+ mcpGlobalConfigPath: join(home, ".mcpjam", "mcp.json")
1838
1844
  },
1839
1845
  {
1840
1846
  id: "mux",
1841
1847
  name: "Mux",
1842
1848
  projectPath: ".mux/skills",
1843
- globalPath: join2(home, ".mux", "skills"),
1849
+ globalPath: join(home, ".mux", "skills"),
1844
1850
  mcpProjectConfigPath: ".mux/mcp.json",
1845
- mcpGlobalConfigPath: join2(home, ".mux", "mcp.json")
1851
+ mcpGlobalConfigPath: join(home, ".mux", "mcp.json")
1846
1852
  },
1847
1853
  {
1848
1854
  id: "opencode",
1849
1855
  name: "OpenCode",
1850
1856
  projectPath: ".opencode/skills",
1851
- globalPath: join2(home, ".config", "opencode", "skills"),
1857
+ globalPath: join(home, ".config", "opencode", "skills"),
1858
+ agentsProjectPath: ".opencode/agents",
1859
+ agentsGlobalPath: join(home, ".config", "opencode", "agents"),
1852
1860
  mcpProjectConfigPath: ".opencode/mcp.json",
1853
- mcpGlobalConfigPath: join2(home, ".config", "opencode", "mcp.json")
1861
+ mcpGlobalConfigPath: join(home, ".config", "opencode", "mcp.json")
1854
1862
  },
1855
1863
  {
1856
1864
  id: "openhands",
1857
1865
  name: "OpenHands",
1858
1866
  projectPath: ".openhands/skills",
1859
- globalPath: join2(home, ".openhands", "skills"),
1867
+ globalPath: join(home, ".openhands", "skills"),
1860
1868
  mcpProjectConfigPath: ".openhands/mcp.json",
1861
- mcpGlobalConfigPath: join2(home, ".openhands", "mcp.json")
1869
+ mcpGlobalConfigPath: join(home, ".openhands", "mcp.json")
1862
1870
  },
1863
1871
  {
1864
1872
  id: "pi",
1865
1873
  name: "Pi",
1866
1874
  projectPath: ".pi/skills",
1867
- globalPath: join2(home, ".pi", "agent", "skills"),
1875
+ globalPath: join(home, ".pi", "agent", "skills"),
1868
1876
  mcpProjectConfigPath: ".pi/mcp.json",
1869
- mcpGlobalConfigPath: join2(home, ".pi", "agent", "mcp.json")
1877
+ mcpGlobalConfigPath: join(home, ".pi", "agent", "mcp.json")
1870
1878
  },
1871
1879
  {
1872
1880
  id: "qoder",
1873
1881
  name: "Qoder",
1874
1882
  projectPath: ".qoder/skills",
1875
- globalPath: join2(home, ".qoder", "skills"),
1883
+ globalPath: join(home, ".qoder", "skills"),
1876
1884
  mcpProjectConfigPath: ".qoder/mcp.json",
1877
- mcpGlobalConfigPath: join2(home, ".qoder", "mcp.json")
1885
+ mcpGlobalConfigPath: join(home, ".qoder", "mcp.json")
1878
1886
  },
1879
1887
  {
1880
1888
  id: "qwen-code",
1881
1889
  name: "Qwen Code",
1882
1890
  projectPath: ".qwen/skills",
1883
- globalPath: join2(home, ".qwen", "skills"),
1891
+ globalPath: join(home, ".qwen", "skills"),
1884
1892
  mcpProjectConfigPath: ".qwen/mcp.json",
1885
- mcpGlobalConfigPath: join2(home, ".qwen", "mcp.json")
1893
+ mcpGlobalConfigPath: join(home, ".qwen", "mcp.json")
1886
1894
  },
1887
1895
  {
1888
1896
  id: "roo",
1889
1897
  name: "Roo Code",
1890
1898
  projectPath: ".roo/skills",
1891
- globalPath: join2(home, ".roo", "skills"),
1899
+ globalPath: join(home, ".roo", "skills"),
1892
1900
  rulesProjectPath: ".roo/rules",
1893
- rulesGlobalPath: join2(home, ".roo", "rules"),
1901
+ rulesGlobalPath: join(home, ".roo", "rules"),
1894
1902
  ruleFormat: "markdown-dir",
1895
1903
  mcpGlobalConfigPath: vscodeExtSettingsPath(
1896
1904
  "rooveterinaryinc.roo-cline",
@@ -1902,42 +1910,42 @@ var AGENTS = [
1902
1910
  id: "trae",
1903
1911
  name: "Trae",
1904
1912
  projectPath: ".trae/skills",
1905
- globalPath: join2(home, ".trae", "skills"),
1913
+ globalPath: join(home, ".trae", "skills"),
1906
1914
  mcpProjectConfigPath: ".trae/mcp.json",
1907
- mcpGlobalConfigPath: join2(home, ".trae", "mcp.json")
1915
+ mcpGlobalConfigPath: join(home, ".trae", "mcp.json")
1908
1916
  },
1909
1917
  {
1910
1918
  id: "windsurf",
1911
1919
  name: "Windsurf",
1912
1920
  projectPath: ".windsurf/skills",
1913
- globalPath: join2(home, ".codeium", "windsurf", "skills"),
1921
+ globalPath: join(home, ".codeium", "windsurf", "skills"),
1914
1922
  rulesProjectPath: ".windsurfrules",
1915
1923
  ruleFormat: "append-single",
1916
- mcpGlobalConfigPath: join2(home, ".codeium", "windsurf", "mcp_config.json")
1924
+ mcpGlobalConfigPath: join(home, ".codeium", "windsurf", "mcp_config.json")
1917
1925
  },
1918
1926
  {
1919
1927
  id: "zencoder",
1920
1928
  name: "Zencoder",
1921
1929
  projectPath: ".zencoder/skills",
1922
- globalPath: join2(home, ".zencoder", "skills"),
1930
+ globalPath: join(home, ".zencoder", "skills"),
1923
1931
  mcpProjectConfigPath: ".zencoder/mcp.json",
1924
- mcpGlobalConfigPath: join2(home, ".zencoder", "mcp.json")
1932
+ mcpGlobalConfigPath: join(home, ".zencoder", "mcp.json")
1925
1933
  },
1926
1934
  {
1927
1935
  id: "neovate",
1928
1936
  name: "Neovate",
1929
1937
  projectPath: ".neovate/skills",
1930
- globalPath: join2(home, ".neovate", "skills"),
1938
+ globalPath: join(home, ".neovate", "skills"),
1931
1939
  mcpProjectConfigPath: ".neovate/mcp.json",
1932
- mcpGlobalConfigPath: join2(home, ".neovate", "mcp.json")
1940
+ mcpGlobalConfigPath: join(home, ".neovate", "mcp.json")
1933
1941
  },
1934
1942
  {
1935
1943
  id: "pochi",
1936
1944
  name: "Pochi",
1937
1945
  projectPath: ".pochi/skills",
1938
- globalPath: join2(home, ".pochi", "skills"),
1946
+ globalPath: join(home, ".pochi", "skills"),
1939
1947
  mcpProjectConfigPath: ".pochi/mcp.json",
1940
- mcpGlobalConfigPath: join2(home, ".pochi", "mcp.json")
1948
+ mcpGlobalConfigPath: join(home, ".pochi", "mcp.json")
1941
1949
  },
1942
1950
  {
1943
1951
  id: "zed",
@@ -1945,38 +1953,38 @@ var AGENTS = [
1945
1953
  projectPath: "",
1946
1954
  globalPath: "",
1947
1955
  projectMarkerPath: ".zed",
1948
- globalMarkerPath: join2(home, ".config", "zed"),
1956
+ globalMarkerPath: join(home, ".config", "zed"),
1949
1957
  rulesProjectPath: ".zed/rules",
1950
- rulesGlobalPath: join2(home, ".config", "zed", "rules"),
1958
+ rulesGlobalPath: join(home, ".config", "zed", "rules"),
1951
1959
  ruleFormat: "markdown-dir",
1952
- mcpGlobalConfigPath: join2(home, ".config", "zed", "settings.json"),
1960
+ mcpGlobalConfigPath: join(home, ".config", "zed", "settings.json"),
1953
1961
  mcpRootKey: "context_servers",
1954
1962
  mcpEntryStyle: "zed"
1955
1963
  }
1956
1964
  ];
1957
- var directoryExists = (path2) => pipe3(
1958
- Effect3.tryPromise({
1965
+ var directoryExists = (path2) => pipe2(
1966
+ Effect2.tryPromise({
1959
1967
  try: () => access(path2, constants.F_OK),
1960
1968
  catch: () => false
1961
1969
  }),
1962
- Effect3.map(() => true),
1963
- Effect3.orElseSucceed(() => false)
1970
+ Effect2.map(() => true),
1971
+ Effect2.orElseSucceed(() => false)
1964
1972
  );
1965
1973
  var detectAgents = (projectRoot) => {
1966
- const cwd = projectRoot ?? process7.cwd();
1967
- return pipe3(
1968
- Effect3.forEach(
1974
+ const cwd = projectRoot ?? process2.cwd();
1975
+ return pipe2(
1976
+ Effect2.forEach(
1969
1977
  AGENTS,
1970
- (agent) => pipe3(
1971
- Effect3.all({
1972
- hasProjectConfig: agent.projectPath ? directoryExists(join2(cwd, agent.projectPath)) : Effect3.succeed(false),
1973
- hasGlobalConfig: agent.globalPath ? directoryExists(agent.globalPath) : Effect3.succeed(false),
1974
- hasProjectMarker: agent.projectMarkerPath ? directoryExists(join2(cwd, agent.projectMarkerPath)) : Effect3.succeed(false),
1975
- hasGlobalMarker: agent.globalMarkerPath ? directoryExists(agent.globalMarkerPath) : Effect3.succeed(false),
1976
- hasRulesProjectConfig: agent.rulesProjectPath ? directoryExists(join2(cwd, agent.rulesProjectPath)) : Effect3.succeed(false),
1977
- hasRulesGlobalConfig: agent.rulesGlobalPath ? directoryExists(agent.rulesGlobalPath) : Effect3.succeed(false)
1978
+ (agent) => pipe2(
1979
+ Effect2.all({
1980
+ hasProjectConfig: agent.projectPath ? directoryExists(join(cwd, agent.projectPath)) : Effect2.succeed(false),
1981
+ hasGlobalConfig: agent.globalPath ? directoryExists(agent.globalPath) : Effect2.succeed(false),
1982
+ hasProjectMarker: agent.projectMarkerPath ? directoryExists(join(cwd, agent.projectMarkerPath)) : Effect2.succeed(false),
1983
+ hasGlobalMarker: agent.globalMarkerPath ? directoryExists(agent.globalMarkerPath) : Effect2.succeed(false),
1984
+ hasRulesProjectConfig: agent.rulesProjectPath ? directoryExists(join(cwd, agent.rulesProjectPath)) : Effect2.succeed(false),
1985
+ hasRulesGlobalConfig: agent.rulesGlobalPath ? directoryExists(agent.rulesGlobalPath) : Effect2.succeed(false)
1978
1986
  }),
1979
- Effect3.map(
1987
+ Effect2.map(
1980
1988
  ({
1981
1989
  hasProjectConfig,
1982
1990
  hasGlobalConfig,
@@ -1993,14 +2001,14 @@ var detectAgents = (projectRoot) => {
1993
2001
  ),
1994
2002
  { concurrency: "unbounded" }
1995
2003
  ),
1996
- Effect3.map(
1997
- (agents) => agents.filter((a) => a.hasProjectConfig || a.hasGlobalConfig)
2004
+ Effect2.map(
2005
+ (agents2) => agents2.filter((a) => a.hasProjectConfig || a.hasGlobalConfig)
1998
2006
  )
1999
2007
  );
2000
2008
  };
2001
2009
  var getAgentById = (id) => AGENTS.find((a) => a.id === id);
2002
- var detectAgentsAsync = (projectRoot) => Effect3.runPromise(detectAgents(projectRoot));
2003
- var directoryExistsAsync = (path2) => Effect3.runPromise(directoryExists(path2));
2010
+ var detectAgentsAsync = (projectRoot) => Effect2.runPromise(detectAgents(projectRoot));
2011
+ var directoryExistsAsync = (path2) => Effect2.runPromise(directoryExists(path2));
2004
2012
  var resolveInstallPath = (agent, options) => {
2005
2013
  if (options.global) {
2006
2014
  return agent.globalPath || void 0;
@@ -2008,8 +2016,18 @@ var resolveInstallPath = (agent, options) => {
2008
2016
  if (!agent.projectPath) {
2009
2017
  return void 0;
2010
2018
  }
2011
- const cwd = options.projectRoot ?? process7.cwd();
2012
- return join2(cwd, agent.projectPath);
2019
+ const cwd = options.projectRoot ?? process2.cwd();
2020
+ return join(cwd, agent.projectPath);
2021
+ };
2022
+ var resolveAgentsInstallPath = (agent, options) => {
2023
+ if (options.global) {
2024
+ return agent.agentsGlobalPath;
2025
+ }
2026
+ if (!agent.agentsProjectPath) {
2027
+ return void 0;
2028
+ }
2029
+ const cwd = options.projectRoot ?? process2.cwd();
2030
+ return join(cwd, agent.agentsProjectPath);
2013
2031
  };
2014
2032
  var resolveRulesInstallPath = (agent, options) => {
2015
2033
  if (options.global) {
@@ -2018,85 +2036,920 @@ var resolveRulesInstallPath = (agent, options) => {
2018
2036
  if (!agent.rulesProjectPath) {
2019
2037
  return void 0;
2020
2038
  }
2021
- const cwd = options.projectRoot ?? process7.cwd();
2022
- return join2(cwd, agent.rulesProjectPath);
2039
+ const cwd = options.projectRoot ?? process2.cwd();
2040
+ return join(cwd, agent.rulesProjectPath);
2023
2041
  };
2024
2042
  var hasMcpConfig = (agent) => Boolean(agent.mcpProjectConfigPath) || Boolean(agent.mcpGlobalConfigPath);
2043
+ var MCP_PACKAGE_SPEC = "@braid-cloud/mcp@0.1.11";
2025
2044
  var resolveMcpConfigPath = (agent, options) => {
2026
2045
  if (options.global) {
2027
2046
  return agent.mcpGlobalConfigPath;
2028
2047
  }
2029
2048
  if (agent.mcpProjectConfigPath) {
2030
- const cwd = options.projectRoot ?? process7.cwd();
2031
- return join2(cwd, agent.mcpProjectConfigPath);
2049
+ const cwd = options.projectRoot ?? process2.cwd();
2050
+ return join(cwd, agent.mcpProjectConfigPath);
2051
+ }
2052
+ return agent.mcpGlobalConfigPath;
2053
+ };
2054
+ var buildMcpEntry = (style, env) => {
2055
+ const command = "npx";
2056
+ const args = ["-y", MCP_PACKAGE_SPEC];
2057
+ const envObj = Object.keys(env).length > 0 ? env : void 0;
2058
+ switch (style) {
2059
+ case "standard":
2060
+ return envObj ? { command, args, env: envObj } : { command, args };
2061
+ case "typed-stdio":
2062
+ return envObj ? { type: "stdio", command, args, env: envObj } : { type: "stdio", command, args };
2063
+ case "zed":
2064
+ return envObj ? { command: { path: command, args, env: envObj } } : { command: { path: command, args } };
2065
+ case "cline":
2066
+ return envObj ? { command, args, env: envObj, disabled: false } : { command, args, disabled: false };
2067
+ default:
2068
+ return envObj ? { command, args, env: envObj } : { command, args };
2069
+ }
2070
+ };
2071
+ var detectMcpAgents = (projectRoot) => Effect2.runPromise(
2072
+ pipe2(
2073
+ detectAgents(projectRoot),
2074
+ Effect2.map((detected) => detected.filter(hasMcpConfig))
2075
+ )
2076
+ );
2077
+
2078
+ // src/commands/agents.ts
2079
+ init_api();
2080
+ init_tui();
2081
+ var parseCsv = (input) => {
2082
+ if (!input) {
2083
+ return void 0;
2084
+ }
2085
+ const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
2086
+ return values.length > 0 ? values : void 0;
2087
+ };
2088
+ var writeJson = (value) => {
2089
+ process.stdout.write(`${JSON.stringify(value, null, 2)}
2090
+ `);
2091
+ };
2092
+ var fail = (message) => {
2093
+ throw new Error(message);
2094
+ };
2095
+ var exitWithError = (error) => {
2096
+ const message = error instanceof Error ? error.message : String(error);
2097
+ log.error(message);
2098
+ process.exit(1);
2099
+ };
2100
+ var run = (command, args, options) => {
2101
+ const apiOptions = {
2102
+ ...options.server ? { serverUrl: options.server } : {},
2103
+ ...options.apiKey ? { apiKey: options.apiKey } : {}
2104
+ };
2105
+ return runLifecycleCommandAsync(
2106
+ {
2107
+ domain: "agents",
2108
+ command,
2109
+ args
2110
+ },
2111
+ apiOptions
2112
+ );
2113
+ };
2114
+ var getAgentCandidate = (value) => {
2115
+ if (typeof value === "object" && value !== null && "agent" in value) {
2116
+ return value.agent;
2117
+ }
2118
+ return value;
2119
+ };
2120
+ var getMode = (mode) => {
2121
+ if (mode === "primary" || mode === "subagent" || mode === "all") {
2122
+ return mode;
2123
+ }
2124
+ return void 0;
2125
+ };
2126
+ var getRawSkills = (agent) => {
2127
+ if (Array.isArray(agent.skillNames)) {
2128
+ return agent.skillNames;
2129
+ }
2130
+ if (Array.isArray(agent.skills)) {
2131
+ return agent.skills;
2132
+ }
2133
+ return void 0;
2134
+ };
2135
+ var normalizeSpec = (value) => {
2136
+ const candidate = getAgentCandidate(value);
2137
+ if (typeof candidate !== "object" || candidate === null) {
2138
+ throw new Error("Invalid agent payload from API");
2139
+ }
2140
+ const agent = candidate;
2141
+ const name = typeof agent.name === "string" ? agent.name : void 0;
2142
+ const description = typeof agent.description === "string" ? agent.description : void 0;
2143
+ const promptFromPrompt = typeof agent.prompt === "string" ? agent.prompt : void 0;
2144
+ const promptFromContent = typeof agent.content === "string" ? agent.content : void 0;
2145
+ const prompt = promptFromPrompt ?? promptFromContent;
2146
+ if (!(name && description && prompt)) {
2147
+ throw new Error(
2148
+ "Agent payload missing required fields: name, description, prompt"
2149
+ );
2150
+ }
2151
+ const result = {
2152
+ name,
2153
+ description,
2154
+ prompt
2155
+ };
2156
+ const mode = getMode(agent.mode);
2157
+ if (mode) {
2158
+ result.mode = mode;
2159
+ }
2160
+ if (typeof agent.model === "string") {
2161
+ result.model = agent.model;
2162
+ }
2163
+ const rawSkills = getRawSkills(agent);
2164
+ if (rawSkills) {
2165
+ result.linkedSkillNames = rawSkills.filter(
2166
+ (item) => typeof item === "string"
2167
+ );
2168
+ }
2169
+ return result;
2170
+ };
2171
+ var resolveInstallTargets = async (options) => {
2172
+ if (options.agents) {
2173
+ const ids = parseCsv(options.agents) ?? [];
2174
+ const selected = ids.map((id) => getAgentById(id)).filter(
2175
+ (agent) => agent !== void 0
2176
+ );
2177
+ if (selected.length === 0) {
2178
+ fail("No valid target agents selected");
2179
+ }
2180
+ return selected;
2181
+ }
2182
+ const detected = await detectAgentsAsync();
2183
+ const filtered = detected.filter(
2184
+ (agent) => options.global ? agent.hasGlobalConfig : agent.hasProjectConfig
2185
+ );
2186
+ if (filtered.length === 0) {
2187
+ fail(
2188
+ "No supported local agent installations detected. Use --agents to select targets."
2189
+ );
2190
+ }
2191
+ return filtered;
2192
+ };
2193
+ async function agentsListCommand(options) {
2194
+ try {
2195
+ const result = await run("list", {}, options);
2196
+ if (options.json) {
2197
+ writeJson(result);
2198
+ return;
2199
+ }
2200
+ log.success("agents list completed");
2201
+ writeJson(result);
2202
+ } catch (error) {
2203
+ exitWithError(error);
2204
+ }
2205
+ }
2206
+ async function agentsGetCommand(options) {
2207
+ try {
2208
+ const id = options.id ?? fail("agents get requires --id");
2209
+ const result = await run("get", { id }, options);
2210
+ if (options.json) {
2211
+ writeJson(result);
2212
+ return;
2213
+ }
2214
+ log.success("agents get completed");
2215
+ writeJson(result);
2216
+ } catch (error) {
2217
+ exitWithError(error);
2218
+ }
2219
+ }
2220
+ async function agentsCreateCommand(options) {
2221
+ try {
2222
+ const name = options.name ?? fail("agents create requires --name");
2223
+ const description = options.description ?? fail("agents create requires --description");
2224
+ const prompt = options.prompt ?? fail("agents create requires --prompt");
2225
+ const result = await run(
2226
+ "create",
2227
+ {
2228
+ name,
2229
+ description,
2230
+ prompt,
2231
+ scope: options.scope,
2232
+ projectId: options.projectId,
2233
+ model: options.model,
2234
+ mode: options.mode,
2235
+ skills: parseCsv(options.skills)
2236
+ },
2237
+ options
2238
+ );
2239
+ if (options.json) {
2240
+ writeJson(result);
2241
+ return;
2242
+ }
2243
+ log.success("agents create completed");
2244
+ } catch (error) {
2245
+ exitWithError(error);
2246
+ }
2247
+ }
2248
+ async function agentsUpdateCommand(options) {
2249
+ try {
2250
+ const id = options.id ?? fail("agents update requires --id");
2251
+ const result = await run(
2252
+ "update",
2253
+ {
2254
+ id,
2255
+ name: options.name,
2256
+ description: options.description,
2257
+ prompt: options.prompt,
2258
+ model: options.model,
2259
+ mode: options.mode,
2260
+ skills: parseCsv(options.skills)
2261
+ },
2262
+ options
2263
+ );
2264
+ if (options.json) {
2265
+ writeJson(result);
2266
+ return;
2267
+ }
2268
+ log.success("agents update completed");
2269
+ } catch (error) {
2270
+ exitWithError(error);
2271
+ }
2272
+ }
2273
+ async function agentsRemoveCommand(options) {
2274
+ try {
2275
+ const id = options.id ?? fail("agents remove requires --id");
2276
+ if (!options.yes) {
2277
+ fail("agents remove requires --yes");
2278
+ }
2279
+ const result = await run("remove", { id, yes: true }, options);
2280
+ if (options.json) {
2281
+ writeJson(result);
2282
+ return;
2283
+ }
2284
+ log.success("agents remove completed");
2285
+ } catch (error) {
2286
+ exitWithError(error);
2287
+ }
2288
+ }
2289
+ async function agentsInstallCommand(options) {
2290
+ try {
2291
+ const id = options.id ?? fail("agents install requires --id");
2292
+ const payload = await run("get", { id }, options);
2293
+ const spec = normalizeSpec(payload);
2294
+ const targets = await resolveInstallTargets(options);
2295
+ const summary = [];
2296
+ for (const target of targets) {
2297
+ const installPath = resolveAgentsInstallPath(target, { global: options.global === true }) ?? resolveInstallPath(target, { global: options.global === true });
2298
+ if (!installPath) {
2299
+ summary.push({
2300
+ platform: target.id,
2301
+ written: 0,
2302
+ warnings: ["No install path available for this platform"],
2303
+ errors: []
2304
+ });
2305
+ continue;
2306
+ }
2307
+ const result = await writeAgentsForPlatformAsync(
2308
+ target,
2309
+ [spec],
2310
+ installPath
2311
+ );
2312
+ summary.push({
2313
+ platform: target.id,
2314
+ installPath,
2315
+ written: result.written.length,
2316
+ warnings: result.warnings,
2317
+ errors: result.errors
2318
+ });
2319
+ }
2320
+ if (options.json) {
2321
+ writeJson(summary);
2322
+ return;
2323
+ }
2324
+ for (const item of summary) {
2325
+ log.info(
2326
+ `${item.platform}: ${item.written} file(s) written${item.installPath ? ` to ${item.installPath}` : ""}`
2327
+ );
2328
+ for (const warning of item.warnings) {
2329
+ log.warn(` warning: ${warning}`);
2330
+ }
2331
+ for (const error of item.errors) {
2332
+ log.error(` error (${error.agent}): ${error.error}`);
2333
+ }
2334
+ }
2335
+ log.success("agents install completed");
2336
+ } catch (error) {
2337
+ exitWithError(error);
2338
+ }
2339
+ }
2340
+
2341
+ // src/commands/auth.ts
2342
+ init_esm_shims();
2343
+ init_api();
2344
+ init_config();
2345
+ import process6 from "process";
2346
+
2347
+ // src/lib/device-auth.ts
2348
+ init_esm_shims();
2349
+ import { execFile } from "child_process";
2350
+ import { hostname, platform } from "os";
2351
+ var TRAILING_SLASHES = /\/+$/;
2352
+ var DeviceAuthTimeoutError = class extends Error {
2353
+ constructor() {
2354
+ super("Device authorization timed out");
2355
+ this.name = "DeviceAuthTimeoutError";
2356
+ }
2357
+ };
2358
+ var DeviceAuthDeniedError = class extends Error {
2359
+ constructor() {
2360
+ super("Device authorization was denied");
2361
+ this.name = "DeviceAuthDeniedError";
2362
+ }
2363
+ };
2364
+ var DeviceAuthExpiredError = class extends Error {
2365
+ constructor() {
2366
+ super("Device authorization code has expired");
2367
+ this.name = "DeviceAuthExpiredError";
2368
+ }
2369
+ };
2370
+ var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
2371
+ async function fetchAuthConfig(serverUrl) {
2372
+ const url = `${serverUrl.replace(TRAILING_SLASHES, "")}/api/cli/auth-config`;
2373
+ const response = await fetch(url);
2374
+ if (!response.ok) {
2375
+ const body = await response.text();
2376
+ throw new Error(
2377
+ `Failed to fetch auth config (${response.status}): ${body}`
2378
+ );
2379
+ }
2380
+ return response.json();
2381
+ }
2382
+ async function initiateDeviceAuth(convexSiteUrl, deviceInfo) {
2383
+ const url = `${convexSiteUrl.replace(TRAILING_SLASHES, "")}/api/cli/device/authorize`;
2384
+ const response = await fetch(url, {
2385
+ method: "POST",
2386
+ headers: { "Content-Type": "application/json" },
2387
+ body: JSON.stringify(deviceInfo)
2388
+ });
2389
+ if (!response.ok) {
2390
+ const body = await response.text();
2391
+ throw new Error(
2392
+ `Failed to initiate device authorization (${response.status}): ${body}`
2393
+ );
2394
+ }
2395
+ return response.json();
2396
+ }
2397
+ async function pollForSession(convexSiteUrl, deviceCode, interval, expiresIn, timeoutSeconds) {
2398
+ const tokenUrl = `${convexSiteUrl.replace(TRAILING_SLASHES, "")}/api/cli/device/token`;
2399
+ const deadline = Date.now() + Math.min(expiresIn, timeoutSeconds) * 1e3;
2400
+ let currentInterval = interval;
2401
+ while (Date.now() < deadline) {
2402
+ await sleep(currentInterval * 1e3);
2403
+ const response = await fetch(tokenUrl, {
2404
+ method: "POST",
2405
+ headers: { "Content-Type": "application/json" },
2406
+ body: JSON.stringify({ device_code: deviceCode })
2407
+ });
2408
+ if (response.ok) {
2409
+ const result = await response.json();
2410
+ return {
2411
+ sessionToken: result.session_token,
2412
+ expiresAt: result.expires_at,
2413
+ user: result.user
2414
+ };
2415
+ }
2416
+ let errorCode;
2417
+ try {
2418
+ const errorBody = await response.json();
2419
+ errorCode = errorBody.error;
2420
+ } catch {
2421
+ throw new Error(`Device auth polling error: HTTP ${response.status}`);
2422
+ }
2423
+ if (errorCode === "authorization_pending") {
2424
+ continue;
2425
+ }
2426
+ if (errorCode === "slow_down") {
2427
+ currentInterval += 5;
2428
+ continue;
2429
+ }
2430
+ if (errorCode === "expired_token") {
2431
+ throw new DeviceAuthExpiredError();
2432
+ }
2433
+ if (errorCode === "access_denied") {
2434
+ throw new DeviceAuthDeniedError();
2435
+ }
2436
+ throw new Error(
2437
+ `Device auth polling error: ${errorCode ?? `HTTP ${response.status}`}`
2438
+ );
2439
+ }
2440
+ throw new DeviceAuthTimeoutError();
2441
+ }
2442
+ async function fetchSessionInfo(convexSiteUrl, sessionToken) {
2443
+ const url = `${convexSiteUrl.replace(TRAILING_SLASHES, "")}/api/cli/sessions/me`;
2444
+ const response = await fetch(url, {
2445
+ method: "GET",
2446
+ headers: {
2447
+ Authorization: `Bearer ${sessionToken}`
2448
+ }
2449
+ });
2450
+ if (response.status === 404) {
2451
+ return null;
2452
+ }
2453
+ if (!response.ok) {
2454
+ const body = await response.text();
2455
+ throw new Error(
2456
+ `Failed to fetch session info (${response.status}): ${body}`
2457
+ );
2458
+ }
2459
+ return response.json();
2460
+ }
2461
+ async function revokeSession(convexSiteUrl, sessionToken) {
2462
+ const url = `${convexSiteUrl.replace(TRAILING_SLASHES, "")}/api/cli/sessions/me`;
2463
+ const response = await fetch(url, {
2464
+ method: "DELETE",
2465
+ headers: {
2466
+ Authorization: `Bearer ${sessionToken}`
2467
+ }
2468
+ });
2469
+ if (response.status === 404) {
2470
+ return false;
2471
+ }
2472
+ if (!response.ok) {
2473
+ const body = await response.text();
2474
+ throw new Error(`Failed to revoke session (${response.status}): ${body}`);
2475
+ }
2476
+ return true;
2477
+ }
2478
+ var noop = () => {
2479
+ };
2480
+ function openBrowser(url) {
2481
+ const currentPlatform = platform();
2482
+ if (currentPlatform === "darwin") {
2483
+ execFile("open", [url], noop);
2484
+ } else if (currentPlatform === "win32") {
2485
+ execFile("cmd", ["/c", "start", "", url], noop);
2486
+ } else {
2487
+ execFile("xdg-open", [url], noop);
2488
+ }
2489
+ }
2490
+ function getDeviceInfo() {
2491
+ return {
2492
+ deviceName: hostname(),
2493
+ deviceOs: platform(),
2494
+ deviceHostname: hostname()
2495
+ };
2496
+ }
2497
+
2498
+ // src/commands/auth.ts
2499
+ init_tui();
2500
+ var SESSION_TOKEN_PREFIX = "brs_";
2501
+ var TRAILING_SLASHES2 = /\/+$/;
2502
+ var DEFAULT_TIMEOUT_SECONDS = 300;
2503
+ async function configureDefaultScopeAsync(serverUrl) {
2504
+ const shouldConfigureScope = await confirm({
2505
+ message: "Configure organization and scope defaults now?",
2506
+ initialValue: true
2507
+ });
2508
+ if (isCancel(shouldConfigureScope) || !shouldConfigureScope) {
2509
+ return;
2510
+ }
2511
+ const { scopeCommand: scopeCommand2 } = await Promise.resolve().then(() => (init_scope(), scope_exports));
2512
+ const config = await loadMergedConfigAsync();
2513
+ await scopeCommand2({
2514
+ file: "user",
2515
+ server: serverUrl,
2516
+ ...config.token ? { apiKey: config.token } : {}
2517
+ });
2518
+ }
2519
+ async function manualTokenFlow(apiKey, serverUrl, options) {
2520
+ const authSpinner = spinner();
2521
+ let authSpinnerActive = true;
2522
+ const stopAuthSpinner = (message) => {
2523
+ if (!authSpinnerActive) {
2524
+ return;
2525
+ }
2526
+ authSpinner.stop(message);
2527
+ authSpinnerActive = false;
2528
+ };
2529
+ authSpinner.start("Validating API key...");
2530
+ try {
2531
+ const isValid = await validateApiKeyAsync(apiKey, serverUrl);
2532
+ if (!isValid) {
2533
+ stopAuthSpinner("Invalid API key");
2534
+ log.error(
2535
+ "The API key could not be validated. Please check your key and try again."
2536
+ );
2537
+ process6.exit(1);
2538
+ }
2539
+ await setApiKeyAsync(apiKey);
2540
+ const existingUserConfig = await loadUserConfigAsync();
2541
+ if (existingUserConfig?.token) {
2542
+ await saveUserConfigAsync({ ...existingUserConfig, token: apiKey });
2543
+ }
2544
+ stopAuthSpinner("API key validated and saved");
2545
+ if (options.scope !== false) {
2546
+ await configureDefaultScopeAsync(serverUrl);
2547
+ }
2548
+ log.success(`Config saved to ${CONFIG_FILE}`);
2549
+ outro(
2550
+ "You're authenticated! Run 'braid install' to use your saved scope (or pass '--profile <name>' to override)."
2551
+ );
2552
+ } catch (error) {
2553
+ stopAuthSpinner("Validation failed");
2554
+ const message = error instanceof Error ? error.message : String(error);
2555
+ log.error(`Failed to validate API key: ${message}`);
2556
+ process6.exit(1);
2557
+ }
2558
+ }
2559
+ function handlePollingError(error) {
2560
+ if (error instanceof DeviceAuthTimeoutError) {
2561
+ log.error("The device authorization timed out. Please try again.");
2562
+ } else if (error instanceof DeviceAuthExpiredError) {
2563
+ log.error("The device code has expired. Please try again.");
2564
+ } else if (error instanceof DeviceAuthDeniedError) {
2565
+ log.error("The authorization request was denied.");
2566
+ } else {
2567
+ const message = error instanceof Error ? error.message : String(error);
2568
+ log.error(`Authentication failed: ${message}`);
2569
+ }
2570
+ process6.exit(1);
2571
+ }
2572
+ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2573
+ const configSpinner = spinner();
2574
+ configSpinner.start("Fetching auth configuration...");
2575
+ let authConfig;
2576
+ try {
2577
+ authConfig = await fetchAuthConfig(serverUrl);
2578
+ configSpinner.stop("Auth configuration loaded");
2579
+ } catch (error) {
2580
+ configSpinner.stop("Failed to fetch auth configuration");
2581
+ const message = error instanceof Error ? error.message : String(error);
2582
+ log.error(`Could not connect to server: ${message}`);
2583
+ process6.exit(1);
2584
+ }
2585
+ const deviceSpinner = spinner();
2586
+ deviceSpinner.start("Initiating device authorization...");
2587
+ let deviceAuth;
2588
+ try {
2589
+ deviceAuth = await initiateDeviceAuth(
2590
+ authConfig.convexSiteUrl,
2591
+ getDeviceInfo()
2592
+ );
2593
+ deviceSpinner.stop("Device authorization initiated");
2594
+ } catch (error) {
2595
+ deviceSpinner.stop("Failed to initiate device authorization");
2596
+ const message = error instanceof Error ? error.message : String(error);
2597
+ log.error(`Device authorization failed: ${message}`);
2598
+ process6.exit(1);
2599
+ }
2600
+ const verificationUrl = `${serverUrl.replace(TRAILING_SLASHES2, "")}/cli/authorize?user_code=${deviceAuth.user_code}`;
2601
+ log.info("");
2602
+ log.info(" To authenticate, visit:");
2603
+ log.info(` ${verificationUrl}`);
2604
+ log.info("");
2605
+ log.info(` Your code: ${deviceAuth.user_code}`);
2606
+ log.info("");
2607
+ openBrowser(verificationUrl);
2608
+ const expiresMinutes = Math.ceil(
2609
+ Math.min(deviceAuth.expires_in, timeoutSeconds) / 60
2610
+ );
2611
+ const pollSpinner = spinner();
2612
+ pollSpinner.start(
2613
+ `Waiting for authentication... (expires in ${expiresMinutes} minutes)`
2614
+ );
2615
+ let session;
2616
+ try {
2617
+ session = await pollForSession(
2618
+ authConfig.convexSiteUrl,
2619
+ deviceAuth.device_code,
2620
+ deviceAuth.interval,
2621
+ deviceAuth.expires_in,
2622
+ timeoutSeconds
2623
+ );
2624
+ pollSpinner.stop(`Authenticated as ${session.user.email}`);
2625
+ } catch (error) {
2626
+ pollSpinner.stop("Authentication failed");
2627
+ handlePollingError(error);
2628
+ }
2629
+ await setApiKeyAsync(session.sessionToken);
2630
+ if (options.scope !== false) {
2631
+ await configureDefaultScopeAsync(authConfig.convexSiteUrl);
2632
+ }
2633
+ log.success(`Session saved to ${CONFIG_FILE}`);
2634
+ outro(
2635
+ "You're authenticated! Run 'braid install' to use your saved scope (or pass '--profile <name>' to override)."
2636
+ );
2637
+ }
2638
+ async function authCommand(options) {
2639
+ intro("braid auth");
2640
+ const serverUrl = options.server ?? "https://braid.cloud";
2641
+ const config = await loadMergedConfigAsync();
2642
+ if (config.token) {
2643
+ const shouldReplace = await confirm({
2644
+ message: "An API key is already configured. Replace it?",
2645
+ initialValue: false
2646
+ });
2647
+ if (isCancel(shouldReplace) || !shouldReplace) {
2648
+ outro("Auth cancelled.");
2649
+ return;
2650
+ }
2651
+ }
2652
+ if (options.token) {
2653
+ await manualTokenFlow(options.token, serverUrl, options);
2654
+ return;
2655
+ }
2656
+ const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : DEFAULT_TIMEOUT_SECONDS;
2657
+ if (Number.isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
2658
+ log.error("Invalid timeout value. Must be a positive number of seconds.");
2659
+ process6.exit(1);
2660
+ }
2661
+ await deviceFlow(serverUrl, timeoutSeconds, options);
2662
+ }
2663
+ async function displayTokenSource(masked) {
2664
+ if (process6.env.BRAID_API_KEY) {
2665
+ log.info(`Authenticated with key: ${masked}`);
2666
+ log.info("Source: BRAID_API_KEY environment variable");
2667
+ return;
2668
+ }
2669
+ const userConfigPath = await findUserConfigFileAsync();
2670
+ log.info(`Authenticated with key: ${masked}`);
2671
+ log.info(`Source: ${userConfigPath ?? CONFIG_FILE}`);
2672
+ }
2673
+ async function displayProjectConfig() {
2674
+ const projectConfigPath = await findProjectConfigFileAsync();
2675
+ if (projectConfigPath) {
2676
+ log.info(`Project config: ${projectConfigPath}`);
2677
+ }
2678
+ }
2679
+ function displayResolvedSettings(config) {
2680
+ const hasOrgProjects = config.orgProjects && config.orgProjects.length > 0;
2681
+ const hasPersonalProjects = config.personalProjects && config.personalProjects.length > 0;
2682
+ if (!(config.profile || hasOrgProjects || hasPersonalProjects)) {
2683
+ return;
2684
+ }
2685
+ log.info("");
2686
+ if (config.profile) {
2687
+ log.info(`Default profile: ${config.profile}`);
2688
+ }
2689
+ if (hasOrgProjects) {
2690
+ log.info(`Org projects: ${config.orgProjects?.join(", ")}`);
2691
+ }
2692
+ if (hasPersonalProjects) {
2693
+ log.info(`Personal projects: ${config.personalProjects?.join(", ")}`);
2694
+ }
2695
+ if (config.serverUrl !== "https://braid.cloud") {
2696
+ log.info(`Server: ${config.serverUrl}`);
2697
+ }
2698
+ }
2699
+ async function displaySessionInfo(token) {
2700
+ const serverUrl = await getServerUrlAsync();
2701
+ try {
2702
+ const authConfig = await fetchAuthConfig(serverUrl);
2703
+ const info = await fetchSessionInfo(authConfig.convexSiteUrl, token);
2704
+ if (!info) {
2705
+ log.warn(
2706
+ "Session is expired or revoked. Run 'braid auth' to re-authenticate."
2707
+ );
2708
+ return;
2709
+ }
2710
+ log.info(
2711
+ `Authenticated as: ${info.email}${info.name ? ` (${info.name})` : ""}`
2712
+ );
2713
+ log.info("Session type: CLI session");
2714
+ if (info.deviceName || info.deviceHostname) {
2715
+ log.info(
2716
+ `Device: ${info.deviceName ?? info.deviceHostname ?? "unknown"}`
2717
+ );
2718
+ }
2719
+ const expiresDate = new Date(info.expiresAt);
2720
+ const daysRemaining = Math.ceil(
2721
+ (info.expiresAt - Date.now()) / (24 * 60 * 60 * 1e3)
2722
+ );
2723
+ log.info(
2724
+ `Expires: ${expiresDate.toLocaleDateString()} (${daysRemaining} days)`
2725
+ );
2726
+ if (info.lastActiveAt) {
2727
+ log.info(`Last active: ${new Date(info.lastActiveAt).toLocaleString()}`);
2728
+ }
2729
+ } catch (error) {
2730
+ const message = error instanceof Error ? error.message : String(error);
2731
+ log.warn(`Could not fetch session details: ${message}`);
2732
+ const masked = `${token.slice(0, 7)}...${token.slice(-4)}`;
2733
+ log.info(`Session token: ${masked}`);
2734
+ }
2735
+ }
2736
+ async function authStatusCommand() {
2737
+ const config = await loadMergedConfigAsync();
2738
+ if (!config.token) {
2739
+ log.warn("Not authenticated. Run 'braid auth' to configure your API key.");
2740
+ log.info(
2741
+ `Or create a ${USER_CONFIG_FILENAME} file with: { "token": "br_xxx" }`
2742
+ );
2743
+ return;
2744
+ }
2745
+ if (config.token.startsWith(SESSION_TOKEN_PREFIX)) {
2746
+ await displaySessionInfo(config.token);
2747
+ await displayProjectConfig();
2748
+ displayResolvedSettings(config);
2749
+ return;
2750
+ }
2751
+ const masked = `${config.token.slice(0, 7)}...${config.token.slice(-4)}`;
2752
+ await displayTokenSource(masked);
2753
+ await displayProjectConfig();
2754
+ displayResolvedSettings(config);
2755
+ }
2756
+ async function revokeSessionIfActive(token) {
2757
+ const serverUrl = await getServerUrlAsync();
2758
+ try {
2759
+ const authConfig = await fetchAuthConfig(serverUrl);
2760
+ await revokeSession(authConfig.convexSiteUrl, token);
2761
+ } catch {
2762
+ return;
2763
+ }
2764
+ }
2765
+ async function authLogoutCommand() {
2766
+ const config = await loadMergedConfigAsync();
2767
+ const { clearApiKeyAsync: clearApiKeyAsync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2768
+ if (config.token?.startsWith(SESSION_TOKEN_PREFIX)) {
2769
+ await revokeSessionIfActive(config.token);
2770
+ }
2771
+ await clearApiKeyAsync2();
2772
+ log.success("Logged out. API key removed from config.");
2773
+ }
2774
+
2775
+ // src/commands/discover.ts
2776
+ init_esm_shims();
2777
+ init_api();
2778
+ init_config();
2779
+ init_tui();
2780
+ import process7 from "process";
2781
+ var parseCsv2 = (input) => {
2782
+ if (!input) {
2783
+ return void 0;
2784
+ }
2785
+ const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
2786
+ return values.length > 0 ? values : void 0;
2787
+ };
2788
+ var writeJson2 = (value) => {
2789
+ process7.stdout.write(`${JSON.stringify(value, null, 2)}
2790
+ `);
2791
+ };
2792
+ var exitWithError2 = (error) => {
2793
+ const message = error instanceof Error ? error.message : String(error);
2794
+ log.error(message);
2795
+ process7.exit(1);
2796
+ };
2797
+ var applyCommonOptions = (base, options) => ({
2798
+ ...base,
2799
+ ...options.server ? { serverUrl: options.server } : {},
2800
+ ...options.apiKey ? { apiKey: options.apiKey } : {}
2801
+ });
2802
+ var applyOptionalString = (target, key, value) => {
2803
+ if (value) {
2804
+ target[key] = value;
2805
+ }
2806
+ };
2807
+ var applyOptionalArray = (target, key, value) => {
2808
+ if (value && value.length > 0) {
2809
+ target[key] = value;
2810
+ }
2811
+ };
2812
+ var applyOptionalBoolean = (target, key, value) => {
2813
+ if (value !== void 0) {
2814
+ target[key] = value;
2815
+ }
2816
+ };
2817
+ async function projectsListCommand(options) {
2818
+ try {
2819
+ const result = await fetchScopeOptionsAsync(
2820
+ applyCommonOptions({}, options)
2821
+ );
2822
+ if (options.json) {
2823
+ writeJson2(result);
2824
+ return;
2825
+ }
2826
+ log.info("Personal projects:");
2827
+ if (result.personalProjects.length === 0) {
2828
+ log.info(" (none)");
2829
+ }
2830
+ for (const project of result.personalProjects) {
2831
+ log.info(` ${project.id} ${project.name}`);
2832
+ }
2833
+ for (const orgProjects of result.orgProjects) {
2834
+ log.info(`
2835
+ Organization: ${orgProjects.orgName} (${orgProjects.orgId})`);
2836
+ if (orgProjects.projects.length === 0) {
2837
+ log.info(" (none)");
2838
+ continue;
2839
+ }
2840
+ for (const project of orgProjects.projects) {
2841
+ log.info(` ${project.id} ${project.name}`);
2842
+ }
2843
+ }
2844
+ } catch (error) {
2845
+ exitWithError2(error);
2846
+ }
2847
+ }
2848
+ var buildRulesRequest = async (options) => {
2849
+ const config = await loadMergedConfigAsync();
2850
+ const orgProjects = parseCsv2(options.orgProjects) ?? config.orgProjects;
2851
+ const personalProjects = parseCsv2(options.personalProjects) ?? config.personalProjects;
2852
+ const request = applyCommonOptions({}, options);
2853
+ applyOptionalString(request, "orgId", options.orgId ?? config.org);
2854
+ applyOptionalArray(request, "orgProjects", orgProjects);
2855
+ applyOptionalArray(request, "personalProjects", personalProjects);
2856
+ applyOptionalBoolean(request, "includeUserGlobal", options.includeUserGlobal);
2857
+ applyOptionalBoolean(request, "includeOrgGlobal", options.includeOrgGlobal);
2858
+ return request;
2859
+ };
2860
+ async function rulesListCommand(options) {
2861
+ try {
2862
+ const result = await fetchRuleOptionsAsync(
2863
+ await buildRulesRequest(options)
2864
+ );
2865
+ if (options.json) {
2866
+ writeJson2(result);
2867
+ return;
2868
+ }
2869
+ if (result.rules.length === 0) {
2870
+ log.info("No rules found.");
2871
+ return;
2872
+ }
2873
+ for (const rule of result.rules) {
2874
+ log.info(`${rule.id} ${rule.title}`);
2875
+ }
2876
+ } catch (error) {
2877
+ exitWithError2(error);
2032
2878
  }
2033
- return agent.mcpGlobalConfigPath;
2879
+ }
2880
+ var buildSkillsRequest = async (options) => {
2881
+ const config = await loadMergedConfigAsync();
2882
+ const orgProjects = parseCsv2(options.orgProjects) ?? config.orgProjects;
2883
+ const personalProjects = parseCsv2(options.personalProjects) ?? config.personalProjects;
2884
+ const request = applyCommonOptions({}, options);
2885
+ applyOptionalString(request, "profile", options.profile ?? config.profile);
2886
+ applyOptionalArray(request, "orgProjects", orgProjects);
2887
+ applyOptionalArray(request, "personalProjects", personalProjects);
2888
+ applyOptionalBoolean(request, "includeUserGlobal", options.includeUserGlobal);
2889
+ applyOptionalBoolean(request, "includeOrgGlobal", options.includeOrgGlobal);
2890
+ return request;
2034
2891
  };
2035
- var buildMcpEntry = (style, env) => {
2036
- const command = "npx";
2037
- const args = ["-y", "@braid-cloud/mcp"];
2038
- const envObj = Object.keys(env).length > 0 ? env : void 0;
2039
- switch (style) {
2040
- case "standard":
2041
- return envObj ? { command, args, env: envObj } : { command, args };
2042
- case "typed-stdio":
2043
- return envObj ? { type: "stdio", command, args, env: envObj } : { type: "stdio", command, args };
2044
- case "zed":
2045
- return envObj ? { command: { path: command, args, env: envObj } } : { command: { path: command, args } };
2046
- case "cline":
2047
- return envObj ? { command, args, env: envObj, disabled: false } : { command, args, disabled: false };
2048
- default:
2049
- return envObj ? { command, args, env: envObj } : { command, args };
2892
+ async function skillsListCommand(options) {
2893
+ try {
2894
+ const result = await fetchSkillsAsync(await buildSkillsRequest(options));
2895
+ if (options.json) {
2896
+ writeJson2(result);
2897
+ return;
2898
+ }
2899
+ if (result.skills.length === 0) {
2900
+ log.info("No skills found.");
2901
+ return;
2902
+ }
2903
+ for (const skill of result.skills) {
2904
+ log.info(`${skill.name}`);
2905
+ }
2906
+ } catch (error) {
2907
+ exitWithError2(error);
2050
2908
  }
2051
- };
2052
- var detectMcpAgents = (projectRoot) => Effect3.runPromise(
2053
- pipe3(
2054
- detectAgents(projectRoot),
2055
- Effect3.map((detected) => detected.filter(hasMcpConfig))
2056
- )
2057
- );
2909
+ }
2058
2910
 
2059
2911
  // src/commands/install.ts
2912
+ init_esm_shims();
2060
2913
  init_api();
2061
2914
  init_config();
2062
2915
 
2063
2916
  // src/lib/metadata.ts
2064
2917
  init_esm_shims();
2065
- import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
2918
+ import { readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
2066
2919
  import { join as join3 } from "path";
2067
- import { Data as Data3, Effect as Effect4, pipe as pipe4 } from "effect";
2920
+ import { Data as Data4, Effect as Effect5, pipe as pipe5 } from "effect";
2068
2921
  var METADATA_FILENAME = ".braidskills-metadata.json";
2069
- var MetadataReadError = class extends Data3.TaggedError("MetadataReadError") {
2922
+ var MetadataReadError = class extends Data4.TaggedError("MetadataReadError") {
2070
2923
  };
2071
- var MetadataWriteError = class extends Data3.TaggedError("MetadataWriteError") {
2924
+ var MetadataWriteError = class extends Data4.TaggedError("MetadataWriteError") {
2072
2925
  };
2073
2926
  var getMetadataPath = (skillsDir) => join3(skillsDir, METADATA_FILENAME);
2074
2927
  var readMetadata = (skillsDir) => {
2075
2928
  const metadataPath = getMetadataPath(skillsDir);
2076
- return pipe4(
2077
- Effect4.tryPromise({
2929
+ return pipe5(
2930
+ Effect5.tryPromise({
2078
2931
  try: () => readFile2(metadataPath, "utf-8"),
2079
2932
  catch: (e) => new MetadataReadError({ path: metadataPath, cause: e })
2080
2933
  }),
2081
- Effect4.flatMap(
2082
- (content) => Effect4.try({
2934
+ Effect5.flatMap(
2935
+ (content) => Effect5.try({
2083
2936
  try: () => JSON.parse(content),
2084
2937
  catch: () => ({ skills: [] })
2085
2938
  })
2086
2939
  ),
2087
- Effect4.orElseSucceed(() => ({ skills: [] }))
2940
+ Effect5.orElseSucceed(() => ({ skills: [] }))
2088
2941
  );
2089
2942
  };
2090
2943
  var writeMetadata = (skillsDir, metadata) => {
2091
2944
  const metadataPath = getMetadataPath(skillsDir);
2092
- return Effect4.tryPromise({
2093
- try: () => writeFile2(metadataPath, JSON.stringify(metadata, null, 2), "utf-8"),
2945
+ return Effect5.tryPromise({
2946
+ try: () => writeFile3(metadataPath, JSON.stringify(metadata, null, 2), "utf-8"),
2094
2947
  catch: (e) => new MetadataWriteError({ path: metadataPath, cause: e })
2095
2948
  });
2096
2949
  };
2097
- var updateMetadata = (skillsDir, newSkills) => pipe4(
2950
+ var updateMetadata = (skillsDir, newSkills) => pipe5(
2098
2951
  readMetadata(skillsDir),
2099
- Effect4.map((existing) => {
2952
+ Effect5.map((existing) => {
2100
2953
  const now = (/* @__PURE__ */ new Date()).toISOString();
2101
2954
  const updatedSkills = [...existing.skills];
2102
2955
  for (const skill of newSkills) {
@@ -2118,43 +2971,43 @@ var updateMetadata = (skillsDir, newSkills) => pipe4(
2118
2971
  }
2119
2972
  return { skills: updatedSkills };
2120
2973
  }),
2121
- Effect4.flatMap((metadata) => writeMetadata(skillsDir, metadata))
2974
+ Effect5.flatMap((metadata) => writeMetadata(skillsDir, metadata))
2122
2975
  );
2123
- var removeFromMetadata = (skillsDir, skillName) => pipe4(
2976
+ var removeFromMetadata = (skillsDir, skillName) => pipe5(
2124
2977
  readMetadata(skillsDir),
2125
- Effect4.map((metadata) => ({
2978
+ Effect5.map((metadata) => ({
2126
2979
  skills: metadata.skills.filter((s) => s.name !== skillName)
2127
2980
  })),
2128
- Effect4.flatMap((metadata) => writeMetadata(skillsDir, metadata))
2981
+ Effect5.flatMap((metadata) => writeMetadata(skillsDir, metadata))
2129
2982
  );
2130
- var readMetadataAsync = (skillsDir) => Effect4.runPromise(readMetadata(skillsDir));
2131
- var updateMetadataAsync = (skillsDir, newSkills) => Effect4.runPromise(updateMetadata(skillsDir, newSkills));
2132
- var removeFromMetadataAsync = (skillsDir, skillName) => Effect4.runPromise(removeFromMetadata(skillsDir, skillName));
2983
+ var readMetadataAsync = (skillsDir) => Effect5.runPromise(readMetadata(skillsDir));
2984
+ var updateMetadataAsync = (skillsDir, newSkills) => Effect5.runPromise(updateMetadata(skillsDir, newSkills));
2985
+ var removeFromMetadataAsync = (skillsDir, skillName) => Effect5.runPromise(removeFromMetadata(skillsDir, skillName));
2133
2986
 
2134
2987
  // src/lib/rule-writer.ts
2135
2988
  init_esm_shims();
2136
- import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
2137
- import { dirname as dirname2, resolve, sep } from "path";
2138
- import { Data as Data4, Effect as Effect5, pipe as pipe5 } from "effect";
2139
- var RuleWriteError = class extends Data4.TaggedError("RuleWriteError") {
2989
+ import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
2990
+ import { dirname as dirname3, resolve as resolve2, sep as sep2 } from "path";
2991
+ import { Data as Data5, Effect as Effect6, pipe as pipe6 } from "effect";
2992
+ var RuleWriteError = class extends Data5.TaggedError("RuleWriteError") {
2140
2993
  };
2141
- var createDirectory = (dir) => Effect5.tryPromise({
2142
- try: () => mkdir2(dir, { recursive: true }),
2994
+ var createDirectory = (dir) => Effect6.tryPromise({
2995
+ try: () => mkdir3(dir, { recursive: true }),
2143
2996
  catch: (e) => new RuleWriteError({ path: dir, operation: "mkdir", cause: e })
2144
2997
  });
2145
- var writeTextFile = (fullPath, content) => Effect5.tryPromise({
2146
- try: () => writeFile3(fullPath, content, "utf-8"),
2998
+ var writeTextFile = (fullPath, content) => Effect6.tryPromise({
2999
+ try: () => writeFile4(fullPath, content, "utf-8"),
2147
3000
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "write", cause: e })
2148
3001
  });
2149
- var readTextFile = (fullPath) => Effect5.tryPromise({
3002
+ var readTextFile = (fullPath) => Effect6.tryPromise({
2150
3003
  try: () => readFile3(fullPath, "utf-8"),
2151
3004
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "read", cause: e })
2152
3005
  });
2153
3006
  var assertRulePathWithinBase = (basePath, ruleName) => {
2154
- const resolvedBase = resolve(basePath);
2155
- const resolvedFull = resolve(basePath, ruleName);
2156
- if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep)) {
2157
- return Effect5.fail(
3007
+ const resolvedBase = resolve2(basePath);
3008
+ const resolvedFull = resolve2(basePath, ruleName);
3009
+ if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep2)) {
3010
+ return Effect6.fail(
2158
3011
  new RuleWriteError({
2159
3012
  path: ruleName,
2160
3013
  operation: "write",
@@ -2164,7 +3017,7 @@ var assertRulePathWithinBase = (basePath, ruleName) => {
2164
3017
  })
2165
3018
  );
2166
3019
  }
2167
- return Effect5.succeed(resolvedFull);
3020
+ return Effect6.succeed(resolvedFull);
2168
3021
  };
2169
3022
  var BRAID_SECTION_START = "<!-- braid:rules:start -->";
2170
3023
  var BRAID_SECTION_END = "<!-- braid:rules:end -->";
@@ -2197,30 +3050,30 @@ function buildAppendContent(rules2) {
2197
3050
  lines.push(BRAID_SECTION_END);
2198
3051
  return lines.join("\n");
2199
3052
  }
2200
- var writeMdcRules = (basePath, rules2) => pipe5(
3053
+ var writeMdcRules = (basePath, rules2) => pipe6(
2201
3054
  createDirectory(basePath),
2202
- Effect5.flatMap(
2203
- () => Effect5.forEach(
3055
+ Effect6.flatMap(
3056
+ () => Effect6.forEach(
2204
3057
  rules2,
2205
- (rule) => pipe5(
3058
+ (rule) => pipe6(
2206
3059
  assertRulePathWithinBase(basePath, `${rule.name}.mdc`),
2207
- Effect5.flatMap(
3060
+ Effect6.flatMap(
2208
3061
  (filePath) => writeTextFile(filePath, buildMdcContent(rule))
2209
3062
  )
2210
3063
  ),
2211
3064
  { concurrency: "unbounded" }
2212
3065
  )
2213
3066
  ),
2214
- Effect5.asVoid
3067
+ Effect6.asVoid
2215
3068
  );
2216
- var writeMarkdownDirRules = (basePath, rules2) => pipe5(
3069
+ var writeMarkdownDirRules = (basePath, rules2) => pipe6(
2217
3070
  createDirectory(basePath),
2218
- Effect5.flatMap(
2219
- () => Effect5.forEach(
3071
+ Effect6.flatMap(
3072
+ () => Effect6.forEach(
2220
3073
  rules2,
2221
- (rule) => pipe5(
3074
+ (rule) => pipe6(
2222
3075
  assertRulePathWithinBase(basePath, `${rule.name}.md`),
2223
- Effect5.flatMap(
3076
+ Effect6.flatMap(
2224
3077
  (filePath) => writeTextFile(filePath, `# ${rule.title}
2225
3078
 
2226
3079
  ${rule.content}`)
@@ -2229,17 +3082,17 @@ ${rule.content}`)
2229
3082
  { concurrency: "unbounded" }
2230
3083
  )
2231
3084
  ),
2232
- Effect5.asVoid
3085
+ Effect6.asVoid
2233
3086
  );
2234
- var writeAppendSingleRules = (filePath, rules2) => pipe5(
2235
- createDirectory(dirname2(filePath)),
2236
- Effect5.flatMap(
2237
- () => pipe5(
3087
+ var writeAppendSingleRules = (filePath, rules2) => pipe6(
3088
+ createDirectory(dirname3(filePath)),
3089
+ Effect6.flatMap(
3090
+ () => pipe6(
2238
3091
  readTextFile(filePath),
2239
- Effect5.orElseSucceed(() => "")
3092
+ Effect6.orElseSucceed(() => "")
2240
3093
  )
2241
3094
  ),
2242
- Effect5.flatMap((existing) => {
3095
+ Effect6.flatMap((existing) => {
2243
3096
  const braidContent = buildAppendContent(rules2);
2244
3097
  const startIdx = existing.indexOf(BRAID_SECTION_START);
2245
3098
  const endIdx = existing.indexOf(BRAID_SECTION_END);
@@ -2263,21 +3116,21 @@ var writeRulesForFormat = (basePath, rules2, format) => {
2263
3116
  case "append-single":
2264
3117
  return writeAppendSingleRules(basePath, rules2);
2265
3118
  default:
2266
- return Effect5.void;
3119
+ return Effect6.void;
2267
3120
  }
2268
3121
  };
2269
3122
  var writeRulesForAgent = (agent, rules2, rulesPath) => {
2270
3123
  if (!agent.ruleFormat || rules2.length === 0) {
2271
- return Effect5.succeed({ written: 0, errors: [] });
3124
+ return Effect6.succeed({ written: 0, errors: [] });
2272
3125
  }
2273
- return pipe5(
3126
+ return pipe6(
2274
3127
  writeRulesForFormat(rulesPath, rules2, agent.ruleFormat),
2275
- Effect5.map(() => ({
3128
+ Effect6.map(() => ({
2276
3129
  written: rules2.length,
2277
3130
  errors: []
2278
3131
  })),
2279
- Effect5.catchAll(
2280
- (error) => Effect5.succeed({
3132
+ Effect6.catchAll(
3133
+ (error) => Effect6.succeed({
2281
3134
  written: 0,
2282
3135
  errors: [
2283
3136
  {
@@ -2289,36 +3142,36 @@ var writeRulesForAgent = (agent, rules2, rulesPath) => {
2289
3142
  )
2290
3143
  );
2291
3144
  };
2292
- var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect5.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
3145
+ var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect6.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
2293
3146
 
2294
3147
  // src/lib/skill-writer.ts
2295
3148
  init_esm_shims();
2296
- import { chmod, mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
2297
- import { dirname as dirname3, resolve as resolve2, sep as sep2 } from "path";
2298
- import { Data as Data5, Effect as Effect6, pipe as pipe6 } from "effect";
2299
- var WriteError = class extends Data5.TaggedError("WriteError") {
3149
+ import { chmod, mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
3150
+ import { dirname as dirname4, resolve as resolve3, sep as sep3 } from "path";
3151
+ import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
3152
+ var WriteError = class extends Data6.TaggedError("WriteError") {
2300
3153
  };
2301
- var createDirectory2 = (dir, fullPath) => Effect6.tryPromise({
2302
- try: () => mkdir3(dir, { recursive: true }),
3154
+ var createDirectory2 = (dir, fullPath) => Effect7.tryPromise({
3155
+ try: () => mkdir4(dir, { recursive: true }),
2303
3156
  catch: (e) => new WriteError({ path: fullPath, operation: "mkdir", cause: e })
2304
3157
  });
2305
- var writeTextFile2 = (fullPath, content) => Effect6.tryPromise({
2306
- try: () => writeFile4(fullPath, content, "utf-8"),
3158
+ var writeTextFile2 = (fullPath, content) => Effect7.tryPromise({
3159
+ try: () => writeFile5(fullPath, content, "utf-8"),
2307
3160
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
2308
3161
  });
2309
- var writeBinaryFile = (fullPath, content) => Effect6.tryPromise({
2310
- try: () => writeFile4(fullPath, content),
3162
+ var writeBinaryFile = (fullPath, content) => Effect7.tryPromise({
3163
+ try: () => writeFile5(fullPath, content),
2311
3164
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
2312
3165
  });
2313
- var makeExecutable = (fullPath) => Effect6.tryPromise({
3166
+ var makeExecutable = (fullPath) => Effect7.tryPromise({
2314
3167
  try: () => chmod(fullPath, 493),
2315
3168
  catch: (e) => new WriteError({ path: fullPath, operation: "chmod", cause: e })
2316
3169
  });
2317
- var assertWithinBase = (basePath, untrustedPath) => {
2318
- const resolvedBase = resolve2(basePath);
2319
- const resolvedFull = resolve2(basePath, untrustedPath);
2320
- if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep2)) {
2321
- return Effect6.fail(
3170
+ var assertWithinBase2 = (basePath, untrustedPath) => {
3171
+ const resolvedBase = resolve3(basePath);
3172
+ const resolvedFull = resolve3(basePath, untrustedPath);
3173
+ if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep3)) {
3174
+ return Effect7.fail(
2322
3175
  new WriteError({
2323
3176
  path: untrustedPath,
2324
3177
  operation: "write",
@@ -2328,7 +3181,7 @@ var assertWithinBase = (basePath, untrustedPath) => {
2328
3181
  })
2329
3182
  );
2330
3183
  }
2331
- return Effect6.succeed(resolvedFull);
3184
+ return Effect7.succeed(resolvedFull);
2332
3185
  };
2333
3186
  var COMPATIBILITY_REGEX = /^compatibility:\s*.+$/m;
2334
3187
  var rewriteCompatibility = (content, agentId) => {
@@ -2371,41 +3224,41 @@ var isScriptFile = (path2) => {
2371
3224
  return inScriptsDir && scriptExtensions.some((ext) => lowerPath.endsWith(ext));
2372
3225
  };
2373
3226
  var writeFileContent = (fullPath, file, agentId) => isBinaryFile(file.path) ? writeBinaryFile(fullPath, decodeFileContentBinary(file)) : writeTextFile2(fullPath, decodeFileContent(file, agentId));
2374
- var setExecutableIfScript = (fullPath, filePath) => isScriptFile(filePath) ? makeExecutable(fullPath) : Effect6.void;
2375
- var writeSkillFile = (basePath, file, agentId) => pipe6(
2376
- assertWithinBase(basePath, file.path),
2377
- Effect6.flatMap((fullPath) => {
2378
- const dir = dirname3(fullPath);
2379
- return pipe6(
3227
+ var setExecutableIfScript = (fullPath, file, agentId) => isScriptFile(file.path) && decodeFileContent(file, agentId).startsWith("#!") ? makeExecutable(fullPath) : Effect7.void;
3228
+ var writeSkillFile = (basePath, file, agentId) => pipe7(
3229
+ assertWithinBase2(basePath, file.path),
3230
+ Effect7.flatMap((fullPath) => {
3231
+ const dir = dirname4(fullPath);
3232
+ return pipe7(
2380
3233
  createDirectory2(dir, fullPath),
2381
- Effect6.flatMap(() => writeFileContent(fullPath, file, agentId)),
2382
- Effect6.flatMap(() => setExecutableIfScript(fullPath, file.path))
3234
+ Effect7.flatMap(() => writeFileContent(fullPath, file, agentId)),
3235
+ Effect7.flatMap(() => setExecutableIfScript(fullPath, file, agentId))
2383
3236
  );
2384
3237
  })
2385
3238
  );
2386
- var writeSkill = (basePath, skill, agentId) => pipe6(
2387
- assertWithinBase(basePath, skill.name),
2388
- Effect6.flatMap(
2389
- (skillDir) => pipe6(
2390
- Effect6.forEach(
3239
+ var writeSkill = (basePath, skill, agentId) => pipe7(
3240
+ assertWithinBase2(basePath, skill.name),
3241
+ Effect7.flatMap(
3242
+ (skillDir) => pipe7(
3243
+ Effect7.forEach(
2391
3244
  skill.files,
2392
3245
  (file) => writeSkillFile(skillDir, file, agentId),
2393
3246
  {
2394
3247
  concurrency: "unbounded"
2395
3248
  }
2396
3249
  ),
2397
- Effect6.map(() => void 0)
3250
+ Effect7.map(() => void 0)
2398
3251
  )
2399
3252
  )
2400
3253
  );
2401
- var writeSkills = (basePath, skills2, agentId) => pipe6(
2402
- Effect6.forEach(
3254
+ var writeSkills = (basePath, skills2, agentId) => pipe7(
3255
+ Effect7.forEach(
2403
3256
  skills2,
2404
- (skill) => pipe6(
3257
+ (skill) => pipe7(
2405
3258
  writeSkill(basePath, skill, agentId),
2406
- Effect6.map(() => ({ success: true, skill: skill.name })),
2407
- Effect6.catchAll(
2408
- (error) => Effect6.succeed({
3259
+ Effect7.map(() => ({ success: true, skill: skill.name })),
3260
+ Effect7.catchAll(
3261
+ (error) => Effect7.succeed({
2409
3262
  success: false,
2410
3263
  skill: skill.name,
2411
3264
  error: error.cause instanceof Error ? error.cause.message : String(error.cause)
@@ -2414,14 +3267,14 @@ var writeSkills = (basePath, skills2, agentId) => pipe6(
2414
3267
  ),
2415
3268
  { concurrency: "unbounded" }
2416
3269
  ),
2417
- Effect6.map((results) => ({
3270
+ Effect7.map((results) => ({
2418
3271
  written: results.filter((r) => r.success).map((r) => r.skill),
2419
3272
  errors: results.filter(
2420
3273
  (r) => !r.success
2421
3274
  ).map((r) => ({ skill: r.skill, error: r.error }))
2422
3275
  }))
2423
3276
  );
2424
- var writeSkillsAsync = (basePath, skills2, agentId) => Effect6.runPromise(writeSkills(basePath, skills2, agentId));
3277
+ var writeSkillsAsync = (basePath, skills2, agentId) => Effect7.runPromise(writeSkills(basePath, skills2, agentId));
2425
3278
 
2426
3279
  // src/commands/install.ts
2427
3280
  init_tui();
@@ -2982,31 +3835,34 @@ init_config();
2982
3835
 
2983
3836
  // src/lib/mcp-config.ts
2984
3837
  init_esm_shims();
2985
- import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
2986
- import { dirname as dirname4 } from "path";
2987
- import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
2988
- var McpConfigReadError = class extends Data6.TaggedError("McpConfigReadError") {
3838
+ import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile6 } from "fs/promises";
3839
+ import { dirname as dirname5 } from "path";
3840
+ import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
3841
+ var McpConfigReadError = class extends Data7.TaggedError("McpConfigReadError") {
2989
3842
  };
2990
- var McpConfigWriteError = class extends Data6.TaggedError("McpConfigWriteError") {
3843
+ var McpConfigWriteError = class extends Data7.TaggedError("McpConfigWriteError") {
2991
3844
  };
2992
- var readMcpConfig = (path2) => pipe7(
2993
- Effect7.tryPromise({
3845
+ var readMcpConfig = (path2) => pipe8(
3846
+ Effect8.tryPromise({
2994
3847
  try: () => readFile4(path2, "utf-8"),
2995
3848
  catch: (e) => new McpConfigReadError({ path: path2, cause: e })
2996
3849
  }),
2997
- Effect7.flatMap(
2998
- (content) => Effect7.try({
3850
+ Effect8.flatMap(
3851
+ (content) => Effect8.try({
2999
3852
  try: () => JSON.parse(content),
3000
3853
  catch: () => ({})
3001
3854
  })
3002
3855
  ),
3003
- Effect7.orElseSucceed(() => ({}))
3856
+ Effect8.orElseSucceed(() => ({}))
3004
3857
  );
3005
- var writeMcpConfig = (path2, config) => Effect7.tryPromise({
3858
+ var writeMcpConfig = (path2, config) => Effect8.tryPromise({
3006
3859
  try: async () => {
3007
- await mkdir4(dirname4(path2), { recursive: true });
3008
- await writeFile5(path2, `${JSON.stringify(config, null, 2)}
3009
- `, "utf-8");
3860
+ await mkdir5(dirname5(path2), { recursive: true, mode: 448 });
3861
+ await writeFile6(path2, `${JSON.stringify(config, null, 2)}
3862
+ `, {
3863
+ encoding: "utf-8",
3864
+ mode: 384
3865
+ });
3010
3866
  },
3011
3867
  catch: (e) => new McpConfigWriteError({ path: path2, cause: e })
3012
3868
  });
@@ -3032,8 +3888,8 @@ var hasbraidEntry = (config, rootKey, entryName) => {
3032
3888
  const servers = config[rootKey];
3033
3889
  return servers !== void 0 && entryName in servers;
3034
3890
  };
3035
- var readMcpConfigAsync = (path2) => Effect7.runPromise(readMcpConfig(path2));
3036
- var writeMcpConfigAsync = (path2, config) => Effect7.runPromise(writeMcpConfig(path2, config));
3891
+ var readMcpConfigAsync = (path2) => Effect8.runPromise(readMcpConfig(path2));
3892
+ var writeMcpConfigAsync = (path2, config) => Effect8.runPromise(writeMcpConfig(path2, config));
3037
3893
 
3038
3894
  // src/commands/mcp.ts
3039
3895
  init_tui();
@@ -3346,16 +4202,16 @@ init_esm_shims();
3346
4202
  init_api();
3347
4203
  init_tui();
3348
4204
  import process9 from "process";
3349
- var writeJson2 = (value) => {
4205
+ var writeJson3 = (value) => {
3350
4206
  process9.stdout.write(`${JSON.stringify(value, null, 2)}
3351
4207
  `);
3352
4208
  };
3353
- var exitWithError2 = (error) => {
4209
+ var exitWithError3 = (error) => {
3354
4210
  const message = error instanceof Error ? error.message : String(error);
3355
4211
  log.error(message);
3356
4212
  process9.exit(1);
3357
4213
  };
3358
- var fail = (message) => {
4214
+ var fail2 = (message) => {
3359
4215
  throw new Error(message);
3360
4216
  };
3361
4217
  var parseContext = (contextJson) => {
@@ -3364,7 +4220,7 @@ var parseContext = (contextJson) => {
3364
4220
  }
3365
4221
  return JSON.parse(contextJson);
3366
4222
  };
3367
- var run = async (command, args, options) => {
4223
+ var run2 = async (command, args, options) => {
3368
4224
  const apiOptions = {
3369
4225
  ...options.server ? { serverUrl: options.server } : {},
3370
4226
  ...options.apiKey ? { apiKey: options.apiKey } : {}
@@ -3378,44 +4234,44 @@ var run = async (command, args, options) => {
3378
4234
  apiOptions
3379
4235
  );
3380
4236
  if (options.json) {
3381
- writeJson2(result);
4237
+ writeJson3(result);
3382
4238
  return;
3383
4239
  }
3384
4240
  log.success(`profiles ${command} completed`);
3385
4241
  };
3386
4242
  async function profilesListCommand(options) {
3387
4243
  try {
3388
- await run("list", {}, options);
4244
+ await run2("list", {}, options);
3389
4245
  } catch (error) {
3390
- exitWithError2(error);
4246
+ exitWithError3(error);
3391
4247
  }
3392
4248
  }
3393
4249
  async function profilesGetCommand(options) {
3394
4250
  try {
3395
4251
  if (!(options.id || options.name)) {
3396
- fail("profiles get requires --id or --name");
4252
+ fail2("profiles get requires --id or --name");
3397
4253
  }
3398
- await run("get", { id: options.id, name: options.name }, options);
4254
+ await run2("get", { id: options.id, name: options.name }, options);
3399
4255
  } catch (error) {
3400
- exitWithError2(error);
4256
+ exitWithError3(error);
3401
4257
  }
3402
4258
  }
3403
4259
  async function profilesCreateCommand(options) {
3404
4260
  try {
3405
- const name = options.name ?? fail("profiles create requires --name");
3406
- await run(
4261
+ const name = options.name ?? fail2("profiles create requires --name");
4262
+ await run2(
3407
4263
  "create",
3408
4264
  { name, context: parseContext(options.contextJson) },
3409
4265
  options
3410
4266
  );
3411
4267
  } catch (error) {
3412
- exitWithError2(error);
4268
+ exitWithError3(error);
3413
4269
  }
3414
4270
  }
3415
4271
  async function profilesUpdateCommand(options) {
3416
4272
  try {
3417
- const id = options.id ?? fail("profiles update requires --id");
3418
- await run(
4273
+ const id = options.id ?? fail2("profiles update requires --id");
4274
+ await run2(
3419
4275
  "update",
3420
4276
  {
3421
4277
  id,
@@ -3425,26 +4281,26 @@ async function profilesUpdateCommand(options) {
3425
4281
  options
3426
4282
  );
3427
4283
  } catch (error) {
3428
- exitWithError2(error);
4284
+ exitWithError3(error);
3429
4285
  }
3430
4286
  }
3431
4287
  async function profilesRemoveCommand(options) {
3432
4288
  try {
3433
- const id = options.id ?? fail("profiles remove requires --id");
4289
+ const id = options.id ?? fail2("profiles remove requires --id");
3434
4290
  if (!options.yes) {
3435
- fail("profiles remove requires --yes");
4291
+ fail2("profiles remove requires --yes");
3436
4292
  }
3437
- await run("remove", { id, yes: true }, options);
4293
+ await run2("remove", { id, yes: true }, options);
3438
4294
  } catch (error) {
3439
- exitWithError2(error);
4295
+ exitWithError3(error);
3440
4296
  }
3441
4297
  }
3442
4298
  async function profilesSetDefaultCommand(options) {
3443
4299
  try {
3444
- const id = options.id ?? fail("profiles set-default requires --id");
3445
- await run("set-default", { id }, options);
4300
+ const id = options.id ?? fail2("profiles set-default requires --id");
4301
+ await run2("set-default", { id }, options);
3446
4302
  } catch (error) {
3447
- exitWithError2(error);
4303
+ exitWithError3(error);
3448
4304
  }
3449
4305
  }
3450
4306
 
@@ -3453,14 +4309,14 @@ init_esm_shims();
3453
4309
  init_api();
3454
4310
  init_tui();
3455
4311
  import process10 from "process";
3456
- var writeJson3 = (value) => {
4312
+ var writeJson4 = (value) => {
3457
4313
  process10.stdout.write(`${JSON.stringify(value, null, 2)}
3458
4314
  `);
3459
4315
  };
3460
- var fail2 = (message) => {
4316
+ var fail3 = (message) => {
3461
4317
  throw new Error(message);
3462
4318
  };
3463
- var run2 = async (command, args, options) => {
4319
+ var run3 = async (command, args, options) => {
3464
4320
  const apiOptions = {
3465
4321
  ...options.server ? { serverUrl: options.server } : {},
3466
4322
  ...options.apiKey ? { apiKey: options.apiKey } : {}
@@ -3474,40 +4330,40 @@ var run2 = async (command, args, options) => {
3474
4330
  apiOptions
3475
4331
  );
3476
4332
  if (options.json) {
3477
- writeJson3(result);
4333
+ writeJson4(result);
3478
4334
  return;
3479
4335
  }
3480
4336
  log.success(`projects ${command} completed`);
3481
4337
  };
3482
- var exitWithError3 = (error) => {
4338
+ var exitWithError4 = (error) => {
3483
4339
  const message = error instanceof Error ? error.message : String(error);
3484
4340
  log.error(message);
3485
4341
  process10.exit(1);
3486
4342
  };
3487
4343
  async function projectsGetCommand(options) {
3488
4344
  try {
3489
- const id = options.id ?? fail2("projects get requires --id");
3490
- await run2("get", { id }, options);
4345
+ const id = options.id ?? fail3("projects get requires --id");
4346
+ await run3("get", { id }, options);
3491
4347
  } catch (error) {
3492
- exitWithError3(error);
4348
+ exitWithError4(error);
3493
4349
  }
3494
4350
  }
3495
4351
  async function projectsCreateCommand(options) {
3496
4352
  try {
3497
- const name = options.name ?? fail2("projects create requires --name");
3498
- await run2(
4353
+ const name = options.name ?? fail3("projects create requires --name");
4354
+ await run3(
3499
4355
  "create",
3500
4356
  { name, description: options.description, orgId: options.orgId },
3501
4357
  options
3502
4358
  );
3503
4359
  } catch (error) {
3504
- exitWithError3(error);
4360
+ exitWithError4(error);
3505
4361
  }
3506
4362
  }
3507
4363
  async function projectsUpdateCommand(options) {
3508
4364
  try {
3509
- const id = options.id ?? fail2("projects update requires --id");
3510
- await run2(
4365
+ const id = options.id ?? fail3("projects update requires --id");
4366
+ await run3(
3511
4367
  "update",
3512
4368
  {
3513
4369
  id,
@@ -3517,18 +4373,18 @@ async function projectsUpdateCommand(options) {
3517
4373
  options
3518
4374
  );
3519
4375
  } catch (error) {
3520
- exitWithError3(error);
4376
+ exitWithError4(error);
3521
4377
  }
3522
4378
  }
3523
4379
  async function projectsRemoveCommand(options) {
3524
4380
  try {
3525
- const id = options.id ?? fail2("projects remove requires --id");
4381
+ const id = options.id ?? fail3("projects remove requires --id");
3526
4382
  if (!options.yes) {
3527
- fail2("projects remove requires --yes");
4383
+ fail3("projects remove requires --yes");
3528
4384
  }
3529
- await run2("remove", { id, yes: true }, options);
4385
+ await run3("remove", { id, yes: true }, options);
3530
4386
  } catch (error) {
3531
- exitWithError3(error);
4387
+ exitWithError4(error);
3532
4388
  }
3533
4389
  }
3534
4390
 
@@ -3537,26 +4393,26 @@ init_esm_shims();
3537
4393
  init_api();
3538
4394
  init_tui();
3539
4395
  import process11 from "process";
3540
- var writeJson4 = (value) => {
4396
+ var writeJson5 = (value) => {
3541
4397
  process11.stdout.write(`${JSON.stringify(value, null, 2)}
3542
4398
  `);
3543
4399
  };
3544
- var parseCsv2 = (input) => {
4400
+ var parseCsv3 = (input) => {
3545
4401
  if (!input) {
3546
4402
  return void 0;
3547
4403
  }
3548
4404
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
3549
4405
  return values.length > 0 ? values : void 0;
3550
4406
  };
3551
- var exitWithError4 = (error) => {
4407
+ var exitWithError5 = (error) => {
3552
4408
  const message = error instanceof Error ? error.message : String(error);
3553
4409
  log.error(message);
3554
4410
  process11.exit(1);
3555
4411
  };
3556
- var fail3 = (message) => {
4412
+ var fail4 = (message) => {
3557
4413
  throw new Error(message);
3558
4414
  };
3559
- var run3 = async (command, args, options) => {
4415
+ var run4 = async (command, args, options) => {
3560
4416
  const apiOptions = {
3561
4417
  ...options.server ? { serverUrl: options.server } : {},
3562
4418
  ...options.apiKey ? { apiKey: options.apiKey } : {}
@@ -3570,76 +4426,76 @@ var run3 = async (command, args, options) => {
3570
4426
  apiOptions
3571
4427
  );
3572
4428
  if (options.json) {
3573
- writeJson4(result);
4429
+ writeJson5(result);
3574
4430
  return;
3575
4431
  }
3576
4432
  log.success(`references ${command} completed`);
3577
4433
  };
3578
4434
  async function referencesListCommand(options) {
3579
4435
  try {
3580
- const ruleId = options.ruleId ?? fail3("references list requires --rule-id");
3581
- await run3("list", { ruleId }, options);
4436
+ const ruleId = options.ruleId ?? fail4("references list requires --rule-id");
4437
+ await run4("list", { ruleId }, options);
3582
4438
  } catch (error) {
3583
- exitWithError4(error);
4439
+ exitWithError5(error);
3584
4440
  }
3585
4441
  }
3586
4442
  async function referencesGetCommand(options) {
3587
4443
  try {
3588
- const id = options.id ?? fail3("references get requires --id");
3589
- await run3("get", { id }, options);
4444
+ const id = options.id ?? fail4("references get requires --id");
4445
+ await run4("get", { id }, options);
3590
4446
  } catch (error) {
3591
- exitWithError4(error);
4447
+ exitWithError5(error);
3592
4448
  }
3593
4449
  }
3594
4450
  async function referencesCreateCommand(options) {
3595
4451
  try {
3596
- const ruleId = options.ruleId ?? fail3("references create requires --rule-id");
3597
- const file = options.file ?? fail3("references create requires --file");
3598
- await run3("create", { ruleId, file }, options);
4452
+ const ruleId = options.ruleId ?? fail4("references create requires --rule-id");
4453
+ const file = options.file ?? fail4("references create requires --file");
4454
+ await run4("create", { ruleId, file }, options);
3599
4455
  } catch (error) {
3600
- exitWithError4(error);
4456
+ exitWithError5(error);
3601
4457
  }
3602
4458
  }
3603
4459
  async function referencesUpdateCommand(options) {
3604
4460
  try {
3605
- const id = options.id ?? fail3("references update requires --id");
3606
- await run3(
4461
+ const id = options.id ?? fail4("references update requires --id");
4462
+ await run4(
3607
4463
  "update",
3608
4464
  { id, label: options.label, replaceFile: options.replaceFile },
3609
4465
  options
3610
4466
  );
3611
4467
  } catch (error) {
3612
- exitWithError4(error);
4468
+ exitWithError5(error);
3613
4469
  }
3614
4470
  }
3615
4471
  async function referencesRemoveCommand(options) {
3616
4472
  try {
3617
- const id = options.id ?? fail3("references remove requires --id");
4473
+ const id = options.id ?? fail4("references remove requires --id");
3618
4474
  if (!options.yes) {
3619
- fail3("references remove requires --yes");
4475
+ fail4("references remove requires --yes");
3620
4476
  }
3621
- await run3("remove", { id, yes: true }, options);
4477
+ await run4("remove", { id, yes: true }, options);
3622
4478
  } catch (error) {
3623
- exitWithError4(error);
4479
+ exitWithError5(error);
3624
4480
  }
3625
4481
  }
3626
4482
  async function referencesReorderCommand(options) {
3627
4483
  try {
3628
- const ruleId = options.ruleId ?? fail3("references reorder requires --rule-id");
3629
- const orderedIds = parseCsv2(options.orderedIds);
4484
+ const ruleId = options.ruleId ?? fail4("references reorder requires --rule-id");
4485
+ const orderedIds = parseCsv3(options.orderedIds);
3630
4486
  if (!orderedIds || orderedIds.length === 0) {
3631
- fail3("references reorder requires --ordered-ids");
4487
+ fail4("references reorder requires --ordered-ids");
3632
4488
  }
3633
- await run3("reorder", { ruleId, orderedIds }, options);
4489
+ await run4("reorder", { ruleId, orderedIds }, options);
3634
4490
  } catch (error) {
3635
- exitWithError4(error);
4491
+ exitWithError5(error);
3636
4492
  }
3637
4493
  }
3638
4494
 
3639
4495
  // src/commands/remove.ts
3640
4496
  init_esm_shims();
3641
4497
  import { rm } from "fs/promises";
3642
- import { join as join4, resolve as resolve3 } from "path";
4498
+ import { join as join4, resolve as resolve4 } from "path";
3643
4499
  import process12 from "process";
3644
4500
  init_tui();
3645
4501
  async function collectInstalledSkills(detectedAgents, options) {
@@ -3712,8 +4568,8 @@ async function confirmRemoval(selectedCount, options) {
3712
4568
  async function removeSkill(skill, removeSpinner) {
3713
4569
  removeSpinner.start(`Removing ${skill.name} from ${skill.agentName}...`);
3714
4570
  try {
3715
- const resolvedSkillPath = resolve3(skill.skillPath);
3716
- const resolvedInstallPath = resolve3(skill.installPath);
4571
+ const resolvedSkillPath = resolve4(skill.skillPath);
4572
+ const resolvedInstallPath = resolve4(skill.installPath);
3717
4573
  if (!resolvedSkillPath.startsWith(`${resolvedInstallPath}/`)) {
3718
4574
  removeSpinner.stop(`Unsafe path for ${skill.name}`);
3719
4575
  log.warn(" Skill path escapes install directory, skipping.");
@@ -3779,26 +4635,26 @@ init_esm_shims();
3779
4635
  init_api();
3780
4636
  init_tui();
3781
4637
  import process13 from "process";
3782
- var parseCsv3 = (input) => {
4638
+ var parseCsv4 = (input) => {
3783
4639
  if (!input) {
3784
4640
  return void 0;
3785
4641
  }
3786
4642
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
3787
4643
  return values.length > 0 ? values : void 0;
3788
4644
  };
3789
- var writeJson5 = (value) => {
4645
+ var writeJson6 = (value) => {
3790
4646
  process13.stdout.write(`${JSON.stringify(value, null, 2)}
3791
4647
  `);
3792
4648
  };
3793
- var exitWithError5 = (error) => {
4649
+ var exitWithError6 = (error) => {
3794
4650
  const message = error instanceof Error ? error.message : String(error);
3795
4651
  log.error(message);
3796
4652
  process13.exit(1);
3797
4653
  };
3798
- var fail4 = (message) => {
4654
+ var fail5 = (message) => {
3799
4655
  throw new Error(message);
3800
4656
  };
3801
- var run4 = async (command, args, options) => {
4657
+ var run5 = async (command, args, options) => {
3802
4658
  const apiOptions = {
3803
4659
  ...options.server ? { serverUrl: options.server } : {},
3804
4660
  ...options.apiKey ? { apiKey: options.apiKey } : {}
@@ -3812,161 +4668,161 @@ var run4 = async (command, args, options) => {
3812
4668
  apiOptions
3813
4669
  );
3814
4670
  if (options.json) {
3815
- writeJson5(result);
4671
+ writeJson6(result);
3816
4672
  return;
3817
4673
  }
3818
4674
  log.success(`rules ${command} completed`);
3819
4675
  };
3820
4676
  async function rulesGetCommand(options) {
3821
4677
  try {
3822
- const id = options.id ?? fail4("rules get requires --id");
3823
- await run4("get", { id }, options);
4678
+ const id = options.id ?? fail5("rules get requires --id");
4679
+ await run5("get", { id }, options);
3824
4680
  } catch (error) {
3825
- exitWithError5(error);
4681
+ exitWithError6(error);
3826
4682
  }
3827
4683
  }
3828
4684
  async function rulesCreateCommand(options) {
3829
4685
  try {
3830
- const title = options.title ?? fail4("rules create requires --title");
3831
- const content = options.content ?? fail4("rules create requires --content");
3832
- await run4(
4686
+ const title = options.title ?? fail5("rules create requires --title");
4687
+ const content = options.content ?? fail5("rules create requires --content");
4688
+ await run5(
3833
4689
  "create",
3834
4690
  {
3835
4691
  title,
3836
4692
  content,
3837
4693
  projectId: options.projectId,
3838
- tags: parseCsv3(options.tags),
4694
+ tags: parseCsv4(options.tags),
3839
4695
  priority: options.priority
3840
4696
  },
3841
4697
  options
3842
4698
  );
3843
4699
  } catch (error) {
3844
- exitWithError5(error);
4700
+ exitWithError6(error);
3845
4701
  }
3846
4702
  }
3847
4703
  async function rulesUpdateCommand(options) {
3848
4704
  try {
3849
- const id = options.id ?? fail4("rules update requires --id");
3850
- await run4(
4705
+ const id = options.id ?? fail5("rules update requires --id");
4706
+ await run5(
3851
4707
  "update",
3852
4708
  {
3853
4709
  id,
3854
4710
  title: options.title,
3855
4711
  content: options.content,
3856
- tags: parseCsv3(options.tags),
4712
+ tags: parseCsv4(options.tags),
3857
4713
  priority: options.priority
3858
4714
  },
3859
4715
  options
3860
4716
  );
3861
4717
  } catch (error) {
3862
- exitWithError5(error);
4718
+ exitWithError6(error);
3863
4719
  }
3864
4720
  }
3865
4721
  async function rulesRemoveCommand(options) {
3866
4722
  try {
3867
- const id = options.id ?? fail4("rules remove requires --id");
4723
+ const id = options.id ?? fail5("rules remove requires --id");
3868
4724
  if (!options.yes) {
3869
- fail4("rules remove requires --yes");
4725
+ fail5("rules remove requires --yes");
3870
4726
  }
3871
- await run4("remove", { id, yes: true }, options);
4727
+ await run5("remove", { id, yes: true }, options);
3872
4728
  } catch (error) {
3873
- exitWithError5(error);
4729
+ exitWithError6(error);
3874
4730
  }
3875
4731
  }
3876
4732
  async function rulesEnableCommand(options) {
3877
4733
  try {
3878
- const id = options.id ?? fail4("rules enable requires --id");
3879
- await run4("enable", { id }, options);
4734
+ const id = options.id ?? fail5("rules enable requires --id");
4735
+ await run5("enable", { id }, options);
3880
4736
  } catch (error) {
3881
- exitWithError5(error);
4737
+ exitWithError6(error);
3882
4738
  }
3883
4739
  }
3884
4740
  async function rulesDisableCommand(options) {
3885
4741
  try {
3886
- const id = options.id ?? fail4("rules disable requires --id");
3887
- await run4("disable", { id }, options);
4742
+ const id = options.id ?? fail5("rules disable requires --id");
4743
+ await run5("disable", { id }, options);
3888
4744
  } catch (error) {
3889
- exitWithError5(error);
4745
+ exitWithError6(error);
3890
4746
  }
3891
4747
  }
3892
4748
  async function rulesMoveCommand(options) {
3893
4749
  try {
3894
- const id = options.id ?? fail4("rules move requires --id");
3895
- const projectId = options.projectId ?? fail4("rules move requires --project-id");
3896
- await run4("move", { id, projectId }, options);
4750
+ const id = options.id ?? fail5("rules move requires --id");
4751
+ const projectId = options.projectId ?? fail5("rules move requires --project-id");
4752
+ await run5("move", { id, projectId }, options);
3897
4753
  } catch (error) {
3898
- exitWithError5(error);
4754
+ exitWithError6(error);
3899
4755
  }
3900
4756
  }
3901
4757
  async function rulesDuplicateCommand(options) {
3902
4758
  try {
3903
- const id = options.id ?? fail4("rules duplicate requires --id");
3904
- await run4(
4759
+ const id = options.id ?? fail5("rules duplicate requires --id");
4760
+ await run5(
3905
4761
  "duplicate",
3906
4762
  { id, targetProjectId: options.targetProjectId },
3907
4763
  options
3908
4764
  );
3909
4765
  } catch (error) {
3910
- exitWithError5(error);
4766
+ exitWithError6(error);
3911
4767
  }
3912
4768
  }
3913
4769
  async function rulesForkCommand(options) {
3914
4770
  try {
3915
- const id = options.id ?? fail4("rules fork requires --id");
3916
- await run4(
4771
+ const id = options.id ?? fail5("rules fork requires --id");
4772
+ await run5(
3917
4773
  "fork",
3918
4774
  { id, targetProjectId: options.targetProjectId },
3919
4775
  options
3920
4776
  );
3921
4777
  } catch (error) {
3922
- exitWithError5(error);
4778
+ exitWithError6(error);
3923
4779
  }
3924
4780
  }
3925
4781
  async function rulesSyncStatusCommand(options) {
3926
4782
  try {
3927
- await run4("sync-status", { id: options.id }, options);
4783
+ await run5("sync-status", { id: options.id }, options);
3928
4784
  } catch (error) {
3929
- exitWithError5(error);
4785
+ exitWithError6(error);
3930
4786
  }
3931
4787
  }
3932
4788
  async function rulesSyncHistoryCommand(options) {
3933
4789
  try {
3934
- const id = options.id ?? fail4("rules sync-history requires --id");
3935
- await run4("sync-history", { id }, options);
4790
+ const id = options.id ?? fail5("rules sync-history requires --id");
4791
+ await run5("sync-history", { id }, options);
3936
4792
  } catch (error) {
3937
- exitWithError5(error);
4793
+ exitWithError6(error);
3938
4794
  }
3939
4795
  }
3940
4796
  async function rulesSyncEnableCommand(options) {
3941
4797
  try {
3942
- const id = options.id ?? fail4("rules sync-enable requires --id");
3943
- await run4("sync-enable", { id }, options);
4798
+ const id = options.id ?? fail5("rules sync-enable requires --id");
4799
+ await run5("sync-enable", { id }, options);
3944
4800
  } catch (error) {
3945
- exitWithError5(error);
4801
+ exitWithError6(error);
3946
4802
  }
3947
4803
  }
3948
4804
  async function rulesSyncDisableCommand(options) {
3949
4805
  try {
3950
- const id = options.id ?? fail4("rules sync-disable requires --id");
3951
- await run4("sync-disable", { id }, options);
4806
+ const id = options.id ?? fail5("rules sync-disable requires --id");
4807
+ await run5("sync-disable", { id }, options);
3952
4808
  } catch (error) {
3953
- exitWithError5(error);
4809
+ exitWithError6(error);
3954
4810
  }
3955
4811
  }
3956
4812
  async function rulesSyncCheckCommand(options) {
3957
4813
  try {
3958
- const id = options.id ?? fail4("rules sync-check requires --id");
3959
- await run4("sync-check", { id }, options);
4814
+ const id = options.id ?? fail5("rules sync-check requires --id");
4815
+ await run5("sync-check", { id }, options);
3960
4816
  } catch (error) {
3961
- exitWithError5(error);
4817
+ exitWithError6(error);
3962
4818
  }
3963
4819
  }
3964
4820
  async function rulesSyncNowCommand(options) {
3965
4821
  try {
3966
- const id = options.id ?? fail4("rules sync-now requires --id");
3967
- await run4("sync-now", { id }, options);
4822
+ const id = options.id ?? fail5("rules sync-now requires --id");
4823
+ await run5("sync-now", { id }, options);
3968
4824
  } catch (error) {
3969
- exitWithError5(error);
4825
+ exitWithError6(error);
3970
4826
  }
3971
4827
  }
3972
4828
 
@@ -3983,11 +4839,11 @@ import {
3983
4839
  outro as outro2,
3984
4840
  spinner as spinner3
3985
4841
  } from "@clack/prompts";
3986
- import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
4842
+ import { Data as Data8, Effect as Effect9, pipe as pipe9 } from "effect";
3987
4843
  init_api();
3988
- var UpdateError = class extends Data7.TaggedError("UpdateError") {
4844
+ var UpdateError = class extends Data8.TaggedError("UpdateError") {
3989
4845
  };
3990
- var UserCancelledError = class extends Data7.TaggedError("UserCancelledError") {
4846
+ var UserCancelledError = class extends Data8.TaggedError("UserCancelledError") {
3991
4847
  };
3992
4848
  async function resolveValidInstallPath(agent, options) {
3993
4849
  const installPath = resolveInstallPath(agent, {
@@ -3999,7 +4855,7 @@ async function resolveValidInstallPath(agent, options) {
3999
4855
  const exists = await directoryExistsAsync(installPath);
4000
4856
  return exists ? installPath : null;
4001
4857
  }
4002
- var collectSourcesFromAgent = (agent, options, sourcesToUpdate) => Effect8.tryPromise({
4858
+ var collectSourcesFromAgent = (agent, options, sourcesToUpdate) => Effect9.tryPromise({
4003
4859
  try: async () => {
4004
4860
  const installPath = await resolveValidInstallPath(agent, options);
4005
4861
  if (!installPath) {
@@ -4030,10 +4886,10 @@ var collectSourcesFromAgent = (agent, options, sourcesToUpdate) => Effect8.tryPr
4030
4886
  },
4031
4887
  catch: () => new UpdateError({ message: "Failed to collect sources" })
4032
4888
  });
4033
- var collectSources = (detectedAgents, options) => pipe8(
4034
- Effect8.succeed(/* @__PURE__ */ new Map()),
4035
- Effect8.tap(
4036
- (sourcesToUpdate) => Effect8.forEach(
4889
+ var collectSources = (detectedAgents, options) => pipe9(
4890
+ Effect9.succeed(/* @__PURE__ */ new Map()),
4891
+ Effect9.tap(
4892
+ (sourcesToUpdate) => Effect9.forEach(
4037
4893
  detectedAgents,
4038
4894
  (agent) => collectSourcesFromAgent(agent, options, sourcesToUpdate),
4039
4895
  { concurrency: 1 }
@@ -4042,9 +4898,9 @@ var collectSources = (detectedAgents, options) => pipe8(
4042
4898
  );
4043
4899
  var selectSources = (sourcesToUpdate, options) => {
4044
4900
  if (options.yes) {
4045
- return Effect8.succeed(sourcesToUpdate);
4901
+ return Effect9.succeed(sourcesToUpdate);
4046
4902
  }
4047
- return Effect8.tryPromise({
4903
+ return Effect9.tryPromise({
4048
4904
  try: async () => {
4049
4905
  const sources = Array.from(sourcesToUpdate.entries()).map(
4050
4906
  ([key, source]) => ({
@@ -4095,7 +4951,7 @@ var buildFetchOptionsForSource = (source, options) => {
4095
4951
  }
4096
4952
  return fetchOptions;
4097
4953
  };
4098
- var updateAgentSkills = (agentId, agentName, installPath, response, serverUrl, updateSpinner) => Effect8.tryPromise({
4954
+ var updateAgentSkills = (agentId, agentName, installPath, response, serverUrl, updateSpinner) => Effect9.tryPromise({
4099
4955
  try: async () => {
4100
4956
  updateSpinner.start(`Updating ${agentName}...`);
4101
4957
  const result = await writeSkillsAsync(
@@ -4133,15 +4989,15 @@ var updateSource = (source, options, updateSpinner) => {
4133
4989
  const serverUrl = options.server ?? source.serverUrl;
4134
4990
  const fetchOptions = buildFetchOptionsForSource(source, options);
4135
4991
  if (fetchOptions === null) {
4136
- return Effect8.fail(
4992
+ return Effect9.fail(
4137
4993
  new UpdateError({
4138
4994
  message: "Skills installed with legacy metadata format. Please reinstall using 'braid install --profile <name>' or 'braid install --projects <names>'.",
4139
4995
  source: sourceDesc
4140
4996
  })
4141
4997
  );
4142
4998
  }
4143
- return pipe8(
4144
- Effect8.tryPromise({
4999
+ return pipe9(
5000
+ Effect9.tryPromise({
4145
5001
  try: async () => {
4146
5002
  updateSpinner.start(`Fetching latest skills from ${sourceDesc}...`);
4147
5003
  const response = await fetchSkillsAsync(fetchOptions);
@@ -4155,9 +5011,9 @@ var updateSource = (source, options, updateSpinner) => {
4155
5011
  source: sourceDesc
4156
5012
  })
4157
5013
  }),
4158
- Effect8.flatMap(
4159
- (response) => pipe8(
4160
- Effect8.forEach(
5014
+ Effect9.flatMap(
5015
+ (response) => pipe9(
5016
+ Effect9.forEach(
4161
5017
  source.agents,
4162
5018
  ({ agentId, agentName, installPath }) => updateAgentSkills(
4163
5019
  agentId,
@@ -4169,7 +5025,7 @@ var updateSource = (source, options, updateSpinner) => {
4169
5025
  ),
4170
5026
  { concurrency: 1 }
4171
5027
  ),
4172
- Effect8.map((results) => ({
5028
+ Effect9.map((results) => ({
4173
5029
  updated: results.reduce((sum, r) => sum + r.updated, 0),
4174
5030
  errors: results.reduce((sum, r) => sum + r.errors, 0)
4175
5031
  }))
@@ -4177,22 +5033,22 @@ var updateSource = (source, options, updateSpinner) => {
4177
5033
  )
4178
5034
  );
4179
5035
  };
4180
- var updateAllSources = (sources, options, updateSpinner) => pipe8(
4181
- Effect8.forEach(
5036
+ var updateAllSources = (sources, options, updateSpinner) => pipe9(
5037
+ Effect9.forEach(
4182
5038
  Array.from(sources.values()),
4183
- (source) => pipe8(
5039
+ (source) => pipe9(
4184
5040
  updateSource(source, options, updateSpinner),
4185
- Effect8.catchAll((error) => {
5041
+ Effect9.catchAll((error) => {
4186
5042
  updateSpinner.stop(
4187
5043
  `Failed to update from ${getSourceDesc(source)}`
4188
5044
  );
4189
5045
  log3.error(` ${error.message}`);
4190
- return Effect8.succeed({ updated: 0, errors: 1 });
5046
+ return Effect9.succeed({ updated: 0, errors: 1 });
4191
5047
  })
4192
5048
  ),
4193
5049
  { concurrency: 1 }
4194
5050
  ),
4195
- Effect8.map((results) => ({
5051
+ Effect9.map((results) => ({
4196
5052
  totalUpdated: results.reduce((sum, r) => sum + r.updated, 0),
4197
5053
  totalErrors: results.reduce((sum, r) => sum + r.errors, 0)
4198
5054
  }))
@@ -4231,28 +5087,28 @@ var handleProgramExit = (result, updateSpinner) => {
4231
5087
  async function updateCommand(options) {
4232
5088
  const updateSpinner = spinner3();
4233
5089
  updateSpinner.start("Scanning for installed skills...");
4234
- const program2 = pipe8(
4235
- Effect8.tryPromise({
5090
+ const program2 = pipe9(
5091
+ Effect9.tryPromise({
4236
5092
  try: () => detectAgentsAsync(),
4237
5093
  catch: () => new UpdateError({ message: "Failed to detect agents" })
4238
5094
  }),
4239
- Effect8.filterOrFail(
4240
- (agents) => agents.length > 0,
5095
+ Effect9.filterOrFail(
5096
+ (agents2) => agents2.length > 0,
4241
5097
  () => new UpdateError({ message: "No AI coding agents detected." })
4242
5098
  ),
4243
- Effect8.flatMap((detectedAgents) => collectSources(detectedAgents, options)),
4244
- Effect8.tap((sources) => {
5099
+ Effect9.flatMap((detectedAgents) => collectSources(detectedAgents, options)),
5100
+ Effect9.tap((sources) => {
4245
5101
  updateSpinner.stop(`Found ${sources.size} source(s) to update`);
4246
5102
  }),
4247
- Effect8.filterOrFail(
5103
+ Effect9.filterOrFail(
4248
5104
  (sources) => sources.size > 0,
4249
5105
  () => new UpdateError({ message: "No skills installed via braid." })
4250
5106
  ),
4251
- Effect8.flatMap((sources) => selectSources(sources, options)),
4252
- Effect8.flatMap(
5107
+ Effect9.flatMap((sources) => selectSources(sources, options)),
5108
+ Effect9.flatMap(
4253
5109
  (selectedSources) => updateAllSources(selectedSources, options, updateSpinner)
4254
5110
  ),
4255
- Effect8.tap(({ totalUpdated, totalErrors }) => {
5111
+ Effect9.tap(({ totalUpdated, totalErrors }) => {
4256
5112
  if (totalErrors > 0) {
4257
5113
  outro2(`Updated ${totalUpdated} skills with ${totalErrors} errors.`);
4258
5114
  } else {
@@ -4260,10 +5116,136 @@ async function updateCommand(options) {
4260
5116
  }
4261
5117
  })
4262
5118
  );
4263
- const result = await Effect8.runPromiseExit(program2);
5119
+ const result = await Effect9.runPromiseExit(program2);
4264
5120
  handleProgramExit(result, updateSpinner);
4265
5121
  }
4266
5122
 
5123
+ // src/commands/workflows.ts
5124
+ init_esm_shims();
5125
+ init_api();
5126
+ init_tui();
5127
+ import process14 from "process";
5128
+ var writeJson7 = (value) => {
5129
+ process14.stdout.write(`${JSON.stringify(value, null, 2)}
5130
+ `);
5131
+ };
5132
+ var fail6 = (message) => {
5133
+ throw new Error(message);
5134
+ };
5135
+ var parseMetadata = (metadata) => {
5136
+ if (!metadata) {
5137
+ return void 0;
5138
+ }
5139
+ try {
5140
+ return JSON.parse(metadata);
5141
+ } catch {
5142
+ throw new Error("metadata must be valid JSON");
5143
+ }
5144
+ };
5145
+ var run6 = async (command, args, options) => {
5146
+ const apiOptions = {
5147
+ ...options.server ? { serverUrl: options.server } : {},
5148
+ ...options.apiKey ? { apiKey: options.apiKey } : {}
5149
+ };
5150
+ const result = await runLifecycleCommandAsync(
5151
+ {
5152
+ domain: "workflows",
5153
+ command,
5154
+ args
5155
+ },
5156
+ apiOptions
5157
+ );
5158
+ if (options.json) {
5159
+ writeJson7(result);
5160
+ return;
5161
+ }
5162
+ log.success(`workflows ${command} completed`);
5163
+ };
5164
+ var exitWithError7 = (error) => {
5165
+ const message = error instanceof Error ? error.message : String(error);
5166
+ log.error(message);
5167
+ process14.exit(1);
5168
+ };
5169
+ async function workflowsListCommand(options) {
5170
+ try {
5171
+ await run6("list", { projectId: options.projectId }, options);
5172
+ } catch (error) {
5173
+ exitWithError7(error);
5174
+ }
5175
+ }
5176
+ async function workflowsStartCommand(options) {
5177
+ try {
5178
+ const sessionId = options.sessionId ?? fail6("workflows start requires --session-id");
5179
+ const flowId = options.flowId ?? fail6("workflows start requires --flow-id");
5180
+ await run6(
5181
+ "start",
5182
+ {
5183
+ sessionId,
5184
+ flowId,
5185
+ currentNodeId: options.currentNodeId,
5186
+ currentStepLabel: options.currentStepLabel,
5187
+ metadata: parseMetadata(options.metadata)
5188
+ },
5189
+ options
5190
+ );
5191
+ } catch (error) {
5192
+ exitWithError7(error);
5193
+ }
5194
+ }
5195
+ async function workflowsProgressCommand(options) {
5196
+ try {
5197
+ const executionId = options.executionId ?? fail6("workflows progress requires --execution-id");
5198
+ await run6(
5199
+ "progress",
5200
+ {
5201
+ executionId,
5202
+ currentNodeId: options.currentNodeId,
5203
+ currentStepLabel: options.currentStepLabel,
5204
+ metadata: parseMetadata(options.metadata)
5205
+ },
5206
+ options
5207
+ );
5208
+ } catch (error) {
5209
+ exitWithError7(error);
5210
+ }
5211
+ }
5212
+ async function workflowsActiveCommand(options) {
5213
+ try {
5214
+ const sessionId = options.sessionId ?? fail6("workflows active requires --session-id");
5215
+ await run6("active", { sessionId }, options);
5216
+ } catch (error) {
5217
+ exitWithError7(error);
5218
+ }
5219
+ }
5220
+ async function workflowsCompleteCommand(options) {
5221
+ try {
5222
+ const executionId = options.executionId ?? fail6("workflows complete requires --execution-id");
5223
+ await run6("complete", { executionId }, options);
5224
+ } catch (error) {
5225
+ exitWithError7(error);
5226
+ }
5227
+ }
5228
+ async function workflowsFailCommand(options) {
5229
+ try {
5230
+ const executionId = options.executionId ?? fail6("workflows fail requires --execution-id");
5231
+ await run6(
5232
+ "fail",
5233
+ { executionId, errorMessage: options.errorMessage },
5234
+ options
5235
+ );
5236
+ } catch (error) {
5237
+ exitWithError7(error);
5238
+ }
5239
+ }
5240
+ async function workflowsCancelCommand(options) {
5241
+ try {
5242
+ const executionId = options.executionId ?? fail6("workflows cancel requires --execution-id");
5243
+ await run6("cancel", { executionId }, options);
5244
+ } catch (error) {
5245
+ exitWithError7(error);
5246
+ }
5247
+ }
5248
+
4267
5249
  // src/index.ts
4268
5250
  var require2 = createRequire(import.meta.url);
4269
5251
  var { version: PACKAGE_VERSION } = require2("../package.json");
@@ -4272,7 +5254,13 @@ program.name("braid").description(
4272
5254
  "Install braid prompts as agent skills to your local development environment"
4273
5255
  ).version(PACKAGE_VERSION);
4274
5256
  var auth = program.command("auth").description("Configure API key for braid authentication");
4275
- auth.command("login", { isDefault: true }).description("Configure API key").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--token <token>", "API key for non-interactive login").option("--no-scope", "Skip scope setup after login").action(authCommand);
5257
+ 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(
5258
+ "--token <token>",
5259
+ "API key for non-interactive login (skips device flow)"
5260
+ ).option(
5261
+ "--timeout <seconds>",
5262
+ "Device flow timeout in seconds (default: 300)"
5263
+ ).option("--no-scope", "Skip scope setup after login").action(authCommand);
4276
5264
  auth.command("status").description("Show current authentication status").action(authStatusCommand);
4277
5265
  auth.command("logout").description("Remove stored API key").action(authLogoutCommand);
4278
5266
  program.command("install").alias("add").description(
@@ -4353,6 +5341,24 @@ profiles.command("create").description("Create profile").requiredOption("--name
4353
5341
  profiles.command("update").description("Update profile").requiredOption("--id <id>", "Profile ID").option("--name <name>", "Profile name").option("--context-json <json>", "Context JSON").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(profilesUpdateCommand);
4354
5342
  profiles.command("remove").description("Remove profile").requiredOption("--id <id>", "Profile ID").option("-y, --yes", "Confirm destructive action").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(profilesRemoveCommand);
4355
5343
  profiles.command("set-default").description("Set default profile").requiredOption("--id <id>", "Profile 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(profilesSetDefaultCommand);
5344
+ var agents = program.command("agents").description("Manage agent artifacts");
5345
+ var workflows = program.command("workflows").description("Manage local workflow execution lifecycle");
5346
+ workflows.command("list").description("List control flows available for execution").option("--project-id <id>", "Project ID filter").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(workflowsListCommand);
5347
+ workflows.command("start").description("Start a workflow execution for a CLI session").requiredOption("--session-id <id>", "CLI session ID").requiredOption("--flow-id <id>", "Control flow ID").option("--current-node-id <id>", "Current node ID").option("--current-step-label <label>", "Current step label").option("--metadata <json>", "JSON metadata payload").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(workflowsStartCommand);
5348
+ workflows.command("progress").description("Update workflow execution progress").requiredOption("--execution-id <id>", "Workflow execution ID").option("--current-node-id <id>", "Current node ID").option("--current-step-label <label>", "Current step label").option("--metadata <json>", "JSON metadata payload").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(workflowsProgressCommand);
5349
+ workflows.command("active").description("Get active workflow execution for a CLI session").requiredOption("--session-id <id>", "CLI session 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(workflowsActiveCommand);
5350
+ workflows.command("complete").description("Mark a workflow execution complete").requiredOption("--execution-id <id>", "Workflow execution 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(workflowsCompleteCommand);
5351
+ workflows.command("fail").description("Mark a workflow execution failed").requiredOption("--execution-id <id>", "Workflow execution ID").option("--error-message <message>", "Failure message").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(workflowsFailCommand);
5352
+ workflows.command("cancel").description("Cancel a workflow execution").requiredOption("--execution-id <id>", "Workflow execution 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(workflowsCancelCommand);
5353
+ agents.command("list").description("List saved agents").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(agentsListCommand);
5354
+ agents.command("get").description("Get an agent by id").requiredOption("--id <id>", "Agent 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(agentsGetCommand);
5355
+ agents.command("create").description("Create an agent").requiredOption("--name <name>", "Agent name").requiredOption("--description <description>", "Agent description").requiredOption("--prompt <prompt>", "Agent system prompt").option("--scope <scope>", "Scope: global or project").option("--project-id <id>", "Project ID for project-scoped agent").option("--mode <mode>", "Mode: primary, subagent, all").option("--model <model>", "Model override").option("--skills <list>", "Comma-separated skill names").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(agentsCreateCommand);
5356
+ agents.command("update").description("Update an agent").requiredOption("--id <id>", "Agent ID").option("--name <name>", "Agent name").option("--description <description>", "Agent description").option("--prompt <prompt>", "Agent system prompt").option("--mode <mode>", "Mode: primary, subagent, all").option("--model <model>", "Model override").option("--skills <list>", "Comma-separated skill names").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(agentsUpdateCommand);
5357
+ agents.command("remove").description("Remove an agent").requiredOption("--id <id>", "Agent ID").option("-y, --yes", "Confirm destructive action").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(agentsRemoveCommand);
5358
+ agents.command("install").description("Install a saved agent to local coding tools").requiredOption("--id <id>", "Agent ID").option(
5359
+ "-a, --agents <list>",
5360
+ "Comma-separated list of target coding tools (eg. claude-code,opencode)"
5361
+ ).option("-g, --global", "Install to global agent directories").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option("--api-key <token>", "API key override").option("--json", "Output JSON").action(agentsInstallCommand);
4356
5362
  var references = program.command("references").description("Manage rule references");
4357
5363
  references.command("list").description("List references for a rule").requiredOption("--rule-id <id>", "Rule 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(referencesListCommand);
4358
5364
  references.command("get").description("Get a reference by id").requiredOption("--id <id>", "Reference 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(referencesGetCommand);