@mcp-s/skills 1.0.2 → 1.3.1

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/index.js DELETED
@@ -1,482 +0,0 @@
1
- #!/usr/bin/env node
2
- import { program } from "commander";
3
- import * as p from "@clack/prompts";
4
- import chalk from "chalk";
5
- import { homedir } from "os";
6
- import { fetchSkills } from "./api.js";
7
- import { detectInstalledAgents, agents } from "./agents.js";
8
- import { installSkillForAgent, isSkillInstalled, getCanonicalPath, } from "./installer.js";
9
- import { getOrCreateAuth, refreshToken, authenticate, saveAuthConfig, } from "./auth.js";
10
- function shortenPath(fullPath, cwd) {
11
- const home = homedir();
12
- if (fullPath.startsWith(home)) {
13
- return fullPath.replace(home, "~");
14
- }
15
- if (fullPath.startsWith(cwd)) {
16
- return "." + fullPath.slice(cwd.length);
17
- }
18
- return fullPath;
19
- }
20
- function formatList(items, maxShow = 5) {
21
- if (items.length <= maxShow) {
22
- return items.join(", ");
23
- }
24
- const shown = items.slice(0, maxShow);
25
- const remaining = items.length - maxShow;
26
- return `${shown.join(", ")} +${remaining} more`;
27
- }
28
- function buildBaseUrl(org, baseUrl) {
29
- if (baseUrl) {
30
- return baseUrl;
31
- }
32
- if (org) {
33
- return `https://${org}.mcp-s.com/skills`;
34
- }
35
- throw new Error("Either --org or --base-url must be provided");
36
- }
37
- program
38
- .name("skills")
39
- .description("Install skills onto coding agents from MCP-S server (Claude Code, Cline, Codex, Cursor, and more)")
40
- .version("1.0.0")
41
- .option("-o, --org <org>", "Organization name (builds URL as https://<org>.mcp-s.com/skills)")
42
- .option("-b, --base-url <url>", "Full base URL of the MCP-S skills server")
43
- .option("-g, --global", "Install skill globally (user-level) instead of project-level")
44
- .option("-a, --agent <agents...>", "Specify agents to install to (claude-code, cline, codex, cursor, and more)")
45
- .option("-s, --skill <skills...>", "Specify skill slugs to install (skip selection prompt)")
46
- .option("-l, --list", "List available skills without installing")
47
- .option("-y, --yes", "Skip confirmation prompts")
48
- .option("--all", "Install all skills to all agents without any prompts (implies -y -g)")
49
- .configureOutput({
50
- outputError: (str, write) => {
51
- if (str.includes("error: required option")) {
52
- console.log();
53
- console.log(chalk.bgRed.white.bold(" ERROR ") +
54
- " " +
55
- chalk.red("Missing required option: --org or --base-url"));
56
- console.log();
57
- console.log(chalk.dim(" Usage:"));
58
- console.log(` ${chalk.cyan("npx @mcp-s/skills")} ${chalk.yellow("--org <org>")} ${chalk.dim("[options]")}`);
59
- console.log(` ${chalk.cyan("npx @mcp-s/skills")} ${chalk.yellow("--base-url <url>")} ${chalk.dim("[options]")}`);
60
- console.log();
61
- console.log(chalk.dim(" Examples:"));
62
- console.log(` ${chalk.cyan("npx @mcp-s/skills")} ${chalk.yellow("--org mycompany")}`);
63
- console.log(` ${chalk.cyan("npx @mcp-s/skills")} ${chalk.yellow("--base-url https://custom.example.com/skills")}`);
64
- console.log();
65
- console.log(chalk.dim(" Run") +
66
- ` ${chalk.cyan("npx @mcp-s/skills --help")}` +
67
- chalk.dim(" for more information."));
68
- console.log();
69
- }
70
- else {
71
- write(str);
72
- }
73
- },
74
- })
75
- .action(async (options) => {
76
- await main(options);
77
- });
78
- program.parse();
79
- async function main(options) {
80
- var _a;
81
- // Validate that either org or baseUrl is provided
82
- if (!options.org && !options.baseUrl) {
83
- console.log();
84
- console.log(chalk.bgRed.white.bold(" ERROR ") +
85
- " " +
86
- chalk.red("Missing required option: --org or --base-url"));
87
- console.log();
88
- console.log(chalk.dim(" Usage:"));
89
- console.log(` ${chalk.cyan("npx @mcp-s/skills")} ${chalk.yellow("--org <org>")} ${chalk.dim("[options]")}`);
90
- console.log(` ${chalk.cyan("npx @mcp-s/skills")} ${chalk.yellow("--base-url <url>")} ${chalk.dim("[options]")}`);
91
- console.log();
92
- process.exit(1);
93
- }
94
- if (options.all) {
95
- options.yes = true;
96
- options.global = true;
97
- }
98
- console.log();
99
- p.intro(chalk.bgCyan.black(" MCP-S Skills "));
100
- const spinner = p.spinner();
101
- const baseUrl = buildBaseUrl(options.org, options.baseUrl);
102
- try {
103
- // Get or create authentication
104
- spinner.start("Checking authentication...");
105
- let auth = await getOrCreateAuth(options.org);
106
- spinner.stop(auth.isNew
107
- ? "New authentication created"
108
- : "Using existing authentication");
109
- // Try to fetch skills
110
- spinner.start("Fetching skills from server...");
111
- let skills;
112
- let needsAuth = false;
113
- try {
114
- skills = await fetchSkills(baseUrl, auth);
115
- }
116
- catch (error) {
117
- if (error &&
118
- typeof error === "object" &&
119
- "status" in error &&
120
- error.status === 401) {
121
- needsAuth = true;
122
- skills = [];
123
- }
124
- else {
125
- throw error;
126
- }
127
- }
128
- // Handle authentication if needed
129
- if (needsAuth || auth.isNew) {
130
- spinner.stop("Authentication required");
131
- p.log.info("You need to authenticate to access skills.");
132
- // Generate new token and authenticate
133
- const newAuth = await refreshToken(options.org);
134
- auth = Object.assign(Object.assign({}, newAuth), { isNew: true });
135
- const { url, opened } = await authenticate({
136
- baseUrl,
137
- userAccessKey: auth.userAccessKey,
138
- userOTT: auth.userOTT,
139
- openBrowser: true,
140
- });
141
- if (opened) {
142
- p.log.info("Opening authentication page in your browser...");
143
- }
144
- else {
145
- p.log.info("Please open this URL in your browser to authenticate:");
146
- p.log.message(chalk.cyan(url));
147
- }
148
- // Wait for user confirmation
149
- const confirmed = await p.confirm({
150
- message: "Have you completed authentication in the browser?",
151
- });
152
- if (p.isCancel(confirmed) || !confirmed) {
153
- p.cancel("Authentication cancelled");
154
- process.exit(0);
155
- }
156
- // Save the auth config
157
- await saveAuthConfig({
158
- userAccessKey: auth.userAccessKey,
159
- userOTT: auth.userOTT,
160
- org: options.org,
161
- });
162
- // Retry fetching skills
163
- spinner.start("Fetching skills from server...");
164
- try {
165
- skills = await fetchSkills(baseUrl, auth);
166
- }
167
- catch (error) {
168
- if (error &&
169
- typeof error === "object" &&
170
- "status" in error &&
171
- error.status === 401) {
172
- spinner.stop(chalk.red("Authentication failed"));
173
- p.log.error("Authentication failed. Please try again or check your credentials.");
174
- process.exit(1);
175
- }
176
- throw error;
177
- }
178
- }
179
- spinner.stop(`Found ${skills.length} skill${skills.length !== 1 ? "s" : ""}`);
180
- if (skills.length === 0) {
181
- p.log.warn("No skills available on this server.");
182
- p.outro(chalk.yellow("Nothing to install."));
183
- process.exit(0);
184
- }
185
- // List mode
186
- if (options.list) {
187
- console.log();
188
- p.log.step(chalk.bold("Available Skills"));
189
- for (const skill of skills) {
190
- p.log.message(` ${chalk.cyan(skill.slug || skill.name)} - ${chalk.dim(skill.description)}`);
191
- }
192
- console.log();
193
- p.outro("Run without --list to install");
194
- process.exit(0);
195
- }
196
- // Select skills
197
- let selectedSkills;
198
- if (options.skill && options.skill.length > 0) {
199
- // Filter by specified skill slugs
200
- selectedSkills = skills.filter((s) => options.skill.includes(s.slug) || options.skill.includes(s.name));
201
- if (selectedSkills.length === 0) {
202
- p.log.error(`No matching skills found for: ${options.skill.join(", ")}`);
203
- p.log.info(`Available skills: ${skills.map((s) => s.slug || s.name).join(", ")}`);
204
- process.exit(1);
205
- }
206
- }
207
- else if (options.all) {
208
- selectedSkills = skills;
209
- }
210
- else {
211
- // Interactive selection
212
- const skillChoices = skills.map((s) => ({
213
- value: s,
214
- label: s.name,
215
- hint: s.description,
216
- }));
217
- const selected = await p.multiselect({
218
- message: "Select skills to install",
219
- options: skillChoices,
220
- required: true,
221
- });
222
- if (p.isCancel(selected)) {
223
- p.cancel("Installation cancelled");
224
- process.exit(0);
225
- }
226
- selectedSkills = selected;
227
- }
228
- p.log.info(`Selected ${selectedSkills.length} skill${selectedSkills.length !== 1 ? "s" : ""}: ${chalk.cyan(selectedSkills.map((s) => s.slug || s.name).join(", "))}`);
229
- // Select agents
230
- let targetAgents;
231
- const validAgents = Object.keys(agents);
232
- if (options.agent && options.agent.length > 0) {
233
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
234
- if (invalidAgents.length > 0) {
235
- p.log.error(`Invalid agents: ${invalidAgents.join(", ")}`);
236
- p.log.info(`Valid agents: ${validAgents.join(", ")}`);
237
- process.exit(1);
238
- }
239
- targetAgents = options.agent;
240
- }
241
- else {
242
- spinner.start("Detecting installed agents...");
243
- const installedAgents = await detectInstalledAgents();
244
- spinner.stop(`Detected ${installedAgents.length} agent${installedAgents.length !== 1 ? "s" : ""}`);
245
- if (installedAgents.length === 0) {
246
- if (options.yes) {
247
- targetAgents = validAgents;
248
- p.log.info("Installing to all agents (none detected)");
249
- }
250
- else {
251
- p.log.warn("No coding agents detected. You can still install skills.");
252
- const allAgentChoices = Object.entries(agents).map(([key, config]) => ({
253
- value: key,
254
- label: config.displayName,
255
- }));
256
- const selected = await p.multiselect({
257
- message: "Select agents to install skills to",
258
- options: allAgentChoices,
259
- required: true,
260
- initialValues: Object.keys(agents),
261
- });
262
- if (p.isCancel(selected)) {
263
- p.cancel("Installation cancelled");
264
- process.exit(0);
265
- }
266
- targetAgents = selected;
267
- }
268
- }
269
- else if (installedAgents.length === 1 || options.yes) {
270
- targetAgents = installedAgents;
271
- if (installedAgents.length === 1) {
272
- const firstAgent = installedAgents[0];
273
- p.log.info(`Installing to: ${chalk.cyan(agents[firstAgent].displayName)}`);
274
- }
275
- else {
276
- p.log.info(`Installing to: ${installedAgents.map((a) => chalk.cyan(agents[a].displayName)).join(", ")}`);
277
- }
278
- }
279
- else {
280
- const agentChoices = installedAgents.map((a) => ({
281
- value: a,
282
- label: agents[a].displayName,
283
- hint: `${options.global ? agents[a].globalSkillsDir : agents[a].skillsDir}`,
284
- }));
285
- const selected = await p.multiselect({
286
- message: "Select agents to install skills to",
287
- options: agentChoices,
288
- required: true,
289
- initialValues: installedAgents,
290
- });
291
- if (p.isCancel(selected)) {
292
- p.cancel("Installation cancelled");
293
- process.exit(0);
294
- }
295
- targetAgents = selected;
296
- }
297
- }
298
- // Installation scope
299
- let installGlobally = (_a = options.global) !== null && _a !== void 0 ? _a : false;
300
- if (options.global === undefined && !options.yes) {
301
- const scope = await p.select({
302
- message: "Installation scope",
303
- options: [
304
- {
305
- value: false,
306
- label: "Project",
307
- hint: "Install in current directory (committed with your project)",
308
- },
309
- {
310
- value: true,
311
- label: "Global",
312
- hint: "Install in home directory (available across all projects)",
313
- },
314
- ],
315
- });
316
- if (p.isCancel(scope)) {
317
- p.cancel("Installation cancelled");
318
- process.exit(0);
319
- }
320
- installGlobally = scope;
321
- }
322
- // Installation method
323
- let installMode = "symlink";
324
- if (!options.yes) {
325
- const modeChoice = await p.select({
326
- message: "Installation method",
327
- options: [
328
- {
329
- value: "symlink",
330
- label: "Symlink (Recommended)",
331
- hint: "Single source of truth, easy updates",
332
- },
333
- {
334
- value: "copy",
335
- label: "Copy to all agents",
336
- hint: "Independent copies for each agent",
337
- },
338
- ],
339
- });
340
- if (p.isCancel(modeChoice)) {
341
- p.cancel("Installation cancelled");
342
- process.exit(0);
343
- }
344
- installMode = modeChoice;
345
- }
346
- // Check for existing installations
347
- const cwd = process.cwd();
348
- const overwriteStatus = new Map();
349
- for (const skill of selectedSkills) {
350
- const skillStatus = new Map();
351
- for (const agent of targetAgents) {
352
- skillStatus.set(agent, await isSkillInstalled(skill.slug || skill.name, agent, {
353
- global: installGlobally,
354
- }));
355
- }
356
- overwriteStatus.set(skill.slug || skill.name, skillStatus);
357
- }
358
- // Show summary
359
- const summaryLines = [];
360
- const agentNames = targetAgents.map((a) => agents[a].displayName);
361
- for (const skill of selectedSkills) {
362
- const skillName = skill.slug || skill.name;
363
- if (installMode === "symlink") {
364
- const canonicalPath = getCanonicalPath(skillName, {
365
- global: installGlobally,
366
- });
367
- const shortCanonical = shortenPath(canonicalPath, cwd);
368
- summaryLines.push(`${chalk.cyan(skillName)}`);
369
- summaryLines.push(` ${chalk.dim(shortCanonical)}`);
370
- summaryLines.push(` ${chalk.dim("symlink →")} ${formatList(agentNames)}`);
371
- }
372
- else {
373
- summaryLines.push(`${chalk.cyan(skillName)}`);
374
- summaryLines.push(` ${chalk.dim("copy →")} ${formatList(agentNames)}`);
375
- }
376
- const skillOverwrites = overwriteStatus.get(skillName);
377
- const overwriteAgents = targetAgents
378
- .filter((a) => skillOverwrites === null || skillOverwrites === void 0 ? void 0 : skillOverwrites.get(a))
379
- .map((a) => agents[a].displayName);
380
- if (overwriteAgents.length > 0) {
381
- summaryLines.push(` ${chalk.yellow("overwrites:")} ${formatList(overwriteAgents)}`);
382
- }
383
- }
384
- console.log();
385
- p.note(summaryLines.join("\n"), "Installation Summary");
386
- // Confirm
387
- if (!options.yes) {
388
- const confirmed = await p.confirm({
389
- message: "Proceed with installation?",
390
- });
391
- if (p.isCancel(confirmed) || !confirmed) {
392
- p.cancel("Installation cancelled");
393
- process.exit(0);
394
- }
395
- }
396
- // Install
397
- spinner.start("Installing skills...");
398
- const results = [];
399
- for (const skill of selectedSkills) {
400
- for (const agent of targetAgents) {
401
- const result = await installSkillForAgent(skill, agent, {
402
- global: installGlobally,
403
- mode: installMode,
404
- });
405
- results.push(Object.assign({ skill: skill.slug || skill.name, agent: agents[agent].displayName }, result));
406
- }
407
- }
408
- spinner.stop("Installation complete");
409
- console.log();
410
- const successful = results.filter((r) => r.success);
411
- const failed = results.filter((r) => !r.success);
412
- if (successful.length > 0) {
413
- const resultLines = [];
414
- // Group by skill
415
- const skillGroups = new Map();
416
- for (const r of successful) {
417
- if (!skillGroups.has(r.skill)) {
418
- skillGroups.set(r.skill, []);
419
- }
420
- skillGroups.get(r.skill).push(r);
421
- }
422
- for (const [skillName, skillResults] of skillGroups) {
423
- const firstResult = skillResults[0];
424
- if (firstResult.mode === "copy") {
425
- resultLines.push(`${chalk.green("✓")} ${skillName} ${chalk.dim("(copied)")}`);
426
- for (const r of skillResults) {
427
- const shortPath = shortenPath(r.path, cwd);
428
- resultLines.push(` ${chalk.dim("→")} ${r.agent}: ${shortPath}`);
429
- }
430
- }
431
- else {
432
- if (firstResult.canonicalPath) {
433
- const shortPath = shortenPath(firstResult.canonicalPath, cwd);
434
- resultLines.push(`${chalk.green("✓")} ${skillName}`);
435
- resultLines.push(` ${chalk.dim(shortPath)}`);
436
- }
437
- else {
438
- resultLines.push(`${chalk.green("✓")} ${skillName}`);
439
- }
440
- const symlinked = skillResults
441
- .filter((r) => !r.symlinkFailed)
442
- .map((r) => r.agent);
443
- const copied = skillResults
444
- .filter((r) => r.symlinkFailed)
445
- .map((r) => r.agent);
446
- if (symlinked.length > 0) {
447
- resultLines.push(` ${chalk.dim("symlink →")} ${formatList(symlinked)}`);
448
- }
449
- if (copied.length > 0) {
450
- resultLines.push(` ${chalk.yellow("copied →")} ${formatList(copied)}`);
451
- }
452
- }
453
- }
454
- const skillCount = skillGroups.size;
455
- const agentCount = new Set(successful.map((r) => r.agent)).size;
456
- const title = chalk.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""} to ${agentCount} agent${agentCount !== 1 ? "s" : ""}`);
457
- p.note(resultLines.join("\n"), title);
458
- const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
459
- if (symlinkFailures.length > 0) {
460
- const copiedAgentNames = [
461
- ...new Set(symlinkFailures.map((r) => r.agent)),
462
- ];
463
- p.log.warn(chalk.yellow(`Symlinks failed for: ${formatList(copiedAgentNames)}`));
464
- p.log.message(chalk.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
465
- }
466
- }
467
- if (failed.length > 0) {
468
- console.log();
469
- p.log.error(chalk.red(`Failed to install ${failed.length}`));
470
- for (const r of failed) {
471
- p.log.message(` ${chalk.red("✗")} ${r.skill} → ${r.agent}: ${chalk.dim(r.error)}`);
472
- }
473
- }
474
- console.log();
475
- p.outro(chalk.green("Done!"));
476
- }
477
- catch (error) {
478
- spinner.stop(chalk.red("Error"));
479
- p.log.error(chalk.red(error instanceof Error ? error.message : "Unknown error"));
480
- process.exit(1);
481
- }
482
- }
package/dist/installer.js DELETED
@@ -1,188 +0,0 @@
1
- import { mkdir, access, symlink, lstat, rm, readlink, writeFile, } from "fs/promises";
2
- import { join, normalize, resolve, sep, relative } from "path";
3
- import { homedir, platform } from "os";
4
- import { agents } from "./agents.js";
5
- const AGENTS_DIR = ".agents";
6
- const SKILLS_SUBDIR = "skills";
7
- function sanitizeName(name) {
8
- let sanitized = name.replace(/[\/\\:\0]/g, "");
9
- sanitized = sanitized.replace(/^[.\s]+|[.\s]+$/g, "");
10
- sanitized = sanitized.replace(/^\.+/, "");
11
- if (!sanitized || sanitized.length === 0) {
12
- sanitized = "unnamed-skill";
13
- }
14
- if (sanitized.length > 255) {
15
- sanitized = sanitized.substring(0, 255);
16
- }
17
- return sanitized;
18
- }
19
- function isPathSafe(basePath, targetPath) {
20
- const normalizedBase = normalize(resolve(basePath));
21
- const normalizedTarget = normalize(resolve(targetPath));
22
- return (normalizedTarget.startsWith(normalizedBase + sep) ||
23
- normalizedTarget === normalizedBase);
24
- }
25
- function getCanonicalSkillsDir(global, cwd) {
26
- const baseDir = global ? homedir() : cwd || process.cwd();
27
- return join(baseDir, AGENTS_DIR, SKILLS_SUBDIR);
28
- }
29
- async function createSymlink(target, linkPath) {
30
- try {
31
- try {
32
- const stats = await lstat(linkPath);
33
- if (stats.isSymbolicLink()) {
34
- const existingTarget = await readlink(linkPath);
35
- if (resolve(existingTarget) === resolve(target)) {
36
- return true;
37
- }
38
- await rm(linkPath);
39
- }
40
- else {
41
- await rm(linkPath, { recursive: true });
42
- }
43
- }
44
- catch (err) {
45
- if (err &&
46
- typeof err === "object" &&
47
- "code" in err &&
48
- err.code === "ELOOP") {
49
- try {
50
- await rm(linkPath, { force: true });
51
- }
52
- catch (_a) {
53
- // Ignore cleanup errors
54
- }
55
- }
56
- }
57
- const linkDir = join(linkPath, "..");
58
- await mkdir(linkDir, { recursive: true });
59
- const relativePath = relative(linkDir, target);
60
- const symlinkType = platform() === "win32" ? "junction" : undefined;
61
- await symlink(relativePath, linkPath, symlinkType);
62
- return true;
63
- }
64
- catch (_b) {
65
- return false;
66
- }
67
- }
68
- export async function installSkillForAgent(skill, agentType, options = {}) {
69
- var _a, _b;
70
- const agent = agents[agentType];
71
- const isGlobal = (_a = options.global) !== null && _a !== void 0 ? _a : false;
72
- const cwd = options.cwd || process.cwd();
73
- const skillName = sanitizeName(skill.slug || skill.name);
74
- const canonicalBase = getCanonicalSkillsDir(isGlobal, cwd);
75
- const canonicalDir = join(canonicalBase, skillName);
76
- const agentBase = isGlobal
77
- ? agent.globalSkillsDir
78
- : join(cwd, agent.skillsDir);
79
- const agentDir = join(agentBase, skillName);
80
- const installMode = (_b = options.mode) !== null && _b !== void 0 ? _b : "symlink";
81
- if (!isPathSafe(canonicalBase, canonicalDir)) {
82
- return {
83
- success: false,
84
- path: agentDir,
85
- mode: installMode,
86
- error: "Invalid skill name: potential path traversal detected",
87
- };
88
- }
89
- if (!isPathSafe(agentBase, agentDir)) {
90
- return {
91
- success: false,
92
- path: agentDir,
93
- mode: installMode,
94
- error: "Invalid skill name: potential path traversal detected",
95
- };
96
- }
97
- try {
98
- if (installMode === "copy") {
99
- await mkdir(agentDir, { recursive: true });
100
- const skillMdPath = join(agentDir, "SKILL.md");
101
- await writeFile(skillMdPath, skill.content, "utf-8");
102
- return {
103
- success: true,
104
- path: agentDir,
105
- mode: "copy",
106
- };
107
- }
108
- // Symlink mode
109
- await mkdir(canonicalDir, { recursive: true });
110
- const skillMdPath = join(canonicalDir, "SKILL.md");
111
- await writeFile(skillMdPath, skill.content, "utf-8");
112
- const symlinkCreated = await createSymlink(canonicalDir, agentDir);
113
- if (!symlinkCreated) {
114
- // Fallback to copy
115
- try {
116
- await rm(agentDir, { recursive: true, force: true });
117
- }
118
- catch (_c) {
119
- // Ignore
120
- }
121
- await mkdir(agentDir, { recursive: true });
122
- const agentSkillMdPath = join(agentDir, "SKILL.md");
123
- await writeFile(agentSkillMdPath, skill.content, "utf-8");
124
- return {
125
- success: true,
126
- path: agentDir,
127
- canonicalPath: canonicalDir,
128
- mode: "symlink",
129
- symlinkFailed: true,
130
- };
131
- }
132
- return {
133
- success: true,
134
- path: agentDir,
135
- canonicalPath: canonicalDir,
136
- mode: "symlink",
137
- };
138
- }
139
- catch (error) {
140
- return {
141
- success: false,
142
- path: agentDir,
143
- mode: installMode,
144
- error: error instanceof Error ? error.message : "Unknown error",
145
- };
146
- }
147
- }
148
- export async function isSkillInstalled(skillName, agentType, options = {}) {
149
- const agent = agents[agentType];
150
- const sanitized = sanitizeName(skillName);
151
- const targetBase = options.global
152
- ? agent.globalSkillsDir
153
- : join(options.cwd || process.cwd(), agent.skillsDir);
154
- const skillDir = join(targetBase, sanitized);
155
- if (!isPathSafe(targetBase, skillDir)) {
156
- return false;
157
- }
158
- try {
159
- await access(skillDir);
160
- return true;
161
- }
162
- catch (_a) {
163
- return false;
164
- }
165
- }
166
- export function getInstallPath(skillName, agentType, options = {}) {
167
- const agent = agents[agentType];
168
- const cwd = options.cwd || process.cwd();
169
- const sanitized = sanitizeName(skillName);
170
- const targetBase = options.global
171
- ? agent.globalSkillsDir
172
- : join(cwd, agent.skillsDir);
173
- const installPath = join(targetBase, sanitized);
174
- if (!isPathSafe(targetBase, installPath)) {
175
- throw new Error("Invalid skill name: potential path traversal detected");
176
- }
177
- return installPath;
178
- }
179
- export function getCanonicalPath(skillName, options = {}) {
180
- var _a;
181
- const sanitized = sanitizeName(skillName);
182
- const canonicalBase = getCanonicalSkillsDir((_a = options.global) !== null && _a !== void 0 ? _a : false, options.cwd);
183
- const canonicalPath = join(canonicalBase, sanitized);
184
- if (!isPathSafe(canonicalBase, canonicalPath)) {
185
- throw new Error("Invalid skill name: potential path traversal detected");
186
- }
187
- return canonicalPath;
188
- }
package/dist/types.js DELETED
@@ -1 +0,0 @@
1
- export {};