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.
- package/README.md +446 -284
- package/dist/chunk-DBEVDI27.js +1826 -0
- package/dist/chunk-DBEVDI27.js.map +1 -0
- package/dist/chunk-UMN3UJFN.js +836 -0
- package/dist/chunk-UMN3UJFN.js.map +1 -0
- package/dist/cli.js +4 -1815
- package/dist/cli.js.map +1 -1
- package/dist/index.js +4 -1814
- package/dist/index.js.map +1 -1
- package/dist/launch-tui-PHWJPIQZ.js +1790 -0
- package/dist/launch-tui-PHWJPIQZ.js.map +1 -0
- package/package.json +11 -2
|
@@ -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
|