skillmux 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,2544 +1,8 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import { Command } from "commander";
5
-
6
- // src/config/default-agent-rules.ts
7
- var supportedPlatforms = ["win32", "linux", "darwin"];
8
- var builtInAgentIds = [
9
- "codex",
10
- "claude",
11
- "gemini",
12
- "agents",
13
- "openclaw"
14
- ];
15
- var defaultAgentRules = [
16
- {
17
- id: "codex",
18
- stableName: "OpenAI Codex",
19
- supportedPlatforms: [...supportedPlatforms],
20
- homeRelativeRootPath: ".codex",
21
- skillsDirectoryPath: "skills",
22
- enabledByDefault: true,
23
- discovery: "builtin"
24
- },
25
- {
26
- id: "claude",
27
- stableName: "Claude Code",
28
- supportedPlatforms: [...supportedPlatforms],
29
- homeRelativeRootPath: ".claude",
30
- skillsDirectoryPath: "skills",
31
- enabledByDefault: true,
32
- discovery: "builtin"
33
- },
34
- {
35
- id: "gemini",
36
- stableName: "Gemini CLI",
37
- supportedPlatforms: [...supportedPlatforms],
38
- homeRelativeRootPath: ".gemini",
39
- skillsDirectoryPath: "skills",
40
- enabledByDefault: true,
41
- discovery: "builtin"
42
- },
43
- {
44
- id: "agents",
45
- stableName: "Agents",
46
- supportedPlatforms: [...supportedPlatforms],
47
- homeRelativeRootPath: ".agents",
48
- skillsDirectoryPath: "skills",
49
- enabledByDefault: true,
50
- discovery: "builtin"
51
- },
52
- {
53
- id: "openclaw",
54
- stableName: "OpenClaw",
55
- supportedPlatforms: [...supportedPlatforms],
56
- homeRelativeRootPath: ".openclaw",
57
- skillsDirectoryPath: "skills",
58
- enabledByDefault: true,
59
- discovery: "builtin"
60
- }
61
- ];
62
- var defaultAgentRuleMap = Object.fromEntries(
63
- defaultAgentRules.map((rule) => [rule.id, rule])
64
- );
65
-
66
- // src/commands/adopt.ts
67
- import { homedir } from "os";
68
- import { join as join7, resolve as resolve8 } from "path";
69
-
70
- // src/core/errors.ts
71
- var SkillMuxError = class extends Error {
72
- constructor(message) {
73
- super(message);
74
- this.name = new.target.name;
75
- }
76
- };
77
- var InvalidIdentifierError = class extends SkillMuxError {
78
- constructor(kind, value) {
79
- super(`Invalid ${kind}: ${value}`);
80
- this.kind = kind;
81
- this.value = value;
82
- }
83
- kind;
84
- value;
85
- };
86
- var ManifestValidationError = class extends SkillMuxError {
87
- constructor(message) {
88
- super(message);
89
- }
90
- };
91
- var UserConfigValidationError = class extends SkillMuxError {
92
- constructor(message) {
93
- super(message);
94
- }
95
- };
96
- var AdoptionError = class extends SkillMuxError {
97
- constructor(message) {
98
- super(message);
99
- }
100
- };
101
-
102
- // src/core/ids.ts
103
- var ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
104
- function normalizeId(value) {
105
- const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
106
- return normalized.length > 0 ? normalized : "skill";
107
- }
108
- function isValidId(value) {
109
- return ID_PATTERN.test(value);
110
- }
111
-
112
- // src/config/resolve-skillmux-home.ts
113
- import { join, resolve } from "path";
114
- function buildConfigPath(skillmuxHome) {
115
- return join(resolve(skillmuxHome), "config.json");
116
- }
117
- function resolveSkillmuxHome(homeDir) {
118
- const resolvedHomeDir = resolve(homeDir);
119
- const skillmuxHome = join(resolvedHomeDir, ".skillmux");
120
- return {
121
- skillmuxHome,
122
- configPath: buildConfigPath(skillmuxHome)
123
- };
124
- }
125
-
126
- // src/discovery/discover-agents.ts
127
- import * as fs2 from "fs/promises";
128
- import { join as join2, resolve as resolve2 } from "path";
129
-
130
- // src/config/load-user-config.ts
131
- import * as fs from "fs/promises";
132
- import { z } from "zod";
133
- var supportedPlatformSchema = z.enum(supportedPlatforms);
134
- var agentOverrideSchema = z.object({
135
- stableName: z.string().min(1).optional(),
136
- supportedPlatforms: z.array(supportedPlatformSchema).min(1).optional(),
137
- homeRelativeRootPath: z.string().min(1).optional(),
138
- skillsDirectoryPath: z.string().min(1).optional(),
139
- enabledByDefault: z.boolean().optional()
140
- }).strict();
141
- var userConfigSchema = z.object({
142
- version: z.literal(1),
143
- agents: z.record(z.string().min(1), agentOverrideSchema)
144
- }).strict();
145
- function createEmptyUserConfig() {
146
- return {
147
- version: 1,
148
- agents: {}
149
- };
150
- }
151
- function stripUtf8Bom(contents) {
152
- return contents.charCodeAt(0) === 65279 ? contents.slice(1) : contents;
153
- }
154
- function formatValidationIssues(error) {
155
- return error.issues.map((issue) => {
156
- const path = issue.path.length > 0 ? issue.path.join(".") : "<root>";
157
- return `${path}: ${issue.message}`;
158
- }).join("; ");
159
- }
160
- async function loadUserConfig(skillmuxHome) {
161
- const configPath = buildConfigPath(skillmuxHome);
162
- try {
163
- const contents = await fs.readFile(configPath, "utf8");
164
- const parsed = JSON.parse(stripUtf8Bom(contents));
165
- const validated = userConfigSchema.safeParse(parsed);
166
- if (!validated.success) {
167
- throw new UserConfigValidationError(
168
- `Invalid config at ${configPath}: ${formatValidationIssues(validated.error)}`
169
- );
170
- }
171
- return validated.data;
172
- } catch (error) {
173
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
174
- return createEmptyUserConfig();
175
- }
176
- if (error instanceof SyntaxError) {
177
- throw new UserConfigValidationError(
178
- `Invalid config at ${configPath}: malformed JSON`
179
- );
180
- }
181
- throw error;
182
- }
183
- }
184
-
185
- // src/discovery/discover-agents.ts
186
- function mergeRule(rule, override) {
187
- if (override === void 0) {
188
- return rule;
189
- }
190
- return {
191
- ...rule,
192
- ...override
193
- };
194
- }
195
- function buildCustomRule(id, override) {
196
- if (override.homeRelativeRootPath === void 0 || override.skillsDirectoryPath === void 0) {
197
- throw new Error(
198
- `Custom agent override "${id}" must define homeRelativeRootPath and skillsDirectoryPath`
199
- );
200
- }
201
- return {
202
- id,
203
- stableName: override.stableName ?? id,
204
- supportedPlatforms: override.supportedPlatforms ?? [...supportedPlatforms],
205
- homeRelativeRootPath: override.homeRelativeRootPath,
206
- skillsDirectoryPath: override.skillsDirectoryPath,
207
- enabledByDefault: override.enabledByDefault ?? true,
208
- discovery: "custom"
209
- };
210
- }
211
- async function pathExists(path) {
212
- try {
213
- await fs2.access(path);
214
- return true;
215
- } catch {
216
- return false;
217
- }
218
- }
219
- function resolveAgentRulePaths(homeDir, rule) {
220
- const absoluteRootPath = resolve2(homeDir, rule.homeRelativeRootPath);
221
- return {
222
- absoluteRootPath,
223
- absoluteSkillsDirectoryPath: join2(
224
- absoluteRootPath,
225
- rule.skillsDirectoryPath
226
- )
227
- };
228
- }
229
- async function discoverAgents(options) {
230
- const platform = options.platform ?? process.platform;
231
- const homeDir = resolve2(options.homeDir);
232
- const skillmuxHome = options.skillmuxHome ?? resolveSkillmuxHome(homeDir).skillmuxHome;
233
- const userConfig = await loadUserConfig(skillmuxHome);
234
- const discoveredAgents = [];
235
- for (const agentId of builtInAgentIds) {
236
- const mergedRule = mergeRule(
237
- defaultAgentRuleMap[agentId],
238
- userConfig.agents[agentId]
239
- );
240
- const resolvedPaths = resolveAgentRulePaths(homeDir, mergedRule);
241
- discoveredAgents.push({
242
- ...mergedRule,
243
- ...resolvedPaths,
244
- exists: await pathExists(resolvedPaths.absoluteSkillsDirectoryPath),
245
- supportedOnPlatform: mergedRule.supportedPlatforms.some(
246
- (supportedPlatform) => supportedPlatform === platform
247
- )
248
- });
249
- }
250
- for (const [agentId, override] of Object.entries(userConfig.agents)) {
251
- if (Object.hasOwn(defaultAgentRuleMap, agentId)) {
252
- continue;
253
- }
254
- const customRule = buildCustomRule(agentId, override);
255
- const resolvedPaths = resolveAgentRulePaths(homeDir, customRule);
256
- discoveredAgents.push({
257
- ...customRule,
258
- ...resolvedPaths,
259
- exists: await pathExists(resolvedPaths.absoluteSkillsDirectoryPath),
260
- supportedOnPlatform: customRule.supportedPlatforms.some(
261
- (supportedPlatform) => supportedPlatform === platform
262
- )
263
- });
264
- }
265
- return discoveredAgents;
266
- }
267
-
268
- // src/discovery/scan-agent-skills.ts
269
- import * as fs5 from "fs/promises";
270
- import { join as join3 } from "path";
271
-
272
- // src/discovery/infer-skill-entry.ts
273
- import * as fs4 from "fs/promises";
274
- import { basename, resolve as resolve4 } from "path";
275
-
276
- // src/fs/path-utils.ts
277
- import * as fs3 from "fs/promises";
278
- import { dirname, parse, relative, resolve as resolve3, sep } from "path";
279
- function normalizeAbsolutePath(path) {
280
- const normalized = resolve3(path);
281
- return process.platform === "win32" ? normalized.replaceAll("/", "\\").toLowerCase() : normalized;
282
- }
283
- function pathsAreEqual(left, right) {
284
- return normalizeAbsolutePath(left) === normalizeAbsolutePath(right);
285
- }
286
- function isPathInside(parentPath, childPath) {
287
- const parent = normalizeAbsolutePath(parentPath);
288
- const child = normalizeAbsolutePath(childPath);
289
- if (parse(parent).root !== parse(child).root) {
290
- return false;
291
- }
292
- const relativePath = relative(parent, child);
293
- if (relativePath === "") {
294
- return true;
295
- }
296
- if (relativePath === "..") {
297
- return false;
298
- }
299
- if (relativePath.startsWith(`..${sep}`)) {
300
- return false;
301
- }
302
- return true;
303
- }
304
- async function assertNoSymlinkAncestors(path, options) {
305
- let current = options?.includeLeaf === true ? resolve3(path) : dirname(resolve3(path));
306
- while (true) {
307
- try {
308
- const entry = await fs3.lstat(current);
309
- if (entry.isSymbolicLink()) {
310
- throw new Error(`Refusing to use path with symlink ancestor at ${current}`);
311
- }
312
- } catch (error) {
313
- if (error.code !== "ENOENT") {
314
- throw error;
315
- }
316
- }
317
- const parent = dirname(current);
318
- if (parent === current) {
319
- return;
320
- }
321
- current = parent;
322
- }
323
- }
324
-
325
- // src/discovery/infer-skill-entry.ts
326
- function buildIssue(code, severity, message, path) {
327
- return { code, severity, message, path };
328
- }
329
- async function inferSkillEntry(options) {
330
- const absolutePath = resolve4(options.path);
331
- const skillName = basename(absolutePath);
332
- const stats = await fs4.lstat(absolutePath);
333
- if (stats.isSymbolicLink()) {
334
- try {
335
- const targetPath = await fs4.realpath(absolutePath);
336
- if (isPathInside(options.skillmuxHome, targetPath)) {
337
- return {
338
- entry: {
339
- agentId: options.agentId,
340
- agentName: options.agentName,
341
- skillName,
342
- kind: "managed-link",
343
- path: absolutePath,
344
- targetPath
345
- }
346
- };
347
- }
348
- return {
349
- entry: {
350
- agentId: options.agentId,
351
- agentName: options.agentName,
352
- skillName,
353
- kind: "unmanaged-link",
354
- path: absolutePath,
355
- targetPath
356
- }
357
- };
358
- } catch (error) {
359
- if (error.code !== "ENOENT") {
360
- throw error;
361
- }
362
- return {
363
- entry: {
364
- agentId: options.agentId,
365
- agentName: options.agentName,
366
- skillName,
367
- kind: "broken-link",
368
- path: absolutePath
369
- },
370
- issue: buildIssue(
371
- "broken-link",
372
- "error",
373
- "Skill entry points to a missing target",
374
- absolutePath
375
- )
376
- };
377
- }
378
- }
379
- if (stats.isDirectory()) {
380
- return {
381
- entry: {
382
- agentId: options.agentId,
383
- agentName: options.agentName,
384
- skillName,
385
- kind: "unmanaged-directory",
386
- path: absolutePath
387
- }
388
- };
389
- }
390
- return {
391
- entry: {
392
- agentId: options.agentId,
393
- agentName: options.agentName,
394
- skillName,
395
- kind: "unknown",
396
- path: absolutePath
397
- },
398
- issue: buildIssue(
399
- "unknown-entry",
400
- "warning",
401
- "Skill entry is neither a managed link nor a directory",
402
- absolutePath
403
- )
404
- };
405
- }
406
-
407
- // src/discovery/scan-agent-skills.ts
408
- async function scanAgentSkills(agent, skillmuxHome) {
409
- if (!agent.exists || !agent.supportedOnPlatform) {
410
- return {
411
- entries: [],
412
- issues: []
413
- };
414
- }
415
- const directoryEntries = await fs5.readdir(agent.absoluteSkillsDirectoryPath, {
416
- withFileTypes: true
417
- });
418
- const sortedDirectoryEntries = [...directoryEntries].sort(
419
- (left, right) => left.name.localeCompare(right.name)
420
- );
421
- const entries = [];
422
- const issues = [];
423
- for (const directoryEntry of sortedDirectoryEntries) {
424
- const result = await inferSkillEntry({
425
- agentId: agent.id,
426
- agentName: agent.stableName,
427
- path: join3(agent.absoluteSkillsDirectoryPath, directoryEntry.name),
428
- skillmuxHome
429
- });
430
- entries.push(result.entry);
431
- if (result.issue !== void 0) {
432
- issues.push(result.issue);
433
- }
434
- }
435
- return {
436
- entries,
437
- issues
438
- };
439
- }
440
-
441
- // src/fs/safe-copy.ts
442
- import * as fs6 from "fs/promises";
443
- import { dirname as dirname2, join as join4, resolve as resolve5 } from "path";
444
- async function assertDirectory(path) {
445
- const entry = await fs6.lstat(path);
446
- if (!entry.isDirectory()) {
447
- throw new Error(`Expected a directory at ${path}`);
448
- }
449
- }
450
- async function assertRegularFile(path, label) {
451
- const entry = await fs6.lstat(path);
452
- if (!entry.isFile()) {
453
- throw new Error(`Expected ${label} to be a regular file at ${path}`);
454
- }
455
- }
456
- async function assertTargetDoesNotExist(path) {
457
- try {
458
- await fs6.lstat(path);
459
- throw new Error(`Refusing to overwrite existing path at ${path}`);
460
- } catch (error) {
461
- if (error.code !== "ENOENT") {
462
- throw error;
463
- }
464
- }
465
- }
466
- async function copyDirectoryContents(sourcePath, targetPath) {
467
- await fs6.mkdir(targetPath, { recursive: true });
468
- const entries = await fs6.readdir(sourcePath, { withFileTypes: true });
469
- for (const entry of entries) {
470
- const sourceEntryPath = join4(sourcePath, entry.name);
471
- const targetEntryPath = join4(targetPath, entry.name);
472
- const entryStats = await fs6.lstat(sourceEntryPath);
473
- if (entryStats.isSymbolicLink()) {
474
- throw new Error(`Refusing to copy source symlink at ${sourceEntryPath}`);
475
- }
476
- if (entryStats.isDirectory()) {
477
- await copyDirectoryContents(sourceEntryPath, targetEntryPath);
478
- continue;
479
- }
480
- if (entryStats.isFile()) {
481
- await fs6.mkdir(dirname2(targetEntryPath), { recursive: true });
482
- await fs6.copyFile(sourceEntryPath, targetEntryPath);
483
- continue;
484
- }
485
- throw new Error(`Unsupported filesystem entry at ${sourceEntryPath}`);
486
- }
487
- }
488
- async function assertSkillSourceLayout(sourcePath) {
489
- const resolvedSourcePath = resolve5(sourcePath);
490
- const skillFilePath = join4(resolvedSourcePath, "SKILL.md");
491
- await assertNoSymlinkAncestors(resolvedSourcePath, { includeLeaf: true });
492
- await assertDirectory(resolvedSourcePath);
493
- try {
494
- await assertRegularFile(skillFilePath, "SKILL.md");
495
- } catch (error) {
496
- if (error.code === "ENOENT") {
497
- throw new Error(`Refusing to import ${resolvedSourcePath} without a root SKILL.md`);
498
- }
499
- throw error;
500
- }
501
- }
502
- async function hasRootSkillFile(sourcePath) {
503
- const resolvedSourcePath = resolve5(sourcePath);
504
- const skillFilePath = join4(resolvedSourcePath, "SKILL.md");
505
- try {
506
- await assertDirectory(resolvedSourcePath);
507
- await assertRegularFile(skillFilePath, "SKILL.md");
508
- return true;
509
- } catch (error) {
510
- if (error.code === "ENOENT") {
511
- return false;
512
- }
513
- throw error;
514
- }
515
- }
516
- async function copySkillContentsToManagedStore(sourcePath, targetPath) {
517
- const resolvedSourcePath = resolve5(sourcePath);
518
- const resolvedTargetPath = resolve5(targetPath);
519
- if (pathsAreEqual(resolvedSourcePath, resolvedTargetPath)) {
520
- throw new Error("Source and target paths must differ");
521
- }
522
- if (isPathInside(resolvedSourcePath, resolvedTargetPath)) {
523
- throw new Error("Refusing to copy into a child of the source directory");
524
- }
525
- await assertSkillSourceLayout(resolvedSourcePath);
526
- await assertNoSymlinkAncestors(resolvedTargetPath);
527
- await assertTargetDoesNotExist(resolvedTargetPath);
528
- await copyDirectoryContents(resolvedSourcePath, resolvedTargetPath);
529
- }
530
-
531
- // src/core/batch-operation-error.ts
532
- function getCauseMessage(cause) {
533
- if (cause instanceof Error) {
534
- return cause.message;
535
- }
536
- return String(cause);
537
- }
538
- function buildBatchOperationMessage(options) {
539
- const completedSuffix = options.completedItems.length > 0 ? ` after ${options.completedAction}: ${options.completedItems.join(", ")}` : "";
540
- return `Failed to ${options.failedAction}${completedSuffix}: ${getCauseMessage(options.cause)}`;
541
- }
542
- var BatchOperationError = class extends Error {
543
- operation;
544
- failedItem;
545
- completedItems;
546
- cause;
547
- constructor(options) {
548
- super(buildBatchOperationMessage(options), { cause: options.cause });
549
- this.name = "BatchOperationError";
550
- this.operation = options.operation;
551
- this.failedItem = options.failedItem;
552
- this.completedItems = [...options.completedItems];
553
- this.cause = options.cause;
554
- }
555
- };
556
-
557
- // src/fs/link-ops.ts
558
- import * as fs7 from "fs/promises";
559
- import { dirname as dirname3, resolve as resolve6 } from "path";
560
- var directoryLinkType = process.platform === "win32" ? "junction" : "dir";
561
- async function createManagedLink(linkPath, targetPath) {
562
- const resolvedLinkPath = resolve6(linkPath);
563
- const resolvedTargetPath = resolve6(targetPath);
564
- await assertNoSymlinkAncestors(resolvedLinkPath);
565
- await assertNoSymlinkAncestors(resolvedTargetPath, { includeLeaf: true });
566
- await fs7.mkdir(dirname3(resolvedLinkPath), { recursive: true });
567
- try {
568
- const existingEntry = await fs7.lstat(resolvedLinkPath);
569
- if (!existingEntry.isSymbolicLink()) {
570
- throw new Error(`Refusing to replace non-link entry at ${resolvedLinkPath}`);
571
- }
572
- const currentTargetPath = await fs7.realpath(resolvedLinkPath);
573
- if (pathsAreEqual(currentTargetPath, resolvedTargetPath)) {
574
- return;
575
- }
576
- throw new Error(`Refusing to replace link at ${resolvedLinkPath}`);
577
- } catch (error) {
578
- if (error.code === "ENOENT" && await fs7.lstat(resolvedLinkPath).then((entry) => entry.isSymbolicLink()).catch(() => false)) {
579
- await fs7.rm(resolvedLinkPath, { recursive: true, force: false });
580
- } else if (error.code !== "ENOENT") {
581
- throw error;
582
- }
583
- }
584
- await fs7.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
585
- }
586
- async function replaceEntryWithManagedLink(linkPath, targetPath, expectedCurrentPath) {
587
- const resolvedLinkPath = resolve6(linkPath);
588
- const resolvedTargetPath = resolve6(targetPath);
589
- const resolvedExpectedCurrentPath = resolve6(expectedCurrentPath);
590
- await assertNoSymlinkAncestors(resolvedLinkPath);
591
- await assertNoSymlinkAncestors(resolvedTargetPath, { includeLeaf: true });
592
- await fs7.mkdir(dirname3(resolvedLinkPath), { recursive: true });
593
- const existingEntry = await fs7.lstat(resolvedLinkPath);
594
- if (existingEntry.isSymbolicLink()) {
595
- const currentTargetPath = await fs7.realpath(resolvedLinkPath);
596
- if (pathsAreEqual(currentTargetPath, resolvedTargetPath)) {
597
- return false;
598
- }
599
- if (!pathsAreEqual(currentTargetPath, resolvedExpectedCurrentPath)) {
600
- throw new Error(`Refusing to replace unexpected link at ${resolvedLinkPath}`);
601
- }
602
- await fs7.rm(resolvedLinkPath, { recursive: true, force: false });
603
- await fs7.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
604
- return true;
605
- }
606
- if (!existingEntry.isDirectory()) {
607
- throw new Error(`Refusing to replace non-directory entry at ${resolvedLinkPath}`);
608
- }
609
- const currentPath = await fs7.realpath(resolvedLinkPath);
610
- if (!pathsAreEqual(currentPath, resolvedExpectedCurrentPath)) {
611
- throw new Error(`Refusing to replace unexpected directory at ${resolvedLinkPath}`);
612
- }
613
- await fs7.rm(resolvedLinkPath, { recursive: true, force: false });
614
- await fs7.symlink(resolvedTargetPath, resolvedLinkPath, directoryLinkType);
615
- return true;
616
- }
617
- async function isLinkPointingToTarget(linkPath, targetPath) {
618
- try {
619
- const entry = await fs7.lstat(linkPath);
620
- if (!entry.isSymbolicLink()) {
621
- return false;
622
- }
623
- const resolvedTargetPath = await fs7.realpath(linkPath);
624
- return pathsAreEqual(resolvedTargetPath, targetPath);
625
- } catch (error) {
626
- if (error.code === "ENOENT") {
627
- return false;
628
- }
629
- throw error;
630
- }
631
- }
632
-
633
- // src/manifest/read-manifest.ts
634
- import * as fs9 from "fs/promises";
635
- import { join as join6, resolve as resolve7 } from "path";
636
-
637
- // src/manifest/build-empty-manifest.ts
638
- function buildEmptyManifest(skillmuxHome) {
639
- return {
640
- version: 1,
641
- skillmuxHome,
642
- skills: {},
643
- agents: {},
644
- activations: [],
645
- lastScan: {
646
- at: null,
647
- issues: []
648
- }
649
- };
650
- }
651
-
652
- // src/manifest/manifest-schema.ts
653
- import { z as z2 } from "zod";
654
- var idSchema = z2.string().min(1).refine(isValidId, "Expected a canonical lowercase slug identifier");
655
- var scanIssueSchema = z2.object({
656
- code: z2.string().min(1),
657
- severity: z2.enum(["info", "warning", "error"]),
658
- message: z2.string().min(1),
659
- path: z2.string().min(1).optional()
660
- }).strict();
661
- var managedSkillSchema = z2.object({
662
- id: idSchema,
663
- name: z2.string().min(1),
664
- path: z2.string().min(1),
665
- source: z2.object({
666
- kind: z2.enum(["local", "imported"]),
667
- path: z2.string().min(1)
668
- }).strict(),
669
- importedAt: z2.string().min(1)
670
- }).strict();
671
- var agentRecordSchema = z2.object({
672
- id: idSchema,
673
- name: z2.string().min(1),
674
- path: z2.string().min(1),
675
- discovery: z2.enum(["builtin", "custom"]),
676
- available: z2.boolean(),
677
- lastSeenAt: z2.string().min(1).nullable()
678
- }).strict();
679
- var activationRecordSchema = z2.object({
680
- skillId: idSchema,
681
- agentId: idSchema,
682
- linkPath: z2.string().min(1),
683
- state: z2.enum(["enabled", "disabled"]),
684
- updatedAt: z2.string().min(1)
685
- }).strict();
686
- var manifestSchema = z2.object({
687
- version: z2.literal(1),
688
- skillmuxHome: z2.string().min(1),
689
- skills: z2.record(z2.string(), managedSkillSchema),
690
- agents: z2.record(z2.string(), agentRecordSchema),
691
- activations: z2.array(activationRecordSchema),
692
- lastScan: z2.object({
693
- at: z2.string().min(1).nullable(),
694
- issues: z2.array(scanIssueSchema)
695
- }).strict()
696
- }).strict().superRefine((manifest, ctx) => {
697
- for (const [skillId, skill] of Object.entries(manifest.skills)) {
698
- if (!isValidId(skillId)) {
699
- ctx.addIssue({
700
- code: z2.ZodIssueCode.custom,
701
- path: ["skills", skillId],
702
- message: `Invalid skill id key: ${skillId}`
703
- });
704
- }
705
- if (skill.id !== skillId) {
706
- ctx.addIssue({
707
- code: z2.ZodIssueCode.custom,
708
- path: ["skills", skillId, "id"],
709
- message: `Skill id must match its record key: ${skillId}`
710
- });
711
- }
712
- }
713
- for (const [agentId, agent] of Object.entries(manifest.agents)) {
714
- if (!isValidId(agentId)) {
715
- ctx.addIssue({
716
- code: z2.ZodIssueCode.custom,
717
- path: ["agents", agentId],
718
- message: `Invalid agent id key: ${agentId}`
719
- });
720
- }
721
- if (agent.id !== agentId) {
722
- ctx.addIssue({
723
- code: z2.ZodIssueCode.custom,
724
- path: ["agents", agentId, "id"],
725
- message: `Agent id must match its record key: ${agentId}`
726
- });
727
- }
728
- }
729
- const activationPairs = /* @__PURE__ */ new Set();
730
- manifest.activations.forEach((activation, index) => {
731
- if (!(activation.skillId in manifest.skills)) {
732
- ctx.addIssue({
733
- code: z2.ZodIssueCode.custom,
734
- path: ["activations", index, "skillId"],
735
- message: `Unknown skill reference: ${activation.skillId}`
736
- });
737
- }
738
- if (!(activation.agentId in manifest.agents)) {
739
- ctx.addIssue({
740
- code: z2.ZodIssueCode.custom,
741
- path: ["activations", index, "agentId"],
742
- message: `Unknown agent reference: ${activation.agentId}`
743
- });
744
- }
745
- const pairKey = `${activation.skillId}:${activation.agentId}`;
746
- if (activationPairs.has(pairKey)) {
747
- ctx.addIssue({
748
- code: z2.ZodIssueCode.custom,
749
- path: ["activations", index],
750
- message: `Duplicate activation for ${pairKey}`
751
- });
752
- return;
753
- }
754
- activationPairs.add(pairKey);
755
- });
756
- });
757
-
758
- // src/manifest/write-manifest.ts
759
- import { randomUUID } from "crypto";
760
- import * as fs8 from "fs/promises";
761
- import { join as join5 } from "path";
762
- function getManifestPath(home) {
763
- return join5(home, "manifest.json");
764
- }
765
- function createManifestTempPath(manifestPath) {
766
- return `${manifestPath}.${process.pid}.${randomUUID()}.tmp`;
767
- }
768
- async function writeManifest(home, manifest) {
769
- await fs8.mkdir(home, { recursive: true });
770
- const manifestPath = getManifestPath(home);
771
- const tempPath = createManifestTempPath(manifestPath);
772
- const contents = `${JSON.stringify(manifest, null, 2)}
773
- `;
774
- await fs8.writeFile(tempPath, contents, "utf8");
775
- try {
776
- await fs8.rename(tempPath, manifestPath);
777
- } catch (error) {
778
- await fs8.unlink(tempPath).catch(() => void 0);
779
- throw error;
780
- }
781
- }
782
-
783
- // src/manifest/read-manifest.ts
784
- function getManifestPath2(home) {
785
- return join6(home, "manifest.json");
786
- }
787
- function normalizeHomePath(home) {
788
- const resolvedHome = resolve7(home);
789
- return process.platform === "win32" ? resolvedHome.toLowerCase() : resolvedHome;
790
- }
791
- function formatValidationIssues2(error) {
792
- return error.issues.map((issue) => {
793
- const path = issue.path.length > 0 ? issue.path.join(".") : "<root>";
794
- return `${path}: ${issue.message}`;
795
- }).join("; ");
796
- }
797
- async function readManifest(home) {
798
- const manifestPath = getManifestPath2(home);
799
- try {
800
- const contents = await fs9.readFile(manifestPath, "utf8");
801
- const parsedJson = JSON.parse(contents);
802
- const parsedManifest = manifestSchema.safeParse(parsedJson);
803
- if (!parsedManifest.success) {
804
- throw new ManifestValidationError(
805
- `Invalid manifest at ${manifestPath}: ${formatValidationIssues2(parsedManifest.error)}`
806
- );
807
- }
808
- if (normalizeHomePath(parsedManifest.data.skillmuxHome) !== normalizeHomePath(home)) {
809
- throw new ManifestValidationError(
810
- `Invalid manifest at ${manifestPath}: skillmuxHome must match ${home}`
811
- );
812
- }
813
- return parsedManifest.data;
814
- } catch (error) {
815
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
816
- const emptyManifest = buildEmptyManifest(home);
817
- await writeManifest(home, emptyManifest);
818
- return emptyManifest;
819
- }
820
- if (error instanceof SyntaxError) {
821
- throw new ManifestValidationError(
822
- `Invalid manifest at ${manifestPath}: malformed JSON`
823
- );
824
- }
825
- throw error;
826
- }
827
- }
828
-
829
- // src/output/print-json.ts
830
- function printJson(value) {
831
- return `${JSON.stringify(value, null, 2)}
832
- `;
833
- }
834
-
835
- // src/commands/adopt.ts
836
- function buildManagedSkillPath(skillmuxHome, skillId) {
837
- return resolve8(skillmuxHome, "skills", skillId);
838
- }
839
- function buildAgentRecord(agent, timestamp) {
840
- return {
841
- id: agent.id,
842
- name: agent.stableName,
843
- path: agent.absoluteSkillsDirectoryPath,
844
- discovery: agent.discovery,
845
- available: agent.exists && agent.supportedOnPlatform,
846
- lastSeenAt: agent.exists ? timestamp : null
847
- };
848
- }
849
- function buildActivationRecord(skillId, agentId, linkPath, timestamp) {
850
- return {
851
- skillId,
852
- agentId,
853
- linkPath,
854
- state: "enabled",
855
- updatedAt: timestamp
856
- };
857
- }
858
- function upsertActivation(manifest, activation) {
859
- const index = manifest.activations.findIndex(
860
- (entry) => entry.skillId === activation.skillId && entry.agentId === activation.agentId
861
- );
862
- if (index === -1) {
863
- manifest.activations.push(activation);
864
- return;
865
- }
866
- manifest.activations[index] = activation;
867
- }
868
- async function resolveTargetAgent(homeDir, skillmuxHome, agentName) {
869
- const agentId = normalizeId(agentName);
870
- const agents = await discoverAgents({ homeDir, skillmuxHome });
871
- const agent = agents.find((entry) => entry.id === agentId);
872
- if (agent === void 0) {
873
- throw new AdoptionError(`Unknown agent: ${agentName}`);
874
- }
875
- if (!agent.supportedOnPlatform) {
876
- throw new AdoptionError(
877
- `Agent ${agent.id} is not supported on ${process.platform}`
878
- );
879
- }
880
- return agent;
881
- }
882
- function filterEntries(scannedAgent, skillFilter) {
883
- if (skillFilter === void 0) {
884
- return scannedAgent.entries;
885
- }
886
- const skillId = normalizeId(skillFilter);
887
- return scannedAgent.entries.filter(
888
- (entry) => normalizeId(entry.skillName) === skillId
889
- );
890
- }
891
- async function resolveAdoptionSource(entry) {
892
- if (entry.kind === "unmanaged-link") {
893
- return entry.targetPath;
894
- }
895
- if (entry.kind === "unmanaged-directory") {
896
- return entry.path;
897
- }
898
- return void 0;
899
- }
900
- function buildManagedSkill(skillId, skillName, managedPath, sourcePath, timestamp) {
901
- return {
902
- id: skillId,
903
- name: skillName,
904
- path: managedPath,
905
- source: {
906
- kind: "imported",
907
- path: sourcePath
908
- },
909
- importedAt: timestamp
910
- };
911
- }
912
- async function reconcileManagedLink(manifest, skillId, entry, agentId, timestamp) {
913
- if (entry.targetPath === void 0) {
914
- throw new AdoptionError(`Managed link target is missing for ${entry.path}`);
915
- }
916
- await assertSkillSourceLayout(entry.targetPath);
917
- const skill = manifest.skills[skillId];
918
- if (skill === void 0 || !pathsAreEqual(skill.path, entry.targetPath)) {
919
- throw new AdoptionError(
920
- `Managed link for ${agentId}/${skillId} has no matching manifest skill record`
921
- );
922
- }
923
- upsertActivation(
924
- manifest,
925
- buildActivationRecord(skillId, agentId, entry.path, timestamp)
926
- );
927
- }
928
- function buildOutput(result, json) {
929
- if (json) {
930
- return printJson({
931
- agent: result.agent,
932
- adopted: result.adopted,
933
- skipped: result.skipped
934
- });
935
- }
936
- if (result.adopted.length === 0) {
937
- return `No skills adopted for ${result.agent.id}.
938
- `;
939
- }
940
- const adoptedSkills = result.adopted.map((entry) => entry.skillId).sort((left, right) => left.localeCompare(right)).join(", ");
941
- return `Adopted ${adoptedSkills} for ${result.agent.id}
942
- `;
943
- }
944
- async function runAdoptSingle(options) {
945
- const homeDir = options.homeDir ?? homedir();
946
- const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
947
- const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
948
- const timestamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
949
- const manifest = await readManifest(skillmuxHome);
950
- const agent = await resolveTargetAgent(homeDir, skillmuxHome, options.agent);
951
- const agentRecord = buildAgentRecord(agent, timestamp);
952
- const scannedAgent = await scanAgentSkills(agent, skillmuxHome);
953
- const entries = filterEntries(scannedAgent, options.skill);
954
- const adopted = [];
955
- const skipped = [];
956
- manifest.agents[agent.id] = agentRecord;
957
- for (const entry of entries) {
958
- const skillId = normalizeId(entry.skillName);
959
- if (entry.kind === "managed-link") {
960
- await reconcileManagedLink(
961
- manifest,
962
- skillId,
963
- entry,
964
- agent.id,
965
- timestamp
966
- );
967
- skipped.push({
968
- skillId,
969
- agentId: agent.id,
970
- path: entry.path,
971
- reason: "already-managed"
972
- });
973
- await writeManifest(skillmuxHome, manifest);
974
- continue;
975
- }
976
- const sourcePath = await resolveAdoptionSource(entry);
977
- if (sourcePath === void 0) {
978
- skipped.push({
979
- skillId,
980
- agentId: agent.id,
981
- path: entry.path,
982
- reason: "not-adoptable"
983
- });
984
- continue;
985
- }
986
- if (!await hasRootSkillFile(sourcePath)) {
987
- skipped.push({
988
- skillId,
989
- agentId: agent.id,
990
- path: entry.path,
991
- reason: "missing-skill-file"
992
- });
993
- continue;
994
- }
995
- const managedPath = buildManagedSkillPath(skillmuxHome, skillId);
996
- if (manifest.skills[skillId] === void 0) {
997
- await copySkillContentsToManagedStore(sourcePath, managedPath);
998
- manifest.skills[skillId] = buildManagedSkill(
999
- skillId,
1000
- entry.skillName,
1001
- managedPath,
1002
- sourcePath,
1003
- timestamp
1004
- );
1005
- } else if (await isLinkPointingToTarget(entry.path, manifest.skills[skillId].path)) {
1006
- skipped.push({
1007
- skillId,
1008
- agentId: agent.id,
1009
- path: entry.path,
1010
- reason: "already-managed"
1011
- });
1012
- continue;
1013
- } else {
1014
- await assertSkillSourceLayout(manifest.skills[skillId].path);
1015
- }
1016
- await replaceEntryWithManagedLink(
1017
- entry.path,
1018
- manifest.skills[skillId].path,
1019
- sourcePath
1020
- );
1021
- const activation = buildActivationRecord(
1022
- skillId,
1023
- agent.id,
1024
- join7(agent.absoluteSkillsDirectoryPath, entry.skillName),
1025
- timestamp
1026
- );
1027
- upsertActivation(manifest, activation);
1028
- adopted.push({
1029
- skillId,
1030
- agentId: agent.id,
1031
- sourcePath,
1032
- managedPath: manifest.skills[skillId].path,
1033
- linkPath: activation.linkPath
1034
- });
1035
- await writeManifest(skillmuxHome, manifest);
1036
- }
1037
- await writeManifest(skillmuxHome, manifest);
1038
- const resultWithoutOutput = {
1039
- agent: agentRecord,
1040
- adopted,
1041
- skipped,
1042
- manifest
1043
- };
1044
- return {
1045
- ...resultWithoutOutput,
1046
- output: buildOutput(resultWithoutOutput, options.json === true)
1047
- };
1048
- }
1049
- async function runAdopt(options) {
1050
- if (options.skills !== void 0) {
1051
- const results = [];
1052
- const completedSkills = [];
1053
- for (const skill of options.skills) {
1054
- try {
1055
- results.push(await runAdoptSingle({ ...options, skill, skills: void 0 }));
1056
- completedSkills.push(skill);
1057
- } catch (error) {
1058
- throw new BatchOperationError({
1059
- operation: "adopt",
1060
- failedItem: skill,
1061
- failedAction: `adopt ${skill} for ${options.agent}`,
1062
- completedAction: "adopting",
1063
- completedItems: completedSkills,
1064
- cause: error
1065
- });
1066
- }
1067
- }
1068
- if (results.length === 0) {
1069
- throw new AdoptionError("Adopt requires at least one target skill");
1070
- }
1071
- const lastResult = results[results.length - 1];
1072
- const resultWithoutOutput = {
1073
- agent: lastResult.agent,
1074
- adopted: results.flatMap((result) => result.adopted),
1075
- skipped: results.flatMap((result) => result.skipped),
1076
- manifest: lastResult.manifest,
1077
- results
1078
- };
1079
- return {
1080
- ...resultWithoutOutput,
1081
- output: options.json === true ? printJson({
1082
- agent: resultWithoutOutput.agent,
1083
- adopted: resultWithoutOutput.adopted,
1084
- skipped: resultWithoutOutput.skipped,
1085
- results: resultWithoutOutput.results
1086
- }) : results.map((result) => result.output).join("")
1087
- };
1088
- }
1089
- return runAdoptSingle(options);
1090
- }
1091
-
1092
- // src/commands/agents.ts
1093
- import { homedir as homedir2 } from "os";
1094
-
1095
- // src/output/print-table.ts
1096
- function printTable(rows, columns) {
1097
- const renderedRows = rows.map(
1098
- (row) => columns.map((column) => String(row[column.key] ?? ""))
1099
- );
1100
- const widths = columns.map(
1101
- (column, index) => Math.max(
1102
- column.label.length,
1103
- ...renderedRows.map((row) => row[index]?.length ?? 0)
1104
- )
1105
- );
1106
- const header = columns.map((column, index) => column.label.padEnd(widths[index])).join(" ");
1107
- const separator = widths.map((width) => "-".repeat(width)).join(" ");
1108
- const body = renderedRows.map(
1109
- (row) => row.map((cell, index) => cell.padEnd(widths[index])).join(" ")
1110
- );
1111
- return `${[header, separator, ...body].join("\n")}
1112
- `;
1113
- }
1114
-
1115
- // src/commands/agents.ts
1116
- function buildTableOutput(agents) {
1117
- return printTable(
1118
- agents.map((agent) => ({
1119
- id: agent.id,
1120
- name: agent.stableName,
1121
- path: agent.absoluteSkillsDirectoryPath,
1122
- exists: String(agent.exists),
1123
- supported: String(agent.supportedOnPlatform),
1124
- discovery: agent.discovery
1125
- })),
1126
- [
1127
- { key: "id", label: "Agent" },
1128
- { key: "name", label: "Name" },
1129
- { key: "path", label: "Path" },
1130
- { key: "exists", label: "Exists" },
1131
- { key: "supported", label: "Supported" },
1132
- { key: "discovery", label: "Discovery" }
1133
- ]
1134
- );
1135
- }
1136
- async function runAgents(options = {}) {
1137
- const homeDir = options.homeDir ?? homedir2();
1138
- const resolvedPaths = resolveSkillmuxHome(homeDir);
1139
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1140
- const agents = await discoverAgents({
1141
- homeDir,
1142
- skillmuxHome,
1143
- platform: options.platform
1144
- });
1145
- return {
1146
- agents,
1147
- output: options.json === true ? printJson(agents) : buildTableOutput(agents)
1148
- };
1149
- }
1150
-
1151
- // src/commands/config-add-agent.ts
1152
- import { homedir as homedir3 } from "os";
1153
-
1154
- // src/config/agent-override-validation.ts
1155
- import { isAbsolute } from "path";
1156
- function normalizeRelativePath(value, field) {
1157
- const trimmed = value.trim();
1158
- if (trimmed.length === 0) {
1159
- throw new UserConfigValidationError(`${field} must not be empty`);
1160
- }
1161
- if (isAbsolute(trimmed)) {
1162
- throw new UserConfigValidationError(`${field} must be a relative path`);
1163
- }
1164
- const normalized = trimmed.replaceAll("\\", "/");
1165
- if (normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.includes("/../")) {
1166
- throw new UserConfigValidationError(`${field} must stay within the configured home-relative tree`);
1167
- }
1168
- return normalized.replace(/^\.\/+/, "");
1169
- }
1170
- function normalizeAgentId(value) {
1171
- const trimmed = value.trim();
1172
- if (trimmed.length === 0 || /[a-z0-9]/i.test(trimmed) === false) {
1173
- throw new InvalidIdentifierError("agent id", value);
1174
- }
1175
- return normalizeId(trimmed);
1176
- }
1177
- function normalizePlatforms(value) {
1178
- if (value === void 0 || value.length === 0) {
1179
- return [process.platform];
1180
- }
1181
- const normalized = [...new Set(value.map((entry) => entry.trim().toLowerCase()))];
1182
- const invalid = normalized.filter(
1183
- (entry) => supportedPlatforms.includes(entry) === false
1184
- );
1185
- if (invalid.length > 0) {
1186
- throw new UserConfigValidationError(
1187
- `platform must be one of: ${supportedPlatforms.join(", ")}`
1188
- );
1189
- }
1190
- return normalized;
1191
- }
1192
-
1193
- // src/config/write-user-config.ts
1194
- import * as fs10 from "fs/promises";
1195
- async function writeUserConfig(skillmuxHome, config) {
1196
- const configPath = buildConfigPath(skillmuxHome);
1197
- await fs10.mkdir(skillmuxHome, { recursive: true });
1198
- await fs10.writeFile(configPath, `${JSON.stringify(config, null, 2)}
1199
- `, "utf8");
1200
- return {
1201
- skillmuxHome,
1202
- configPath
1203
- };
1204
- }
1205
-
1206
- // src/commands/config-add-agent.ts
1207
- function buildAgentOverride(options) {
1208
- const agentId = normalizeAgentId(options.id);
1209
- const agent = {
1210
- supportedPlatforms: normalizePlatforms(options.platforms),
1211
- homeRelativeRootPath: normalizeRelativePath(options.root, "root"),
1212
- skillsDirectoryPath: normalizeRelativePath(options.skills ?? "skills", "skills")
1213
- };
1214
- if (options.name !== void 0 && options.name.trim().length > 0) {
1215
- agent.stableName = options.name.trim();
1216
- }
1217
- if (options.disabledByDefault === true) {
1218
- agent.enabledByDefault = false;
1219
- }
1220
- return { agentId, agent };
1221
- }
1222
- function buildTableOutput2(result) {
1223
- const summary = printTable(
1224
- [
1225
- {
1226
- agentId: result.agentId,
1227
- configPath: result.configPath,
1228
- changed: String(result.changed)
1229
- }
1230
- ],
1231
- [
1232
- { key: "agentId", label: "Agent" },
1233
- { key: "configPath", label: "Config Path" },
1234
- { key: "changed", label: "Changed" }
1235
- ]
1236
- );
1237
- const detail = printTable(
1238
- [
1239
- {
1240
- stableName: result.agent.stableName ?? "",
1241
- platforms: (result.agent.supportedPlatforms ?? []).join(","),
1242
- root: result.agent.homeRelativeRootPath ?? "",
1243
- skills: result.agent.skillsDirectoryPath ?? "",
1244
- enabledByDefault: result.agent.enabledByDefault === void 0 ? "" : String(result.agent.enabledByDefault)
1245
- }
1246
- ],
1247
- [
1248
- { key: "stableName", label: "Name" },
1249
- { key: "platforms", label: "Platforms" },
1250
- { key: "root", label: "Root" },
1251
- { key: "skills", label: "Skills Dir" },
1252
- { key: "enabledByDefault", label: "Enabled By Default" }
1253
- ]
1254
- );
1255
- return `${summary}${detail}`;
1256
- }
1257
- async function runConfigAddAgent(options) {
1258
- const homeDir = options.homeDir ?? homedir3();
1259
- const resolvedPaths = resolveSkillmuxHome(homeDir);
1260
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1261
- const configPath = buildConfigPath(skillmuxHome);
1262
- const config = await loadUserConfig(skillmuxHome);
1263
- const { agentId, agent } = buildAgentOverride(options);
1264
- const previous = config.agents[agentId];
1265
- const changed = JSON.stringify(previous ?? null) !== JSON.stringify(agent);
1266
- const nextConfig = {
1267
- ...config,
1268
- agents: {
1269
- ...config.agents,
1270
- [agentId]: agent
1271
- }
1272
- };
1273
- await writeUserConfig(skillmuxHome, nextConfig);
1274
- const resultWithoutOutput = {
1275
- skillmuxHome,
1276
- configPath,
1277
- agentId,
1278
- changed,
1279
- agent,
1280
- config: nextConfig
1281
- };
1282
- return {
1283
- ...resultWithoutOutput,
1284
- output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput2(resultWithoutOutput)
1285
- };
1286
- }
1287
-
1288
- // src/commands/config-remove-agent.ts
1289
- import { homedir as homedir4 } from "os";
1290
- function normalizeAgentId2(value) {
1291
- const trimmed = value.trim();
1292
- if (trimmed.length === 0 || /[a-z0-9]/i.test(trimmed) === false) {
1293
- throw new InvalidIdentifierError("agent id", value);
1294
- }
1295
- return normalizeId(trimmed);
1296
- }
1297
- function buildTableOutput3(result) {
1298
- return printTable(
1299
- [
1300
- {
1301
- agentId: result.agentId,
1302
- configPath: result.configPath,
1303
- changed: String(result.changed),
1304
- removed: String(result.removed)
1305
- }
1306
- ],
1307
- [
1308
- { key: "agentId", label: "Agent" },
1309
- { key: "configPath", label: "Config Path" },
1310
- { key: "changed", label: "Changed" },
1311
- { key: "removed", label: "Removed" }
1312
- ]
1313
- );
1314
- }
1315
- async function runConfigRemoveAgent(options) {
1316
- const homeDir = options.homeDir ?? homedir4();
1317
- const resolvedPaths = resolveSkillmuxHome(homeDir);
1318
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1319
- const configPath = buildConfigPath(skillmuxHome);
1320
- const config = await loadUserConfig(skillmuxHome);
1321
- const agentId = normalizeAgentId2(options.id);
1322
- const removed = agentId in config.agents;
1323
- const nextConfig = {
1324
- ...config,
1325
- agents: Object.fromEntries(
1326
- Object.entries(config.agents).filter(([currentAgentId]) => currentAgentId !== agentId)
1327
- )
1328
- };
1329
- if (removed) {
1330
- await writeUserConfig(skillmuxHome, nextConfig);
1331
- }
1332
- const resultWithoutOutput = {
1333
- skillmuxHome,
1334
- configPath,
1335
- agentId,
1336
- changed: removed,
1337
- removed,
1338
- config: nextConfig
1339
- };
1340
- return {
1341
- ...resultWithoutOutput,
1342
- output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput3(resultWithoutOutput)
1343
- };
1344
- }
1345
-
1346
- // src/commands/config-update-agent.ts
1347
- import { homedir as homedir5 } from "os";
1348
- function buildAgentPatch(options) {
1349
- const patch = {};
1350
- if (options.root !== void 0) {
1351
- patch.homeRelativeRootPath = normalizeRelativePath(options.root, "root");
1352
- }
1353
- if (options.skills !== void 0) {
1354
- patch.skillsDirectoryPath = normalizeRelativePath(options.skills, "skills");
1355
- }
1356
- if (options.name !== void 0 && options.name.trim().length > 0) {
1357
- patch.stableName = options.name.trim();
1358
- }
1359
- if (options.platforms !== void 0) {
1360
- patch.supportedPlatforms = normalizePlatforms(options.platforms);
1361
- }
1362
- if (options.enabledByDefault !== void 0 && options.disabledByDefault === true) {
1363
- throw new UserConfigValidationError(
1364
- "enabled-by-default and disabled-by-default cannot both be set"
1365
- );
1366
- }
1367
- if (options.enabledByDefault !== void 0) {
1368
- patch.enabledByDefault = options.enabledByDefault;
1369
- }
1370
- if (options.disabledByDefault === true) {
1371
- patch.enabledByDefault = false;
1372
- }
1373
- return patch;
1374
- }
1375
- function buildTableOutput4(result) {
1376
- const summary = printTable(
1377
- [
1378
- {
1379
- agentId: result.agentId,
1380
- configPath: result.configPath,
1381
- changed: String(result.changed)
1382
- }
1383
- ],
1384
- [
1385
- { key: "agentId", label: "Agent" },
1386
- { key: "configPath", label: "Config Path" },
1387
- { key: "changed", label: "Changed" }
1388
- ]
1389
- );
1390
- const detail = printTable(
1391
- [
1392
- {
1393
- stableName: result.agent.stableName ?? "",
1394
- platforms: (result.agent.supportedPlatforms ?? []).join(","),
1395
- root: result.agent.homeRelativeRootPath ?? "",
1396
- skills: result.agent.skillsDirectoryPath ?? "",
1397
- enabledByDefault: result.agent.enabledByDefault === void 0 ? "" : String(result.agent.enabledByDefault)
1398
- }
1399
- ],
1400
- [
1401
- { key: "stableName", label: "Name" },
1402
- { key: "platforms", label: "Platforms" },
1403
- { key: "root", label: "Root" },
1404
- { key: "skills", label: "Skills Dir" },
1405
- { key: "enabledByDefault", label: "Enabled By Default" }
1406
- ]
1407
- );
1408
- return `${summary}${detail}`;
1409
- }
1410
- async function runConfigUpdateAgent(options) {
1411
- const homeDir = options.homeDir ?? homedir5();
1412
- const resolvedPaths = resolveSkillmuxHome(homeDir);
1413
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1414
- const configPath = buildConfigPath(skillmuxHome);
1415
- const config = await loadUserConfig(skillmuxHome);
1416
- const agentId = normalizeAgentId(options.id);
1417
- const previous = config.agents[agentId];
1418
- if (previous === void 0) {
1419
- throw new UserConfigValidationError(`Agent override does not exist: ${agentId}`);
1420
- }
1421
- const agent = {
1422
- ...previous,
1423
- ...buildAgentPatch(options)
1424
- };
1425
- const changed = JSON.stringify(previous) !== JSON.stringify(agent);
1426
- const nextConfig = {
1427
- ...config,
1428
- agents: {
1429
- ...config.agents,
1430
- [agentId]: agent
1431
- }
1432
- };
1433
- if (changed) {
1434
- await writeUserConfig(skillmuxHome, nextConfig);
1435
- }
1436
- const resultWithoutOutput = {
1437
- skillmuxHome,
1438
- configPath,
1439
- agentId,
1440
- changed,
1441
- agent,
1442
- config: nextConfig
1443
- };
1444
- return {
1445
- ...resultWithoutOutput,
1446
- output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput4(resultWithoutOutput)
1447
- };
1448
- }
1449
-
1450
- // src/commands/config.ts
1451
- import { homedir as homedir6 } from "os";
1452
- function buildTableOutput5(result) {
1453
- const summary = printTable(
1454
- [
1455
- {
1456
- skillmuxHome: result.skillmuxHome,
1457
- configPath: result.configPath,
1458
- overrides: String(Object.keys(result.config.agents).length)
1459
- }
1460
- ],
1461
- [
1462
- { key: "skillmuxHome", label: "SkillMux Home" },
1463
- { key: "configPath", label: "Config Path" },
1464
- { key: "overrides", label: "Overrides" }
1465
- ]
1466
- );
1467
- const agentRows = Object.entries(result.config.agents).sort(([left], [right]) => left.localeCompare(right)).map(([agentId, agent]) => ({
1468
- agentId,
1469
- stableName: agent.stableName ?? "",
1470
- root: agent.homeRelativeRootPath ?? "",
1471
- skills: agent.skillsDirectoryPath ?? ""
1472
- }));
1473
- if (agentRows.length === 0) {
1474
- return `${summary}
1475
- No user overrides configured.
1476
- `;
1477
- }
1478
- return `${summary}
1479
- ${printTable(agentRows, [
1480
- { key: "agentId", label: "Agent" },
1481
- { key: "stableName", label: "Name" },
1482
- { key: "root", label: "Root" },
1483
- { key: "skills", label: "Skills Dir" }
1484
- ])}`;
1485
- }
1486
- async function runConfig(options = {}) {
1487
- const homeDir = options.homeDir ?? homedir6();
1488
- const resolvedPaths = resolveSkillmuxHome(homeDir);
1489
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1490
- const config = await loadUserConfig(skillmuxHome);
1491
- const resultWithoutOutput = {
1492
- skillmuxHome,
1493
- configPath: buildConfigPath(skillmuxHome),
1494
- config
1495
- };
1496
- return {
1497
- ...resultWithoutOutput,
1498
- output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput5(resultWithoutOutput)
1499
- };
1500
- }
1501
-
1502
- // src/commands/doctor.ts
1503
- import * as fs11 from "fs/promises";
1504
- import { homedir as homedir7 } from "os";
1505
- import { join as join8 } from "path";
1506
- function buildIssue2(code, severity, message, path) {
1507
- return path === void 0 ? { code, severity, message } : { code, severity, message, path };
1508
- }
1509
- async function pathExists2(path) {
1510
- try {
1511
- await fs11.access(path);
1512
- return true;
1513
- } catch (error) {
1514
- if (error.code === "ENOENT") {
1515
- return false;
1516
- }
1517
- throw error;
1518
- }
1519
- }
1520
- async function addUnmanagedDirectoryIssues(entries, issues) {
1521
- for (const entry of entries) {
1522
- if (entry.kind !== "unmanaged-directory") {
1523
- continue;
1524
- }
1525
- if (await pathExists2(join8(entry.path, "SKILL.md"))) {
1526
- issues.push(
1527
- buildIssue2(
1528
- "unmanaged-skill-directory",
1529
- "warning",
1530
- `Unmanaged skill directory is present for ${entry.agentId}/${entry.skillName}`,
1531
- entry.path
1532
- )
1533
- );
1534
- }
1535
- }
1536
- }
1537
- async function addMissingManagedSkillIssues(manifest, issues) {
1538
- for (const skill of Object.values(manifest.skills)) {
1539
- if (await pathExists2(skill.path)) {
1540
- continue;
1541
- }
1542
- issues.push(
1543
- buildIssue2(
1544
- "missing-managed-skill-path",
1545
- "error",
1546
- `Managed skill path is missing for ${skill.id}`,
1547
- skill.path
1548
- )
1549
- );
1550
- }
1551
- }
1552
- function addConflictingAgentPathIssues(agents, issues) {
1553
- const pathToAgents = /* @__PURE__ */ new Map();
1554
- for (const agent of agents) {
1555
- const key = normalizeAbsolutePath(agent.absoluteSkillsDirectoryPath);
1556
- const current = pathToAgents.get(key) ?? [];
1557
- current.push(agent);
1558
- pathToAgents.set(key, current);
1559
- }
1560
- for (const conflictedAgents of pathToAgents.values()) {
1561
- if (conflictedAgents.length < 2) {
1562
- continue;
1563
- }
1564
- const agentIds = conflictedAgents.map((agent) => agent.id).sort((left, right) => left.localeCompare(right));
1565
- issues.push(
1566
- buildIssue2(
1567
- "conflicting-agent-path",
1568
- "warning",
1569
- `Multiple agents resolve to the same skills directory: ${agentIds.join(", ")}`,
1570
- conflictedAgents[0].absoluteSkillsDirectoryPath
1571
- )
1572
- );
1573
- }
1574
- }
1575
- function buildTableOutput6(issues) {
1576
- if (issues.length === 0) {
1577
- return "No doctor issues found.\n";
1578
- }
1579
- return printTable(
1580
- issues.map((issue) => ({
1581
- severity: issue.severity,
1582
- code: issue.code,
1583
- path: issue.path ?? "",
1584
- message: issue.message
1585
- })),
1586
- [
1587
- { key: "severity", label: "Severity" },
1588
- { key: "code", label: "Code" },
1589
- { key: "path", label: "Path" },
1590
- { key: "message", label: "Message" }
1591
- ]
1592
- );
1593
- }
1594
- function buildJsonOutput(result) {
1595
- return printJson({
1596
- skillmuxHome: result.skillmuxHome,
1597
- issues: result.issues,
1598
- agents: result.agents.map((agent) => ({
1599
- id: agent.id,
1600
- path: agent.absoluteSkillsDirectoryPath,
1601
- supportedOnPlatform: agent.supportedOnPlatform
1602
- })),
1603
- entries: result.entries
1604
- });
1605
- }
1606
- async function runDoctor(options = {}) {
1607
- const homeDir = options.homeDir ?? homedir7();
1608
- const resolvedPaths = resolveSkillmuxHome(homeDir);
1609
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1610
- const [manifest, config, agents] = await Promise.all([
1611
- readManifest(skillmuxHome),
1612
- loadUserConfig(skillmuxHome),
1613
- discoverAgents({
1614
- homeDir,
1615
- skillmuxHome,
1616
- platform: options.platform
1617
- })
1618
- ]);
1619
- const entries = [];
1620
- const issues = [];
1621
- for (const agent of agents) {
1622
- const scannedAgent = await scanAgentSkills(agent, skillmuxHome);
1623
- entries.push(...scannedAgent.entries);
1624
- issues.push(...scannedAgent.issues);
1625
- }
1626
- await addUnmanagedDirectoryIssues(entries, issues);
1627
- await addMissingManagedSkillIssues(manifest, issues);
1628
- addConflictingAgentPathIssues(agents, issues);
1629
- const dedupedIssues = [...issues].sort((left, right) => {
1630
- const leftKey = `${left.severity}:${left.code}:${left.path ?? ""}:${left.message}`;
1631
- const rightKey = `${right.severity}:${right.code}:${right.path ?? ""}:${right.message}`;
1632
- return leftKey.localeCompare(rightKey);
1633
- });
1634
- const resultWithoutOutput = {
1635
- skillmuxHome,
1636
- manifest,
1637
- config,
1638
- agents,
1639
- entries,
1640
- issues: dedupedIssues
1641
- };
1642
- return {
1643
- ...resultWithoutOutput,
1644
- output: options.json === true ? buildJsonOutput(resultWithoutOutput) : buildTableOutput6(dedupedIssues)
1645
- };
1646
- }
1647
-
1648
- // src/commands/disable.ts
1649
- import * as fs13 from "fs/promises";
1650
- import { homedir as homedir8 } from "os";
1651
- import { join as join9, resolve as resolve9 } from "path";
1652
-
1653
- // src/fs/safe-remove-link.ts
1654
- import * as fs12 from "fs/promises";
1655
- async function safeRemoveLink(path) {
1656
- try {
1657
- const entry = await fs12.lstat(path);
1658
- if (!entry.isSymbolicLink()) {
1659
- return false;
1660
- }
1661
- await fs12.rm(path, { recursive: true, force: false });
1662
- return true;
1663
- } catch (error) {
1664
- if (error.code === "ENOENT") {
1665
- return false;
1666
- }
1667
- throw error;
1668
- }
1669
- }
1670
-
1671
- // src/commands/disable.ts
1672
- function buildAgentRecord2(agent, timestamp) {
1673
- return {
1674
- id: agent.id,
1675
- name: agent.stableName,
1676
- path: agent.absoluteSkillsDirectoryPath,
1677
- discovery: agent.discovery,
1678
- available: agent.exists && agent.supportedOnPlatform,
1679
- lastSeenAt: agent.exists ? timestamp : null
1680
- };
1681
- }
1682
- function buildActivationRecord2(skillId, agentId, linkPath, timestamp) {
1683
- return {
1684
- skillId,
1685
- agentId,
1686
- linkPath,
1687
- state: "disabled",
1688
- updatedAt: timestamp
1689
- };
1690
- }
1691
- function upsertActivation2(manifest, activation) {
1692
- const index = manifest.activations.findIndex(
1693
- (entry) => entry.skillId === activation.skillId && entry.agentId === activation.agentId
1694
- );
1695
- if (index === -1) {
1696
- manifest.activations.push(activation);
1697
- return;
1698
- }
1699
- manifest.activations[index] = activation;
1700
- }
1701
- function buildManagedSkillPath2(skillmuxHome, skillId) {
1702
- return resolve9(skillmuxHome, "skills", skillId);
1703
- }
1704
- async function tryAdoptManagedSkill(manifest, skillmuxHome, skillId, skillName, linkPath, timestamp) {
1705
- try {
1706
- const entry = await fs13.lstat(linkPath);
1707
- if (!entry.isSymbolicLink()) {
1708
- return void 0;
1709
- }
1710
- } catch (error) {
1711
- if (error.code === "ENOENT") {
1712
- return void 0;
1713
- }
1714
- throw error;
1715
- }
1716
- const sourcePath = await fs13.realpath(linkPath);
1717
- await assertSkillSourceLayout(sourcePath);
1718
- const managedSkillPath = buildManagedSkillPath2(skillmuxHome, skillId);
1719
- await copySkillContentsToManagedStore(sourcePath, managedSkillPath);
1720
- const skill = {
1721
- id: skillId,
1722
- name: skillName,
1723
- path: managedSkillPath,
1724
- source: {
1725
- kind: "imported",
1726
- path: sourcePath
1727
- },
1728
- importedAt: timestamp
1729
- };
1730
- manifest.skills[skillId] = skill;
1731
- return { skill, sourcePath };
1732
- }
1733
- async function resolveTargetAgent2(homeDir, skillmuxHome, agentName) {
1734
- const agentId = normalizeId(agentName);
1735
- const agents = await discoverAgents({ homeDir, skillmuxHome });
1736
- const agent = agents.find((entry) => entry.id === agentId);
1737
- if (agent === void 0) {
1738
- throw new Error(`Unknown agent: ${agentName}`);
1739
- }
1740
- if (!agent.supportedOnPlatform) {
1741
- throw new Error(`Agent ${agent.id} is not supported on ${process.platform}`);
1742
- }
1743
- return agent;
1744
- }
1745
- async function pathExists3(path) {
1746
- try {
1747
- await fs13.lstat(path);
1748
- return true;
1749
- } catch (error) {
1750
- if (error.code === "ENOENT") {
1751
- return false;
1752
- }
1753
- throw error;
1754
- }
1755
- }
1756
- async function runDisableSingle(options) {
1757
- if (options.agent === void 0) {
1758
- throw new Error("Disable requires one target agent");
1759
- }
1760
- const homeDir = options.homeDir ?? homedir8();
1761
- const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
1762
- const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
1763
- const timestamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
1764
- const manifest = await readManifest(skillmuxHome);
1765
- const skillId = normalizeId(options.skill);
1766
- const agent = await resolveTargetAgent2(homeDir, skillmuxHome, options.agent);
1767
- const linkPath = join9(agent.absoluteSkillsDirectoryPath, skillId);
1768
- const adoption = manifest.skills[skillId] ? void 0 : await tryAdoptManagedSkill(
1769
- manifest,
1770
- skillmuxHome,
1771
- skillId,
1772
- options.skill,
1773
- linkPath,
1774
- timestamp
1775
- );
1776
- const skill = manifest.skills[skillId] ?? adoption?.skill;
1777
- if (skill === void 0) {
1778
- throw new Error(`Managed skill not found: ${skillId}`);
1779
- }
1780
- const currentActivation = manifest.activations.find(
1781
- (entry) => entry.skillId === skill.id && entry.agentId === agent.id
1782
- );
1783
- const activationLinkPath = currentActivation?.linkPath ?? linkPath;
1784
- const agentRecord = buildAgentRecord2(agent, timestamp);
1785
- manifest.agents[agent.id] = agentRecord;
1786
- const adoptedLinkRemoved = adoption !== void 0 ? await safeRemoveLink(linkPath) : false;
1787
- const linkMatchesSkill = adoption === void 0 ? await isLinkPointingToTarget(activationLinkPath, skill.path) : false;
1788
- if (adoption === void 0 && !linkMatchesSkill && await pathExists3(activationLinkPath)) {
1789
- throw new Error(`Refusing to disable non-managed entry at ${linkPath}`);
1790
- }
1791
- const removedLink = adoptedLinkRemoved ? true : linkMatchesSkill ? await safeRemoveLink(activationLinkPath) : false;
1792
- if (removedLink === false && currentActivation?.state !== "enabled") {
1793
- return {
1794
- changed: false,
1795
- skill,
1796
- agent: agentRecord,
1797
- activation: currentActivation ?? null,
1798
- manifest,
1799
- output: `${skill.id} is already disabled for ${agent.id}
1800
- `
1801
- };
1802
- }
1803
- const activation = buildActivationRecord2(skill.id, agent.id, linkPath, timestamp);
1804
- upsertActivation2(manifest, activation);
1805
- await writeManifest(skillmuxHome, manifest);
1806
- return {
1807
- changed: true,
1808
- skill,
1809
- agent: agentRecord,
1810
- activation,
1811
- manifest,
1812
- output: `Disabled ${skill.id} for ${agent.id}
1813
- `
1814
- };
1815
- }
1816
- async function runDisable(options) {
1817
- if (options.agents !== void 0) {
1818
- const results = [];
1819
- for (const agent of options.agents) {
1820
- try {
1821
- results.push(await runDisableSingle({ ...options, agent, agents: void 0 }));
1822
- } catch (error) {
1823
- throw new BatchOperationError({
1824
- operation: "disable",
1825
- failedItem: agent,
1826
- failedAction: `disable ${options.skill} for ${agent}`,
1827
- completedAction: "disabling",
1828
- completedItems: results.map((result) => result.agent.id),
1829
- cause: error
1830
- });
1831
- }
1832
- }
1833
- if (results.length === 0) {
1834
- throw new Error("Disable requires at least one target agent");
1835
- }
1836
- const lastResult = results[results.length - 1];
1837
- return {
1838
- changed: results.some((result) => result.changed),
1839
- skill: lastResult.skill,
1840
- results,
1841
- changedAgents: results.filter((result) => result.changed).map((result) => result.agent.id),
1842
- manifest: lastResult.manifest,
1843
- output: results.map((result) => result.output).join("")
1844
- };
1845
- }
1846
- return runDisableSingle(options);
1847
- }
1848
-
1849
- // src/commands/enable.ts
1850
- import * as fs14 from "fs/promises";
1851
- import { homedir as homedir9 } from "os";
1852
- import { join as join10 } from "path";
1853
- function buildAgentRecord3(agent, timestamp) {
1854
- return {
1855
- id: agent.id,
1856
- name: agent.stableName,
1857
- path: agent.absoluteSkillsDirectoryPath,
1858
- discovery: agent.discovery,
1859
- available: agent.supportedOnPlatform,
1860
- lastSeenAt: timestamp
1861
- };
1862
- }
1863
- function buildActivationRecord3(skillId, agentId, linkPath, timestamp, state) {
1864
- return {
1865
- skillId,
1866
- agentId,
1867
- linkPath,
1868
- state,
1869
- updatedAt: timestamp
1870
- };
1871
- }
1872
- function upsertActivation3(manifest, activation) {
1873
- const index = manifest.activations.findIndex(
1874
- (entry) => entry.skillId === activation.skillId && entry.agentId === activation.agentId
1875
- );
1876
- if (index === -1) {
1877
- manifest.activations.push(activation);
1878
- return;
1879
- }
1880
- manifest.activations[index] = activation;
1881
- }
1882
- async function resolveTargetAgent3(homeDir, skillmuxHome, agentName) {
1883
- const agentId = normalizeId(agentName);
1884
- const agents = await discoverAgents({ homeDir, skillmuxHome });
1885
- const agent = agents.find((entry) => entry.id === agentId);
1886
- if (agent === void 0) {
1887
- throw new Error(`Unknown agent: ${agentName}`);
1888
- }
1889
- if (!agent.supportedOnPlatform) {
1890
- throw new Error(`Agent ${agent.id} is not supported on ${process.platform}`);
1891
- }
1892
- return agent;
1893
- }
1894
- async function runEnableSingle(options) {
1895
- if (options.agent === void 0) {
1896
- throw new Error("Enable requires one target agent");
1897
- }
1898
- const homeDir = options.homeDir ?? homedir9();
1899
- const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
1900
- const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
1901
- const timestamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
1902
- const manifest = await readManifest(skillmuxHome);
1903
- const skillId = normalizeId(options.skill);
1904
- const skill = manifest.skills[skillId];
1905
- if (skill === void 0) {
1906
- throw new Error(`Managed skill not found: ${skillId}`);
1907
- }
1908
- const agent = await resolveTargetAgent3(homeDir, skillmuxHome, options.agent);
1909
- const linkPath = join10(agent.absoluteSkillsDirectoryPath, skill.id);
1910
- const currentActivation = manifest.activations.find(
1911
- (entry) => entry.skillId === skill.id && entry.agentId === agent.id
1912
- );
1913
- const agentRecord = buildAgentRecord3(agent, timestamp);
1914
- manifest.agents[agent.id] = agentRecord;
1915
- await fs14.mkdir(agent.absoluteSkillsDirectoryPath, { recursive: true });
1916
- const linkAlreadyEnabled = await isLinkPointingToTarget(linkPath, skill.path);
1917
- const activationAlreadyEnabled = currentActivation?.state === "enabled" && currentActivation.linkPath === linkPath;
1918
- if (linkAlreadyEnabled && activationAlreadyEnabled) {
1919
- return {
1920
- changed: false,
1921
- skill,
1922
- agent: agentRecord,
1923
- activation: currentActivation,
1924
- manifest,
1925
- output: `${skill.id} is already enabled for ${agent.id}
1926
- `
1927
- };
1928
- }
1929
- await createManagedLink(linkPath, skill.path);
1930
- const activation = buildActivationRecord3(
1931
- skill.id,
1932
- agent.id,
1933
- linkPath,
1934
- timestamp,
1935
- "enabled"
1936
- );
1937
- upsertActivation3(manifest, activation);
1938
- await writeManifest(skillmuxHome, manifest);
1939
- return {
1940
- changed: true,
1941
- skill,
1942
- agent: agentRecord,
1943
- activation,
1944
- manifest,
1945
- output: `Enabled ${skill.id} for ${agent.id}
1946
- `
1947
- };
1948
- }
1949
- async function runEnable(options) {
1950
- if (options.agents !== void 0) {
1951
- const results = [];
1952
- for (const agent of options.agents) {
1953
- try {
1954
- results.push(await runEnableSingle({ ...options, agent, agents: void 0 }));
1955
- } catch (error) {
1956
- throw new BatchOperationError({
1957
- operation: "enable",
1958
- failedItem: agent,
1959
- failedAction: `enable ${options.skill} for ${agent}`,
1960
- completedAction: "enabling",
1961
- completedItems: results.map((result) => result.agent.id),
1962
- cause: error
1963
- });
1964
- }
1965
- }
1966
- if (results.length === 0) {
1967
- throw new Error("Enable requires at least one target agent");
1968
- }
1969
- const lastResult = results[results.length - 1];
1970
- return {
1971
- changed: results.some((result) => result.changed),
1972
- skill: lastResult.skill,
1973
- results,
1974
- changedAgents: results.filter((result) => result.changed).map((result) => result.agent.id),
1975
- manifest: lastResult.manifest,
1976
- output: results.map((result) => result.output).join("")
1977
- };
1978
- }
1979
- return runEnableSingle(options);
1980
- }
1981
-
1982
- // src/commands/import.ts
1983
- import { resolve as resolve10 } from "path";
1984
- import { homedir as homedir10 } from "os";
1985
- function buildManagedSkillPath3(skillmuxHome, skillId) {
1986
- return resolve10(skillmuxHome, "skills", skillId);
1987
- }
1988
- async function runImport(options) {
1989
- const homeDir = options.homeDir ?? homedir10();
1990
- const resolvedPaths = resolveSkillmuxHome(homeDir);
1991
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
1992
- const sourcePath = resolve10(options.sourcePath);
1993
- const skillId = normalizeId(options.skillName);
1994
- const importedAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
1995
- const manifest = await readManifest(skillmuxHome);
1996
- const managedSkillPath = buildManagedSkillPath3(skillmuxHome, skillId);
1997
- await assertSkillSourceLayout(sourcePath);
1998
- if (manifest.skills[skillId] !== void 0) {
1999
- throw new Error(`Managed skill already exists for ${skillId}`);
2000
- }
2001
- await copySkillContentsToManagedStore(sourcePath, managedSkillPath);
2002
- const skill = {
2003
- id: skillId,
2004
- name: options.skillName,
2005
- path: managedSkillPath,
2006
- source: {
2007
- kind: "local",
2008
- path: sourcePath
2009
- },
2010
- importedAt
2011
- };
2012
- manifest.skills[skillId] = skill;
2013
- await writeManifest(skillmuxHome, manifest);
2014
- return {
2015
- skill,
2016
- manifest,
2017
- output: `Imported ${skillId} to ${managedSkillPath}
2018
- `
2019
- };
2020
- }
2021
-
2022
- // src/commands/scan.ts
2023
- import { homedir as homedir11 } from "os";
2024
-
2025
- // src/output/format-issue.ts
2026
- function formatIssue(issue) {
2027
- if (issue.path === void 0) {
2028
- return `[${issue.severity}] ${issue.code}: ${issue.message}`;
2029
- }
2030
- return `[${issue.severity}] ${issue.code} @ ${issue.path}: ${issue.message}`;
2031
- }
2032
-
2033
- // src/commands/scan.ts
2034
- function buildAgentRecord4(agent, timestamp, previousRecord) {
2035
- const lastSeenAt = agent.exists ? timestamp : previousRecord?.lastSeenAt ?? null;
2036
- return {
2037
- id: agent.id,
2038
- name: agent.stableName,
2039
- path: agent.absoluteSkillsDirectoryPath,
2040
- discovery: agent.discovery,
2041
- available: agent.exists && agent.supportedOnPlatform,
2042
- lastSeenAt
2043
- };
2044
- }
2045
- function buildScanOutput(result, json) {
2046
- if (json) {
2047
- return printJson({
2048
- lastScan: result.manifest.lastScan,
2049
- agents: result.agents.map((agent) => ({
2050
- id: agent.id,
2051
- name: agent.stableName,
2052
- path: agent.absoluteSkillsDirectoryPath,
2053
- exists: agent.exists,
2054
- supportedOnPlatform: agent.supportedOnPlatform
2055
- })),
2056
- entries: result.entries
2057
- });
2058
- }
2059
- if (result.entries.length === 0) {
2060
- return "No skill entries found.\n";
2061
- }
2062
- const table = printTable(
2063
- result.entries.map((entry) => ({
2064
- agent: entry.agentId,
2065
- skill: entry.skillName,
2066
- kind: entry.kind,
2067
- path: entry.path
2068
- })),
2069
- [
2070
- { key: "agent", label: "Agent" },
2071
- { key: "skill", label: "Skill" },
2072
- { key: "kind", label: "Kind" },
2073
- { key: "path", label: "Path" }
2074
- ]
2075
- );
2076
- if (result.issues.length === 0) {
2077
- return table;
2078
- }
2079
- return `${table}
2080
- ${result.issues.map(formatIssue).join("\n")}
2081
- `;
2082
- }
2083
- async function runScan(options = {}) {
2084
- const homeDir = options.homeDir ?? homedir11();
2085
- const resolvedPaths = resolveSkillmuxHome(homeDir);
2086
- const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
2087
- const manifest = await readManifest(skillmuxHome);
2088
- const agents = await discoverAgents({
2089
- homeDir,
2090
- platform: options.platform,
2091
- skillmuxHome
2092
- });
2093
- const timestamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
2094
- const entries = [];
2095
- const issues = [];
2096
- for (const agent of agents) {
2097
- const scannedAgent = await scanAgentSkills(agent, skillmuxHome);
2098
- entries.push(...scannedAgent.entries);
2099
- issues.push(...scannedAgent.issues);
2100
- manifest.agents[agent.id] = buildAgentRecord4(
2101
- agent,
2102
- timestamp,
2103
- manifest.agents[agent.id]
2104
- );
2105
- }
2106
- manifest.lastScan = {
2107
- at: timestamp,
2108
- issues
2109
- };
2110
- await writeManifest(skillmuxHome, manifest);
2111
- const resultWithoutOutput = {
2112
- manifest,
2113
- agents,
2114
- entries,
2115
- issues
2116
- };
2117
- return {
2118
- ...resultWithoutOutput,
2119
- output: buildScanOutput(resultWithoutOutput, options.json === true)
2120
- };
2121
- }
2122
-
2123
- // src/commands/list.ts
2124
- function buildRecordsView(scanResult) {
2125
- return {
2126
- view: "records",
2127
- records: scanResult.entries,
2128
- issues: scanResult.issues
2129
- };
2130
- }
2131
- function buildAgentsView(scanResult) {
2132
- const groups = /* @__PURE__ */ new Map();
2133
- for (const agent of scanResult.agents) {
2134
- groups.set(agent.id, {
2135
- agentId: agent.id,
2136
- agentName: agent.stableName,
2137
- entries: []
2138
- });
2139
- }
2140
- for (const entry of scanResult.entries) {
2141
- const current = groups.get(entry.agentId) ?? {
2142
- agentId: entry.agentId,
2143
- agentName: entry.agentName,
2144
- entries: []
2145
- };
2146
- current.entries.push(entry);
2147
- groups.set(entry.agentId, current);
2148
- }
2149
- return {
2150
- view: "agents",
2151
- agents: [...groups.values()].sort(
2152
- (left, right) => left.agentId.localeCompare(right.agentId)
2153
- ),
2154
- issues: scanResult.issues
2155
- };
2156
- }
2157
- function buildSkillsView(scanResult) {
2158
- const groups = /* @__PURE__ */ new Map();
2159
- for (const skill of Object.values(scanResult.manifest.skills)) {
2160
- groups.set(skill.id, {
2161
- skillName: skill.id,
2162
- entries: []
2163
- });
2164
- }
2165
- for (const entry of scanResult.entries) {
2166
- const current = groups.get(entry.skillName) ?? {
2167
- skillName: entry.skillName,
2168
- entries: []
2169
- };
2170
- current.entries.push(entry);
2171
- groups.set(entry.skillName, current);
2172
- }
2173
- return {
2174
- view: "skills",
2175
- skills: [...groups.values()].sort(
2176
- (left, right) => left.skillName.localeCompare(right.skillName)
2177
- ),
2178
- issues: scanResult.issues
2179
- };
2180
- }
2181
- function buildListData(scanResult, view) {
2182
- if (view === "agents") {
2183
- return buildAgentsView(scanResult);
2184
- }
2185
- if (view === "skills") {
2186
- return buildSkillsView(scanResult);
2187
- }
2188
- return buildRecordsView(scanResult);
2189
- }
2190
- function buildTableOutput7(data, view) {
2191
- if (view === "agents") {
2192
- const agentRows = data.agents;
2193
- return printTable(
2194
- agentRows.map((agent) => ({
2195
- agent: agent.agentId,
2196
- name: agent.agentName,
2197
- entries: String(agent.entries.length)
2198
- })),
2199
- [
2200
- { key: "agent", label: "Agent" },
2201
- { key: "name", label: "Name" },
2202
- { key: "entries", label: "Entries" }
2203
- ]
2204
- );
2205
- }
2206
- if (view === "skills") {
2207
- const skillRows = data.skills;
2208
- return printTable(
2209
- skillRows.map((skill) => ({
2210
- skill: skill.skillName,
2211
- entries: String(skill.entries.length)
2212
- })),
2213
- [
2214
- { key: "skill", label: "Skill" },
2215
- { key: "entries", label: "Entries" }
2216
- ]
2217
- );
2218
- }
2219
- const records = data.records;
2220
- return printTable(
2221
- records.map((record) => ({
2222
- agent: record.agentId,
2223
- skill: record.skillName,
2224
- kind: record.kind
2225
- })),
2226
- [
2227
- { key: "agent", label: "Agent" },
2228
- { key: "skill", label: "Skill" },
2229
- { key: "kind", label: "Kind" }
2230
- ]
2231
- );
2232
- }
2233
- async function runList(options = {}) {
2234
- const view = options.view ?? "records";
2235
- const format = options.format ?? "table";
2236
- const scanResult = await runScan(options);
2237
- const data = buildListData(scanResult, view);
2238
- return {
2239
- data,
2240
- output: format === "json" ? printJson(data) : buildTableOutput7(data, view)
2241
- };
2242
- }
2243
-
2244
- // src/commands/remove.ts
2245
- import * as fs15 from "fs/promises";
2246
- import { homedir as homedir12 } from "os";
2247
- import { resolve as resolve11 } from "path";
2248
- function buildManagedSkillPath4(skillmuxHome, skillId) {
2249
- return resolve11(skillmuxHome, "skills", skillId);
2250
- }
2251
- function buildManifestPath(skillmuxHome) {
2252
- return resolve11(skillmuxHome, "manifest.json");
2253
- }
2254
- function buildConfigPath2(skillmuxHome) {
2255
- return resolve11(skillmuxHome, "config.json");
2256
- }
2257
- function resolveManagedSkill(manifest, skillNameOrId) {
2258
- const skillId = normalizeId(skillNameOrId);
2259
- const directMatch = manifest.skills[skillId];
2260
- if (directMatch !== void 0) {
2261
- return directMatch;
2262
- }
2263
- const nameMatches = Object.values(manifest.skills).filter(
2264
- (skill) => normalizeId(skill.name) === skillId
2265
- );
2266
- if (nameMatches.length === 1) {
2267
- return nameMatches[0];
2268
- }
2269
- if (nameMatches.length > 1) {
2270
- const candidateIds = nameMatches.map((skill) => skill.id).sort((left, right) => left.localeCompare(right));
2271
- throw new Error(
2272
- `Ambiguous managed skill name ${skillNameOrId}: ${candidateIds.join(", ")}`
2273
- );
2274
- }
2275
- throw new Error(`Managed skill not found: ${skillId}`);
2276
- }
2277
- function buildHumanOutput(skill, managedSkillPath) {
2278
- return `Removed ${skill.id} from ${managedSkillPath}
2279
- `;
2280
- }
2281
- function buildJsonOutput2(result) {
2282
- return printJson({
2283
- changed: result.changed,
2284
- removedSkillId: result.removedSkillId,
2285
- skill: result.skill,
2286
- location: result.location,
2287
- manifest: result.manifest
2288
- });
2289
- }
2290
- async function pathExists4(path) {
2291
- try {
2292
- await fs15.lstat(path);
2293
- return true;
2294
- } catch (error) {
2295
- if (error.code === "ENOENT") {
2296
- return false;
2297
- }
2298
- throw error;
2299
- }
2300
- }
2301
- async function assertManagedSkillRemovalSafety(skillmuxHome, skillPath, skillId) {
2302
- const expectedSkillPath = buildManagedSkillPath4(skillmuxHome, skillId);
2303
- if (!pathsAreEqual(skillPath, expectedSkillPath)) {
2304
- throw new Error(`Refusing to remove unmanaged skill path at ${skillPath}`);
2305
- }
2306
- await assertNoSymlinkAncestors(skillPath, { includeLeaf: true });
2307
- if (!await pathExists4(skillPath)) {
2308
- return;
2309
- }
2310
- const entry = await fs15.lstat(skillPath);
2311
- if (entry.isSymbolicLink()) {
2312
- throw new Error(`Refusing to remove symlinked managed skill path at ${skillPath}`);
2313
- }
2314
- if (!entry.isDirectory()) {
2315
- throw new Error(`Refusing to remove non-directory managed skill path at ${skillPath}`);
2316
- }
2317
- }
2318
- async function runRemoveSingle(options) {
2319
- if (options.skill === void 0) {
2320
- throw new Error("Remove requires one target skill");
2321
- }
2322
- const homeDir = options.homeDir ?? homedir12();
2323
- const { skillmuxHome: defaultSkillmuxHome } = resolveSkillmuxHome(homeDir);
2324
- const skillmuxHome = options.skillmuxHome ?? defaultSkillmuxHome;
2325
- const manifestPath = buildManifestPath(skillmuxHome);
2326
- const configPath = buildConfigPath2(skillmuxHome);
2327
- const manifest = await readManifest(skillmuxHome);
2328
- const skill = resolveManagedSkill(manifest, options.skill);
2329
- const managedSkillsDirectory = resolve11(skillmuxHome, "skills");
2330
- const managedSkillPath = skill.path;
2331
- const enabledActivations = manifest.activations.filter(
2332
- (activation) => activation.skillId === skill.id && activation.state === "enabled"
2333
- );
2334
- if (enabledActivations.length > 0) {
2335
- const enabledAgents = [...new Set(enabledActivations.map((activation) => activation.agentId))].sort(
2336
- (left, right) => left.localeCompare(right)
2337
- );
2338
- throw new Error(
2339
- `Cannot remove ${skill.id}; it is still enabled for: ${enabledAgents.join(", ")}`
2340
- );
2341
- }
2342
- await assertManagedSkillRemovalSafety(skillmuxHome, managedSkillPath, skill.id);
2343
- if (await pathExists4(managedSkillPath)) {
2344
- await fs15.rm(managedSkillPath, { recursive: true, force: false });
2345
- }
2346
- delete manifest.skills[skill.id];
2347
- manifest.activations = manifest.activations.filter(
2348
- (activation) => activation.skillId !== skill.id
2349
- );
2350
- await writeManifest(skillmuxHome, manifest);
2351
- const resultWithoutOutput = {
2352
- changed: true,
2353
- removedSkillId: skill.id,
2354
- skill,
2355
- location: {
2356
- skillmuxHome,
2357
- configPath,
2358
- manifestPath,
2359
- managedSkillsDirectory,
2360
- managedSkillPath
2361
- },
2362
- manifest
2363
- };
2364
- return {
2365
- ...resultWithoutOutput,
2366
- output: options.json === true ? buildJsonOutput2(resultWithoutOutput) : buildHumanOutput(skill, managedSkillPath)
2367
- };
2368
- }
2369
- async function runRemove(options) {
2370
- if (options.skills !== void 0) {
2371
- const results = [];
2372
- for (const skill of options.skills) {
2373
- try {
2374
- results.push(await runRemoveSingle({ ...options, skill, skills: void 0 }));
2375
- } catch (error) {
2376
- throw new BatchOperationError({
2377
- operation: "remove",
2378
- failedItem: skill,
2379
- failedAction: `remove ${skill}`,
2380
- completedAction: "removing",
2381
- completedItems: results.map((result) => result.removedSkillId),
2382
- cause: error
2383
- });
2384
- }
2385
- }
2386
- if (results.length === 0) {
2387
- throw new Error("Remove requires at least one target skill");
2388
- }
2389
- const lastResult = results[results.length - 1];
2390
- const resultWithoutOutput = {
2391
- changed: results.some((result) => result.changed),
2392
- removedSkillIds: results.map((result) => result.removedSkillId),
2393
- results,
2394
- manifest: lastResult.manifest
2395
- };
2396
- return {
2397
- ...resultWithoutOutput,
2398
- output: options.json === true ? printJson(resultWithoutOutput) : results.map((result) => result.output).join("")
2399
- };
2400
- }
2401
- return runRemoveSingle(options);
2402
- }
2403
-
2404
- // src/index.ts
2405
- function collectValues(value, previous = []) {
2406
- return [...previous, value];
2407
- }
2408
- function requireSingleValue(values, label) {
2409
- if (values.length !== 1) {
2410
- throw new Error(`Expected exactly one ${label}`);
2411
- }
2412
- return values[0];
2413
- }
2414
- function requireAtLeastOneValue(values, label) {
2415
- if (values.length === 0) {
2416
- throw new Error(`Expected at least one ${label}`);
2417
- }
2418
- return values;
2419
- }
2420
- function buildCli() {
2421
- const program = new Command();
2422
- program.name("skillmux");
2423
- program.command("adopt").requiredOption("--agent <agent>", "Source agent id").option("--skill <skill>", "Repeatable installed skill to adopt", collectValues, []).option("--json", "Emit structured JSON output").action(async (options) => {
2424
- const result = options.skill.length === 0 ? await runAdopt({
2425
- agent: options.agent,
2426
- json: options.json === true
2427
- }) : options.skill.length === 1 ? await runAdopt({
2428
- agent: options.agent,
2429
- skill: options.skill[0],
2430
- json: options.json === true
2431
- }) : await runAdopt({
2432
- agent: options.agent,
2433
- skills: options.skill,
2434
- json: options.json === true
2435
- });
2436
- process.stdout.write(result.output);
2437
- });
2438
- program.command("scan").option("--json", "Emit structured JSON output").action(async (options) => {
2439
- const result = await runScan({ json: options.json === true });
2440
- process.stdout.write(result.output);
2441
- });
2442
- program.command("agents").option("--json", "Emit structured JSON output").action(async (options) => {
2443
- const result = await runAgents({ json: options.json === true });
2444
- process.stdout.write(result.output);
2445
- });
2446
- program.command("list").option("--view <view>", "Select records, agents, or skills view", "records").option("--format <format>", "Select table or json output", "table").action(async (options) => {
2447
- const result = await runList({
2448
- view: options.view,
2449
- format: options.format
2450
- });
2451
- process.stdout.write(result.output);
2452
- });
2453
- program.command("import").requiredOption("--source <path>", "Local skill source directory").requiredOption("--name <name>", "Managed skill name").action(async (options) => {
2454
- const result = await runImport({
2455
- sourcePath: options.source,
2456
- skillName: options.name
2457
- });
2458
- process.stdout.write(result.output);
2459
- });
2460
- program.command("doctor").option("--json", "Emit structured JSON output").action(async (options) => {
2461
- const result = await runDoctor({ json: options.json === true });
2462
- process.stdout.write(result.output);
2463
- });
2464
- const configCommand = program.command("config");
2465
- configCommand.option("--json", "Emit structured JSON output").action(async (options) => {
2466
- const result = await runConfig({ json: options.json === true });
2467
- process.stdout.write(result.output);
2468
- });
2469
- configCommand.command("add-agent").requiredOption("--id <id>", "Agent id").requiredOption("--root <path>", "Home-relative root path").option("--skills <path>", "Skills directory path relative to the agent root", "skills").option("--name <name>", "Stable display name").option(
2470
- "--platform <platform>",
2471
- `Supported platform (${supportedPlatforms.join(", ")})`,
2472
- (value, previous = []) => [...previous, value],
2473
- []
2474
- ).option("--disabled-by-default", "Mark this custom agent as disabled by default").option("--json", "Emit structured JSON output").action(
2475
- async (options) => {
2476
- const result = await runConfigAddAgent({
2477
- id: options.id,
2478
- root: options.root,
2479
- skills: options.skills,
2480
- name: options.name,
2481
- platforms: options.platform,
2482
- disabledByDefault: options.disabledByDefault === true,
2483
- json: options.json === true
2484
- });
2485
- process.stdout.write(result.output);
2486
- }
2487
- );
2488
- configCommand.command("remove-agent").requiredOption("--id <id>", "Agent id").option("--json", "Emit structured JSON output").action(async (options) => {
2489
- const result = await runConfigRemoveAgent({
2490
- id: options.id,
2491
- json: options.json === true
2492
- });
2493
- process.stdout.write(result.output);
2494
- });
2495
- configCommand.command("update-agent").requiredOption("--id <id>", "Agent id").option("--root <path>", "Home-relative root path").option("--skills <path>", "Skills directory path relative to the agent root").option("--name <name>", "Stable display name").option(
2496
- "--platform <platform>",
2497
- `Supported platform (${supportedPlatforms.join(", ")})`,
2498
- (value, previous = []) => [...previous, value],
2499
- []
2500
- ).option("--enabled-by-default", "Mark this custom agent as enabled by default").option("--disabled-by-default", "Mark this custom agent as disabled by default").option("--json", "Emit structured JSON output").action(
2501
- async (options) => {
2502
- const result = await runConfigUpdateAgent({
2503
- id: options.id,
2504
- root: options.root,
2505
- skills: options.skills,
2506
- name: options.name,
2507
- platforms: options.platform !== void 0 && options.platform.length > 0 ? options.platform : void 0,
2508
- enabledByDefault: options.enabledByDefault === true ? true : void 0,
2509
- disabledByDefault: options.disabledByDefault === true,
2510
- json: options.json === true
2511
- });
2512
- process.stdout.write(result.output);
2513
- }
2514
- );
2515
- program.command("enable").requiredOption("--skill <skill>", "Managed skill name or id", collectValues, []).requiredOption("--agent <agent>", "Repeatable target agent", collectValues, []).action(async (options) => {
2516
- const result = await runEnable({
2517
- skill: requireSingleValue(options.skill, "skill"),
2518
- agents: requireAtLeastOneValue(options.agent, "agent")
2519
- });
2520
- process.stdout.write(result.output);
2521
- });
2522
- program.command("disable").requiredOption("--skill <skill>", "Managed skill name or id", collectValues, []).requiredOption("--agent <agent>", "Repeatable target agent", collectValues, []).action(async (options) => {
2523
- const result = await runDisable({
2524
- skill: requireSingleValue(options.skill, "skill"),
2525
- agents: requireAtLeastOneValue(options.agent, "agent")
2526
- });
2527
- process.stdout.write(result.output);
2528
- });
2529
- program.command("remove").requiredOption("--skill <skill>", "Repeatable managed skill name or id", collectValues, []).option("--json", "Emit structured JSON output").action(async (options) => {
2530
- const skills = requireAtLeastOneValue(options.skill, "skill");
2531
- const result = skills.length === 1 ? await runRemove({
2532
- skill: skills[0],
2533
- json: options.json === true
2534
- }) : await runRemove({
2535
- skills,
2536
- json: options.json === true
2537
- });
2538
- process.stdout.write(result.output);
2539
- });
2540
- return program;
2541
- }
2
+ import {
3
+ buildCli
4
+ } from "./chunk-UMN3UJFN.js";
5
+ import "./chunk-DBEVDI27.js";
2542
6
 
2543
7
  // src/cli.ts
2544
8
  await buildCli().parseAsync(process.argv);