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