skillmux 0.1.1 → 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.
@@ -0,0 +1,836 @@
1
+ import {
2
+ InvalidIdentifierError,
3
+ UserConfigValidationError,
4
+ assertSkillSourceLayout,
5
+ buildConfigPath,
6
+ collectDoctorIssues,
7
+ copySkillContentsToManagedStore,
8
+ dedupeAndSortIssues,
9
+ discoverAgents,
10
+ loadUserConfig,
11
+ normalizeId,
12
+ printJson,
13
+ printTable,
14
+ readManifest,
15
+ resolveSkillmuxHome,
16
+ runAdopt,
17
+ runDisable,
18
+ runEnable,
19
+ runRemove,
20
+ runScan,
21
+ scanAgentSkills,
22
+ supportedPlatforms,
23
+ writeManifest
24
+ } from "./chunk-DBEVDI27.js";
25
+
26
+ // src/index.ts
27
+ import { Command } from "commander";
28
+
29
+ // src/commands/agents.ts
30
+ import { homedir } from "os";
31
+ function buildTableOutput(agents) {
32
+ return printTable(
33
+ agents.map((agent) => ({
34
+ id: agent.id,
35
+ name: agent.stableName,
36
+ path: agent.absoluteSkillsDirectoryPath,
37
+ exists: String(agent.exists),
38
+ supported: String(agent.supportedOnPlatform),
39
+ discovery: agent.discovery
40
+ })),
41
+ [
42
+ { key: "id", label: "Agent" },
43
+ { key: "name", label: "Name" },
44
+ { key: "path", label: "Path" },
45
+ { key: "exists", label: "Exists" },
46
+ { key: "supported", label: "Supported" },
47
+ { key: "discovery", label: "Discovery" }
48
+ ]
49
+ );
50
+ }
51
+ async function runAgents(options = {}) {
52
+ const homeDir = options.homeDir ?? homedir();
53
+ const resolvedPaths = resolveSkillmuxHome(homeDir);
54
+ const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
55
+ const agents = await discoverAgents({
56
+ homeDir,
57
+ skillmuxHome,
58
+ platform: options.platform
59
+ });
60
+ return {
61
+ agents,
62
+ output: options.json === true ? printJson(agents) : buildTableOutput(agents)
63
+ };
64
+ }
65
+
66
+ // src/commands/config-add-agent.ts
67
+ import { homedir as homedir2 } from "os";
68
+
69
+ // src/config/agent-override-validation.ts
70
+ import { isAbsolute } from "path";
71
+ function normalizeRelativePath(value, field) {
72
+ const trimmed = value.trim();
73
+ if (trimmed.length === 0) {
74
+ throw new UserConfigValidationError(`${field} must not be empty`);
75
+ }
76
+ if (isAbsolute(trimmed)) {
77
+ throw new UserConfigValidationError(`${field} must be a relative path`);
78
+ }
79
+ const normalized = trimmed.replaceAll("\\", "/");
80
+ if (normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.includes("/../")) {
81
+ throw new UserConfigValidationError(`${field} must stay within the configured home-relative tree`);
82
+ }
83
+ return normalized.replace(/^\.\/+/, "");
84
+ }
85
+ function normalizeAgentId(value) {
86
+ const trimmed = value.trim();
87
+ if (trimmed.length === 0 || /[a-z0-9]/i.test(trimmed) === false) {
88
+ throw new InvalidIdentifierError("agent id", value);
89
+ }
90
+ return normalizeId(trimmed);
91
+ }
92
+ function normalizePlatforms(value) {
93
+ if (value === void 0 || value.length === 0) {
94
+ return [process.platform];
95
+ }
96
+ const normalized = [...new Set(value.map((entry) => entry.trim().toLowerCase()))];
97
+ const invalid = normalized.filter(
98
+ (entry) => supportedPlatforms.includes(entry) === false
99
+ );
100
+ if (invalid.length > 0) {
101
+ throw new UserConfigValidationError(
102
+ `platform must be one of: ${supportedPlatforms.join(", ")}`
103
+ );
104
+ }
105
+ return normalized;
106
+ }
107
+
108
+ // src/config/write-user-config.ts
109
+ import * as fs from "fs/promises";
110
+ async function writeUserConfig(skillmuxHome, config) {
111
+ const configPath = buildConfigPath(skillmuxHome);
112
+ await fs.mkdir(skillmuxHome, { recursive: true });
113
+ await fs.writeFile(configPath, `${JSON.stringify(config, null, 2)}
114
+ `, "utf8");
115
+ return {
116
+ skillmuxHome,
117
+ configPath
118
+ };
119
+ }
120
+
121
+ // src/commands/config-add-agent.ts
122
+ function buildAgentOverride(options) {
123
+ const agentId = normalizeAgentId(options.id);
124
+ const agent = {
125
+ supportedPlatforms: normalizePlatforms(options.platforms),
126
+ homeRelativeRootPath: normalizeRelativePath(options.root, "root"),
127
+ skillsDirectoryPath: normalizeRelativePath(options.skills ?? "skills", "skills")
128
+ };
129
+ if (options.name !== void 0 && options.name.trim().length > 0) {
130
+ agent.stableName = options.name.trim();
131
+ }
132
+ if (options.disabledByDefault === true) {
133
+ agent.enabledByDefault = false;
134
+ }
135
+ return { agentId, agent };
136
+ }
137
+ function buildTableOutput2(result) {
138
+ const summary = printTable(
139
+ [
140
+ {
141
+ agentId: result.agentId,
142
+ configPath: result.configPath,
143
+ changed: String(result.changed)
144
+ }
145
+ ],
146
+ [
147
+ { key: "agentId", label: "Agent" },
148
+ { key: "configPath", label: "Config Path" },
149
+ { key: "changed", label: "Changed" }
150
+ ]
151
+ );
152
+ const detail = printTable(
153
+ [
154
+ {
155
+ stableName: result.agent.stableName ?? "",
156
+ platforms: (result.agent.supportedPlatforms ?? []).join(","),
157
+ root: result.agent.homeRelativeRootPath ?? "",
158
+ skills: result.agent.skillsDirectoryPath ?? "",
159
+ enabledByDefault: result.agent.enabledByDefault === void 0 ? "" : String(result.agent.enabledByDefault)
160
+ }
161
+ ],
162
+ [
163
+ { key: "stableName", label: "Name" },
164
+ { key: "platforms", label: "Platforms" },
165
+ { key: "root", label: "Root" },
166
+ { key: "skills", label: "Skills Dir" },
167
+ { key: "enabledByDefault", label: "Enabled By Default" }
168
+ ]
169
+ );
170
+ return `${summary}${detail}`;
171
+ }
172
+ async function runConfigAddAgent(options) {
173
+ const homeDir = options.homeDir ?? homedir2();
174
+ const resolvedPaths = resolveSkillmuxHome(homeDir);
175
+ const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
176
+ const configPath = buildConfigPath(skillmuxHome);
177
+ const config = await loadUserConfig(skillmuxHome);
178
+ const { agentId, agent } = buildAgentOverride(options);
179
+ const previous = config.agents[agentId];
180
+ const changed = JSON.stringify(previous ?? null) !== JSON.stringify(agent);
181
+ const nextConfig = {
182
+ ...config,
183
+ agents: {
184
+ ...config.agents,
185
+ [agentId]: agent
186
+ }
187
+ };
188
+ await writeUserConfig(skillmuxHome, nextConfig);
189
+ const resultWithoutOutput = {
190
+ skillmuxHome,
191
+ configPath,
192
+ agentId,
193
+ changed,
194
+ agent,
195
+ config: nextConfig
196
+ };
197
+ return {
198
+ ...resultWithoutOutput,
199
+ output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput2(resultWithoutOutput)
200
+ };
201
+ }
202
+
203
+ // src/commands/config-remove-agent.ts
204
+ import { homedir as homedir3 } from "os";
205
+ function normalizeAgentId2(value) {
206
+ const trimmed = value.trim();
207
+ if (trimmed.length === 0 || /[a-z0-9]/i.test(trimmed) === false) {
208
+ throw new InvalidIdentifierError("agent id", value);
209
+ }
210
+ return normalizeId(trimmed);
211
+ }
212
+ function buildTableOutput3(result) {
213
+ return printTable(
214
+ [
215
+ {
216
+ agentId: result.agentId,
217
+ configPath: result.configPath,
218
+ changed: String(result.changed),
219
+ removed: String(result.removed)
220
+ }
221
+ ],
222
+ [
223
+ { key: "agentId", label: "Agent" },
224
+ { key: "configPath", label: "Config Path" },
225
+ { key: "changed", label: "Changed" },
226
+ { key: "removed", label: "Removed" }
227
+ ]
228
+ );
229
+ }
230
+ async function runConfigRemoveAgent(options) {
231
+ const homeDir = options.homeDir ?? homedir3();
232
+ const resolvedPaths = resolveSkillmuxHome(homeDir);
233
+ const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
234
+ const configPath = buildConfigPath(skillmuxHome);
235
+ const config = await loadUserConfig(skillmuxHome);
236
+ const agentId = normalizeAgentId2(options.id);
237
+ const removed = agentId in config.agents;
238
+ const nextConfig = {
239
+ ...config,
240
+ agents: Object.fromEntries(
241
+ Object.entries(config.agents).filter(([currentAgentId]) => currentAgentId !== agentId)
242
+ )
243
+ };
244
+ if (removed) {
245
+ await writeUserConfig(skillmuxHome, nextConfig);
246
+ }
247
+ const resultWithoutOutput = {
248
+ skillmuxHome,
249
+ configPath,
250
+ agentId,
251
+ changed: removed,
252
+ removed,
253
+ config: nextConfig
254
+ };
255
+ return {
256
+ ...resultWithoutOutput,
257
+ output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput3(resultWithoutOutput)
258
+ };
259
+ }
260
+
261
+ // src/commands/config-update-agent.ts
262
+ import { homedir as homedir4 } from "os";
263
+ function buildAgentPatch(options) {
264
+ const patch = {};
265
+ if (options.root !== void 0) {
266
+ patch.homeRelativeRootPath = normalizeRelativePath(options.root, "root");
267
+ }
268
+ if (options.skills !== void 0) {
269
+ patch.skillsDirectoryPath = normalizeRelativePath(options.skills, "skills");
270
+ }
271
+ if (options.name !== void 0 && options.name.trim().length > 0) {
272
+ patch.stableName = options.name.trim();
273
+ }
274
+ if (options.platforms !== void 0) {
275
+ patch.supportedPlatforms = normalizePlatforms(options.platforms);
276
+ }
277
+ if (options.enabledByDefault !== void 0 && options.disabledByDefault === true) {
278
+ throw new UserConfigValidationError(
279
+ "enabled-by-default and disabled-by-default cannot both be set"
280
+ );
281
+ }
282
+ if (options.enabledByDefault !== void 0) {
283
+ patch.enabledByDefault = options.enabledByDefault;
284
+ }
285
+ if (options.disabledByDefault === true) {
286
+ patch.enabledByDefault = false;
287
+ }
288
+ return patch;
289
+ }
290
+ function buildTableOutput4(result) {
291
+ const summary = printTable(
292
+ [
293
+ {
294
+ agentId: result.agentId,
295
+ configPath: result.configPath,
296
+ changed: String(result.changed)
297
+ }
298
+ ],
299
+ [
300
+ { key: "agentId", label: "Agent" },
301
+ { key: "configPath", label: "Config Path" },
302
+ { key: "changed", label: "Changed" }
303
+ ]
304
+ );
305
+ const detail = printTable(
306
+ [
307
+ {
308
+ stableName: result.agent.stableName ?? "",
309
+ platforms: (result.agent.supportedPlatforms ?? []).join(","),
310
+ root: result.agent.homeRelativeRootPath ?? "",
311
+ skills: result.agent.skillsDirectoryPath ?? "",
312
+ enabledByDefault: result.agent.enabledByDefault === void 0 ? "" : String(result.agent.enabledByDefault)
313
+ }
314
+ ],
315
+ [
316
+ { key: "stableName", label: "Name" },
317
+ { key: "platforms", label: "Platforms" },
318
+ { key: "root", label: "Root" },
319
+ { key: "skills", label: "Skills Dir" },
320
+ { key: "enabledByDefault", label: "Enabled By Default" }
321
+ ]
322
+ );
323
+ return `${summary}${detail}`;
324
+ }
325
+ async function runConfigUpdateAgent(options) {
326
+ const homeDir = options.homeDir ?? homedir4();
327
+ const resolvedPaths = resolveSkillmuxHome(homeDir);
328
+ const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
329
+ const configPath = buildConfigPath(skillmuxHome);
330
+ const config = await loadUserConfig(skillmuxHome);
331
+ const agentId = normalizeAgentId(options.id);
332
+ const previous = config.agents[agentId];
333
+ if (previous === void 0) {
334
+ throw new UserConfigValidationError(`Agent override does not exist: ${agentId}`);
335
+ }
336
+ const agent = {
337
+ ...previous,
338
+ ...buildAgentPatch(options)
339
+ };
340
+ const changed = JSON.stringify(previous) !== JSON.stringify(agent);
341
+ const nextConfig = {
342
+ ...config,
343
+ agents: {
344
+ ...config.agents,
345
+ [agentId]: agent
346
+ }
347
+ };
348
+ if (changed) {
349
+ await writeUserConfig(skillmuxHome, nextConfig);
350
+ }
351
+ const resultWithoutOutput = {
352
+ skillmuxHome,
353
+ configPath,
354
+ agentId,
355
+ changed,
356
+ agent,
357
+ config: nextConfig
358
+ };
359
+ return {
360
+ ...resultWithoutOutput,
361
+ output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput4(resultWithoutOutput)
362
+ };
363
+ }
364
+
365
+ // src/commands/config.ts
366
+ import { homedir as homedir5 } from "os";
367
+ function buildTableOutput5(result) {
368
+ const summary = printTable(
369
+ [
370
+ {
371
+ skillmuxHome: result.skillmuxHome,
372
+ configPath: result.configPath,
373
+ overrides: String(Object.keys(result.config.agents).length)
374
+ }
375
+ ],
376
+ [
377
+ { key: "skillmuxHome", label: "SkillMux Home" },
378
+ { key: "configPath", label: "Config Path" },
379
+ { key: "overrides", label: "Overrides" }
380
+ ]
381
+ );
382
+ const agentRows = Object.entries(result.config.agents).sort(([left], [right]) => left.localeCompare(right)).map(([agentId, agent]) => ({
383
+ agentId,
384
+ stableName: agent.stableName ?? "",
385
+ root: agent.homeRelativeRootPath ?? "",
386
+ skills: agent.skillsDirectoryPath ?? ""
387
+ }));
388
+ if (agentRows.length === 0) {
389
+ return `${summary}
390
+ No user overrides configured.
391
+ `;
392
+ }
393
+ return `${summary}
394
+ ${printTable(agentRows, [
395
+ { key: "agentId", label: "Agent" },
396
+ { key: "stableName", label: "Name" },
397
+ { key: "root", label: "Root" },
398
+ { key: "skills", label: "Skills Dir" }
399
+ ])}`;
400
+ }
401
+ async function runConfig(options = {}) {
402
+ const homeDir = options.homeDir ?? homedir5();
403
+ const resolvedPaths = resolveSkillmuxHome(homeDir);
404
+ const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
405
+ const config = await loadUserConfig(skillmuxHome);
406
+ const resultWithoutOutput = {
407
+ skillmuxHome,
408
+ configPath: buildConfigPath(skillmuxHome),
409
+ config
410
+ };
411
+ return {
412
+ ...resultWithoutOutput,
413
+ output: options.json === true ? printJson(resultWithoutOutput) : buildTableOutput5(resultWithoutOutput)
414
+ };
415
+ }
416
+
417
+ // src/commands/doctor.ts
418
+ import { homedir as homedir6 } from "os";
419
+ function buildTableOutput6(issues) {
420
+ if (issues.length === 0) {
421
+ return "No doctor issues found.\n";
422
+ }
423
+ return printTable(
424
+ issues.map((issue) => ({
425
+ severity: issue.severity,
426
+ code: issue.code,
427
+ path: issue.path ?? "",
428
+ message: issue.message
429
+ })),
430
+ [
431
+ { key: "severity", label: "Severity" },
432
+ { key: "code", label: "Code" },
433
+ { key: "path", label: "Path" },
434
+ { key: "message", label: "Message" }
435
+ ]
436
+ );
437
+ }
438
+ function buildJsonOutput(result) {
439
+ return printJson({
440
+ skillmuxHome: result.skillmuxHome,
441
+ issues: result.issues,
442
+ agents: result.agents.map((agent) => ({
443
+ id: agent.id,
444
+ path: agent.absoluteSkillsDirectoryPath,
445
+ supportedOnPlatform: agent.supportedOnPlatform
446
+ })),
447
+ entries: result.entries
448
+ });
449
+ }
450
+ async function runDoctor(options = {}) {
451
+ const homeDir = options.homeDir ?? homedir6();
452
+ const resolvedPaths = resolveSkillmuxHome(homeDir);
453
+ const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
454
+ const [manifest, config, agents] = await Promise.all([
455
+ readManifest(skillmuxHome),
456
+ loadUserConfig(skillmuxHome),
457
+ discoverAgents({
458
+ homeDir,
459
+ skillmuxHome,
460
+ platform: options.platform
461
+ })
462
+ ]);
463
+ const entries = [];
464
+ const issues = [];
465
+ for (const agent of agents) {
466
+ const scannedAgent = await scanAgentSkills(agent, skillmuxHome);
467
+ entries.push(...scannedAgent.entries);
468
+ issues.push(...scannedAgent.issues);
469
+ }
470
+ const doctorIssues = await collectDoctorIssues({
471
+ manifest,
472
+ agents,
473
+ entries
474
+ });
475
+ const dedupedIssues = dedupeAndSortIssues([...issues, ...doctorIssues]);
476
+ const resultWithoutOutput = {
477
+ skillmuxHome,
478
+ manifest,
479
+ config,
480
+ agents,
481
+ entries,
482
+ issues: dedupedIssues
483
+ };
484
+ return {
485
+ ...resultWithoutOutput,
486
+ output: options.json === true ? buildJsonOutput(resultWithoutOutput) : buildTableOutput6(dedupedIssues)
487
+ };
488
+ }
489
+
490
+ // src/commands/import.ts
491
+ import { resolve } from "path";
492
+ import { homedir as homedir7 } from "os";
493
+ function buildManagedSkillPath(skillmuxHome, skillId) {
494
+ return resolve(skillmuxHome, "skills", skillId);
495
+ }
496
+ async function runImport(options) {
497
+ const homeDir = options.homeDir ?? homedir7();
498
+ const resolvedPaths = resolveSkillmuxHome(homeDir);
499
+ const skillmuxHome = options.skillmuxHome ?? resolvedPaths.skillmuxHome;
500
+ const sourcePath = resolve(options.sourcePath);
501
+ const skillId = normalizeId(options.skillName);
502
+ const importedAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
503
+ const manifest = await readManifest(skillmuxHome);
504
+ const managedSkillPath = buildManagedSkillPath(skillmuxHome, skillId);
505
+ await assertSkillSourceLayout(sourcePath);
506
+ if (manifest.skills[skillId] !== void 0) {
507
+ throw new Error(`Managed skill already exists for ${skillId}`);
508
+ }
509
+ await copySkillContentsToManagedStore(sourcePath, managedSkillPath);
510
+ const skill = {
511
+ id: skillId,
512
+ name: options.skillName,
513
+ path: managedSkillPath,
514
+ source: {
515
+ kind: "local",
516
+ path: sourcePath
517
+ },
518
+ importedAt
519
+ };
520
+ manifest.skills[skillId] = skill;
521
+ await writeManifest(skillmuxHome, manifest);
522
+ return {
523
+ skill,
524
+ manifest,
525
+ output: `Imported ${skillId} to ${managedSkillPath}
526
+ `
527
+ };
528
+ }
529
+
530
+ // src/commands/list.ts
531
+ function buildRecordsView(scanResult) {
532
+ return {
533
+ view: "records",
534
+ records: scanResult.entries,
535
+ issues: scanResult.issues
536
+ };
537
+ }
538
+ function buildAgentsView(scanResult) {
539
+ const groups = /* @__PURE__ */ new Map();
540
+ for (const agent of scanResult.agents) {
541
+ groups.set(agent.id, {
542
+ agentId: agent.id,
543
+ agentName: agent.stableName,
544
+ entries: []
545
+ });
546
+ }
547
+ for (const entry of scanResult.entries) {
548
+ const current = groups.get(entry.agentId) ?? {
549
+ agentId: entry.agentId,
550
+ agentName: entry.agentName,
551
+ entries: []
552
+ };
553
+ current.entries.push(entry);
554
+ groups.set(entry.agentId, current);
555
+ }
556
+ return {
557
+ view: "agents",
558
+ agents: [...groups.values()].sort(
559
+ (left, right) => left.agentId.localeCompare(right.agentId)
560
+ ),
561
+ issues: scanResult.issues
562
+ };
563
+ }
564
+ function buildSkillsView(scanResult) {
565
+ const groups = /* @__PURE__ */ new Map();
566
+ for (const skill of Object.values(scanResult.manifest.skills)) {
567
+ groups.set(skill.id, {
568
+ skillName: skill.id,
569
+ entries: []
570
+ });
571
+ }
572
+ for (const entry of scanResult.entries) {
573
+ const current = groups.get(entry.skillName) ?? {
574
+ skillName: entry.skillName,
575
+ entries: []
576
+ };
577
+ current.entries.push(entry);
578
+ groups.set(entry.skillName, current);
579
+ }
580
+ return {
581
+ view: "skills",
582
+ skills: [...groups.values()].sort(
583
+ (left, right) => left.skillName.localeCompare(right.skillName)
584
+ ),
585
+ issues: scanResult.issues
586
+ };
587
+ }
588
+ function buildListData(scanResult, view) {
589
+ if (view === "agents") {
590
+ return buildAgentsView(scanResult);
591
+ }
592
+ if (view === "skills") {
593
+ return buildSkillsView(scanResult);
594
+ }
595
+ return buildRecordsView(scanResult);
596
+ }
597
+ function buildTableOutput7(data, view) {
598
+ if (view === "agents") {
599
+ const agentRows = data.agents;
600
+ return printTable(
601
+ agentRows.map((agent) => ({
602
+ agent: agent.agentId,
603
+ name: agent.agentName,
604
+ entries: String(agent.entries.length)
605
+ })),
606
+ [
607
+ { key: "agent", label: "Agent" },
608
+ { key: "name", label: "Name" },
609
+ { key: "entries", label: "Entries" }
610
+ ]
611
+ );
612
+ }
613
+ if (view === "skills") {
614
+ const skillRows = data.skills;
615
+ return printTable(
616
+ skillRows.map((skill) => ({
617
+ skill: skill.skillName,
618
+ entries: String(skill.entries.length)
619
+ })),
620
+ [
621
+ { key: "skill", label: "Skill" },
622
+ { key: "entries", label: "Entries" }
623
+ ]
624
+ );
625
+ }
626
+ const records = data.records;
627
+ return printTable(
628
+ records.map((record) => ({
629
+ agent: record.agentId,
630
+ skill: record.skillName,
631
+ kind: record.kind
632
+ })),
633
+ [
634
+ { key: "agent", label: "Agent" },
635
+ { key: "skill", label: "Skill" },
636
+ { key: "kind", label: "Kind" }
637
+ ]
638
+ );
639
+ }
640
+ async function runList(options = {}) {
641
+ const view = options.view ?? "records";
642
+ const format = options.format ?? "table";
643
+ const scanResult = await runScan(options);
644
+ const data = buildListData(scanResult, view);
645
+ return {
646
+ data,
647
+ output: format === "json" ? printJson(data) : buildTableOutput7(data, view)
648
+ };
649
+ }
650
+
651
+ // src/tui/tty.ts
652
+ function isInteractiveTerminal(stdin, stdout) {
653
+ return stdin.isTTY === true && stdout.isTTY === true;
654
+ }
655
+
656
+ // src/commands/tui.ts
657
+ var TuiNonInteractiveTerminalError = class extends Error {
658
+ constructor() {
659
+ super("skillmux tui requires an interactive terminal");
660
+ this.name = "TuiNonInteractiveTerminalError";
661
+ }
662
+ };
663
+ async function launchDefaultTui(options) {
664
+ const { launchTui } = await import("./launch-tui-PHWJPIQZ.js");
665
+ await launchTui(options);
666
+ }
667
+ async function runTui(options = {}) {
668
+ const stdin = options.stdin ?? process.stdin;
669
+ const stdout = options.stdout ?? process.stdout;
670
+ const stderr = options.stderr ?? process.stderr;
671
+ if (!isInteractiveTerminal(stdin, stdout)) {
672
+ stderr.write(
673
+ "skillmux tui requires an interactive terminal. Use skillmux list, skillmux scan, or skillmux doctor for non-interactive output.\n"
674
+ );
675
+ throw new TuiNonInteractiveTerminalError();
676
+ }
677
+ await (options.launch ?? launchDefaultTui)({
678
+ homeDir: options.homeDir,
679
+ skillmuxHome: options.skillmuxHome
680
+ });
681
+ }
682
+
683
+ // src/index.ts
684
+ function collectValues(value, previous = []) {
685
+ return [...previous, value];
686
+ }
687
+ function requireSingleValue(values, label) {
688
+ if (values.length !== 1) {
689
+ throw new Error(`Expected exactly one ${label}`);
690
+ }
691
+ return values[0];
692
+ }
693
+ function requireAtLeastOneValue(values, label) {
694
+ if (values.length === 0) {
695
+ throw new Error(`Expected at least one ${label}`);
696
+ }
697
+ return values;
698
+ }
699
+ function buildCli() {
700
+ const program = new Command();
701
+ program.name("skillmux");
702
+ 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) => {
703
+ const result = options.skill.length === 0 ? await runAdopt({
704
+ agent: options.agent,
705
+ json: options.json === true
706
+ }) : options.skill.length === 1 ? await runAdopt({
707
+ agent: options.agent,
708
+ skill: options.skill[0],
709
+ json: options.json === true
710
+ }) : await runAdopt({
711
+ agent: options.agent,
712
+ skills: options.skill,
713
+ json: options.json === true
714
+ });
715
+ process.stdout.write(result.output);
716
+ });
717
+ program.command("scan").option("--json", "Emit structured JSON output").action(async (options) => {
718
+ const result = await runScan({ json: options.json === true });
719
+ process.stdout.write(result.output);
720
+ });
721
+ program.command("agents").option("--json", "Emit structured JSON output").action(async (options) => {
722
+ const result = await runAgents({ json: options.json === true });
723
+ process.stdout.write(result.output);
724
+ });
725
+ 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) => {
726
+ const result = await runList({
727
+ view: options.view,
728
+ format: options.format
729
+ });
730
+ process.stdout.write(result.output);
731
+ });
732
+ program.command("import").requiredOption("--source <path>", "Local skill source directory").requiredOption("--name <name>", "Managed skill name").action(async (options) => {
733
+ const result = await runImport({
734
+ sourcePath: options.source,
735
+ skillName: options.name
736
+ });
737
+ process.stdout.write(result.output);
738
+ });
739
+ program.command("doctor").option("--json", "Emit structured JSON output").action(async (options) => {
740
+ const result = await runDoctor({ json: options.json === true });
741
+ process.stdout.write(result.output);
742
+ });
743
+ program.command("tui").description("Open the interactive SkillMux dashboard").action(async () => {
744
+ try {
745
+ await runTui();
746
+ } catch (error) {
747
+ if (error instanceof TuiNonInteractiveTerminalError) {
748
+ process.exitCode = 1;
749
+ return;
750
+ }
751
+ throw error;
752
+ }
753
+ });
754
+ const configCommand = program.command("config");
755
+ configCommand.option("--json", "Emit structured JSON output").action(async (options) => {
756
+ const result = await runConfig({ json: options.json === true });
757
+ process.stdout.write(result.output);
758
+ });
759
+ 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(
760
+ "--platform <platform>",
761
+ `Supported platform (${supportedPlatforms.join(", ")})`,
762
+ (value, previous = []) => [...previous, value],
763
+ []
764
+ ).option("--disabled-by-default", "Mark this custom agent as disabled by default").option("--json", "Emit structured JSON output").action(
765
+ async (options) => {
766
+ const result = await runConfigAddAgent({
767
+ id: options.id,
768
+ root: options.root,
769
+ skills: options.skills,
770
+ name: options.name,
771
+ platforms: options.platform,
772
+ disabledByDefault: options.disabledByDefault === true,
773
+ json: options.json === true
774
+ });
775
+ process.stdout.write(result.output);
776
+ }
777
+ );
778
+ configCommand.command("remove-agent").requiredOption("--id <id>", "Agent id").option("--json", "Emit structured JSON output").action(async (options) => {
779
+ const result = await runConfigRemoveAgent({
780
+ id: options.id,
781
+ json: options.json === true
782
+ });
783
+ process.stdout.write(result.output);
784
+ });
785
+ 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(
786
+ "--platform <platform>",
787
+ `Supported platform (${supportedPlatforms.join(", ")})`,
788
+ (value, previous = []) => [...previous, value],
789
+ []
790
+ ).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(
791
+ async (options) => {
792
+ const result = await runConfigUpdateAgent({
793
+ id: options.id,
794
+ root: options.root,
795
+ skills: options.skills,
796
+ name: options.name,
797
+ platforms: options.platform !== void 0 && options.platform.length > 0 ? options.platform : void 0,
798
+ enabledByDefault: options.enabledByDefault === true ? true : void 0,
799
+ disabledByDefault: options.disabledByDefault === true,
800
+ json: options.json === true
801
+ });
802
+ process.stdout.write(result.output);
803
+ }
804
+ );
805
+ program.command("enable").requiredOption("--skill <skill>", "Managed skill name or id", collectValues, []).requiredOption("--agent <agent>", "Repeatable target agent", collectValues, []).action(async (options) => {
806
+ const result = await runEnable({
807
+ skill: requireSingleValue(options.skill, "skill"),
808
+ agents: requireAtLeastOneValue(options.agent, "agent")
809
+ });
810
+ process.stdout.write(result.output);
811
+ });
812
+ program.command("disable").requiredOption("--skill <skill>", "Managed skill name or id", collectValues, []).requiredOption("--agent <agent>", "Repeatable target agent", collectValues, []).action(async (options) => {
813
+ const result = await runDisable({
814
+ skill: requireSingleValue(options.skill, "skill"),
815
+ agents: requireAtLeastOneValue(options.agent, "agent")
816
+ });
817
+ process.stdout.write(result.output);
818
+ });
819
+ program.command("remove").requiredOption("--skill <skill>", "Repeatable managed skill name or id", collectValues, []).option("--json", "Emit structured JSON output").action(async (options) => {
820
+ const skills = requireAtLeastOneValue(options.skill, "skill");
821
+ const result = skills.length === 1 ? await runRemove({
822
+ skill: skills[0],
823
+ json: options.json === true
824
+ }) : await runRemove({
825
+ skills,
826
+ json: options.json === true
827
+ });
828
+ process.stdout.write(result.output);
829
+ });
830
+ return program;
831
+ }
832
+
833
+ export {
834
+ buildCli
835
+ };
836
+ //# sourceMappingURL=chunk-UMN3UJFN.js.map