@braid-cloud/cli 0.1.5 → 0.1.8

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
@@ -47,6 +47,10 @@ __export(config_exports, {
47
47
  loadUserConfigAsync: () => loadUserConfigAsync,
48
48
  saveConfig: () => saveConfig,
49
49
  saveConfigAsync: () => saveConfigAsync,
50
+ saveProjectConfig: () => saveProjectConfig,
51
+ saveProjectConfigAsync: () => saveProjectConfigAsync,
52
+ saveUserConfig: () => saveUserConfig,
53
+ saveUserConfigAsync: () => saveUserConfigAsync,
50
54
  setApiKey: () => setApiKey,
51
55
  setApiKeyAsync: () => setApiKeyAsync
52
56
  });
@@ -56,7 +60,7 @@ import { homedir } from "os";
56
60
  import { dirname, join, parse } from "path";
57
61
  import process2 from "process";
58
62
  import { Data, Effect, pipe } from "effect";
59
- var CONFIG_DIR, CONFIG_FILE, PROJECT_CONFIG_FILENAME, USER_CONFIG_FILENAME, ConfigReadError, ConfigWriteError, findConfigFile, findProjectConfigFile, findUserConfigFile, loadProjectConfig, loadUserConfig, isValidServerUrl, resolveServerUrlFromConfig, applyConfigSource, applyEnvOverrides, createDefaultMergedConfig, loadMergedConfig, loadConfig, saveConfig, getApiKey, setApiKey, getServerUrl, clearApiKey, loadConfigAsync, loadProjectConfigAsync, loadUserConfigAsync, loadMergedConfigAsync, findProjectConfigFileAsync, findUserConfigFileAsync, saveConfigAsync, getApiKeyAsync, setApiKeyAsync, getServerUrlAsync, clearApiKeyAsync;
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;
60
64
  var init_config = __esm({
61
65
  "src/lib/config.ts"() {
62
66
  "use strict";
@@ -107,6 +111,40 @@ var init_config = __esm({
107
111
  },
108
112
  catch: () => void 0
109
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()) => {
117
+ const targetPath = resolveUserConfigWritePath(startDir);
118
+ return pipe(
119
+ Effect.tryPromise({
120
+ try: async () => {
121
+ await mkdir(dirname(targetPath), { recursive: true, mode: 448 });
122
+ await writeFile(targetPath, JSON.stringify(config, null, 2), {
123
+ encoding: "utf-8",
124
+ mode: 384
125
+ });
126
+ return targetPath;
127
+ },
128
+ catch: (e) => new ConfigWriteError({ path: targetPath, cause: e })
129
+ })
130
+ );
131
+ };
132
+ saveProjectConfig = (config, startDir = process2.cwd()) => {
133
+ const targetPath = resolveProjectConfigWritePath(startDir);
134
+ return pipe(
135
+ Effect.tryPromise({
136
+ try: async () => {
137
+ await mkdir(dirname(targetPath), { recursive: true, mode: 448 });
138
+ await writeFile(targetPath, JSON.stringify(config, null, 2), {
139
+ encoding: "utf-8",
140
+ mode: 384
141
+ });
142
+ return targetPath;
143
+ },
144
+ catch: (e) => new ConfigWriteError({ path: targetPath, cause: e })
145
+ })
146
+ );
147
+ };
110
148
  isValidServerUrl = (url) => {
111
149
  try {
112
150
  const parsed = new URL(url);
@@ -136,12 +174,24 @@ var init_config = __esm({
136
174
  if (config.profile) {
137
175
  merged.profile = config.profile;
138
176
  }
177
+ if (config.org) {
178
+ merged.org = config.org;
179
+ }
139
180
  if (config.orgProjects) {
140
181
  merged.orgProjects = config.orgProjects;
141
182
  }
142
183
  if (config.personalProjects) {
143
184
  merged.personalProjects = config.personalProjects;
144
185
  }
186
+ if (config.ruleIds) {
187
+ merged.ruleIds = config.ruleIds;
188
+ }
189
+ if (config.excludedRuleIds) {
190
+ merged.excludedRuleIds = config.excludedRuleIds;
191
+ }
192
+ if (config.resolveOverlays !== void 0) {
193
+ merged.resolveOverlays = config.resolveOverlays;
194
+ }
145
195
  if (config.includeUserGlobal !== void 0) {
146
196
  merged.includeUserGlobal = config.includeUserGlobal;
147
197
  }
@@ -261,6 +311,8 @@ var init_config = __esm({
261
311
  findProjectConfigFileAsync = (startDir) => findProjectConfigFile(startDir);
262
312
  findUserConfigFileAsync = (startDir) => findUserConfigFile(startDir);
263
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));
264
316
  getApiKeyAsync = () => Effect.runPromise(getApiKey());
265
317
  setApiKeyAsync = (apiKey) => Effect.runPromise(setApiKey(apiKey));
266
318
  getServerUrlAsync = () => Effect.runPromise(getServerUrl());
@@ -268,172 +320,864 @@ var init_config = __esm({
268
320
  }
269
321
  });
270
322
 
271
- // src/index.ts
272
- init_esm_shims();
273
- import { createRequire } from "module";
274
- import { Command } from "commander";
275
-
276
- // src/commands/auth.ts
277
- init_esm_shims();
278
- import process3 from "process";
279
- import {
280
- cancel,
281
- confirm,
282
- intro,
283
- isCancel,
284
- log,
285
- outro,
286
- password,
287
- spinner
288
- } from "@clack/prompts";
289
-
290
323
  // src/lib/api.ts
291
- init_esm_shims();
292
- init_config();
293
324
  import { Data as Data2, Effect as Effect2, pipe as pipe2 } from "effect";
294
- var TRAILING_SLASH_REGEX = /\/$/;
295
- var ApiError = class extends Data2.TaggedError("ApiError") {
296
- };
297
- var AuthenticationError = class extends Data2.TaggedError("AuthenticationError") {
298
- };
299
- var NetworkError = class extends Data2.TaggedError("NetworkError") {
300
- };
301
- var resolveApiKey = (optionsApiKey) => {
302
- if (optionsApiKey) {
303
- return Effect2.succeed(optionsApiKey);
304
- }
305
- return pipe2(
306
- Effect2.tryPromise({
307
- try: () => getApiKeyAsync(),
308
- catch: () => new NetworkError({ message: "Failed to read config" })
309
- }),
310
- Effect2.flatMap(
311
- (key) => key ? Effect2.succeed(key) : Effect2.fail(
312
- new AuthenticationError({
313
- message: 'No API key configured. Run "braid auth" to authenticate.'
325
+ var TRAILING_SLASH_REGEX, ApiError, AuthenticationError, NetworkError, resolveApiKey, resolveServerUrl, parseResponse, buildApiUrl, buildExportUrl, executeApiRequest, parseScopeOptionsResponse, fetchScopeOptions, buildRuleOptionsUrl, fetchRuleOptions, fetchSkills, validateApiKey, fetchSkillsAsync, validateApiKeyAsync, fetchScopeOptionsAsync, fetchRuleOptionsAsync;
326
+ var init_api = __esm({
327
+ "src/lib/api.ts"() {
328
+ "use strict";
329
+ init_esm_shims();
330
+ init_config();
331
+ TRAILING_SLASH_REGEX = /\/$/;
332
+ ApiError = class extends Data2.TaggedError("ApiError") {
333
+ };
334
+ AuthenticationError = class extends Data2.TaggedError("AuthenticationError") {
335
+ };
336
+ NetworkError = class extends Data2.TaggedError("NetworkError") {
337
+ };
338
+ resolveApiKey = (optionsApiKey) => {
339
+ if (optionsApiKey) {
340
+ return Effect2.succeed(optionsApiKey);
341
+ }
342
+ return pipe2(
343
+ Effect2.tryPromise({
344
+ try: () => getApiKeyAsync(),
345
+ catch: () => new NetworkError({ message: "Failed to read config" })
346
+ }),
347
+ Effect2.flatMap(
348
+ (key) => key ? Effect2.succeed(key) : Effect2.fail(
349
+ new AuthenticationError({
350
+ message: 'No API key configured. Run "braid auth" to authenticate.'
351
+ })
352
+ )
353
+ )
354
+ );
355
+ };
356
+ resolveServerUrl = (optionsServerUrl) => {
357
+ if (optionsServerUrl) {
358
+ return Effect2.succeed(optionsServerUrl);
359
+ }
360
+ return Effect2.tryPromise({
361
+ try: () => getServerUrlAsync(),
362
+ catch: () => new NetworkError({ message: "Failed to read config" })
363
+ });
364
+ };
365
+ parseResponse = (response, json) => {
366
+ if (!response.ok) {
367
+ const errorResponse = json;
368
+ if (response.status === 401) {
369
+ return Effect2.fail(
370
+ new AuthenticationError({
371
+ message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
372
+ })
373
+ );
374
+ }
375
+ return Effect2.fail(
376
+ new ApiError({
377
+ message: errorResponse.error || "API request failed",
378
+ code: errorResponse.code || "UNKNOWN_ERROR",
379
+ status: response.status
380
+ })
381
+ );
382
+ }
383
+ return Effect2.succeed(json);
384
+ };
385
+ buildApiUrl = (serverUrl, path2) => {
386
+ const baseUrl = serverUrl.replace(TRAILING_SLASH_REGEX, "");
387
+ return new URL(`${baseUrl}${path2}`);
388
+ };
389
+ buildExportUrl = (serverUrl, options) => {
390
+ const url = buildApiUrl(serverUrl, "/api/skills/export");
391
+ if (options.profile) {
392
+ url.searchParams.set("profile", options.profile);
393
+ }
394
+ if (options.orgProjects && options.orgProjects.length > 0) {
395
+ url.searchParams.set("orgProjects", options.orgProjects.join(","));
396
+ }
397
+ if (options.personalProjects && options.personalProjects.length > 0) {
398
+ url.searchParams.set(
399
+ "personalProjects",
400
+ options.personalProjects.join(",")
401
+ );
402
+ }
403
+ if (options.includeUserGlobal !== void 0) {
404
+ url.searchParams.set(
405
+ "includeUserGlobal",
406
+ String(options.includeUserGlobal)
407
+ );
408
+ }
409
+ if (options.includeOrgGlobal !== void 0) {
410
+ url.searchParams.set("includeOrgGlobal", String(options.includeOrgGlobal));
411
+ }
412
+ if (options.ruleIds && options.ruleIds.length > 0) {
413
+ url.searchParams.set("ruleIds", options.ruleIds.join(","));
414
+ }
415
+ if (options.excludedRuleIds && options.excludedRuleIds.length > 0) {
416
+ url.searchParams.set("excludedRuleIds", options.excludedRuleIds.join(","));
417
+ }
418
+ if (options.resolveOverlays !== void 0) {
419
+ url.searchParams.set("resolveOverlays", String(options.resolveOverlays));
420
+ }
421
+ return url;
422
+ };
423
+ executeApiRequest = (url, apiKey, serverUrl) => pipe2(
424
+ Effect2.tryPromise({
425
+ try: () => fetch(url.toString(), {
426
+ method: "GET",
427
+ headers: {
428
+ Authorization: `Bearer ${apiKey}`,
429
+ "Content-Type": "application/json"
430
+ }
431
+ }),
432
+ catch: (e) => new NetworkError({
433
+ message: `Failed to connect to ${serverUrl}`,
434
+ cause: e
314
435
  })
436
+ }),
437
+ Effect2.flatMap(
438
+ (response) => pipe2(
439
+ Effect2.tryPromise({
440
+ try: () => response.json(),
441
+ catch: () => new NetworkError({ message: "Failed to parse API response" })
442
+ }),
443
+ Effect2.flatMap((json) => parseResponse(response, json))
444
+ )
315
445
  )
316
- )
317
- );
318
- };
319
- var resolveServerUrl = (optionsServerUrl) => {
320
- if (optionsServerUrl) {
321
- return Effect2.succeed(optionsServerUrl);
322
- }
323
- return Effect2.tryPromise({
324
- try: () => getServerUrlAsync(),
325
- catch: () => new NetworkError({ message: "Failed to read config" })
326
- });
327
- };
328
- var parseResponse = (response, json) => {
329
- if (!response.ok) {
330
- const errorResponse = json;
331
- if (response.status === 401) {
332
- return Effect2.fail(
333
- new AuthenticationError({
334
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
446
+ );
447
+ parseScopeOptionsResponse = (response, json) => {
448
+ if (!response.ok) {
449
+ const errorResponse = json;
450
+ if (response.status === 401) {
451
+ return Effect2.fail(
452
+ new AuthenticationError({
453
+ message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
454
+ })
455
+ );
456
+ }
457
+ return Effect2.fail(
458
+ new ApiError({
459
+ message: errorResponse.error || "API request failed",
460
+ code: errorResponse.code || "UNKNOWN_ERROR",
461
+ status: response.status
462
+ })
463
+ );
464
+ }
465
+ return Effect2.succeed(json);
466
+ };
467
+ fetchScopeOptions = (options = {}) => pipe2(
468
+ Effect2.all({
469
+ apiKey: resolveApiKey(options.apiKey),
470
+ serverUrl: resolveServerUrl(options.serverUrl)
471
+ }),
472
+ Effect2.flatMap(({ apiKey, serverUrl }) => {
473
+ const url = buildApiUrl(serverUrl, "/api/skills/scope-options");
474
+ return pipe2(
475
+ Effect2.tryPromise({
476
+ try: () => fetch(url.toString(), {
477
+ method: "GET",
478
+ headers: {
479
+ Authorization: `Bearer ${apiKey}`,
480
+ "Content-Type": "application/json"
481
+ }
482
+ }),
483
+ catch: (e) => new NetworkError({
484
+ message: `Failed to connect to ${serverUrl}`,
485
+ cause: e
486
+ })
487
+ }),
488
+ Effect2.flatMap(
489
+ (response) => pipe2(
490
+ Effect2.tryPromise({
491
+ try: () => response.json(),
492
+ catch: () => new NetworkError({ message: "Failed to parse API response" })
493
+ }),
494
+ Effect2.flatMap((json) => parseScopeOptionsResponse(response, json))
495
+ )
496
+ )
497
+ );
498
+ })
499
+ );
500
+ buildRuleOptionsUrl = (serverUrl, options) => {
501
+ const url = buildApiUrl(serverUrl, "/api/skills/rule-options");
502
+ if (options.orgId) {
503
+ url.searchParams.set("orgId", options.orgId);
504
+ }
505
+ if (options.orgProjects && options.orgProjects.length > 0) {
506
+ url.searchParams.set("orgProjects", options.orgProjects.join(","));
507
+ }
508
+ if (options.personalProjects && options.personalProjects.length > 0) {
509
+ url.searchParams.set(
510
+ "personalProjects",
511
+ options.personalProjects.join(",")
512
+ );
513
+ }
514
+ if (options.includeUserGlobal !== void 0) {
515
+ url.searchParams.set(
516
+ "includeUserGlobal",
517
+ String(options.includeUserGlobal)
518
+ );
519
+ }
520
+ if (options.includeOrgGlobal !== void 0) {
521
+ url.searchParams.set("includeOrgGlobal", String(options.includeOrgGlobal));
522
+ }
523
+ return url;
524
+ };
525
+ fetchRuleOptions = (options = {}) => pipe2(
526
+ Effect2.all({
527
+ apiKey: resolveApiKey(options.apiKey),
528
+ serverUrl: resolveServerUrl(options.serverUrl)
529
+ }),
530
+ Effect2.flatMap(
531
+ ({ apiKey, serverUrl }) => pipe2(
532
+ Effect2.tryPromise({
533
+ try: () => fetch(buildRuleOptionsUrl(serverUrl, options).toString(), {
534
+ method: "GET",
535
+ headers: {
536
+ Authorization: `Bearer ${apiKey}`,
537
+ "Content-Type": "application/json"
538
+ }
539
+ }),
540
+ catch: (e) => new NetworkError({
541
+ message: `Failed to connect to ${serverUrl}`,
542
+ cause: e
543
+ })
544
+ }),
545
+ Effect2.flatMap(
546
+ (response) => pipe2(
547
+ Effect2.tryPromise({
548
+ try: () => response.json(),
549
+ catch: () => new NetworkError({ message: "Failed to parse API response" })
550
+ }),
551
+ Effect2.flatMap((json) => {
552
+ if (!response.ok) {
553
+ return parseResponse(response, json).pipe(
554
+ Effect2.flatMap(
555
+ () => Effect2.fail(
556
+ new ApiError({
557
+ message: "API request failed",
558
+ code: "UNKNOWN_ERROR",
559
+ status: response.status
560
+ })
561
+ )
562
+ )
563
+ );
564
+ }
565
+ return Effect2.succeed(json);
566
+ })
567
+ )
568
+ )
569
+ )
570
+ )
571
+ );
572
+ fetchSkills = (options) => pipe2(
573
+ Effect2.all({
574
+ apiKey: resolveApiKey(options.apiKey),
575
+ serverUrl: resolveServerUrl(options.serverUrl)
576
+ }),
577
+ Effect2.flatMap(({ apiKey, serverUrl }) => {
578
+ const url = buildExportUrl(serverUrl, options);
579
+ return executeApiRequest(url, apiKey, serverUrl);
580
+ })
581
+ );
582
+ validateApiKey = (apiKey, serverUrl) => pipe2(
583
+ Effect2.tryPromise({
584
+ try: async () => {
585
+ const baseUrl = serverUrl ?? "https://braid.cloud";
586
+ const url = buildApiUrl(baseUrl, "/api/skills/export");
587
+ url.searchParams.set("profile", "default");
588
+ const response = await fetch(url.toString(), {
589
+ method: "GET",
590
+ headers: {
591
+ Authorization: `Bearer ${apiKey}`,
592
+ "Content-Type": "application/json"
593
+ }
594
+ });
595
+ return response.status !== 401;
596
+ },
597
+ catch: (e) => new NetworkError({
598
+ message: `Failed to connect to ${serverUrl ?? "https://braid.cloud"}`,
599
+ cause: e
335
600
  })
336
- );
337
- }
338
- return Effect2.fail(
339
- new ApiError({
340
- message: errorResponse.error || "API request failed",
341
- code: errorResponse.code || "UNKNOWN_ERROR",
342
- status: response.status
343
601
  })
344
602
  );
603
+ fetchSkillsAsync = (options) => Effect2.runPromise(fetchSkills(options));
604
+ validateApiKeyAsync = (apiKey, serverUrl) => Effect2.runPromise(validateApiKey(apiKey, serverUrl));
605
+ fetchScopeOptionsAsync = (options = {}) => Effect2.runPromise(fetchScopeOptions(options));
606
+ fetchRuleOptionsAsync = (options = {}) => Effect2.runPromise(fetchRuleOptions(options));
345
607
  }
346
- return Effect2.succeed(json);
347
- };
348
- var buildApiUrl = (serverUrl, path2) => {
349
- const baseUrl = serverUrl.replace(TRAILING_SLASH_REGEX, "");
350
- return new URL(`${baseUrl}${path2}`);
351
- };
352
- var buildExportUrl = (serverUrl, options) => {
353
- const url = buildApiUrl(serverUrl, "/api/skills/export");
354
- if (options.profile) {
355
- url.searchParams.set("profile", options.profile);
356
- }
357
- if (options.orgProjects && options.orgProjects.length > 0) {
358
- url.searchParams.set("orgProjects", options.orgProjects.join(","));
359
- }
360
- if (options.personalProjects && options.personalProjects.length > 0) {
361
- url.searchParams.set(
362
- "personalProjects",
363
- options.personalProjects.join(",")
364
- );
608
+ });
609
+
610
+ // src/lib/tui.ts
611
+ import process3 from "process";
612
+ import {
613
+ cancel as clackCancel,
614
+ confirm as clackConfirm,
615
+ group as clackGroup,
616
+ groupMultiselect as clackGroupMultiselect,
617
+ intro as clackIntro,
618
+ isCancel as clackIsCancel,
619
+ log as clackLog,
620
+ multiselect as clackMultiselect,
621
+ note as clackNote,
622
+ outro as clackOutro,
623
+ password as clackPassword,
624
+ select as clackSelect,
625
+ selectKey as clackSelectKey,
626
+ spinner as clackSpinner,
627
+ text as clackText
628
+ } from "@clack/prompts";
629
+ function spinner() {
630
+ if (isTTY) {
631
+ return clackSpinner();
365
632
  }
366
- if (options.includeUserGlobal !== void 0) {
367
- url.searchParams.set(
368
- "includeUserGlobal",
369
- String(options.includeUserGlobal)
370
- );
633
+ return {
634
+ start: (message) => {
635
+ if (message) {
636
+ process3.stdout.write(`${message}
637
+ `);
638
+ }
639
+ },
640
+ stop: (message) => {
641
+ if (message) {
642
+ process3.stdout.write(`${message}
643
+ `);
644
+ }
645
+ },
646
+ message: (message) => {
647
+ if (message) {
648
+ process3.stdout.write(`${message}
649
+ `);
650
+ }
651
+ }
652
+ };
653
+ }
654
+ function intro(message) {
655
+ if (isTTY) {
656
+ clackIntro(message);
657
+ } else {
658
+ process3.stdout.write(`
659
+ ${message}
660
+ `);
371
661
  }
372
- if (options.includeOrgGlobal !== void 0) {
373
- url.searchParams.set("includeOrgGlobal", String(options.includeOrgGlobal));
662
+ }
663
+ function outro(message) {
664
+ if (isTTY) {
665
+ clackOutro(message);
666
+ } else {
667
+ process3.stdout.write(`${message}
668
+
669
+ `);
374
670
  }
375
- return url;
376
- };
377
- var executeApiRequest = (url, apiKey, serverUrl) => pipe2(
378
- Effect2.tryPromise({
379
- try: () => fetch(url.toString(), {
380
- method: "GET",
381
- headers: {
382
- Authorization: `Bearer ${apiKey}`,
383
- "Content-Type": "application/json"
671
+ }
672
+ var isTTY, cancel, confirm, isCancel, log, multiselect, password, select, text;
673
+ var init_tui = __esm({
674
+ "src/lib/tui.ts"() {
675
+ "use strict";
676
+ init_esm_shims();
677
+ isTTY = Boolean(process3.stdout.isTTY);
678
+ cancel = clackCancel;
679
+ confirm = clackConfirm;
680
+ isCancel = clackIsCancel;
681
+ log = clackLog;
682
+ multiselect = clackMultiselect;
683
+ password = clackPassword;
684
+ select = clackSelect;
685
+ text = clackText;
686
+ }
687
+ });
688
+
689
+ // src/lib/scope-args.ts
690
+ function parseProjectIds(input) {
691
+ return input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
692
+ }
693
+ function inferScopeSource(flags) {
694
+ if (flags.profile) {
695
+ return "profile";
696
+ }
697
+ if (flags.projects || flags.ruleIds || flags.excludedRuleIds) {
698
+ return "manual";
699
+ }
700
+ return void 0;
701
+ }
702
+ function buildManualSelection(flags) {
703
+ const projectIds = parseProjectIds(flags.projects ?? "");
704
+ const ruleIds = parseProjectIds(flags.ruleIds ?? "");
705
+ const excludedRuleIds = parseProjectIds(flags.excludedRuleIds ?? "");
706
+ return {
707
+ organization: flags.organization,
708
+ source: "manual",
709
+ ...projectIds.length > 0 ? { projectIds } : {},
710
+ ...ruleIds.length > 0 ? { ruleIds } : {},
711
+ ...excludedRuleIds.length > 0 ? { excludedRuleIds } : {},
712
+ includeUserGlobal: flags.includeUserGlobal ?? flags.organization === "personal",
713
+ includeOrgGlobal: flags.includeOrgGlobal ?? flags.organization === "organization"
714
+ };
715
+ }
716
+ function resolveSelectionFromFlags(flags) {
717
+ if (!flags.organization) {
718
+ return null;
719
+ }
720
+ const source = flags.source ?? inferScopeSource(flags);
721
+ if (!source) {
722
+ return null;
723
+ }
724
+ if (source === "profile") {
725
+ const profile = flags.profile?.trim();
726
+ if (!profile) {
727
+ return null;
728
+ }
729
+ return { organization: flags.organization, source, profile };
730
+ }
731
+ return buildManualSelection(flags);
732
+ }
733
+ var init_scope_args = __esm({
734
+ "src/lib/scope-args.ts"() {
735
+ "use strict";
736
+ init_esm_shims();
737
+ }
738
+ });
739
+
740
+ // src/lib/scope-config.ts
741
+ function buildManualScopeConfig(base, selection) {
742
+ let projectScope = {};
743
+ if (selection.projectIds && selection.projectIds.length > 0) {
744
+ projectScope = selection.organization === "organization" ? { orgProjects: selection.projectIds } : { personalProjects: selection.projectIds };
745
+ }
746
+ return {
747
+ ...base,
748
+ ...projectScope,
749
+ ...selection.ruleIds && selection.ruleIds.length > 0 ? { ruleIds: selection.ruleIds } : {},
750
+ ...selection.excludedRuleIds && selection.excludedRuleIds.length > 0 ? { excludedRuleIds: selection.excludedRuleIds } : {},
751
+ includeUserGlobal: selection.includeUserGlobal ?? selection.organization === "personal",
752
+ includeOrgGlobal: selection.includeOrgGlobal ?? selection.organization === "organization"
753
+ };
754
+ }
755
+ function buildScopedConfig(existing, selection) {
756
+ const {
757
+ profile: _oldProfile,
758
+ orgProjects: _oldOrgProjects,
759
+ personalProjects: _oldPersonalProjects,
760
+ ...base
761
+ } = existing;
762
+ if (selection.source === "profile") {
763
+ const profile = selection.profile?.trim();
764
+ return {
765
+ ...base,
766
+ ...profile ? { profile } : {},
767
+ includeUserGlobal: selection.organization === "personal",
768
+ includeOrgGlobal: selection.organization === "organization"
769
+ };
770
+ }
771
+ if (selection.source === "manual") {
772
+ return buildManualScopeConfig(base, selection);
773
+ }
774
+ return base;
775
+ }
776
+ var init_scope_config = __esm({
777
+ "src/lib/scope-config.ts"() {
778
+ "use strict";
779
+ init_esm_shims();
780
+ }
781
+ });
782
+
783
+ // src/commands/scope.ts
784
+ var scope_exports = {};
785
+ __export(scope_exports, {
786
+ scopeCommand: () => scopeCommand
787
+ });
788
+ import process4 from "process";
789
+ function exitCancelled(message) {
790
+ cancel(message);
791
+ process4.exit(0);
792
+ }
793
+ async function selectTargetFile(options) {
794
+ if (options.file) {
795
+ return options.file;
796
+ }
797
+ const target = await select({
798
+ message: "Where should this scope be saved?",
799
+ options: [
800
+ {
801
+ value: "user",
802
+ label: "braid.user.json",
803
+ hint: "Personal machine defaults"
804
+ },
805
+ {
806
+ value: "project",
807
+ label: "braid.json",
808
+ hint: "Shared project defaults"
384
809
  }
385
- }),
386
- catch: (e) => new NetworkError({
387
- message: `Failed to connect to ${serverUrl}`,
388
- cause: e
389
- })
390
- }),
391
- Effect2.flatMap(
392
- (response) => pipe2(
393
- Effect2.tryPromise({
394
- try: () => response.json(),
395
- catch: () => new NetworkError({ message: "Failed to parse API response" })
396
- }),
397
- Effect2.flatMap((json) => parseResponse(response, json))
810
+ ],
811
+ initialValue: "user"
812
+ });
813
+ if (isCancel(target)) {
814
+ exitCancelled("Scope update cancelled.");
815
+ }
816
+ return target;
817
+ }
818
+ async function selectOrganization(scopeOptions) {
819
+ const options = [
820
+ { value: "personal", label: "Personal", hint: "Your personal rules" }
821
+ ];
822
+ for (const org of scopeOptions.organizations.filter(
823
+ (item) => !item.isPersonal
824
+ )) {
825
+ options.push({ value: `org:${org.id}`, label: org.name, hint: org.id });
826
+ }
827
+ const selected = await select({
828
+ message: "Select organization context:",
829
+ options,
830
+ initialValue: "personal"
831
+ });
832
+ if (isCancel(selected)) {
833
+ exitCancelled("Scope update cancelled.");
834
+ }
835
+ if (selected === "personal") {
836
+ return { organization: "personal" };
837
+ }
838
+ return { organization: "organization", orgId: selected.replace("org:", "") };
839
+ }
840
+ async function selectScopeSource() {
841
+ const source = await select({
842
+ message: "Select scope source:",
843
+ options: [
844
+ { value: "profile", label: "Profile", hint: "Use an MCP profile" },
845
+ {
846
+ value: "manual",
847
+ label: "Manual",
848
+ hint: "Mix globals, projects, and rules"
849
+ }
850
+ ],
851
+ initialValue: "profile"
852
+ });
853
+ if (isCancel(source)) {
854
+ exitCancelled("Scope update cancelled.");
855
+ }
856
+ return source;
857
+ }
858
+ async function selectProfileName(scopeOptions) {
859
+ if (scopeOptions.profiles.length === 0) {
860
+ const profileName = await text({
861
+ message: "Profile name:",
862
+ placeholder: "default",
863
+ validate: (value) => (value ?? "").trim().length > 0 ? void 0 : "Profile name is required"
864
+ });
865
+ if (isCancel(profileName)) {
866
+ exitCancelled("Scope update cancelled.");
867
+ }
868
+ return profileName.trim();
869
+ }
870
+ const profile = await select({
871
+ message: "Select profile:",
872
+ options: scopeOptions.profiles.map((p) => ({
873
+ value: p.name,
874
+ label: p.name,
875
+ hint: p.id
876
+ })),
877
+ ...scopeOptions.profiles[0]?.name ? { initialValue: scopeOptions.profiles[0].name } : {}
878
+ });
879
+ if (isCancel(profile)) {
880
+ exitCancelled("Scope update cancelled.");
881
+ }
882
+ return profile;
883
+ }
884
+ function projectsForContext(organizationContext, scopeOptions) {
885
+ if (organizationContext.organization === "personal") {
886
+ return scopeOptions.personalProjects;
887
+ }
888
+ const orgProjects = scopeOptions.orgProjects.find(
889
+ (o) => o.orgId === organizationContext.orgId
890
+ );
891
+ return orgProjects?.projects ?? [];
892
+ }
893
+ async function selectProjects(organizationContext, scopeOptions) {
894
+ const availableProjects = projectsForContext(
895
+ organizationContext,
896
+ scopeOptions
897
+ );
898
+ if (availableProjects.length === 0) {
899
+ return void 0;
900
+ }
901
+ const shouldUseProjects = await confirm({
902
+ message: "Include project rules too?",
903
+ initialValue: true
904
+ });
905
+ if (isCancel(shouldUseProjects)) {
906
+ exitCancelled("Scope update cancelled.");
907
+ }
908
+ if (!shouldUseProjects) {
909
+ return void 0;
910
+ }
911
+ const selected = await multiselect({
912
+ message: "Select projects:",
913
+ options: availableProjects.map((p) => ({
914
+ value: p.id,
915
+ label: p.name,
916
+ hint: p.id
917
+ })),
918
+ required: false
919
+ });
920
+ if (isCancel(selected)) {
921
+ exitCancelled("Scope update cancelled.");
922
+ }
923
+ return selected.length > 0 ? selected : void 0;
924
+ }
925
+ async function pickRuleIds(message, rules) {
926
+ const selected = await multiselect({
927
+ message,
928
+ options: rules.map((rule) => ({
929
+ value: rule.id,
930
+ label: rule.title,
931
+ hint: rule.id
932
+ })),
933
+ required: false
934
+ });
935
+ if (isCancel(selected)) {
936
+ exitCancelled("Scope update cancelled.");
937
+ }
938
+ return selected.length > 0 ? selected : void 0;
939
+ }
940
+ function manualInputsFromFlag(flagSelection) {
941
+ return {
942
+ includeUserGlobal: flagSelection.includeUserGlobal,
943
+ includeOrgGlobal: flagSelection.includeOrgGlobal,
944
+ ...flagSelection.projectIds ? { projectIds: flagSelection.projectIds } : {},
945
+ ...flagSelection.ruleIds ? { ruleIds: flagSelection.ruleIds } : {},
946
+ ...flagSelection.excludedRuleIds ? { excludedRuleIds: flagSelection.excludedRuleIds } : {}
947
+ };
948
+ }
949
+ async function promptRuleFilters(organizationContext, projectIds, includeUserGlobal, includeOrgGlobal, serverUrl, apiKey) {
950
+ const filterMode = await select({
951
+ message: "Rule filtering:",
952
+ options: [
953
+ { value: "none", label: "None", hint: "Use all matched rules" },
954
+ {
955
+ value: "include",
956
+ label: "Only include",
957
+ hint: "Pick explicit rule IDs"
958
+ },
959
+ { value: "exclude", label: "Exclude", hint: "Remove specific rule IDs" },
960
+ { value: "both", label: "Include + exclude", hint: "Both filters" }
961
+ ],
962
+ initialValue: "none"
963
+ });
964
+ if (isCancel(filterMode) || filterMode === "none") {
965
+ if (isCancel(filterMode)) {
966
+ exitCancelled("Scope update cancelled.");
967
+ }
968
+ return {};
969
+ }
970
+ const ruleOptions = await fetchRuleOptionsAsync(
971
+ buildRuleOptionsRequest(
972
+ organizationContext,
973
+ projectIds,
974
+ includeUserGlobal,
975
+ includeOrgGlobal,
976
+ serverUrl,
977
+ apiKey
398
978
  )
399
- )
400
- );
401
- var fetchSkills = (options) => pipe2(
402
- Effect2.all({
403
- apiKey: resolveApiKey(options.apiKey),
404
- serverUrl: resolveServerUrl(options.serverUrl)
405
- }),
406
- Effect2.flatMap(({ apiKey, serverUrl }) => {
407
- const url = buildExportUrl(serverUrl, options);
408
- return executeApiRequest(url, apiKey, serverUrl);
409
- })
410
- );
411
- var validateApiKey = (apiKey, serverUrl) => pipe2(
412
- Effect2.tryPromise({
413
- try: async () => {
414
- const baseUrl = serverUrl ?? "https://braid.cloud";
415
- const url = buildApiUrl(baseUrl, "/api/skills/export");
416
- url.searchParams.set("profile", "default");
417
- const response = await fetch(url.toString(), {
418
- method: "GET",
419
- headers: {
420
- Authorization: `Bearer ${apiKey}`,
421
- "Content-Type": "application/json"
422
- }
423
- });
424
- return response.status !== 401;
425
- },
426
- catch: (e) => new NetworkError({
427
- message: `Failed to connect to ${serverUrl ?? "https://braid.cloud"}`,
428
- cause: e
429
- })
430
- })
431
- );
432
- var fetchSkillsAsync = (options) => Effect2.runPromise(fetchSkills(options));
433
- var validateApiKeyAsync = (apiKey, serverUrl) => Effect2.runPromise(validateApiKey(apiKey, serverUrl));
979
+ );
980
+ const ruleIds = filterMode === "include" || filterMode === "both" ? await pickRuleIds("Select rules to include:", ruleOptions.rules) : void 0;
981
+ const excludedRuleIds = filterMode === "exclude" || filterMode === "both" ? await pickRuleIds("Select rules to exclude:", ruleOptions.rules) : void 0;
982
+ return {
983
+ ...ruleIds ? { ruleIds } : {},
984
+ ...excludedRuleIds ? { excludedRuleIds } : {}
985
+ };
986
+ }
987
+ function buildRuleOptionsRequest(organizationContext, projectIds, includeUserGlobal, includeOrgGlobal, serverUrl, apiKey) {
988
+ let projectScope = {};
989
+ if (projectIds && organizationContext.organization === "organization") {
990
+ projectScope = { orgProjects: projectIds };
991
+ }
992
+ if (projectIds && organizationContext.organization === "personal") {
993
+ projectScope = { personalProjects: projectIds };
994
+ }
995
+ return {
996
+ serverUrl,
997
+ ...apiKey ? { apiKey } : {},
998
+ ...organizationContext.organization === "organization" ? { orgId: organizationContext.orgId } : {},
999
+ ...projectScope,
1000
+ includeUserGlobal,
1001
+ includeOrgGlobal
1002
+ };
1003
+ }
1004
+ async function resolveManualInputs(organizationContext, scopeOptions, serverUrl, apiKey) {
1005
+ const { includeUserGlobal, includeOrgGlobal } = await promptGlobalInclusions(organizationContext);
1006
+ const projectIds = await selectProjects(organizationContext, scopeOptions);
1007
+ const filters = await promptRuleFilters(
1008
+ organizationContext,
1009
+ projectIds,
1010
+ includeUserGlobal,
1011
+ includeOrgGlobal,
1012
+ serverUrl,
1013
+ apiKey
1014
+ );
1015
+ return {
1016
+ includeUserGlobal,
1017
+ includeOrgGlobal,
1018
+ ...projectIds ? { projectIds } : {},
1019
+ ...filters
1020
+ };
1021
+ }
1022
+ async function promptGlobalInclusions(organizationContext) {
1023
+ const includeUserGlobal = await confirm({
1024
+ message: "Include personal global rules?",
1025
+ initialValue: organizationContext.organization === "personal"
1026
+ });
1027
+ if (isCancel(includeUserGlobal)) {
1028
+ exitCancelled("Scope update cancelled.");
1029
+ }
1030
+ if (organizationContext.organization === "personal") {
1031
+ return { includeUserGlobal, includeOrgGlobal: false };
1032
+ }
1033
+ const includeOrgGlobal = await confirm({
1034
+ message: "Include organization global rules?",
1035
+ initialValue: true
1036
+ });
1037
+ if (isCancel(includeOrgGlobal)) {
1038
+ exitCancelled("Scope update cancelled.");
1039
+ }
1040
+ return { includeUserGlobal, includeOrgGlobal };
1041
+ }
1042
+ async function resolveManualSelection(source, flagSelection, organizationContext, scopeOptions, serverUrl, apiKey) {
1043
+ if (source !== "manual") {
1044
+ return void 0;
1045
+ }
1046
+ if (flagSelection?.source === "manual") {
1047
+ return manualInputsFromFlag(flagSelection);
1048
+ }
1049
+ return await resolveManualInputs(
1050
+ organizationContext,
1051
+ scopeOptions,
1052
+ serverUrl,
1053
+ apiKey
1054
+ );
1055
+ }
1056
+ async function resolveOrganizationContext(flagSelection, scopeOptions) {
1057
+ if (flagSelection?.organization === "personal") {
1058
+ return { organization: "personal" };
1059
+ }
1060
+ return await selectOrganization(scopeOptions);
1061
+ }
1062
+ async function resolveProfileSelection(source, flagSelection, scopeOptions) {
1063
+ if (source !== "profile") {
1064
+ return void 0;
1065
+ }
1066
+ if (flagSelection?.source === "profile") {
1067
+ return flagSelection.profile;
1068
+ }
1069
+ return await selectProfileName(scopeOptions);
1070
+ }
1071
+ function resolveFlagSelection(options) {
1072
+ const flags = {
1073
+ ...options.organization ? { organization: options.organization } : {},
1074
+ ...options.source ? { source: options.source } : {},
1075
+ ...options.profile ? { profile: options.profile } : {},
1076
+ ...options.projects ? { projects: options.projects } : {},
1077
+ ...options.ruleIds ? { ruleIds: options.ruleIds } : {},
1078
+ ...options.excludedRuleIds ? { excludedRuleIds: options.excludedRuleIds } : {},
1079
+ ...options.includeUserGlobal !== void 0 ? { includeUserGlobal: options.includeUserGlobal } : {},
1080
+ ...options.includeOrgGlobal !== void 0 ? { includeOrgGlobal: options.includeOrgGlobal } : {}
1081
+ };
1082
+ return resolveSelectionFromFlags(flags);
1083
+ }
1084
+ async function scopeCommand(options) {
1085
+ intro("braid scope");
1086
+ const config = await loadMergedConfigAsync();
1087
+ const targetFile = await selectTargetFile(options);
1088
+ const loadSpinner = spinner();
1089
+ loadSpinner.start("Loading scope options from braid...");
1090
+ try {
1091
+ const serverUrl = options.server ?? config.serverUrl;
1092
+ const apiKey = options.apiKey ?? config.token;
1093
+ const scopeOptions = await fetchScopeOptionsAsync({
1094
+ serverUrl,
1095
+ ...apiKey ? { apiKey } : {}
1096
+ });
1097
+ loadSpinner.stop("Scope options loaded");
1098
+ const flagSelection = resolveFlagSelection(options);
1099
+ const organizationContext = await resolveOrganizationContext(
1100
+ flagSelection,
1101
+ scopeOptions
1102
+ );
1103
+ const source = flagSelection?.source ?? await selectScopeSource();
1104
+ const profile = await resolveProfileSelection(
1105
+ source,
1106
+ flagSelection,
1107
+ scopeOptions
1108
+ );
1109
+ const manualInputs = await resolveManualSelection(
1110
+ source,
1111
+ flagSelection,
1112
+ organizationContext,
1113
+ scopeOptions,
1114
+ serverUrl,
1115
+ apiKey
1116
+ );
1117
+ const selection = {
1118
+ organization: organizationContext.organization,
1119
+ source,
1120
+ ...profile ? { profile } : {},
1121
+ ...manualInputs ?? {}
1122
+ };
1123
+ if (targetFile === "user") {
1124
+ const existing2 = await loadUserConfigAsync() ?? {};
1125
+ const path3 = await saveUserConfigAsync(
1126
+ buildScopedConfig(existing2, selection)
1127
+ );
1128
+ outro(`Updated ${path3}`);
1129
+ return;
1130
+ }
1131
+ const existing = await loadProjectConfigAsync() ?? {};
1132
+ const path2 = await saveProjectConfigAsync(
1133
+ buildScopedConfig(existing, selection)
1134
+ );
1135
+ outro(`Updated ${path2}`);
1136
+ } catch (error) {
1137
+ loadSpinner.stop("Failed to load scope options");
1138
+ log.error(error instanceof Error ? error.message : String(error));
1139
+ process4.exit(1);
1140
+ }
1141
+ }
1142
+ var init_scope = __esm({
1143
+ "src/commands/scope.ts"() {
1144
+ "use strict";
1145
+ init_esm_shims();
1146
+ init_api();
1147
+ init_config();
1148
+ init_scope_args();
1149
+ init_scope_config();
1150
+ init_tui();
1151
+ }
1152
+ });
1153
+
1154
+ // src/index.ts
1155
+ init_esm_shims();
1156
+ import { createRequire } from "module";
1157
+ import { Command } from "commander";
434
1158
 
435
1159
  // src/commands/auth.ts
1160
+ init_esm_shims();
1161
+ init_api();
436
1162
  init_config();
1163
+ init_tui();
1164
+ import process5 from "process";
1165
+ async function configureDefaultScopeAsync(serverUrl) {
1166
+ const shouldConfigureScope = await confirm({
1167
+ message: "Configure organization and scope defaults now?",
1168
+ initialValue: true
1169
+ });
1170
+ if (isCancel(shouldConfigureScope) || !shouldConfigureScope) {
1171
+ return;
1172
+ }
1173
+ const { scopeCommand: scopeCommand2 } = await Promise.resolve().then(() => (init_scope(), scope_exports));
1174
+ const config = await loadMergedConfigAsync();
1175
+ await scopeCommand2({
1176
+ file: "user",
1177
+ server: serverUrl,
1178
+ ...config.token ? { apiKey: config.token } : {}
1179
+ });
1180
+ }
437
1181
  async function authCommand(options) {
438
1182
  intro("braid auth");
439
1183
  const config = await loadMergedConfigAsync();
@@ -448,7 +1192,7 @@ async function authCommand(options) {
448
1192
  }
449
1193
  }
450
1194
  const apiKey = await password({
451
- message: "Enter your Braid API key:",
1195
+ message: "Enter your braid API key:",
452
1196
  validate: (value) => {
453
1197
  if (!value) {
454
1198
  return "API key is required";
@@ -461,35 +1205,48 @@ async function authCommand(options) {
461
1205
  });
462
1206
  if (isCancel(apiKey)) {
463
1207
  cancel("Auth cancelled.");
464
- process3.exit(0);
1208
+ process5.exit(0);
465
1209
  }
466
1210
  const authSpinner = spinner();
1211
+ let authSpinnerActive = true;
1212
+ const stopAuthSpinner = (message) => {
1213
+ if (!authSpinnerActive) {
1214
+ return;
1215
+ }
1216
+ authSpinner.stop(message);
1217
+ authSpinnerActive = false;
1218
+ };
467
1219
  authSpinner.start("Validating API key...");
468
1220
  try {
469
1221
  const serverUrl = options.server ?? "https://braid.cloud";
470
1222
  const isValid = await validateApiKeyAsync(apiKey, serverUrl);
471
1223
  if (!isValid) {
472
- authSpinner.stop("Invalid API key");
1224
+ stopAuthSpinner("Invalid API key");
473
1225
  log.error(
474
1226
  "The API key could not be validated. Please check your key and try again."
475
1227
  );
476
- process3.exit(1);
1228
+ process5.exit(1);
477
1229
  }
478
1230
  await setApiKeyAsync(apiKey);
479
- authSpinner.stop("API key validated and saved");
1231
+ const existingUserConfig = await loadUserConfigAsync();
1232
+ if (existingUserConfig?.token) {
1233
+ await saveUserConfigAsync({ ...existingUserConfig, token: apiKey });
1234
+ }
1235
+ stopAuthSpinner("API key validated and saved");
1236
+ await configureDefaultScopeAsync(serverUrl);
480
1237
  log.success(`Config saved to ${CONFIG_FILE}`);
481
1238
  outro(
482
1239
  "You're authenticated! Run 'braid install --profile <name>' to install skills."
483
1240
  );
484
1241
  } catch (error) {
485
- authSpinner.stop("Validation failed");
1242
+ stopAuthSpinner("Validation failed");
486
1243
  const message = error instanceof Error ? error.message : String(error);
487
1244
  log.error(`Failed to validate API key: ${message}`);
488
- process3.exit(1);
1245
+ process5.exit(1);
489
1246
  }
490
1247
  }
491
1248
  async function displayTokenSource(masked) {
492
- if (process3.env.BRAID_API_KEY) {
1249
+ if (process5.env.BRAID_API_KEY) {
493
1250
  log.info(`Authenticated with key: ${masked}`);
494
1251
  log.info("Source: BRAID_API_KEY environment variable");
495
1252
  return;
@@ -552,42 +1309,114 @@ init_esm_shims();
552
1309
  import { access, constants } from "fs/promises";
553
1310
  import { homedir as homedir2 } from "os";
554
1311
  import { join as join2 } from "path";
555
- import process4 from "process";
1312
+ import process6 from "process";
556
1313
  import { Effect as Effect3, pipe as pipe3 } from "effect";
557
1314
  var home = homedir2();
1315
+ var vscodeExtSettingsPath = (extensionId, filename) => {
1316
+ if (process6.platform === "darwin") {
1317
+ return join2(
1318
+ home,
1319
+ "Library",
1320
+ "Application Support",
1321
+ "Code",
1322
+ "User",
1323
+ "globalStorage",
1324
+ extensionId,
1325
+ "settings",
1326
+ filename
1327
+ );
1328
+ }
1329
+ if (process6.platform === "win32") {
1330
+ const appData = process6.env.APPDATA ?? join2(home, "AppData", "Roaming");
1331
+ return join2(
1332
+ appData,
1333
+ "Code",
1334
+ "User",
1335
+ "globalStorage",
1336
+ extensionId,
1337
+ "settings",
1338
+ filename
1339
+ );
1340
+ }
1341
+ return join2(
1342
+ home,
1343
+ ".config",
1344
+ "Code",
1345
+ "User",
1346
+ "globalStorage",
1347
+ extensionId,
1348
+ "settings",
1349
+ filename
1350
+ );
1351
+ };
1352
+ var claudeDesktopConfigPath = () => {
1353
+ if (process6.platform === "darwin") {
1354
+ return join2(
1355
+ home,
1356
+ "Library",
1357
+ "Application Support",
1358
+ "Claude",
1359
+ "claude_desktop_config.json"
1360
+ );
1361
+ }
1362
+ if (process6.platform === "win32") {
1363
+ const appData = process6.env.APPDATA ?? join2(home, "AppData", "Roaming");
1364
+ return join2(appData, "Claude", "claude_desktop_config.json");
1365
+ }
1366
+ return join2(home, ".config", "Claude", "claude_desktop_config.json");
1367
+ };
558
1368
  var AGENTS = [
559
1369
  {
560
1370
  id: "amp",
561
- name: "Amp, Kimi Code CLI",
1371
+ name: "Amp",
562
1372
  projectPath: ".agents/skills",
563
- globalPath: join2(home, ".config", "agents", "skills")
1373
+ globalPath: join2(home, ".config", "agents", "skills"),
1374
+ mcpProjectConfigPath: ".amp/mcp.json",
1375
+ mcpGlobalConfigPath: join2(home, ".amp", "mcp.json")
564
1376
  },
565
1377
  {
566
1378
  id: "kimi-cli",
567
1379
  name: "Kimi Code CLI",
568
1380
  projectPath: ".agents/skills",
569
- globalPath: join2(home, ".config", "agents", "skills")
1381
+ globalPath: join2(home, ".config", "agents", "skills"),
1382
+ mcpProjectConfigPath: ".agents/mcp.json",
1383
+ mcpGlobalConfigPath: join2(home, ".config", "agents", "mcp.json")
570
1384
  },
571
1385
  {
572
1386
  id: "antigravity",
573
1387
  name: "Antigravity",
574
1388
  projectPath: ".agent/skills",
575
- globalPath: join2(home, ".gemini", "antigravity", "global_skills")
1389
+ globalPath: join2(home, ".gemini", "antigravity", "global_skills"),
1390
+ mcpProjectConfigPath: ".agent/mcp.json",
1391
+ mcpGlobalConfigPath: join2(home, ".gemini", "antigravity", "mcp.json")
576
1392
  },
577
1393
  {
578
1394
  id: "claude-code",
579
1395
  name: "Claude Code",
580
1396
  projectPath: ".claude/skills",
581
1397
  globalPath: join2(home, ".claude", "skills"),
1398
+ projectMarkerPath: ".claude",
1399
+ globalMarkerPath: join2(home, ".claude"),
582
1400
  rulesProjectPath: ".claude/rules",
583
1401
  rulesGlobalPath: join2(home, ".claude", "rules"),
584
- ruleFormat: "markdown-dir"
1402
+ ruleFormat: "markdown-dir",
1403
+ mcpProjectConfigPath: ".mcp.json",
1404
+ mcpEntryStyle: "typed-stdio"
1405
+ },
1406
+ {
1407
+ id: "claude-desktop",
1408
+ name: "Claude Desktop",
1409
+ projectPath: "",
1410
+ globalPath: "",
1411
+ mcpGlobalConfigPath: claudeDesktopConfigPath()
585
1412
  },
586
1413
  {
587
1414
  id: "moltbot",
588
1415
  name: "Moltbot",
589
1416
  projectPath: "skills",
590
- globalPath: join2(home, ".moltbot", "skills")
1417
+ globalPath: join2(home, ".moltbot", "skills"),
1418
+ mcpProjectConfigPath: "mcp.json",
1419
+ mcpGlobalConfigPath: join2(home, ".moltbot", "mcp.json")
591
1420
  },
592
1421
  {
593
1422
  id: "cline",
@@ -595,37 +1424,52 @@ var AGENTS = [
595
1424
  projectPath: ".cline/skills",
596
1425
  globalPath: join2(home, ".cline", "skills"),
597
1426
  rulesProjectPath: ".clinerules",
598
- ruleFormat: "append-single"
1427
+ ruleFormat: "append-single",
1428
+ mcpGlobalConfigPath: vscodeExtSettingsPath(
1429
+ "saoudrizwan.claude-dev",
1430
+ "cline_mcp_settings.json"
1431
+ ),
1432
+ mcpEntryStyle: "cline"
599
1433
  },
600
1434
  {
601
1435
  id: "codebuddy",
602
1436
  name: "CodeBuddy",
603
1437
  projectPath: ".codebuddy/skills",
604
- globalPath: join2(home, ".codebuddy", "skills")
1438
+ globalPath: join2(home, ".codebuddy", "skills"),
1439
+ mcpProjectConfigPath: ".codebuddy/mcp.json",
1440
+ mcpGlobalConfigPath: join2(home, ".codebuddy", "mcp.json")
605
1441
  },
606
1442
  {
607
1443
  id: "codex",
608
1444
  name: "Codex",
609
1445
  projectPath: ".codex/skills",
610
- globalPath: join2(home, ".codex", "skills")
1446
+ globalPath: join2(home, ".codex", "skills"),
1447
+ mcpProjectConfigPath: ".codex/mcp.json",
1448
+ mcpGlobalConfigPath: join2(home, ".codex", "mcp.json")
611
1449
  },
612
1450
  {
613
1451
  id: "command-code",
614
1452
  name: "Command Code",
615
1453
  projectPath: ".commandcode/skills",
616
- globalPath: join2(home, ".commandcode", "skills")
1454
+ globalPath: join2(home, ".commandcode", "skills"),
1455
+ mcpProjectConfigPath: ".commandcode/mcp.json",
1456
+ mcpGlobalConfigPath: join2(home, ".commandcode", "mcp.json")
617
1457
  },
618
1458
  {
619
1459
  id: "continue",
620
1460
  name: "Continue",
621
1461
  projectPath: ".continue/skills",
622
- globalPath: join2(home, ".continue", "skills")
1462
+ globalPath: join2(home, ".continue", "skills"),
1463
+ mcpProjectConfigPath: ".continue/mcp.json",
1464
+ mcpGlobalConfigPath: join2(home, ".continue", "mcp.json")
623
1465
  },
624
1466
  {
625
1467
  id: "crush",
626
1468
  name: "Crush",
627
1469
  projectPath: ".crush/skills",
628
- globalPath: join2(home, ".config", "crush", "skills")
1470
+ globalPath: join2(home, ".config", "crush", "skills"),
1471
+ mcpProjectConfigPath: ".crush/mcp.json",
1472
+ mcpGlobalConfigPath: join2(home, ".config", "crush", "mcp.json")
629
1473
  },
630
1474
  {
631
1475
  id: "cursor",
@@ -633,19 +1477,25 @@ var AGENTS = [
633
1477
  projectPath: ".cursor/skills",
634
1478
  globalPath: join2(home, ".cursor", "skills"),
635
1479
  rulesProjectPath: ".cursor/rules",
636
- ruleFormat: "mdc"
1480
+ ruleFormat: "mdc",
1481
+ mcpProjectConfigPath: ".cursor/mcp.json",
1482
+ mcpGlobalConfigPath: join2(home, ".cursor", "mcp.json")
637
1483
  },
638
1484
  {
639
1485
  id: "droid",
640
1486
  name: "Droid",
641
1487
  projectPath: ".factory/skills",
642
- globalPath: join2(home, ".factory", "skills")
1488
+ globalPath: join2(home, ".factory", "skills"),
1489
+ mcpProjectConfigPath: ".factory/mcp.json",
1490
+ mcpGlobalConfigPath: join2(home, ".factory", "mcp.json")
643
1491
  },
644
1492
  {
645
1493
  id: "gemini-cli",
646
1494
  name: "Gemini CLI",
647
1495
  projectPath: ".gemini/skills",
648
- globalPath: join2(home, ".gemini", "skills")
1496
+ globalPath: join2(home, ".gemini", "skills"),
1497
+ mcpProjectConfigPath: ".gemini/mcp.json",
1498
+ mcpGlobalConfigPath: join2(home, ".gemini", "mcp.json")
649
1499
  },
650
1500
  {
651
1501
  id: "github-copilot",
@@ -653,79 +1503,108 @@ var AGENTS = [
653
1503
  projectPath: ".github/skills",
654
1504
  globalPath: join2(home, ".copilot", "skills"),
655
1505
  rulesProjectPath: ".github/copilot-instructions.md",
656
- ruleFormat: "append-single"
1506
+ ruleFormat: "append-single",
1507
+ mcpProjectConfigPath: ".vscode/mcp.json",
1508
+ mcpRootKey: "servers",
1509
+ mcpEntryStyle: "typed-stdio"
657
1510
  },
658
1511
  {
659
1512
  id: "goose",
660
1513
  name: "Goose",
661
1514
  projectPath: ".goose/skills",
662
- globalPath: join2(home, ".config", "goose", "skills")
1515
+ globalPath: join2(home, ".config", "goose", "skills"),
1516
+ mcpGlobalConfigPath: join2(home, ".config", "goose", "mcp.json")
663
1517
  },
664
1518
  {
665
1519
  id: "junie",
666
1520
  name: "Junie",
667
1521
  projectPath: ".junie/skills",
668
- globalPath: join2(home, ".junie", "skills")
1522
+ globalPath: join2(home, ".junie", "skills"),
1523
+ mcpProjectConfigPath: ".junie/mcp.json",
1524
+ mcpGlobalConfigPath: join2(home, ".junie", "mcp.json")
669
1525
  },
670
1526
  {
671
1527
  id: "kilo",
672
1528
  name: "Kilo Code",
673
1529
  projectPath: ".kilocode/skills",
674
- globalPath: join2(home, ".kilocode", "skills")
1530
+ globalPath: join2(home, ".kilocode", "skills"),
1531
+ mcpGlobalConfigPath: vscodeExtSettingsPath(
1532
+ "kilocode.kilo-code",
1533
+ "mcp_settings.json"
1534
+ ),
1535
+ mcpEntryStyle: "cline"
675
1536
  },
676
1537
  {
677
1538
  id: "kiro-cli",
678
1539
  name: "Kiro CLI",
679
1540
  projectPath: ".kiro/skills",
680
- globalPath: join2(home, ".kiro", "skills")
1541
+ globalPath: join2(home, ".kiro", "skills"),
1542
+ mcpProjectConfigPath: ".kiro/mcp.json",
1543
+ mcpGlobalConfigPath: join2(home, ".kiro", "mcp.json")
681
1544
  },
682
1545
  {
683
1546
  id: "kode",
684
1547
  name: "Kode",
685
1548
  projectPath: ".kode/skills",
686
- globalPath: join2(home, ".kode", "skills")
1549
+ globalPath: join2(home, ".kode", "skills"),
1550
+ mcpProjectConfigPath: ".kode/mcp.json",
1551
+ mcpGlobalConfigPath: join2(home, ".kode", "mcp.json")
687
1552
  },
688
1553
  {
689
1554
  id: "mcpjam",
690
1555
  name: "MCPJam",
691
1556
  projectPath: ".mcpjam/skills",
692
- globalPath: join2(home, ".mcpjam", "skills")
1557
+ globalPath: join2(home, ".mcpjam", "skills"),
1558
+ mcpProjectConfigPath: ".mcpjam/mcp.json",
1559
+ mcpGlobalConfigPath: join2(home, ".mcpjam", "mcp.json")
693
1560
  },
694
1561
  {
695
1562
  id: "mux",
696
1563
  name: "Mux",
697
1564
  projectPath: ".mux/skills",
698
- globalPath: join2(home, ".mux", "skills")
1565
+ globalPath: join2(home, ".mux", "skills"),
1566
+ mcpProjectConfigPath: ".mux/mcp.json",
1567
+ mcpGlobalConfigPath: join2(home, ".mux", "mcp.json")
699
1568
  },
700
1569
  {
701
1570
  id: "opencode",
702
1571
  name: "OpenCode",
703
1572
  projectPath: ".opencode/skills",
704
- globalPath: join2(home, ".config", "opencode", "skills")
1573
+ globalPath: join2(home, ".config", "opencode", "skills"),
1574
+ mcpProjectConfigPath: ".opencode/mcp.json",
1575
+ mcpGlobalConfigPath: join2(home, ".config", "opencode", "mcp.json")
705
1576
  },
706
1577
  {
707
1578
  id: "openhands",
708
1579
  name: "OpenHands",
709
1580
  projectPath: ".openhands/skills",
710
- globalPath: join2(home, ".openhands", "skills")
1581
+ globalPath: join2(home, ".openhands", "skills"),
1582
+ mcpProjectConfigPath: ".openhands/mcp.json",
1583
+ mcpGlobalConfigPath: join2(home, ".openhands", "mcp.json")
711
1584
  },
712
1585
  {
713
1586
  id: "pi",
714
1587
  name: "Pi",
715
1588
  projectPath: ".pi/skills",
716
- globalPath: join2(home, ".pi", "agent", "skills")
1589
+ globalPath: join2(home, ".pi", "agent", "skills"),
1590
+ mcpProjectConfigPath: ".pi/mcp.json",
1591
+ mcpGlobalConfigPath: join2(home, ".pi", "agent", "mcp.json")
717
1592
  },
718
1593
  {
719
1594
  id: "qoder",
720
1595
  name: "Qoder",
721
1596
  projectPath: ".qoder/skills",
722
- globalPath: join2(home, ".qoder", "skills")
1597
+ globalPath: join2(home, ".qoder", "skills"),
1598
+ mcpProjectConfigPath: ".qoder/mcp.json",
1599
+ mcpGlobalConfigPath: join2(home, ".qoder", "mcp.json")
723
1600
  },
724
1601
  {
725
1602
  id: "qwen-code",
726
1603
  name: "Qwen Code",
727
1604
  projectPath: ".qwen/skills",
728
- globalPath: join2(home, ".qwen", "skills")
1605
+ globalPath: join2(home, ".qwen", "skills"),
1606
+ mcpProjectConfigPath: ".qwen/mcp.json",
1607
+ mcpGlobalConfigPath: join2(home, ".qwen", "mcp.json")
729
1608
  },
730
1609
  {
731
1610
  id: "roo",
@@ -734,13 +1613,20 @@ var AGENTS = [
734
1613
  globalPath: join2(home, ".roo", "skills"),
735
1614
  rulesProjectPath: ".roo/rules",
736
1615
  rulesGlobalPath: join2(home, ".roo", "rules"),
737
- ruleFormat: "markdown-dir"
1616
+ ruleFormat: "markdown-dir",
1617
+ mcpGlobalConfigPath: vscodeExtSettingsPath(
1618
+ "rooveterinaryinc.roo-cline",
1619
+ "mcp_settings.json"
1620
+ ),
1621
+ mcpEntryStyle: "cline"
738
1622
  },
739
1623
  {
740
1624
  id: "trae",
741
1625
  name: "Trae",
742
1626
  projectPath: ".trae/skills",
743
- globalPath: join2(home, ".trae", "skills")
1627
+ globalPath: join2(home, ".trae", "skills"),
1628
+ mcpProjectConfigPath: ".trae/mcp.json",
1629
+ mcpGlobalConfigPath: join2(home, ".trae", "mcp.json")
744
1630
  },
745
1631
  {
746
1632
  id: "windsurf",
@@ -748,34 +1634,46 @@ var AGENTS = [
748
1634
  projectPath: ".windsurf/skills",
749
1635
  globalPath: join2(home, ".codeium", "windsurf", "skills"),
750
1636
  rulesProjectPath: ".windsurfrules",
751
- ruleFormat: "append-single"
1637
+ ruleFormat: "append-single",
1638
+ mcpGlobalConfigPath: join2(home, ".codeium", "windsurf", "mcp_config.json")
752
1639
  },
753
1640
  {
754
1641
  id: "zencoder",
755
1642
  name: "Zencoder",
756
1643
  projectPath: ".zencoder/skills",
757
- globalPath: join2(home, ".zencoder", "skills")
1644
+ globalPath: join2(home, ".zencoder", "skills"),
1645
+ mcpProjectConfigPath: ".zencoder/mcp.json",
1646
+ mcpGlobalConfigPath: join2(home, ".zencoder", "mcp.json")
758
1647
  },
759
1648
  {
760
1649
  id: "neovate",
761
1650
  name: "Neovate",
762
1651
  projectPath: ".neovate/skills",
763
- globalPath: join2(home, ".neovate", "skills")
1652
+ globalPath: join2(home, ".neovate", "skills"),
1653
+ mcpProjectConfigPath: ".neovate/mcp.json",
1654
+ mcpGlobalConfigPath: join2(home, ".neovate", "mcp.json")
764
1655
  },
765
1656
  {
766
1657
  id: "pochi",
767
1658
  name: "Pochi",
768
1659
  projectPath: ".pochi/skills",
769
- globalPath: join2(home, ".pochi", "skills")
1660
+ globalPath: join2(home, ".pochi", "skills"),
1661
+ mcpProjectConfigPath: ".pochi/mcp.json",
1662
+ mcpGlobalConfigPath: join2(home, ".pochi", "mcp.json")
770
1663
  },
771
1664
  {
772
1665
  id: "zed",
773
1666
  name: "Zed",
774
1667
  projectPath: "",
775
1668
  globalPath: "",
1669
+ projectMarkerPath: ".zed",
1670
+ globalMarkerPath: join2(home, ".config", "zed"),
776
1671
  rulesProjectPath: ".zed/rules",
777
1672
  rulesGlobalPath: join2(home, ".config", "zed", "rules"),
778
- ruleFormat: "markdown-dir"
1673
+ ruleFormat: "markdown-dir",
1674
+ mcpGlobalConfigPath: join2(home, ".config", "zed", "settings.json"),
1675
+ mcpRootKey: "context_servers",
1676
+ mcpEntryStyle: "zed"
779
1677
  }
780
1678
  ];
781
1679
  var directoryExists = (path2) => pipe3(
@@ -787,7 +1685,7 @@ var directoryExists = (path2) => pipe3(
787
1685
  Effect3.orElseSucceed(() => false)
788
1686
  );
789
1687
  var detectAgents = (projectRoot) => {
790
- const cwd = projectRoot ?? process4.cwd();
1688
+ const cwd = projectRoot ?? process6.cwd();
791
1689
  return pipe3(
792
1690
  Effect3.forEach(
793
1691
  AGENTS,
@@ -795,6 +1693,8 @@ var detectAgents = (projectRoot) => {
795
1693
  Effect3.all({
796
1694
  hasProjectConfig: agent.projectPath ? directoryExists(join2(cwd, agent.projectPath)) : Effect3.succeed(false),
797
1695
  hasGlobalConfig: agent.globalPath ? directoryExists(agent.globalPath) : Effect3.succeed(false),
1696
+ hasProjectMarker: agent.projectMarkerPath ? directoryExists(join2(cwd, agent.projectMarkerPath)) : Effect3.succeed(false),
1697
+ hasGlobalMarker: agent.globalMarkerPath ? directoryExists(agent.globalMarkerPath) : Effect3.succeed(false),
798
1698
  hasRulesProjectConfig: agent.rulesProjectPath ? directoryExists(join2(cwd, agent.rulesProjectPath)) : Effect3.succeed(false),
799
1699
  hasRulesGlobalConfig: agent.rulesGlobalPath ? directoryExists(agent.rulesGlobalPath) : Effect3.succeed(false)
800
1700
  }),
@@ -802,12 +1702,14 @@ var detectAgents = (projectRoot) => {
802
1702
  ({
803
1703
  hasProjectConfig,
804
1704
  hasGlobalConfig,
1705
+ hasProjectMarker,
1706
+ hasGlobalMarker,
805
1707
  hasRulesProjectConfig,
806
1708
  hasRulesGlobalConfig
807
1709
  }) => ({
808
1710
  ...agent,
809
- hasProjectConfig: hasProjectConfig || hasRulesProjectConfig,
810
- hasGlobalConfig: hasGlobalConfig || hasRulesGlobalConfig
1711
+ hasProjectConfig: hasProjectConfig || hasRulesProjectConfig || hasProjectMarker,
1712
+ hasGlobalConfig: hasGlobalConfig || hasRulesGlobalConfig || hasGlobalMarker
811
1713
  })
812
1714
  )
813
1715
  ),
@@ -828,7 +1730,7 @@ var resolveInstallPath = (agent, options) => {
828
1730
  if (!agent.projectPath) {
829
1731
  return void 0;
830
1732
  }
831
- const cwd = options.projectRoot ?? process4.cwd();
1733
+ const cwd = options.projectRoot ?? process6.cwd();
832
1734
  return join2(cwd, agent.projectPath);
833
1735
  };
834
1736
  var resolveRulesInstallPath = (agent, options) => {
@@ -838,11 +1740,46 @@ var resolveRulesInstallPath = (agent, options) => {
838
1740
  if (!agent.rulesProjectPath) {
839
1741
  return void 0;
840
1742
  }
841
- const cwd = options.projectRoot ?? process4.cwd();
1743
+ const cwd = options.projectRoot ?? process6.cwd();
842
1744
  return join2(cwd, agent.rulesProjectPath);
843
1745
  };
1746
+ var hasMcpConfig = (agent) => Boolean(agent.mcpProjectConfigPath) || Boolean(agent.mcpGlobalConfigPath);
1747
+ var resolveMcpConfigPath = (agent, options) => {
1748
+ if (options.global) {
1749
+ return agent.mcpGlobalConfigPath;
1750
+ }
1751
+ if (agent.mcpProjectConfigPath) {
1752
+ const cwd = options.projectRoot ?? process6.cwd();
1753
+ return join2(cwd, agent.mcpProjectConfigPath);
1754
+ }
1755
+ return agent.mcpGlobalConfigPath;
1756
+ };
1757
+ var buildMcpEntry = (style, env) => {
1758
+ const command = "npx";
1759
+ const args = ["-y", "@braid-cloud/mcp"];
1760
+ const envObj = Object.keys(env).length > 0 ? env : void 0;
1761
+ switch (style) {
1762
+ case "standard":
1763
+ return envObj ? { command, args, env: envObj } : { command, args };
1764
+ case "typed-stdio":
1765
+ return envObj ? { type: "stdio", command, args, env: envObj } : { type: "stdio", command, args };
1766
+ case "zed":
1767
+ return envObj ? { command: { path: command, args, env: envObj } } : { command: { path: command, args } };
1768
+ case "cline":
1769
+ return envObj ? { command, args, env: envObj, disabled: false } : { command, args, disabled: false };
1770
+ default:
1771
+ return envObj ? { command, args, env: envObj } : { command, args };
1772
+ }
1773
+ };
1774
+ var detectMcpAgents = (projectRoot) => Effect3.runPromise(
1775
+ pipe3(
1776
+ detectAgents(projectRoot),
1777
+ Effect3.map((detected) => detected.filter(hasMcpConfig))
1778
+ )
1779
+ );
844
1780
 
845
1781
  // src/commands/install.ts
1782
+ init_api();
846
1783
  init_config();
847
1784
 
848
1785
  // src/lib/metadata.ts
@@ -1208,77 +2145,8 @@ var writeSkills = (basePath, skills, agentId) => pipe6(
1208
2145
  );
1209
2146
  var writeSkillsAsync = (basePath, skills, agentId) => Effect6.runPromise(writeSkills(basePath, skills, agentId));
1210
2147
 
1211
- // src/lib/tui.ts
1212
- init_esm_shims();
1213
- import process5 from "process";
1214
- import {
1215
- cancel as clackCancel,
1216
- confirm as clackConfirm,
1217
- group as clackGroup,
1218
- groupMultiselect as clackGroupMultiselect,
1219
- intro as clackIntro,
1220
- isCancel as clackIsCancel,
1221
- log as clackLog,
1222
- multiselect as clackMultiselect,
1223
- note as clackNote,
1224
- outro as clackOutro,
1225
- password as clackPassword,
1226
- select as clackSelect,
1227
- selectKey as clackSelectKey,
1228
- spinner as clackSpinner,
1229
- text as clackText
1230
- } from "@clack/prompts";
1231
- var isTTY = Boolean(process5.stdout.isTTY);
1232
- function spinner2() {
1233
- if (isTTY) {
1234
- return clackSpinner();
1235
- }
1236
- return {
1237
- start: (message) => {
1238
- if (message) {
1239
- process5.stdout.write(`${message}
1240
- `);
1241
- }
1242
- },
1243
- stop: (message) => {
1244
- if (message) {
1245
- process5.stdout.write(`${message}
1246
- `);
1247
- }
1248
- },
1249
- message: (message) => {
1250
- if (message) {
1251
- process5.stdout.write(`${message}
1252
- `);
1253
- }
1254
- }
1255
- };
1256
- }
1257
- function intro2(message) {
1258
- if (isTTY) {
1259
- clackIntro(message);
1260
- } else {
1261
- process5.stdout.write(`
1262
- ${message}
1263
- `);
1264
- }
1265
- }
1266
- function outro2(message) {
1267
- if (isTTY) {
1268
- clackOutro(message);
1269
- } else {
1270
- process5.stdout.write(`${message}
1271
-
1272
- `);
1273
- }
1274
- }
1275
- var cancel2 = clackCancel;
1276
- var confirm2 = clackConfirm;
1277
- var isCancel2 = clackIsCancel;
1278
- var log2 = clackLog;
1279
- var multiselect = clackMultiselect;
1280
-
1281
2148
  // src/commands/install.ts
2149
+ init_tui();
1282
2150
  function resolveInstallConfig(options, config) {
1283
2151
  const orgProjectsFromFlag = options.orgProjects ? options.orgProjects.split(",").map((s) => s.trim()) : void 0;
1284
2152
  const personalProjectsFromFlag = options.personalProjects ? options.personalProjects.split(",").map((s) => s.trim()) : void 0;
@@ -1288,7 +2156,10 @@ function resolveInstallConfig(options, config) {
1288
2156
  includeUserGlobal: options.includeUserGlobals ?? config.includeUserGlobal,
1289
2157
  includeOrgGlobal: options.includeOrgGlobals ?? config.includeOrgGlobal,
1290
2158
  orgProjects: orgProjectsFromFlag ?? config.orgProjects,
1291
- personalProjects: personalProjectsFromFlag ?? config.personalProjects
2159
+ personalProjects: personalProjectsFromFlag ?? config.personalProjects,
2160
+ ruleIds: config.ruleIds,
2161
+ excludedRuleIds: config.excludedRuleIds,
2162
+ resolveOverlays: config.resolveOverlays
1292
2163
  };
1293
2164
  }
1294
2165
  function validateInstallOptions(resolved) {
@@ -1296,17 +2167,17 @@ function validateInstallOptions(resolved) {
1296
2167
  const hasOrgProjects = orgProjects && orgProjects.length > 0;
1297
2168
  const hasPersonalProjects = personalProjects && personalProjects.length > 0;
1298
2169
  if (!(profile || hasOrgProjects || hasPersonalProjects)) {
1299
- log2.error("No profile or project(s) specified.");
1300
- log2.info("Either:");
1301
- log2.info(
2170
+ log.error("No profile or project(s) specified.");
2171
+ log.info("Either:");
2172
+ log.info(
1302
2173
  " - Add 'profile', 'orgProjects', or 'personalProjects' to braid.json/braid.user.json"
1303
2174
  );
1304
- log2.info(" - Use --profile, --org-projects, or --personal-projects flags");
1305
- log2.info("");
1306
- log2.info("Examples:");
1307
- log2.info(" braid install --profile coding-standards");
1308
- log2.info(" braid install --org-projects proj123,proj456");
1309
- log2.info(" braid install --personal-projects myproj1");
2175
+ log.info(" - Use --profile, --org-projects, or --personal-projects flags");
2176
+ log.info("");
2177
+ log.info("Examples:");
2178
+ log.info(" braid install --profile coding-standards");
2179
+ log.info(" braid install --org-projects proj123,proj456");
2180
+ log.info(" braid install --personal-projects myproj1");
1310
2181
  process.exit(1);
1311
2182
  }
1312
2183
  }
@@ -1338,25 +2209,45 @@ function buildFetchOptions(resolved) {
1338
2209
  if (resolved.personalProjects && resolved.personalProjects.length > 0) {
1339
2210
  fetchOptions.personalProjects = resolved.personalProjects;
1340
2211
  }
2212
+ if (resolved.ruleIds && resolved.ruleIds.length > 0) {
2213
+ fetchOptions.ruleIds = resolved.ruleIds;
2214
+ }
2215
+ if (resolved.excludedRuleIds && resolved.excludedRuleIds.length > 0) {
2216
+ fetchOptions.excludedRuleIds = resolved.excludedRuleIds;
2217
+ }
2218
+ if (resolved.resolveOverlays !== void 0) {
2219
+ fetchOptions.resolveOverlays = resolved.resolveOverlays;
2220
+ }
1341
2221
  return fetchOptions;
1342
2222
  }
1343
2223
  function displaySkillsAndExit(skills) {
1344
- log2.info("\nSkills:");
2224
+ log.info("\nSkills:");
1345
2225
  for (const skill of skills) {
1346
2226
  const fileCount = skill.files.length;
1347
- log2.info(
2227
+ log.info(
1348
2228
  ` ${skill.name} (${fileCount} file${fileCount !== 1 ? "s" : ""})`
1349
2229
  );
1350
2230
  }
1351
2231
  process.exit(0);
1352
2232
  }
2233
+ function getSelectableAgents(options) {
2234
+ return AGENTS.filter(
2235
+ (agent) => options.global ? Boolean(agent.globalPath) : Boolean(agent.projectPath)
2236
+ ).map(
2237
+ (agent) => ({
2238
+ ...agent,
2239
+ hasProjectConfig: false,
2240
+ hasGlobalConfig: false
2241
+ })
2242
+ );
2243
+ }
1353
2244
  async function resolveAgents(options, installSpinner) {
1354
2245
  if (options.agents) {
1355
2246
  const agentIds = options.agents.split(",").map((s) => s.trim());
1356
2247
  const selectedAgents = agentIds.map((id) => {
1357
2248
  const agentConfig = getAgentById(id);
1358
2249
  if (!agentConfig) {
1359
- log2.warn(`Unknown agent: ${id}`);
2250
+ log.warn(`Unknown agent: ${id}`);
1360
2251
  return null;
1361
2252
  }
1362
2253
  return {
@@ -1366,10 +2257,13 @@ async function resolveAgents(options, installSpinner) {
1366
2257
  };
1367
2258
  }).filter((a) => a !== null);
1368
2259
  if (selectedAgents.length === 0) {
1369
- log2.error("No valid agents specified.");
2260
+ log.error("No valid agents specified.");
1370
2261
  process.exit(1);
1371
2262
  }
1372
- return selectedAgents;
2263
+ return {
2264
+ availableAgents: selectedAgents,
2265
+ preselectedAgents: selectedAgents
2266
+ };
1373
2267
  }
1374
2268
  installSpinner.start("Detecting installed agents...");
1375
2269
  const allDetectedAgents = await detectAgentsAsync();
@@ -1382,37 +2276,45 @@ async function resolveAgents(options, installSpinner) {
1382
2276
  );
1383
2277
  for (const agent of detectedAgents) {
1384
2278
  const targetPath = options.global ? agent.globalPath : agent.projectPath;
1385
- log2.info(` ${agent.name} \u2192 ${targetPath}`);
2279
+ log.info(` ${agent.name} \u2192 ${targetPath}`);
1386
2280
  }
1387
2281
  if (detectedAgents.length === 0) {
1388
- log2.warn("No AI coding agents detected.");
1389
- log2.info(
2282
+ log.warn("No AI coding agents detected.");
2283
+ log.info(
1390
2284
  "Supported agents: claude-code, opencode, cursor, windsurf, cline, and more."
1391
2285
  );
1392
- log2.info(
1393
- "Use --agents to specify agents manually: braid install -p my-profile -a claude-code"
2286
+ log.info(
2287
+ "Select agents manually below. Detected agents are pre-selected when available."
1394
2288
  );
1395
- process.exit(1);
2289
+ return {
2290
+ availableAgents: getSelectableAgents(options),
2291
+ preselectedAgents: []
2292
+ };
1396
2293
  }
1397
- return detectedAgents;
2294
+ return {
2295
+ availableAgents: getSelectableAgents(options),
2296
+ preselectedAgents: detectedAgents
2297
+ };
1398
2298
  }
1399
- async function selectAgents(detectedAgents, options) {
2299
+ async function selectAgents(availableAgents, preselectedAgents, options) {
1400
2300
  if (options.yes) {
1401
- return detectedAgents;
2301
+ return preselectedAgents.length > 0 ? preselectedAgents : availableAgents;
1402
2302
  }
1403
- const agentChoices = detectedAgents.map((agent) => ({
2303
+ const agentChoices = availableAgents.map((agent) => ({
1404
2304
  value: agent,
1405
2305
  label: agent.name,
1406
- hint: options.global ? agent.globalPath : agent.projectPath
2306
+ ...(options.global ? agent.globalPath || agent.rulesGlobalPath : agent.projectPath || agent.rulesProjectPath) ? {
2307
+ hint: options.global ? agent.globalPath || agent.rulesGlobalPath : agent.projectPath || agent.rulesProjectPath
2308
+ } : {}
1407
2309
  }));
1408
2310
  const selected = await multiselect({
1409
2311
  message: "Select agents to install to:",
1410
2312
  options: agentChoices,
1411
- initialValues: detectedAgents,
2313
+ initialValues: preselectedAgents,
1412
2314
  required: true
1413
2315
  });
1414
- if (isCancel2(selected)) {
1415
- cancel2("Install cancelled.");
2316
+ if (isCancel(selected)) {
2317
+ cancel("Install cancelled.");
1416
2318
  process.exit(0);
1417
2319
  }
1418
2320
  return selected;
@@ -1423,7 +2325,7 @@ async function installSkillsToAgent(agent, response, installPath) {
1423
2325
  }
1424
2326
  const result = await writeSkillsAsync(installPath, response.skills, agent.id);
1425
2327
  for (const err of result.errors) {
1426
- log2.warn(` Failed skill: ${err.skill} - ${err.error}`);
2328
+ log.warn(` Failed skill: ${err.skill} - ${err.error}`);
1427
2329
  }
1428
2330
  return { written: result.written.length, errors: result.errors.length };
1429
2331
  }
@@ -1437,7 +2339,7 @@ async function installRulesToAgent(agent, response, options) {
1437
2339
  }
1438
2340
  const result = await writeRulesForAgentAsync(agent, rules, rulesPath);
1439
2341
  for (const err of result.errors) {
1440
- log2.warn(` Failed rules: ${err.agent} - ${err.error}`);
2342
+ log.warn(` Failed rules: ${err.agent} - ${err.error}`);
1441
2343
  }
1442
2344
  return { written: result.written, errors: result.errors.length };
1443
2345
  }
@@ -1487,9 +2389,9 @@ async function installCommand(options) {
1487
2389
  const resolved = resolveInstallConfig(options, config);
1488
2390
  validateInstallOptions(resolved);
1489
2391
  const sourceDesc = buildSourceDescription(resolved);
1490
- intro2(`Installing from ${sourceDesc}`);
1491
- const installSpinner = spinner2();
1492
- installSpinner.start("Fetching from Braid...");
2392
+ intro(`Installing from ${sourceDesc}`);
2393
+ const installSpinner = spinner();
2394
+ installSpinner.start("Fetching from braid...");
1493
2395
  try {
1494
2396
  const fetchOptions = buildFetchOptions(resolved);
1495
2397
  const response = await fetchSkillsAsync(fetchOptions);
@@ -1504,7 +2406,7 @@ async function installCommand(options) {
1504
2406
  }
1505
2407
  installSpinner.stop(`Found ${foundParts.join(", ") || "nothing"}`);
1506
2408
  if (skillCount === 0 && ruleCount === 0) {
1507
- log2.warn(
2409
+ log.warn(
1508
2410
  "No skills or rules found. Check that your profile/project has enabled prompts."
1509
2411
  );
1510
2412
  process.exit(0);
@@ -1512,10 +2414,12 @@ async function installCommand(options) {
1512
2414
  if (options.list) {
1513
2415
  displaySkillsAndExit(response.skills);
1514
2416
  }
1515
- let selectedAgents = await resolveAgents(options, installSpinner);
1516
- if (!options.agents) {
1517
- selectedAgents = await selectAgents(selectedAgents, options);
1518
- }
2417
+ const agentResolution = await resolveAgents(options, installSpinner);
2418
+ const selectedAgents = await selectAgents(
2419
+ agentResolution.availableAgents,
2420
+ agentResolution.preselectedAgents,
2421
+ options
2422
+ );
1519
2423
  let totalWritten = 0;
1520
2424
  let totalErrors = 0;
1521
2425
  for (const agent of selectedAgents) {
@@ -1530,24 +2434,24 @@ async function installCommand(options) {
1530
2434
  totalErrors += result.errors;
1531
2435
  }
1532
2436
  if (totalErrors > 0) {
1533
- outro2(`Installed ${totalWritten} items with ${totalErrors} errors.`);
2437
+ outro(`Installed ${totalWritten} items with ${totalErrors} errors.`);
1534
2438
  } else {
1535
- outro2(
2439
+ outro(
1536
2440
  `Successfully installed ${totalWritten} items to ${selectedAgents.length} agent(s).`
1537
2441
  );
1538
2442
  }
1539
- log2.info("Run 'braid list' to see installed skills.");
2443
+ log.info("Run 'braid list' to see installed skills.");
1540
2444
  } catch (error) {
1541
2445
  installSpinner.stop("Install failed");
1542
2446
  const message = error instanceof Error ? error.message : String(error);
1543
- log2.error(message);
2447
+ log.error(message);
1544
2448
  process.exit(1);
1545
2449
  }
1546
2450
  }
1547
2451
 
1548
2452
  // src/commands/list.ts
1549
2453
  init_esm_shims();
1550
- import { log as log3, spinner as spinner3 } from "@clack/prompts";
2454
+ import { log as log2, spinner as spinner2 } from "@clack/prompts";
1551
2455
  function formatRelativeTime(isoDate) {
1552
2456
  const date = new Date(isoDate);
1553
2457
  const now = /* @__PURE__ */ new Date();
@@ -1573,16 +2477,16 @@ function displayAgentSkills(agent, installPath, braidSkills) {
1573
2477
  const nameWidth = 25;
1574
2478
  const sourceWidth = 20;
1575
2479
  const installedWidth = 15;
1576
- log3.info("");
1577
- log3.info(`Agent: ${agent.name} (${installPath})`);
1578
- log3.info("\u2500".repeat(60));
2480
+ log2.info("");
2481
+ log2.info(`Agent: ${agent.name} (${installPath})`);
2482
+ log2.info("\u2500".repeat(60));
1579
2483
  const header = [
1580
2484
  "Skill".padEnd(nameWidth),
1581
2485
  "Source".padEnd(sourceWidth),
1582
2486
  "Installed".padEnd(installedWidth)
1583
2487
  ].join(" ");
1584
- log3.info(header);
1585
- log3.info("\u2500".repeat(60));
2488
+ log2.info(header);
2489
+ log2.info("\u2500".repeat(60));
1586
2490
  for (const skill of braidSkills) {
1587
2491
  const sourceName = skill.source.name;
1588
2492
  const row = [
@@ -1590,17 +2494,17 @@ function displayAgentSkills(agent, installPath, braidSkills) {
1590
2494
  sourceName.slice(0, sourceWidth).padEnd(sourceWidth),
1591
2495
  formatRelativeTime(skill.installedAt).padEnd(installedWidth)
1592
2496
  ].join(" ");
1593
- log3.info(row);
2497
+ log2.info(row);
1594
2498
  }
1595
2499
  }
1596
2500
  async function listCommand(options) {
1597
- const listSpinner = spinner3();
2501
+ const listSpinner = spinner2();
1598
2502
  listSpinner.start("Scanning for installed skills...");
1599
2503
  try {
1600
2504
  const detectedAgents = await detectAgentsAsync();
1601
2505
  if (detectedAgents.length === 0) {
1602
2506
  listSpinner.stop("No agents detected");
1603
- log3.warn("No AI coding agents detected.");
2507
+ log2.warn("No AI coding agents detected.");
1604
2508
  return;
1605
2509
  }
1606
2510
  listSpinner.stop(`Found ${detectedAgents.length} agent(s)`);
@@ -1625,25 +2529,372 @@ async function listCommand(options) {
1625
2529
  displayAgentSkills(agent, installPath, braidSkills);
1626
2530
  }
1627
2531
  if (totalSkills === 0) {
1628
- log3.warn("\nNo skills installed via braid.");
1629
- log3.info("Run 'braid install --profile <name>' to install skills.");
2532
+ log2.warn("\nNo skills installed via braid.");
2533
+ log2.info("Run 'braid install --profile <name>' to install skills.");
1630
2534
  } else {
1631
- log3.info("");
1632
- log3.info(`Total: ${totalSkills} skill(s) installed`);
2535
+ log2.info("");
2536
+ log2.info(`Total: ${totalSkills} skill(s) installed`);
1633
2537
  }
1634
2538
  } catch (error) {
1635
2539
  listSpinner.stop("List failed");
1636
2540
  const message = error instanceof Error ? error.message : String(error);
1637
- log3.error(message);
2541
+ log2.error(message);
1638
2542
  process.exit(1);
1639
2543
  }
1640
2544
  }
1641
2545
 
2546
+ // src/commands/mcp.ts
2547
+ init_esm_shims();
2548
+ import process7 from "process";
2549
+ init_config();
2550
+
2551
+ // src/lib/mcp-config.ts
2552
+ init_esm_shims();
2553
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
2554
+ import { dirname as dirname4 } from "path";
2555
+ import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
2556
+ var McpConfigReadError = class extends Data6.TaggedError("McpConfigReadError") {
2557
+ };
2558
+ var McpConfigWriteError = class extends Data6.TaggedError("McpConfigWriteError") {
2559
+ };
2560
+ var readMcpConfig = (path2) => pipe7(
2561
+ Effect7.tryPromise({
2562
+ try: () => readFile4(path2, "utf-8"),
2563
+ catch: (e) => new McpConfigReadError({ path: path2, cause: e })
2564
+ }),
2565
+ Effect7.flatMap(
2566
+ (content) => Effect7.try({
2567
+ try: () => JSON.parse(content),
2568
+ catch: () => ({})
2569
+ })
2570
+ ),
2571
+ Effect7.orElseSucceed(() => ({}))
2572
+ );
2573
+ var writeMcpConfig = (path2, config) => Effect7.tryPromise({
2574
+ try: async () => {
2575
+ await mkdir4(dirname4(path2), { recursive: true });
2576
+ await writeFile5(path2, `${JSON.stringify(config, null, 2)}
2577
+ `, "utf-8");
2578
+ },
2579
+ catch: (e) => new McpConfigWriteError({ path: path2, cause: e })
2580
+ });
2581
+ var mergebraidEntry = (config, rootKey, entryName, entry) => {
2582
+ const servers = config[rootKey] ?? {};
2583
+ return {
2584
+ ...config,
2585
+ [rootKey]: {
2586
+ ...servers,
2587
+ [entryName]: entry
2588
+ }
2589
+ };
2590
+ };
2591
+ var removebraidEntry = (config, rootKey, entryName) => {
2592
+ const servers = { ...config[rootKey] ?? {} };
2593
+ delete servers[entryName];
2594
+ return {
2595
+ ...config,
2596
+ [rootKey]: servers
2597
+ };
2598
+ };
2599
+ var hasbraidEntry = (config, rootKey, entryName) => {
2600
+ const servers = config[rootKey];
2601
+ return servers !== void 0 && entryName in servers;
2602
+ };
2603
+ var readMcpConfigAsync = (path2) => Effect7.runPromise(readMcpConfig(path2));
2604
+ var writeMcpConfigAsync = (path2, config) => Effect7.runPromise(writeMcpConfig(path2, config));
2605
+
2606
+ // src/commands/mcp.ts
2607
+ init_tui();
2608
+ var BRAID_ENTRY_NAME = "braid";
2609
+ function maskToken(token) {
2610
+ if (token.length <= 10) {
2611
+ return token;
2612
+ }
2613
+ return `${token.slice(0, 7)}...${token.slice(-4)}`;
2614
+ }
2615
+ function getMcpAgents() {
2616
+ return AGENTS.filter(hasMcpConfig);
2617
+ }
2618
+ function resolveScope(agent, options) {
2619
+ if (options.global) {
2620
+ return "global";
2621
+ }
2622
+ if (agent.mcpProjectConfigPath) {
2623
+ return "project";
2624
+ }
2625
+ return "global";
2626
+ }
2627
+ async function selectTool(mcpAgents, toolFlag) {
2628
+ if (toolFlag) {
2629
+ const agent = mcpAgents.find((a) => a.id === toolFlag);
2630
+ if (!agent) {
2631
+ log.error(`Unknown or unsupported tool: ${toolFlag}`);
2632
+ log.info(`Supported tools: ${mcpAgents.map((a) => a.id).join(", ")}`);
2633
+ process7.exit(1);
2634
+ }
2635
+ return agent;
2636
+ }
2637
+ const detected = await detectMcpAgents();
2638
+ const detectedIds = new Set(detected.map((a) => a.id));
2639
+ const toolChoice = await select({
2640
+ message: "Select tool to configure:",
2641
+ options: mcpAgents.map((agent) => ({
2642
+ value: agent.id,
2643
+ label: agent.name,
2644
+ hint: [
2645
+ agent.mcpProjectConfigPath ?? agent.mcpGlobalConfigPath,
2646
+ detectedIds.has(agent.id) ? "detected" : void 0
2647
+ ].filter(Boolean).join(" \u2014 ")
2648
+ }))
2649
+ });
2650
+ if (isCancel(toolChoice)) {
2651
+ cancel("MCP setup cancelled.");
2652
+ process7.exit(0);
2653
+ }
2654
+ const found = mcpAgents.find((a) => a.id === toolChoice);
2655
+ if (!found) {
2656
+ log.error("Agent not found.");
2657
+ process7.exit(1);
2658
+ }
2659
+ return found;
2660
+ }
2661
+ async function selectScope(agent, options) {
2662
+ const initial = resolveScope(agent, options);
2663
+ if (options.global || options.yes || !agent.mcpProjectConfigPath || !agent.mcpGlobalConfigPath) {
2664
+ return initial;
2665
+ }
2666
+ const scopeChoice = await select({
2667
+ message: "Where should this be configured?",
2668
+ options: [
2669
+ {
2670
+ value: "project",
2671
+ label: "Project",
2672
+ hint: agent.mcpProjectConfigPath
2673
+ },
2674
+ {
2675
+ value: "global",
2676
+ label: "Global",
2677
+ hint: agent.mcpGlobalConfigPath
2678
+ }
2679
+ ]
2680
+ });
2681
+ if (isCancel(scopeChoice)) {
2682
+ cancel("MCP setup cancelled.");
2683
+ process7.exit(0);
2684
+ }
2685
+ return scopeChoice;
2686
+ }
2687
+ async function resolveToken(options, existingToken) {
2688
+ if (options.auth === false) {
2689
+ return void 0;
2690
+ }
2691
+ if (options.token) {
2692
+ return options.token;
2693
+ }
2694
+ if (existingToken && options.yes) {
2695
+ return existingToken;
2696
+ }
2697
+ if (existingToken) {
2698
+ const authChoice = await select({
2699
+ message: "Authentication:",
2700
+ options: [
2701
+ {
2702
+ value: "existing",
2703
+ label: `Use existing token (${maskToken(existingToken)})`
2704
+ },
2705
+ { value: "new", label: "Enter a new token" },
2706
+ {
2707
+ value: "skip",
2708
+ label: "Skip \u2014 set BRAID_TOKEN env var later"
2709
+ }
2710
+ ]
2711
+ });
2712
+ if (isCancel(authChoice)) {
2713
+ cancel("MCP setup cancelled.");
2714
+ process7.exit(0);
2715
+ }
2716
+ if (authChoice === "existing") {
2717
+ return existingToken;
2718
+ }
2719
+ if (authChoice === "skip") {
2720
+ return void 0;
2721
+ }
2722
+ }
2723
+ const token = await password({
2724
+ message: "Enter your braid API token:",
2725
+ validate: (value) => {
2726
+ if (!value) {
2727
+ return "Token is required";
2728
+ }
2729
+ if (!value.startsWith("br_")) {
2730
+ return "Token should start with 'br_'";
2731
+ }
2732
+ return void 0;
2733
+ }
2734
+ });
2735
+ if (isCancel(token)) {
2736
+ cancel("MCP setup cancelled.");
2737
+ process7.exit(0);
2738
+ }
2739
+ return token;
2740
+ }
2741
+ function buildEnvVars(token, server) {
2742
+ const env = {};
2743
+ if (token) {
2744
+ env.BRAID_TOKEN = token;
2745
+ }
2746
+ if (server) {
2747
+ env.BRAID_MCP_URL = server;
2748
+ }
2749
+ return env;
2750
+ }
2751
+ async function writeEntry(agent, configPath, env) {
2752
+ const entry = buildMcpEntry(agent.mcpEntryStyle ?? "standard", env);
2753
+ const rootKey = agent.mcpRootKey ?? "mcpServers";
2754
+ const existingConfig = await readMcpConfigAsync(configPath);
2755
+ const updatedConfig = mergebraidEntry(
2756
+ existingConfig,
2757
+ rootKey,
2758
+ BRAID_ENTRY_NAME,
2759
+ entry
2760
+ );
2761
+ await writeMcpConfigAsync(configPath, updatedConfig);
2762
+ }
2763
+ async function addFlow(options) {
2764
+ const config = await loadMergedConfigAsync();
2765
+ const mcpAgents = getMcpAgents();
2766
+ if (mcpAgents.length === 0) {
2767
+ log.error("No agents with MCP configuration support found.");
2768
+ process7.exit(1);
2769
+ }
2770
+ const selectedAgent = await selectTool(mcpAgents, options.tool);
2771
+ const scope = await selectScope(selectedAgent, options);
2772
+ const configPath = resolveMcpConfigPath(selectedAgent, {
2773
+ global: scope === "global"
2774
+ });
2775
+ if (!configPath) {
2776
+ log.error(`No ${scope} config path available for ${selectedAgent.name}.`);
2777
+ process7.exit(1);
2778
+ }
2779
+ const token = await resolveToken(options, config.token);
2780
+ const env = buildEnvVars(token, options.server);
2781
+ if (options.scope) {
2782
+ const { scopeCommand: scopeCommand2 } = await Promise.resolve().then(() => (init_scope(), scope_exports));
2783
+ await scopeCommand2({
2784
+ server: options.server ?? config.serverUrl,
2785
+ ...config.token ? { apiKey: config.token } : {}
2786
+ });
2787
+ }
2788
+ const mcpSpinner = spinner();
2789
+ mcpSpinner.start(`Configuring ${selectedAgent.name}...`);
2790
+ try {
2791
+ await writeEntry(selectedAgent, configPath, env);
2792
+ mcpSpinner.stop(`Configured ${selectedAgent.name}`);
2793
+ log.info(`Config written to ${configPath}`);
2794
+ if (!token) {
2795
+ log.info("Set BRAID_TOKEN environment variable before using.");
2796
+ }
2797
+ outro(`Done! Restart ${selectedAgent.name} to activate.`);
2798
+ } catch (error) {
2799
+ mcpSpinner.stop("Configuration failed");
2800
+ const message = error instanceof Error ? error.message : String(error);
2801
+ log.error(`Failed to write config: ${message}`);
2802
+ process7.exit(1);
2803
+ }
2804
+ }
2805
+ async function removeFlow(options) {
2806
+ const mcpAgents = getMcpAgents();
2807
+ const selectedAgent = await selectTool(mcpAgents, options.tool);
2808
+ const scope = resolveScope(selectedAgent, options);
2809
+ const configPath = resolveMcpConfigPath(selectedAgent, {
2810
+ global: scope === "global"
2811
+ });
2812
+ if (!configPath) {
2813
+ log.error(`No ${scope} config path available for ${selectedAgent.name}.`);
2814
+ process7.exit(1);
2815
+ }
2816
+ const removeSpinner = spinner();
2817
+ removeSpinner.start(`Removing braid from ${selectedAgent.name}...`);
2818
+ try {
2819
+ const rootKey = selectedAgent.mcpRootKey ?? "mcpServers";
2820
+ const existingConfig = await readMcpConfigAsync(configPath);
2821
+ if (!hasbraidEntry(existingConfig, rootKey, BRAID_ENTRY_NAME)) {
2822
+ removeSpinner.stop("No braid MCP entry found");
2823
+ log.info(
2824
+ `${selectedAgent.name} doesn't have a braid MCP entry at ${configPath}`
2825
+ );
2826
+ return;
2827
+ }
2828
+ const updatedConfig = removebraidEntry(
2829
+ existingConfig,
2830
+ rootKey,
2831
+ BRAID_ENTRY_NAME
2832
+ );
2833
+ await writeMcpConfigAsync(configPath, updatedConfig);
2834
+ removeSpinner.stop(`Removed braid from ${selectedAgent.name}`);
2835
+ outro(`braid MCP removed from ${configPath}`);
2836
+ } catch (error) {
2837
+ removeSpinner.stop("Removal failed");
2838
+ const message = error instanceof Error ? error.message : String(error);
2839
+ log.error(`Failed to remove config: ${message}`);
2840
+ process7.exit(1);
2841
+ }
2842
+ }
2843
+ async function statusFlow() {
2844
+ const mcpAgents = getMcpAgents();
2845
+ const statusSpinner = spinner();
2846
+ statusSpinner.start("Checking MCP configuration status...");
2847
+ const results = [];
2848
+ for (const agent of mcpAgents) {
2849
+ const rootKey = agent.mcpRootKey ?? "mcpServers";
2850
+ for (const scope of ["project", "global"]) {
2851
+ const configPath = resolveMcpConfigPath(agent, {
2852
+ global: scope === "global"
2853
+ });
2854
+ if (!configPath) {
2855
+ continue;
2856
+ }
2857
+ const config = await readMcpConfigAsync(configPath);
2858
+ if (hasbraidEntry(config, rootKey, BRAID_ENTRY_NAME)) {
2859
+ results.push({
2860
+ name: agent.name,
2861
+ path: configPath,
2862
+ scope
2863
+ });
2864
+ }
2865
+ }
2866
+ }
2867
+ statusSpinner.stop(`Found ${results.length} configured tool(s)`);
2868
+ if (results.length === 0) {
2869
+ log.info("No tools have braid MCP configured.");
2870
+ log.info("Run 'braid mcp' to set up MCP for your AI tools.");
2871
+ return;
2872
+ }
2873
+ for (const result of results) {
2874
+ log.info(` ${result.name} (${result.scope}) \u2192 ${result.path}`);
2875
+ }
2876
+ }
2877
+ async function mcpCommand(options) {
2878
+ if (options.status) {
2879
+ intro("braid mcp status");
2880
+ await statusFlow();
2881
+ return;
2882
+ }
2883
+ if (options.remove) {
2884
+ intro("braid mcp remove");
2885
+ await removeFlow(options);
2886
+ return;
2887
+ }
2888
+ intro("braid mcp");
2889
+ await addFlow(options);
2890
+ }
2891
+
1642
2892
  // src/commands/remove.ts
1643
2893
  init_esm_shims();
1644
2894
  import { rm } from "fs/promises";
1645
2895
  import { join as join4, resolve as resolve3 } from "path";
1646
- import process6 from "process";
2896
+ import process8 from "process";
2897
+ init_tui();
1647
2898
  async function collectInstalledSkills(detectedAgents, options) {
1648
2899
  const skillsToRemove = [];
1649
2900
  for (const agent of detectedAgents) {
@@ -1673,9 +2924,9 @@ async function selectSkillsToRemove(skillsToRemove, options) {
1673
2924
  if (options.skill) {
1674
2925
  const selected = skillsToRemove.filter((s) => s.name === options.skill);
1675
2926
  if (selected.length === 0) {
1676
- log2.error(`Skill '${options.skill}' not found.`);
1677
- log2.info("Run 'braid list' to see installed skills.");
1678
- process6.exit(1);
2927
+ log.error(`Skill '${options.skill}' not found.`);
2928
+ log.info("Run 'braid list' to see installed skills.");
2929
+ process8.exit(1);
1679
2930
  }
1680
2931
  return selected;
1681
2932
  }
@@ -1692,9 +2943,9 @@ async function selectSkillsToRemove(skillsToRemove, options) {
1692
2943
  options: choices,
1693
2944
  required: true
1694
2945
  });
1695
- if (isCancel2(result)) {
1696
- cancel2("Remove cancelled.");
1697
- process6.exit(0);
2946
+ if (isCancel(result)) {
2947
+ cancel("Remove cancelled.");
2948
+ process8.exit(0);
1698
2949
  }
1699
2950
  return result;
1700
2951
  }
@@ -1702,13 +2953,13 @@ async function confirmRemoval(selectedCount, options) {
1702
2953
  if (options.yes || options.skill || options.all) {
1703
2954
  return;
1704
2955
  }
1705
- const confirmed = await confirm2({
2956
+ const confirmed = await confirm({
1706
2957
  message: `Remove ${selectedCount} skill(s)?`,
1707
2958
  initialValue: false
1708
2959
  });
1709
- if (isCancel2(confirmed) || !confirmed) {
1710
- cancel2("Remove cancelled.");
1711
- process6.exit(0);
2960
+ if (isCancel(confirmed) || !confirmed) {
2961
+ cancel("Remove cancelled.");
2962
+ process8.exit(0);
1712
2963
  }
1713
2964
  }
1714
2965
  async function removeSkill(skill, removeSpinner) {
@@ -1718,7 +2969,7 @@ async function removeSkill(skill, removeSpinner) {
1718
2969
  const resolvedInstallPath = resolve3(skill.installPath);
1719
2970
  if (!resolvedSkillPath.startsWith(`${resolvedInstallPath}/`)) {
1720
2971
  removeSpinner.stop(`Unsafe path for ${skill.name}`);
1721
- log2.warn(" Skill path escapes install directory, skipping.");
2972
+ log.warn(" Skill path escapes install directory, skipping.");
1722
2973
  return false;
1723
2974
  }
1724
2975
  await rm(resolvedSkillPath, { recursive: true, force: true });
@@ -1728,18 +2979,18 @@ async function removeSkill(skill, removeSpinner) {
1728
2979
  } catch (error) {
1729
2980
  removeSpinner.stop(`Failed to remove ${skill.name}`);
1730
2981
  const message = error instanceof Error ? error.message : String(error);
1731
- log2.warn(` ${message}`);
2982
+ log.warn(` ${message}`);
1732
2983
  return false;
1733
2984
  }
1734
2985
  }
1735
2986
  async function removeCommand(options) {
1736
- const removeSpinner = spinner2();
2987
+ const removeSpinner = spinner();
1737
2988
  removeSpinner.start("Scanning for installed skills...");
1738
2989
  try {
1739
2990
  const detectedAgents = await detectAgentsAsync();
1740
2991
  if (detectedAgents.length === 0) {
1741
2992
  removeSpinner.stop("No agents detected");
1742
- log2.warn("No AI coding agents detected.");
2993
+ log.warn("No AI coding agents detected.");
1743
2994
  return;
1744
2995
  }
1745
2996
  const skillsToRemove = await collectInstalledSkills(
@@ -1748,7 +2999,7 @@ async function removeCommand(options) {
1748
2999
  );
1749
3000
  removeSpinner.stop(`Found ${skillsToRemove.length} installed skill(s)`);
1750
3001
  if (skillsToRemove.length === 0) {
1751
- log2.warn("No skills installed via braid.");
3002
+ log.warn("No skills installed via braid.");
1752
3003
  return;
1753
3004
  }
1754
3005
  const selected = await selectSkillsToRemove(skillsToRemove, options);
@@ -1764,32 +3015,36 @@ async function removeCommand(options) {
1764
3015
  }
1765
3016
  }
1766
3017
  if (errors > 0) {
1767
- outro2(`Removed ${removed} skill(s) with ${errors} error(s).`);
3018
+ outro(`Removed ${removed} skill(s) with ${errors} error(s).`);
1768
3019
  } else {
1769
- outro2(`Successfully removed ${removed} skill(s).`);
3020
+ outro(`Successfully removed ${removed} skill(s).`);
1770
3021
  }
1771
3022
  } catch (error) {
1772
3023
  removeSpinner.stop("Remove failed");
1773
3024
  const message = error instanceof Error ? error.message : String(error);
1774
- log2.error(message);
1775
- process6.exit(1);
3025
+ log.error(message);
3026
+ process8.exit(1);
1776
3027
  }
1777
3028
  }
1778
3029
 
3030
+ // src/index.ts
3031
+ init_scope();
3032
+
1779
3033
  // src/commands/update.ts
1780
3034
  init_esm_shims();
1781
3035
  import {
1782
- cancel as cancel3,
1783
- isCancel as isCancel3,
1784
- log as log4,
3036
+ cancel as cancel2,
3037
+ isCancel as isCancel2,
3038
+ log as log3,
1785
3039
  multiselect as multiselect2,
1786
- outro as outro3,
1787
- spinner as spinner4
3040
+ outro as outro2,
3041
+ spinner as spinner3
1788
3042
  } from "@clack/prompts";
1789
- import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
1790
- var UpdateError = class extends Data6.TaggedError("UpdateError") {
3043
+ import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
3044
+ init_api();
3045
+ var UpdateError = class extends Data7.TaggedError("UpdateError") {
1791
3046
  };
1792
- var UserCancelledError = class extends Data6.TaggedError("UserCancelledError") {
3047
+ var UserCancelledError = class extends Data7.TaggedError("UserCancelledError") {
1793
3048
  };
1794
3049
  async function resolveValidInstallPath(agent, options) {
1795
3050
  const installPath = resolveInstallPath(agent, {
@@ -1801,7 +3056,7 @@ async function resolveValidInstallPath(agent, options) {
1801
3056
  const exists = await directoryExistsAsync(installPath);
1802
3057
  return exists ? installPath : null;
1803
3058
  }
1804
- var collectSourcesFromAgent = (agent, options, sourcesToUpdate) => Effect7.tryPromise({
3059
+ var collectSourcesFromAgent = (agent, options, sourcesToUpdate) => Effect8.tryPromise({
1805
3060
  try: async () => {
1806
3061
  const installPath = await resolveValidInstallPath(agent, options);
1807
3062
  if (!installPath) {
@@ -1832,10 +3087,10 @@ var collectSourcesFromAgent = (agent, options, sourcesToUpdate) => Effect7.tryPr
1832
3087
  },
1833
3088
  catch: () => new UpdateError({ message: "Failed to collect sources" })
1834
3089
  });
1835
- var collectSources = (detectedAgents, options) => pipe7(
1836
- Effect7.succeed(/* @__PURE__ */ new Map()),
1837
- Effect7.tap(
1838
- (sourcesToUpdate) => Effect7.forEach(
3090
+ var collectSources = (detectedAgents, options) => pipe8(
3091
+ Effect8.succeed(/* @__PURE__ */ new Map()),
3092
+ Effect8.tap(
3093
+ (sourcesToUpdate) => Effect8.forEach(
1839
3094
  detectedAgents,
1840
3095
  (agent) => collectSourcesFromAgent(agent, options, sourcesToUpdate),
1841
3096
  { concurrency: 1 }
@@ -1844,9 +3099,9 @@ var collectSources = (detectedAgents, options) => pipe7(
1844
3099
  );
1845
3100
  var selectSources = (sourcesToUpdate, options) => {
1846
3101
  if (options.yes) {
1847
- return Effect7.succeed(sourcesToUpdate);
3102
+ return Effect8.succeed(sourcesToUpdate);
1848
3103
  }
1849
- return Effect7.tryPromise({
3104
+ return Effect8.tryPromise({
1850
3105
  try: async () => {
1851
3106
  const sources = Array.from(sourcesToUpdate.entries()).map(
1852
3107
  ([key, source]) => ({
@@ -1861,7 +3116,7 @@ var selectSources = (sourcesToUpdate, options) => {
1861
3116
  initialValues: sources.map((s) => s.value),
1862
3117
  required: true
1863
3118
  });
1864
- if (isCancel3(selected)) {
3119
+ if (isCancel2(selected)) {
1865
3120
  throw new Error("cancelled");
1866
3121
  }
1867
3122
  for (const key of sourcesToUpdate.keys()) {
@@ -1897,7 +3152,7 @@ var buildFetchOptionsForSource = (source, options) => {
1897
3152
  }
1898
3153
  return fetchOptions;
1899
3154
  };
1900
- var updateAgentSkills = (agentId, agentName, installPath, response, serverUrl, updateSpinner) => Effect7.tryPromise({
3155
+ var updateAgentSkills = (agentId, agentName, installPath, response, serverUrl, updateSpinner) => Effect8.tryPromise({
1901
3156
  try: async () => {
1902
3157
  updateSpinner.start(`Updating ${agentName}...`);
1903
3158
  const result = await writeSkillsAsync(
@@ -1935,15 +3190,15 @@ var updateSource = (source, options, updateSpinner) => {
1935
3190
  const serverUrl = options.server ?? source.serverUrl;
1936
3191
  const fetchOptions = buildFetchOptionsForSource(source, options);
1937
3192
  if (fetchOptions === null) {
1938
- return Effect7.fail(
3193
+ return Effect8.fail(
1939
3194
  new UpdateError({
1940
3195
  message: "Skills installed with legacy metadata format. Please reinstall using 'braid install --profile <name>' or 'braid install --projects <names>'.",
1941
3196
  source: sourceDesc
1942
3197
  })
1943
3198
  );
1944
3199
  }
1945
- return pipe7(
1946
- Effect7.tryPromise({
3200
+ return pipe8(
3201
+ Effect8.tryPromise({
1947
3202
  try: async () => {
1948
3203
  updateSpinner.start(`Fetching latest skills from ${sourceDesc}...`);
1949
3204
  const response = await fetchSkillsAsync(fetchOptions);
@@ -1957,9 +3212,9 @@ var updateSource = (source, options, updateSpinner) => {
1957
3212
  source: sourceDesc
1958
3213
  })
1959
3214
  }),
1960
- Effect7.flatMap(
1961
- (response) => pipe7(
1962
- Effect7.forEach(
3215
+ Effect8.flatMap(
3216
+ (response) => pipe8(
3217
+ Effect8.forEach(
1963
3218
  source.agents,
1964
3219
  ({ agentId, agentName, installPath }) => updateAgentSkills(
1965
3220
  agentId,
@@ -1971,7 +3226,7 @@ var updateSource = (source, options, updateSpinner) => {
1971
3226
  ),
1972
3227
  { concurrency: 1 }
1973
3228
  ),
1974
- Effect7.map((results) => ({
3229
+ Effect8.map((results) => ({
1975
3230
  updated: results.reduce((sum, r) => sum + r.updated, 0),
1976
3231
  errors: results.reduce((sum, r) => sum + r.errors, 0)
1977
3232
  }))
@@ -1979,22 +3234,22 @@ var updateSource = (source, options, updateSpinner) => {
1979
3234
  )
1980
3235
  );
1981
3236
  };
1982
- var updateAllSources = (sources, options, updateSpinner) => pipe7(
1983
- Effect7.forEach(
3237
+ var updateAllSources = (sources, options, updateSpinner) => pipe8(
3238
+ Effect8.forEach(
1984
3239
  Array.from(sources.values()),
1985
- (source) => pipe7(
3240
+ (source) => pipe8(
1986
3241
  updateSource(source, options, updateSpinner),
1987
- Effect7.catchAll((error) => {
3242
+ Effect8.catchAll((error) => {
1988
3243
  updateSpinner.stop(
1989
3244
  `Failed to update from ${getSourceDesc(source)}`
1990
3245
  );
1991
- log4.error(` ${error.message}`);
1992
- return Effect7.succeed({ updated: 0, errors: 1 });
3246
+ log3.error(` ${error.message}`);
3247
+ return Effect8.succeed({ updated: 0, errors: 1 });
1993
3248
  })
1994
3249
  ),
1995
3250
  { concurrency: 1 }
1996
3251
  ),
1997
- Effect7.map((results) => ({
3252
+ Effect8.map((results) => ({
1998
3253
  totalUpdated: results.reduce((sum, r) => sum + r.updated, 0),
1999
3254
  totalErrors: results.reduce((sum, r) => sum + r.errors, 0)
2000
3255
  }))
@@ -2002,15 +3257,15 @@ var updateAllSources = (sources, options, updateSpinner) => pipe7(
2002
3257
  var handleUpdateError = (error, updateSpinner) => {
2003
3258
  updateSpinner.stop("Update failed");
2004
3259
  if (error.message === "No AI coding agents detected.") {
2005
- log4.warn(error.message);
3260
+ log3.warn(error.message);
2006
3261
  return;
2007
3262
  }
2008
3263
  if (error.message === "No skills installed via braid.") {
2009
- log4.warn(error.message);
2010
- log4.info("Run 'braid install --profile <name>' to install skills first.");
3264
+ log3.warn(error.message);
3265
+ log3.info("Run 'braid install --profile <name>' to install skills first.");
2011
3266
  return;
2012
3267
  }
2013
- log4.error(error.message);
3268
+ log3.error(error.message);
2014
3269
  process.exit(1);
2015
3270
  };
2016
3271
  var handleProgramExit = (result, updateSpinner) => {
@@ -2023,7 +3278,7 @@ var handleProgramExit = (result, updateSpinner) => {
2023
3278
  }
2024
3279
  const error = cause.error;
2025
3280
  if (error._tag === "UserCancelledError") {
2026
- cancel3(error.message);
3281
+ cancel2(error.message);
2027
3282
  process.exit(0);
2028
3283
  }
2029
3284
  if (error._tag === "UpdateError") {
@@ -2031,38 +3286,38 @@ var handleProgramExit = (result, updateSpinner) => {
2031
3286
  }
2032
3287
  };
2033
3288
  async function updateCommand(options) {
2034
- const updateSpinner = spinner4();
3289
+ const updateSpinner = spinner3();
2035
3290
  updateSpinner.start("Scanning for installed skills...");
2036
- const program2 = pipe7(
2037
- Effect7.tryPromise({
3291
+ const program2 = pipe8(
3292
+ Effect8.tryPromise({
2038
3293
  try: () => detectAgentsAsync(),
2039
3294
  catch: () => new UpdateError({ message: "Failed to detect agents" })
2040
3295
  }),
2041
- Effect7.filterOrFail(
3296
+ Effect8.filterOrFail(
2042
3297
  (agents) => agents.length > 0,
2043
3298
  () => new UpdateError({ message: "No AI coding agents detected." })
2044
3299
  ),
2045
- Effect7.flatMap((detectedAgents) => collectSources(detectedAgents, options)),
2046
- Effect7.tap((sources) => {
3300
+ Effect8.flatMap((detectedAgents) => collectSources(detectedAgents, options)),
3301
+ Effect8.tap((sources) => {
2047
3302
  updateSpinner.stop(`Found ${sources.size} source(s) to update`);
2048
3303
  }),
2049
- Effect7.filterOrFail(
3304
+ Effect8.filterOrFail(
2050
3305
  (sources) => sources.size > 0,
2051
3306
  () => new UpdateError({ message: "No skills installed via braid." })
2052
3307
  ),
2053
- Effect7.flatMap((sources) => selectSources(sources, options)),
2054
- Effect7.flatMap(
3308
+ Effect8.flatMap((sources) => selectSources(sources, options)),
3309
+ Effect8.flatMap(
2055
3310
  (selectedSources) => updateAllSources(selectedSources, options, updateSpinner)
2056
3311
  ),
2057
- Effect7.tap(({ totalUpdated, totalErrors }) => {
3312
+ Effect8.tap(({ totalUpdated, totalErrors }) => {
2058
3313
  if (totalErrors > 0) {
2059
- outro3(`Updated ${totalUpdated} skills with ${totalErrors} errors.`);
3314
+ outro2(`Updated ${totalUpdated} skills with ${totalErrors} errors.`);
2060
3315
  } else {
2061
- outro3(`Successfully updated ${totalUpdated} skills.`);
3316
+ outro2(`Successfully updated ${totalUpdated} skills.`);
2062
3317
  }
2063
3318
  })
2064
3319
  );
2065
- const result = await Effect7.runPromiseExit(program2);
3320
+ const result = await Effect8.runPromiseExit(program2);
2066
3321
  handleProgramExit(result, updateSpinner);
2067
3322
  }
2068
3323
 
@@ -2071,10 +3326,10 @@ var require2 = createRequire(import.meta.url);
2071
3326
  var { version: PACKAGE_VERSION } = require2("../package.json");
2072
3327
  var program = new Command();
2073
3328
  program.name("braid").description(
2074
- "Install Braid prompts as agent skills to your local development environment"
3329
+ "Install braid prompts as agent skills to your local development environment"
2075
3330
  ).version(PACKAGE_VERSION);
2076
- var auth = program.command("auth").description("Configure API key for Braid authentication");
2077
- auth.command("login", { isDefault: true }).description("Configure API key").option("-s, --server <url>", "Braid server URL (for review apps, local dev)").action(authCommand);
3331
+ var auth = program.command("auth").description("Configure API key for braid authentication");
3332
+ auth.command("login", { isDefault: true }).description("Configure API key").option("-s, --server <url>", "braid server URL (for review apps, local dev)").action(authCommand);
2078
3333
  auth.command("status").description("Show current authentication status").action(authStatusCommand);
2079
3334
  auth.command("logout").description("Remove stored API key").action(authLogoutCommand);
2080
3335
  program.command("install").alias("add").description("Install skills from a profile or project").option("-p, --profile <name>", "Profile name to install from").option(
@@ -2092,9 +3347,32 @@ program.command("install").alias("add").description("Install skills from a profi
2092
3347
  ).option("--no-include-org-globals", "Exclude organization's global prompts").option(
2093
3348
  "-a, --agents <list>",
2094
3349
  "Comma-separated list of agents (e.g., claude-code,opencode)"
2095
- ).option("-g, --global", "Install to global agent directories").option("-y, --yes", "Skip confirmation prompts").option("-l, --list", "Preview skills without installing").option("-s, --server <url>", "Braid server URL (for review apps, local dev)").action(installCommand);
3350
+ ).option("-g, --global", "Install to global agent directories").option("-y, --yes", "Skip confirmation prompts").option("-l, --list", "Preview skills without installing").option("-s, --server <url>", "braid server URL (for review apps, local dev)").action(installCommand);
3351
+ program.command("scope").description("Interactively configure braid.json or braid.user.json scope").option("--file <target>", "Config file target: user or project").option(
3352
+ "--organization <type>",
3353
+ "Scope organization for non-interactive mode: personal or organization"
3354
+ ).option(
3355
+ "--source <source>",
3356
+ "Scope source for non-interactive mode: profile or manual"
3357
+ ).option("--profile <name>", "Profile name for non-interactive mode").option(
3358
+ "--projects <ids>",
3359
+ "Comma-separated project IDs for non-interactive mode"
3360
+ ).option(
3361
+ "--rule-ids <ids>",
3362
+ "Comma-separated included rule IDs for non-interactive mode"
3363
+ ).option(
3364
+ "--excluded-rule-ids <ids>",
3365
+ "Comma-separated excluded rule IDs for non-interactive mode"
3366
+ ).option(
3367
+ "--include-user-global",
3368
+ "Include personal global rules in non-interactive mode"
3369
+ ).option(
3370
+ "--include-org-global",
3371
+ "Include org global rules in non-interactive mode"
3372
+ ).option("-s, --server <url>", "braid server URL (for review apps, local dev)").action(scopeCommand);
2096
3373
  program.command("list").alias("ls").description("List installed skills").option("-g, --global", "List skills in global directories only").action(listCommand);
2097
- program.command("update").alias("up").description("Update installed skills to the latest version").option("-g, --global", "Update skills in global directories only").option("-y, --yes", "Skip confirmation prompts").option("-s, --server <url>", "Braid server URL (for review apps, local dev)").action(updateCommand);
3374
+ program.command("update").alias("up").description("Update installed skills to the latest version").option("-g, --global", "Update skills in global directories only").option("-y, --yes", "Skip confirmation prompts").option("-s, --server <url>", "braid server URL (for review apps, local dev)").action(updateCommand);
2098
3375
  program.command("remove").alias("rm").description("Remove installed skills").option("-a, --all", "Remove all installed skills").option("-g, --global", "Remove skills from global directories only").option("-y, --yes", "Skip confirmation prompts").option("--skill <name>", "Remove a specific skill by name").action(removeCommand);
3376
+ program.command("mcp").description("Configure braid MCP in your AI coding tools").option("-t, --tool <name>", "Tool to configure").option("-g, --global", "Use global config instead of project").option("--token <token>", "API token to embed").option("--no-auth", "Skip authentication setup").option("--scope", "Run interactive scope configuration").option("--remove", "Remove braid MCP from a tool").option("--status", "Show which tools have braid MCP configured").option("-y, --yes", "Skip confirmation prompts").option("-s, --server <url>", "Custom MCP server URL").action(mcpCommand);
2099
3377
  program.parse();
2100
3378
  //# sourceMappingURL=index.js.map