@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/LICENSE +2 -2
- package/README.md +119 -19
- package/dist/index.js +1679 -401
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
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
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
373
|
-
|
|
662
|
+
}
|
|
663
|
+
function outro(message) {
|
|
664
|
+
if (isTTY) {
|
|
665
|
+
clackOutro(message);
|
|
666
|
+
} else {
|
|
667
|
+
process3.stdout.write(`${message}
|
|
668
|
+
|
|
669
|
+
`);
|
|
374
670
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
var
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1228
|
+
process5.exit(1);
|
|
477
1229
|
}
|
|
478
1230
|
await setApiKeyAsync(apiKey);
|
|
479
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1245
|
+
process5.exit(1);
|
|
489
1246
|
}
|
|
490
1247
|
}
|
|
491
1248
|
async function displayTokenSource(masked) {
|
|
492
|
-
if (
|
|
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
|
|
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
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
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
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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
|
-
|
|
2224
|
+
log.info("\nSkills:");
|
|
1345
2225
|
for (const skill of skills) {
|
|
1346
2226
|
const fileCount = skill.files.length;
|
|
1347
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2260
|
+
log.error("No valid agents specified.");
|
|
1370
2261
|
process.exit(1);
|
|
1371
2262
|
}
|
|
1372
|
-
return
|
|
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
|
-
|
|
2279
|
+
log.info(` ${agent.name} \u2192 ${targetPath}`);
|
|
1386
2280
|
}
|
|
1387
2281
|
if (detectedAgents.length === 0) {
|
|
1388
|
-
|
|
1389
|
-
|
|
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
|
-
|
|
1393
|
-
"
|
|
2286
|
+
log.info(
|
|
2287
|
+
"Select agents manually below. Detected agents are pre-selected when available."
|
|
1394
2288
|
);
|
|
1395
|
-
|
|
2289
|
+
return {
|
|
2290
|
+
availableAgents: getSelectableAgents(options),
|
|
2291
|
+
preselectedAgents: []
|
|
2292
|
+
};
|
|
1396
2293
|
}
|
|
1397
|
-
return
|
|
2294
|
+
return {
|
|
2295
|
+
availableAgents: getSelectableAgents(options),
|
|
2296
|
+
preselectedAgents: detectedAgents
|
|
2297
|
+
};
|
|
1398
2298
|
}
|
|
1399
|
-
async function selectAgents(
|
|
2299
|
+
async function selectAgents(availableAgents, preselectedAgents, options) {
|
|
1400
2300
|
if (options.yes) {
|
|
1401
|
-
return
|
|
2301
|
+
return preselectedAgents.length > 0 ? preselectedAgents : availableAgents;
|
|
1402
2302
|
}
|
|
1403
|
-
const agentChoices =
|
|
2303
|
+
const agentChoices = availableAgents.map((agent) => ({
|
|
1404
2304
|
value: agent,
|
|
1405
2305
|
label: agent.name,
|
|
1406
|
-
|
|
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:
|
|
2313
|
+
initialValues: preselectedAgents,
|
|
1412
2314
|
required: true
|
|
1413
2315
|
});
|
|
1414
|
-
if (
|
|
1415
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1491
|
-
const installSpinner =
|
|
1492
|
-
installSpinner.start("Fetching from
|
|
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
|
-
|
|
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
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
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
|
-
|
|
2437
|
+
outro(`Installed ${totalWritten} items with ${totalErrors} errors.`);
|
|
1534
2438
|
} else {
|
|
1535
|
-
|
|
2439
|
+
outro(
|
|
1536
2440
|
`Successfully installed ${totalWritten} items to ${selectedAgents.length} agent(s).`
|
|
1537
2441
|
);
|
|
1538
2442
|
}
|
|
1539
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
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
|
-
|
|
1585
|
-
|
|
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
|
-
|
|
2497
|
+
log2.info(row);
|
|
1594
2498
|
}
|
|
1595
2499
|
}
|
|
1596
2500
|
async function listCommand(options) {
|
|
1597
|
-
const listSpinner =
|
|
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
|
-
|
|
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
|
-
|
|
1629
|
-
|
|
2532
|
+
log2.warn("\nNo skills installed via braid.");
|
|
2533
|
+
log2.info("Run 'braid install --profile <name>' to install skills.");
|
|
1630
2534
|
} else {
|
|
1631
|
-
|
|
1632
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
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 (
|
|
1696
|
-
|
|
1697
|
-
|
|
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
|
|
2956
|
+
const confirmed = await confirm({
|
|
1706
2957
|
message: `Remove ${selectedCount} skill(s)?`,
|
|
1707
2958
|
initialValue: false
|
|
1708
2959
|
});
|
|
1709
|
-
if (
|
|
1710
|
-
|
|
1711
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2982
|
+
log.warn(` ${message}`);
|
|
1732
2983
|
return false;
|
|
1733
2984
|
}
|
|
1734
2985
|
}
|
|
1735
2986
|
async function removeCommand(options) {
|
|
1736
|
-
const removeSpinner =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3018
|
+
outro(`Removed ${removed} skill(s) with ${errors} error(s).`);
|
|
1768
3019
|
} else {
|
|
1769
|
-
|
|
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
|
-
|
|
1775
|
-
|
|
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
|
|
1783
|
-
isCancel as
|
|
1784
|
-
log as
|
|
3036
|
+
cancel as cancel2,
|
|
3037
|
+
isCancel as isCancel2,
|
|
3038
|
+
log as log3,
|
|
1785
3039
|
multiselect as multiselect2,
|
|
1786
|
-
outro as
|
|
1787
|
-
spinner as
|
|
3040
|
+
outro as outro2,
|
|
3041
|
+
spinner as spinner3
|
|
1788
3042
|
} from "@clack/prompts";
|
|
1789
|
-
import { Data as
|
|
1790
|
-
|
|
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
|
|
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) =>
|
|
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) =>
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
(sourcesToUpdate) =>
|
|
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
|
|
3102
|
+
return Effect8.succeed(sourcesToUpdate);
|
|
1848
3103
|
}
|
|
1849
|
-
return
|
|
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 (
|
|
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) =>
|
|
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
|
|
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
|
|
1946
|
-
|
|
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
|
-
|
|
1961
|
-
(response) =>
|
|
1962
|
-
|
|
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
|
-
|
|
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) =>
|
|
1983
|
-
|
|
3237
|
+
var updateAllSources = (sources, options, updateSpinner) => pipe8(
|
|
3238
|
+
Effect8.forEach(
|
|
1984
3239
|
Array.from(sources.values()),
|
|
1985
|
-
(source) =>
|
|
3240
|
+
(source) => pipe8(
|
|
1986
3241
|
updateSource(source, options, updateSpinner),
|
|
1987
|
-
|
|
3242
|
+
Effect8.catchAll((error) => {
|
|
1988
3243
|
updateSpinner.stop(
|
|
1989
3244
|
`Failed to update from ${getSourceDesc(source)}`
|
|
1990
3245
|
);
|
|
1991
|
-
|
|
1992
|
-
return
|
|
3246
|
+
log3.error(` ${error.message}`);
|
|
3247
|
+
return Effect8.succeed({ updated: 0, errors: 1 });
|
|
1993
3248
|
})
|
|
1994
3249
|
),
|
|
1995
3250
|
{ concurrency: 1 }
|
|
1996
3251
|
),
|
|
1997
|
-
|
|
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
|
-
|
|
3260
|
+
log3.warn(error.message);
|
|
2006
3261
|
return;
|
|
2007
3262
|
}
|
|
2008
3263
|
if (error.message === "No skills installed via braid.") {
|
|
2009
|
-
|
|
2010
|
-
|
|
3264
|
+
log3.warn(error.message);
|
|
3265
|
+
log3.info("Run 'braid install --profile <name>' to install skills first.");
|
|
2011
3266
|
return;
|
|
2012
3267
|
}
|
|
2013
|
-
|
|
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
|
-
|
|
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 =
|
|
3289
|
+
const updateSpinner = spinner3();
|
|
2035
3290
|
updateSpinner.start("Scanning for installed skills...");
|
|
2036
|
-
const program2 =
|
|
2037
|
-
|
|
3291
|
+
const program2 = pipe8(
|
|
3292
|
+
Effect8.tryPromise({
|
|
2038
3293
|
try: () => detectAgentsAsync(),
|
|
2039
3294
|
catch: () => new UpdateError({ message: "Failed to detect agents" })
|
|
2040
3295
|
}),
|
|
2041
|
-
|
|
3296
|
+
Effect8.filterOrFail(
|
|
2042
3297
|
(agents) => agents.length > 0,
|
|
2043
3298
|
() => new UpdateError({ message: "No AI coding agents detected." })
|
|
2044
3299
|
),
|
|
2045
|
-
|
|
2046
|
-
|
|
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
|
-
|
|
3304
|
+
Effect8.filterOrFail(
|
|
2050
3305
|
(sources) => sources.size > 0,
|
|
2051
3306
|
() => new UpdateError({ message: "No skills installed via braid." })
|
|
2052
3307
|
),
|
|
2053
|
-
|
|
2054
|
-
|
|
3308
|
+
Effect8.flatMap((sources) => selectSources(sources, options)),
|
|
3309
|
+
Effect8.flatMap(
|
|
2055
3310
|
(selectedSources) => updateAllSources(selectedSources, options, updateSpinner)
|
|
2056
3311
|
),
|
|
2057
|
-
|
|
3312
|
+
Effect8.tap(({ totalUpdated, totalErrors }) => {
|
|
2058
3313
|
if (totalErrors > 0) {
|
|
2059
|
-
|
|
3314
|
+
outro2(`Updated ${totalUpdated} skills with ${totalErrors} errors.`);
|
|
2060
3315
|
} else {
|
|
2061
|
-
|
|
3316
|
+
outro2(`Successfully updated ${totalUpdated} skills.`);
|
|
2062
3317
|
}
|
|
2063
3318
|
})
|
|
2064
3319
|
);
|
|
2065
|
-
const result = await
|
|
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
|
|
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
|
|
2077
|
-
auth.command("login", { isDefault: true }).description("Configure API key").option("-s, --server <url>", "
|
|
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>", "
|
|
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>", "
|
|
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
|