@empjs/skill 1.0.5

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.cjs ADDED
@@ -0,0 +1,1063 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // node_modules/tsup/assets/cjs_shims.js
27
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
28
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
+
30
+ // src/index.ts
31
+ var import_commander = require("commander");
32
+ var import_fs = require("fs");
33
+ var import_url = require("url");
34
+ var import_path = require("path");
35
+
36
+ // src/commands/install.ts
37
+ var import_node_child_process2 = require("child_process");
38
+ var import_node_util2 = require("util");
39
+ var import_node_fs4 = __toESM(require("fs"), 1);
40
+ var import_node_path5 = __toESM(require("path"), 1);
41
+ var import_node_os2 = __toESM(require("os"), 1);
42
+
43
+ // src/utils/logger.ts
44
+ var import_chalk = __toESM(require("chalk"), 1);
45
+ var import_ora = __toESM(require("ora"), 1);
46
+ var Logger = class {
47
+ spinner = null;
48
+ info(message) {
49
+ if (this.spinner) this.spinner.stop();
50
+ console.log(import_chalk.default.blue("\u2139"), message);
51
+ }
52
+ success(message) {
53
+ if (this.spinner) this.spinner.stop();
54
+ console.log(import_chalk.default.green("\u2713"), message);
55
+ }
56
+ warn(message) {
57
+ if (this.spinner) this.spinner.stop();
58
+ console.log(import_chalk.default.yellow("\u26A0"), message);
59
+ }
60
+ error(message) {
61
+ if (this.spinner) this.spinner.stop();
62
+ console.log(import_chalk.default.red("\u2717"), message);
63
+ }
64
+ start(message) {
65
+ if (this.spinner) this.spinner.stop();
66
+ this.spinner = (0, import_ora.default)(message).start();
67
+ return this.spinner;
68
+ }
69
+ stopSpinner() {
70
+ if (this.spinner) {
71
+ this.spinner.stop();
72
+ this.spinner = null;
73
+ }
74
+ }
75
+ /**
76
+ * Update spinner text without stopping it
77
+ */
78
+ updateSpinner(message) {
79
+ if (this.spinner) {
80
+ this.spinner.text = message;
81
+ }
82
+ }
83
+ /**
84
+ * Log info without stopping spinner (for background info)
85
+ */
86
+ infoWithoutStop(message) {
87
+ if (this.spinner) {
88
+ const currentText = this.spinner.text;
89
+ this.spinner.stop();
90
+ console.log(import_chalk.default.blue("\u2139"), message);
91
+ this.spinner.start(currentText);
92
+ } else {
93
+ console.log(import_chalk.default.blue("\u2139"), message);
94
+ }
95
+ }
96
+ };
97
+ var logger = new Logger();
98
+
99
+ // src/utils/paths.ts
100
+ var import_node_fs = __toESM(require("fs"), 1);
101
+ var import_node_path2 = __toESM(require("path"), 1);
102
+
103
+ // src/config/agents.ts
104
+ var import_node_os = __toESM(require("os"), 1);
105
+ var import_node_path = __toESM(require("path"), 1);
106
+ var HOME = import_node_os.default.homedir();
107
+ function getAgentSkillsDirs(agent, cwd) {
108
+ if (agent.skillsDirs) {
109
+ if (typeof agent.skillsDirs === "function") {
110
+ return agent.skillsDirs(cwd);
111
+ }
112
+ return agent.skillsDirs;
113
+ }
114
+ if (agent.skillsDir) {
115
+ return [agent.skillsDir];
116
+ }
117
+ return [];
118
+ }
119
+ var AGENTS = [
120
+ {
121
+ name: "claude",
122
+ displayName: "Claude Code",
123
+ skillsDir: import_node_path.default.join(HOME, ".claude", "skills"),
124
+ enabled: true
125
+ },
126
+ {
127
+ name: "cursor",
128
+ displayName: "Cursor",
129
+ skillsDir: import_node_path.default.join(HOME, ".cursor", "skills"),
130
+ enabled: true
131
+ },
132
+ {
133
+ name: "windsurf",
134
+ displayName: "Windsurf",
135
+ skillsDir: import_node_path.default.join(HOME, ".windsurf", "skills"),
136
+ enabled: true
137
+ },
138
+ {
139
+ name: "cline",
140
+ displayName: "Cline",
141
+ skillsDir: import_node_path.default.join(HOME, ".cline", "skills"),
142
+ enabled: true
143
+ },
144
+ {
145
+ name: "gemini",
146
+ displayName: "Gemini Code",
147
+ skillsDir: import_node_path.default.join(HOME, ".gemini", "skills"),
148
+ enabled: true
149
+ },
150
+ {
151
+ name: "copilot",
152
+ displayName: "GitHub Copilot",
153
+ skillsDir: import_node_path.default.join(HOME, ".copilot", "skills"),
154
+ enabled: true
155
+ },
156
+ {
157
+ name: "opencode",
158
+ displayName: "OpenCode",
159
+ skillsDir: import_node_path.default.join(HOME, ".opencode", "skills"),
160
+ enabled: true
161
+ },
162
+ {
163
+ name: "antigravity",
164
+ displayName: "Antigravity",
165
+ skillsDirs: (cwd) => {
166
+ const dirs = [];
167
+ dirs.push(import_node_path.default.join(HOME, ".gemini", "antigravity", "skills"));
168
+ if (cwd) {
169
+ dirs.push(import_node_path.default.join(cwd, ".agent", "skills"));
170
+ }
171
+ if (cwd) {
172
+ dirs.push(import_node_path.default.join(cwd, ".shared", "skills"));
173
+ }
174
+ return dirs;
175
+ },
176
+ enabled: true
177
+ },
178
+ {
179
+ name: "kiro",
180
+ displayName: "Kiro",
181
+ skillsDir: import_node_path.default.join(HOME, ".kiro", "skills"),
182
+ enabled: true
183
+ },
184
+ {
185
+ name: "codex",
186
+ displayName: "Codex CLI",
187
+ skillsDir: import_node_path.default.join(HOME, ".codex", "skills"),
188
+ enabled: true
189
+ },
190
+ {
191
+ name: "qoder",
192
+ displayName: "Qoder",
193
+ skillsDir: import_node_path.default.join(HOME, ".qoder", "skills"),
194
+ enabled: true
195
+ },
196
+ {
197
+ name: "roocode",
198
+ displayName: "Roo Code",
199
+ skillsDir: import_node_path.default.join(HOME, ".roo", "skills"),
200
+ enabled: true
201
+ },
202
+ {
203
+ name: "trae",
204
+ displayName: "Trae",
205
+ skillsDir: import_node_path.default.join(HOME, ".trae", "skills"),
206
+ enabled: true
207
+ },
208
+ {
209
+ name: "continue",
210
+ displayName: "Continue",
211
+ skillsDir: import_node_path.default.join(HOME, ".continue", "skills"),
212
+ enabled: true
213
+ }
214
+ ];
215
+ var SHARED_SKILLS_DIR = import_node_path.default.join(HOME, ".emp-agent", "skills");
216
+ var CONFIG_FILE = import_node_path.default.join(HOME, ".emp-agent", "config.json");
217
+ var CACHE_DIR = import_node_path.default.join(HOME, ".emp-agent", "cache");
218
+
219
+ // src/utils/paths.ts
220
+ function getSharedSkillPath(skillName) {
221
+ return import_node_path2.default.join(SHARED_SKILLS_DIR, skillName);
222
+ }
223
+ function getAgentSkillPaths(agentName, skillName, cwd) {
224
+ const agent = AGENTS.find((a) => a.name === agentName);
225
+ if (!agent) {
226
+ throw new Error(`Unknown agent: ${agentName}`);
227
+ }
228
+ const skillsDirs = getAgentSkillsDirs(agent, cwd);
229
+ return skillsDirs.map((dir) => import_node_path2.default.join(dir, skillName));
230
+ }
231
+ function ensureSharedDir() {
232
+ if (!import_node_fs.default.existsSync(SHARED_SKILLS_DIR)) {
233
+ import_node_fs.default.mkdirSync(SHARED_SKILLS_DIR, { recursive: true });
234
+ }
235
+ }
236
+ function detectInstalledAgents(cwd) {
237
+ return AGENTS.filter((agent) => {
238
+ try {
239
+ const skillsDirs = getAgentSkillsDirs(agent, cwd);
240
+ return skillsDirs.some((dir) => {
241
+ return import_node_fs.default.existsSync(dir) || import_node_fs.default.existsSync(import_node_path2.default.dirname(dir));
242
+ });
243
+ } catch {
244
+ return false;
245
+ }
246
+ });
247
+ }
248
+ function extractSkillName(nameOrPath) {
249
+ if (nameOrPath.startsWith("@")) {
250
+ const parts = nameOrPath.split("/");
251
+ if (parts.length === 2) {
252
+ const scope = parts[0].slice(1);
253
+ const name = parts[1].replace("-skill", "");
254
+ return `${scope}-${name}`;
255
+ }
256
+ }
257
+ return import_node_path2.default.basename(nameOrPath);
258
+ }
259
+
260
+ // src/utils/symlink.ts
261
+ var import_node_fs2 = __toESM(require("fs"), 1);
262
+ var import_node_path3 = __toESM(require("path"), 1);
263
+ function createSymlink(skillName, agent, cwd) {
264
+ const source = getSharedSkillPath(skillName);
265
+ if (!import_node_fs2.default.existsSync(source)) {
266
+ logger.error(`Skill not found: ${source}`);
267
+ return false;
268
+ }
269
+ const targets = getAgentSkillPaths(agent.name, skillName, cwd);
270
+ let successCount = 0;
271
+ for (const target of targets) {
272
+ const targetDir = import_node_path3.default.dirname(target);
273
+ if (!import_node_fs2.default.existsSync(targetDir)) {
274
+ try {
275
+ import_node_fs2.default.mkdirSync(targetDir, { recursive: true });
276
+ } catch (error) {
277
+ logger.error(`Failed to create directory ${targetDir}: ${error.message}`);
278
+ continue;
279
+ }
280
+ }
281
+ if (import_node_fs2.default.existsSync(target)) {
282
+ try {
283
+ const stats = import_node_fs2.default.lstatSync(target);
284
+ if (stats.isSymbolicLink()) {
285
+ import_node_fs2.default.unlinkSync(target);
286
+ } else {
287
+ logger.warn(`Target exists but is not a symlink, skipping: ${target}`);
288
+ continue;
289
+ }
290
+ } catch (error) {
291
+ logger.error(`Failed to remove existing target: ${error.message}`);
292
+ continue;
293
+ }
294
+ }
295
+ try {
296
+ import_node_fs2.default.symlinkSync(source, target, "dir");
297
+ successCount++;
298
+ } catch (error) {
299
+ logger.error(`Failed to create symlink at ${target}: ${error.message}`);
300
+ }
301
+ }
302
+ if (successCount > 0) {
303
+ const dirsInfo = targets.length > 1 ? ` (${successCount}/${targets.length} dirs)` : "";
304
+ logger.success(`\u2713 ${agent.displayName}${dirsInfo}`);
305
+ return true;
306
+ }
307
+ return false;
308
+ }
309
+ function removeSymlink(skillName, agent, cwd) {
310
+ const targets = getAgentSkillPaths(agent.name, skillName, cwd);
311
+ let removedCount = 0;
312
+ for (const target of targets) {
313
+ if (!import_node_fs2.default.existsSync(target)) {
314
+ continue;
315
+ }
316
+ try {
317
+ const stats = import_node_fs2.default.lstatSync(target);
318
+ if (stats.isSymbolicLink()) {
319
+ import_node_fs2.default.unlinkSync(target);
320
+ removedCount++;
321
+ } else {
322
+ logger.warn(`Not a symlink: ${target}`);
323
+ }
324
+ } catch (error) {
325
+ logger.error(`Failed to remove symlink at ${target}: ${error.message}`);
326
+ }
327
+ }
328
+ if (removedCount > 0) {
329
+ const dirsInfo = targets.length > 1 ? ` (${removedCount}/${targets.length} dirs)` : "";
330
+ logger.success(`Unlinked from ${agent.displayName}${dirsInfo}`);
331
+ return true;
332
+ }
333
+ return false;
334
+ }
335
+ function isSymlink(filePath) {
336
+ try {
337
+ const stats = import_node_fs2.default.lstatSync(filePath);
338
+ return stats.isSymbolicLink();
339
+ } catch {
340
+ return false;
341
+ }
342
+ }
343
+ function readSymlink(filePath) {
344
+ try {
345
+ return import_node_fs2.default.readlinkSync(filePath);
346
+ } catch {
347
+ return null;
348
+ }
349
+ }
350
+
351
+ // src/utils/registry.ts
352
+ var import_node_child_process = require("child_process");
353
+ var import_node_util = require("util");
354
+ var import_node_fs3 = __toESM(require("fs"), 1);
355
+ var import_node_path4 = __toESM(require("path"), 1);
356
+ var execAsync = (0, import_node_util.promisify)(import_node_child_process.exec);
357
+ function findNpmrc(startDir) {
358
+ let currentDir = import_node_path4.default.resolve(startDir);
359
+ while (currentDir !== import_node_path4.default.dirname(currentDir)) {
360
+ const npmrcPath = import_node_path4.default.join(currentDir, ".npmrc");
361
+ if (import_node_fs3.default.existsSync(npmrcPath)) {
362
+ return npmrcPath;
363
+ }
364
+ currentDir = import_node_path4.default.dirname(currentDir);
365
+ }
366
+ return null;
367
+ }
368
+ function parseNpmrc(npmrcPath) {
369
+ try {
370
+ const content = import_node_fs3.default.readFileSync(npmrcPath, "utf-8");
371
+ const lines = content.split("\n");
372
+ for (const line of lines) {
373
+ const trimmed = line.trim();
374
+ if (!trimmed || trimmed.startsWith("#")) {
375
+ continue;
376
+ }
377
+ const registryMatch = trimmed.match(/^(?:@[^:]+:)?registry\s*=\s*(.+)$/);
378
+ if (registryMatch) {
379
+ return registryMatch[1].trim();
380
+ }
381
+ }
382
+ } catch (error) {
383
+ }
384
+ return null;
385
+ }
386
+ async function getGlobalRegistry() {
387
+ try {
388
+ const { stdout } = await execAsync("npm config get registry");
389
+ const registry = stdout.trim();
390
+ if (registry && registry !== "undefined") {
391
+ return registry;
392
+ }
393
+ } catch (error) {
394
+ }
395
+ return null;
396
+ }
397
+ async function getRegistry(cwd = process.cwd()) {
398
+ const npmrcPath = findNpmrc(cwd);
399
+ if (npmrcPath) {
400
+ const registry = parseNpmrc(npmrcPath);
401
+ if (registry) {
402
+ return registry;
403
+ }
404
+ }
405
+ const globalRegistry = await getGlobalRegistry();
406
+ if (globalRegistry) {
407
+ return globalRegistry;
408
+ }
409
+ return "https://registry.npmjs.org/";
410
+ }
411
+
412
+ // src/utils/git.ts
413
+ function parseGitUrl(url) {
414
+ const githubPatterns = [
415
+ // https://github.com/owner/repo/tree/branch/path/to/dir
416
+ /^https?:\/\/github\.com\/([^\/]+)\/([^\/]+)\/tree\/([^\/]+)(?:\/(.+))?$/,
417
+ // https://github.com/owner/repo
418
+ /^https?:\/\/github\.com\/([^\/]+)\/([^\/]+)(?:\/)?$/,
419
+ // git@github.com:owner/repo.git
420
+ /^git@github\.com:([^\/]+)\/([^\/]+)(?:\.git)?$/
421
+ ];
422
+ const gitlabPatterns = [
423
+ // https://gitlab.com/owner/repo/-/tree/branch/path/to/dir
424
+ /^https?:\/\/gitlab\.com\/([^\/]+)\/([^\/]+)\/-\/tree\/([^\/]+)(?:\/(.+))?$/,
425
+ // https://gitlab.com/owner/repo
426
+ /^https?:\/\/gitlab\.com\/([^\/]+)\/([^\/]+)(?:\/)?$/,
427
+ // git@gitlab.com:owner/repo.git
428
+ /^git@gitlab\.com:([^\/]+)\/([^\/]+)(?:\.git)?$/
429
+ ];
430
+ for (let i = 0; i < githubPatterns.length; i++) {
431
+ const pattern = githubPatterns[i];
432
+ const match = url.match(pattern);
433
+ if (match) {
434
+ const owner = match[1];
435
+ const repo = match[2].replace(/\.git$/, "");
436
+ let branch;
437
+ let path7;
438
+ if (i === 0) {
439
+ branch = match[3];
440
+ path7 = match[4];
441
+ } else if (i === 1) {
442
+ branch = "main";
443
+ } else {
444
+ branch = "main";
445
+ }
446
+ const gitUrl = `https://github.com/${owner}/${repo}.git`;
447
+ return {
448
+ type: "github",
449
+ owner,
450
+ repo,
451
+ branch,
452
+ path: path7,
453
+ gitUrl,
454
+ installUrl: gitUrl
455
+ // Will be used for git clone
456
+ };
457
+ }
458
+ }
459
+ for (let i = 0; i < gitlabPatterns.length; i++) {
460
+ const pattern = gitlabPatterns[i];
461
+ const match = url.match(pattern);
462
+ if (match) {
463
+ const owner = match[1];
464
+ const repo = match[2].replace(/\.git$/, "");
465
+ let branch;
466
+ let path7;
467
+ if (i === 0) {
468
+ branch = match[3];
469
+ path7 = match[4];
470
+ } else if (i === 1) {
471
+ branch = "main";
472
+ } else {
473
+ branch = "main";
474
+ }
475
+ const gitUrl = `https://gitlab.com/${owner}/${repo}.git`;
476
+ return {
477
+ type: "gitlab",
478
+ owner,
479
+ repo,
480
+ branch,
481
+ path: path7,
482
+ gitUrl,
483
+ installUrl: gitUrl
484
+ // Will be used for git clone
485
+ };
486
+ }
487
+ }
488
+ if (url.startsWith("git+") || url.startsWith("git://")) {
489
+ return {
490
+ type: "other",
491
+ owner: "",
492
+ repo: "",
493
+ gitUrl: url.replace(/^git\+/, ""),
494
+ installUrl: url.startsWith("git+") ? url : `git+${url}`
495
+ };
496
+ }
497
+ return null;
498
+ }
499
+ function isGitUrl(str) {
500
+ return str.includes("github.com") || str.includes("gitlab.com") || str.startsWith("git@") || str.startsWith("git+") || str.startsWith("git://");
501
+ }
502
+
503
+ // src/commands/install.ts
504
+ var execAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.exec);
505
+ async function execWithTimeout(command, timeout = 12e4, env) {
506
+ let timeoutId = null;
507
+ const timeoutPromise = new Promise((_, reject) => {
508
+ timeoutId = setTimeout(() => {
509
+ reject(new Error(`Command timeout after ${timeout / 1e3}s`));
510
+ }, timeout);
511
+ });
512
+ try {
513
+ const execOptions = env ? { env } : {};
514
+ const result = await Promise.race([
515
+ execAsync2(command, execOptions),
516
+ timeoutPromise
517
+ ]);
518
+ if (timeoutId) {
519
+ clearTimeout(timeoutId);
520
+ }
521
+ return result;
522
+ } catch (error) {
523
+ if (timeoutId) {
524
+ clearTimeout(timeoutId);
525
+ }
526
+ throw error;
527
+ }
528
+ }
529
+ async function install(skillNameOrPath, options = {}) {
530
+ logger.info(`Installing skill: ${skillNameOrPath}`);
531
+ ensureSharedDir();
532
+ let skillPath;
533
+ let skillName;
534
+ if (isGitUrl(skillNameOrPath)) {
535
+ const gitInfo = parseGitUrl(skillNameOrPath);
536
+ if (!gitInfo) {
537
+ logger.error(`Invalid git URL: ${skillNameOrPath}`);
538
+ logger.info("Please ensure the URL is a valid GitHub or GitLab repository URL");
539
+ process.exit(1);
540
+ }
541
+ skillName = gitInfo.path ? extractSkillName(import_node_path5.default.basename(gitInfo.path)) : extractSkillName(gitInfo.repo);
542
+ const tempDir = import_node_path5.default.join("/tmp", `eskill-${Date.now()}`);
543
+ const cloneDir = import_node_path5.default.join(tempDir, "repo");
544
+ try {
545
+ const timeout = options.timeout || 12e4;
546
+ const spinner = logger.start(`Cloning ${gitInfo.gitUrl}...`);
547
+ logger.infoWithoutStop(`Repository: ${gitInfo.gitUrl}`);
548
+ if (gitInfo.branch) {
549
+ logger.infoWithoutStop(`Branch: ${gitInfo.branch}`);
550
+ }
551
+ if (gitInfo.path) {
552
+ logger.infoWithoutStop(`Path: ${gitInfo.path}`);
553
+ }
554
+ logger.infoWithoutStop(`Timeout: ${timeout / 1e3}s`);
555
+ import_node_fs4.default.mkdirSync(tempDir, { recursive: true });
556
+ const branchFlag = gitInfo.branch ? `-b ${gitInfo.branch}` : "";
557
+ const cloneCommand = branchFlag ? `git clone ${branchFlag} ${gitInfo.gitUrl} ${cloneDir} --depth 1 --quiet` : `git clone ${gitInfo.gitUrl} ${cloneDir} --depth 1 --quiet`;
558
+ try {
559
+ await execWithTimeout(cloneCommand, timeout);
560
+ spinner.succeed(`Repository cloned successfully`);
561
+ } catch (error) {
562
+ spinner.fail("Clone failed");
563
+ if (error.message.includes("timeout")) {
564
+ logger.error(`Clone timeout after ${timeout / 1e3} seconds`);
565
+ logger.info("");
566
+ logger.info("Possible reasons:");
567
+ logger.info(" - Slow network connection");
568
+ logger.info(" - Large repository size");
569
+ logger.info(" - Git server issues");
570
+ logger.info("");
571
+ logger.info(`Try again or increase timeout: eskill add ${skillNameOrPath} --timeout=300000`);
572
+ }
573
+ throw error;
574
+ }
575
+ if (gitInfo.path) {
576
+ skillPath = import_node_path5.default.join(cloneDir, gitInfo.path);
577
+ if (!import_node_fs4.default.existsSync(skillPath)) {
578
+ logger.error(`Path not found in repository: ${gitInfo.path}`);
579
+ logger.info(`Repository cloned to: ${cloneDir}`);
580
+ process.exit(1);
581
+ }
582
+ } else {
583
+ skillPath = cloneDir;
584
+ }
585
+ const skillMdPath = import_node_path5.default.join(skillPath, "SKILL.md");
586
+ if (!import_node_fs4.default.existsSync(skillMdPath)) {
587
+ logger.warn(`Warning: SKILL.md not found in ${skillPath}`);
588
+ logger.info("The directory may not be a valid skill package");
589
+ }
590
+ } catch (error) {
591
+ logger.error(`Failed to clone repository: ${error.message}`);
592
+ if (error.message.includes("timeout")) {
593
+ logger.error(`Clone timeout after 2 minutes`);
594
+ logger.info("This might be due to:");
595
+ logger.info(" - Slow network connection");
596
+ logger.info(" - Large repository size");
597
+ logger.info(" - Git server issues");
598
+ logger.info(`
599
+ Try again or check your network connection`);
600
+ }
601
+ logger.info(`
602
+ Tried to clone: ${gitInfo.gitUrl}`);
603
+ if (gitInfo.branch) {
604
+ logger.info(`Branch: ${gitInfo.branch}`);
605
+ }
606
+ process.exit(1);
607
+ }
608
+ } else if (options.link || import_node_fs4.default.existsSync(skillNameOrPath)) {
609
+ skillPath = import_node_path5.default.resolve(skillNameOrPath);
610
+ if (!import_node_fs4.default.existsSync(skillPath)) {
611
+ logger.error(`Path not found: ${skillPath}`);
612
+ process.exit(1);
613
+ }
614
+ const pkgPath = import_node_path5.default.join(skillPath, "package.json");
615
+ if (import_node_fs4.default.existsSync(pkgPath)) {
616
+ try {
617
+ const pkg = JSON.parse(import_node_fs4.default.readFileSync(pkgPath, "utf-8"));
618
+ skillName = extractSkillName(pkg.name);
619
+ } catch {
620
+ skillName = extractSkillName(import_node_path5.default.basename(skillPath));
621
+ }
622
+ } else {
623
+ skillName = extractSkillName(import_node_path5.default.basename(skillPath));
624
+ }
625
+ } else {
626
+ skillName = extractSkillName(skillNameOrPath);
627
+ const tempDir = import_node_path5.default.join("/tmp", `eskill-${Date.now()}`);
628
+ try {
629
+ const registry = options.registry || await getRegistry();
630
+ const timeout = options.timeout || 18e4;
631
+ const spinner = logger.start(`Installing ${skillNameOrPath}...`);
632
+ logger.infoWithoutStop(`Registry: ${registry}`);
633
+ logger.infoWithoutStop(`Timeout: ${timeout / 1e3}s`);
634
+ import_node_fs4.default.mkdirSync(tempDir, { recursive: true });
635
+ logger.updateSpinner(`Downloading ${skillNameOrPath} from ${registry}...`);
636
+ const homeDir = process.env.HOME || process.env.USERPROFILE || import_node_os2.default.homedir();
637
+ const npmCacheDir = import_node_path5.default.join(homeDir, ".npm");
638
+ const npmConfigPrefix = import_node_path5.default.join(homeDir, ".npm-global");
639
+ import_node_fs4.default.mkdirSync(npmCacheDir, { recursive: true });
640
+ const installCommand = `npm install ${skillNameOrPath} --prefix ${tempDir} --registry=${registry} --no-save --silent --no-bin-links --prefer-offline`;
641
+ const env = {
642
+ ...process.env,
643
+ npm_config_cache: npmCacheDir,
644
+ npm_config_prefix: npmConfigPrefix,
645
+ npm_config_global: "false"
646
+ };
647
+ try {
648
+ await execWithTimeout(installCommand, timeout, env);
649
+ spinner.succeed(`Package ${skillNameOrPath} downloaded successfully`);
650
+ } catch (error) {
651
+ spinner.fail("Download failed");
652
+ const errorMessage = error.message || error.stderr || "";
653
+ if (errorMessage.includes("timeout")) {
654
+ logger.error(`Download timeout after ${timeout / 1e3} seconds`);
655
+ logger.info("");
656
+ logger.info("Possible reasons:");
657
+ logger.info(" - Slow network connection");
658
+ logger.info(" - Registry server issues or unresponsive");
659
+ logger.info(" - Large package size");
660
+ logger.info(" - Network firewall blocking the connection");
661
+ logger.info("");
662
+ logger.info("Suggestions:");
663
+ logger.info(` - Try again: eskill add ${skillNameOrPath}`);
664
+ logger.info(` - Use a different registry: eskill add ${skillNameOrPath} --registry=https://registry.npmjs.org/`);
665
+ logger.info(` - Increase timeout: eskill add ${skillNameOrPath} --timeout=300000`);
666
+ } else if (errorMessage.includes("EACCES") || errorMessage.includes("permission denied") || errorMessage.includes("Permission denied")) {
667
+ logger.error("Permission denied error detected");
668
+ logger.info("");
669
+ logger.info("This error occurs when npm tries to access system directories.");
670
+ logger.info("");
671
+ logger.info("\u{1F527} Quick Fix (Recommended):");
672
+ logger.info("");
673
+ logger.info("1. Configure npm to use user directory:");
674
+ logger.info(` mkdir -p ${npmConfigPrefix}`);
675
+ logger.info(` npm config set prefix '${npmConfigPrefix}'`);
676
+ logger.info("");
677
+ logger.info("2. Add to your ~/.zshrc (or ~/.bash_profile):");
678
+ logger.info(` export PATH="${npmConfigPrefix}/bin:$PATH"`);
679
+ logger.info("");
680
+ logger.info("3. Reload shell configuration:");
681
+ logger.info(" source ~/.zshrc");
682
+ logger.info("");
683
+ logger.info("\u{1F4DA} Alternative Solutions:");
684
+ logger.info("");
685
+ logger.info("Option A: Use NVM (Node Version Manager)");
686
+ logger.info(" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash");
687
+ logger.info(" nvm install --lts");
688
+ logger.info("");
689
+ logger.info("Option B: Fix system directory permissions (requires sudo)");
690
+ logger.info(" sudo chown -R $(whoami) /usr/local/lib/node_modules");
691
+ logger.info("");
692
+ logger.info("Option C: Use sudo (not recommended for security reasons)");
693
+ logger.info(` sudo npm install -g @empjs/skill --registry=${registry}`);
694
+ logger.info("");
695
+ logger.info("After fixing, try again:");
696
+ logger.info(` eskill add ${skillNameOrPath}`);
697
+ } else if (errorMessage.includes("ENOTFOUND") || errorMessage.includes("ECONNREFUSED")) {
698
+ logger.error(`Network connection error: ${errorMessage}`);
699
+ logger.info("");
700
+ logger.info("Please check:");
701
+ logger.info(" - Your internet connection");
702
+ logger.info(` - Registry accessibility: ${registry}`);
703
+ logger.info(` - Try a different registry: eskill add ${skillNameOrPath} --registry=https://registry.npmjs.org/`);
704
+ } else {
705
+ logger.error(`Failed to download: ${errorMessage}`);
706
+ logger.info("");
707
+ logger.info("If this is a permission error, see the solutions above.");
708
+ logger.info("For other errors, please check:");
709
+ logger.info(" - Package name is correct");
710
+ logger.info(" - Registry is accessible");
711
+ logger.info(` - Try: eskill add ${skillNameOrPath} --registry=https://registry.npmjs.org/`);
712
+ }
713
+ process.exit(1);
714
+ }
715
+ skillPath = import_node_path5.default.join(tempDir, "node_modules", skillNameOrPath);
716
+ if (!import_node_fs4.default.existsSync(skillPath)) {
717
+ logger.error(`Failed to download package: ${skillNameOrPath}`);
718
+ process.exit(1);
719
+ }
720
+ } catch (error) {
721
+ logger.error(`Failed to download: ${error.message}`);
722
+ process.exit(1);
723
+ }
724
+ }
725
+ const targetPath = getSharedSkillPath(skillName);
726
+ if (import_node_fs4.default.existsSync(targetPath)) {
727
+ if (options.force) {
728
+ logger.warn("Removing existing installation...");
729
+ import_node_fs4.default.rmSync(targetPath, { recursive: true, force: true });
730
+ } else {
731
+ logger.error(`Skill already exists: ${targetPath}`);
732
+ logger.info("Use --force to overwrite");
733
+ process.exit(1);
734
+ }
735
+ }
736
+ if (options.link) {
737
+ import_node_fs4.default.symlinkSync(skillPath, targetPath, "dir");
738
+ logger.success(`Linked to shared directory (dev mode)`);
739
+ } else {
740
+ copyDir(skillPath, targetPath);
741
+ logger.success(`Installed to shared directory`);
742
+ }
743
+ logger.info(`\u{1F4CD} Location: ${targetPath}`);
744
+ const cwd = process.cwd();
745
+ const installedAgents = detectInstalledAgents(cwd);
746
+ if (installedAgents.length === 0) {
747
+ logger.warn("No AI agents detected");
748
+ logger.info("Skill installed to shared directory, but not linked to any agent");
749
+ logger.info("");
750
+ logger.info("Supported agents:");
751
+ logger.info(" - Claude Code (~/.claude/skills)");
752
+ logger.info(" - Cursor (~/.cursor/skills)");
753
+ logger.info(" - Windsurf (~/.windsurf/skills)");
754
+ return;
755
+ }
756
+ let targetAgents = installedAgents;
757
+ if (options.agent && options.agent !== "all") {
758
+ const agent = installedAgents.find((a) => a.name === options.agent);
759
+ if (!agent) {
760
+ logger.error(`Agent not installed: ${options.agent}`);
761
+ logger.info(`
762
+ Installed agents: ${installedAgents.map((a) => a.name).join(", ")}`);
763
+ process.exit(1);
764
+ }
765
+ targetAgents = [agent];
766
+ }
767
+ logger.info("\nCreating symlinks...");
768
+ let successCount = 0;
769
+ for (const agent of targetAgents) {
770
+ if (createSymlink(skillName, agent, cwd)) {
771
+ successCount++;
772
+ }
773
+ }
774
+ logger.info("");
775
+ logger.success(`\u2705 Skill installed successfully!`);
776
+ logger.info(`
777
+ Linked to ${successCount}/${targetAgents.length} agents`);
778
+ if (options.link) {
779
+ logger.info("\n\u{1F4A1} Dev mode: changes to source files will reflect immediately");
780
+ }
781
+ }
782
+ function copyDir(src, dest) {
783
+ import_node_fs4.default.mkdirSync(dest, { recursive: true });
784
+ const entries = import_node_fs4.default.readdirSync(src, { withFileTypes: true });
785
+ for (const entry of entries) {
786
+ const srcPath = import_node_path5.default.join(src, entry.name);
787
+ const destPath = import_node_path5.default.join(dest, entry.name);
788
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) {
789
+ continue;
790
+ }
791
+ if (entry.isDirectory()) {
792
+ copyDir(srcPath, destPath);
793
+ } else {
794
+ import_node_fs4.default.copyFileSync(srcPath, destPath);
795
+ }
796
+ }
797
+ }
798
+
799
+ // src/commands/list.ts
800
+ var import_node_fs5 = __toESM(require("fs"), 1);
801
+ var import_node_path6 = __toESM(require("path"), 1);
802
+ var import_chalk2 = __toESM(require("chalk"), 1);
803
+ function list() {
804
+ if (!import_node_fs5.default.existsSync(SHARED_SKILLS_DIR)) {
805
+ logger.info("No skills installed");
806
+ logger.info(`
807
+ To install a skill, run: ${import_chalk2.default.cyan("eskill install <skill-name>")}`);
808
+ return;
809
+ }
810
+ const skills = import_node_fs5.default.readdirSync(SHARED_SKILLS_DIR);
811
+ if (skills.length === 0) {
812
+ logger.info("No skills installed");
813
+ logger.info(`
814
+ To install a skill, run: ${import_chalk2.default.cyan("eskill install <skill-name>")}`);
815
+ return;
816
+ }
817
+ console.log(import_chalk2.default.bold(`
818
+ Installed skills in ${SHARED_SKILLS_DIR}:
819
+ `));
820
+ for (const skill of skills) {
821
+ const skillPath = import_node_path6.default.join(SHARED_SKILLS_DIR, skill);
822
+ try {
823
+ const stats = import_node_fs5.default.lstatSync(skillPath);
824
+ if (!stats.isDirectory() && !stats.isSymbolicLink()) {
825
+ continue;
826
+ }
827
+ let version2 = "unknown";
828
+ const pkgPath = import_node_path6.default.join(skillPath, "package.json");
829
+ if (import_node_fs5.default.existsSync(pkgPath)) {
830
+ try {
831
+ const pkgContent = import_node_fs5.default.readFileSync(pkgPath, "utf-8");
832
+ const pkg = JSON.parse(pkgContent);
833
+ if (pkg.version && typeof pkg.version === "string") {
834
+ version2 = pkg.version;
835
+ }
836
+ } catch (error) {
837
+ }
838
+ }
839
+ if (version2 === "unknown") {
840
+ const skillMdPath = import_node_path6.default.join(skillPath, "SKILL.md");
841
+ if (import_node_fs5.default.existsSync(skillMdPath)) {
842
+ try {
843
+ const skillMdContent = import_node_fs5.default.readFileSync(skillMdPath, "utf-8");
844
+ const frontmatterMatch = skillMdContent.match(/^---\s*\n([\s\S]*?)\n---/);
845
+ if (frontmatterMatch) {
846
+ const frontmatter = frontmatterMatch[1];
847
+ const versionMatch = frontmatter.match(/^version:\s*(.+)$/m);
848
+ if (versionMatch) {
849
+ version2 = versionMatch[1].trim().replace(/^["']|["']$/g, "");
850
+ }
851
+ }
852
+ } catch (error) {
853
+ }
854
+ }
855
+ }
856
+ if (version2 === "unknown" && isSymlink(skillPath)) {
857
+ try {
858
+ const targetPath = readSymlink(skillPath);
859
+ if (targetPath) {
860
+ const targetPkgPath = import_node_path6.default.join(targetPath, "package.json");
861
+ if (import_node_fs5.default.existsSync(targetPkgPath)) {
862
+ const pkg = JSON.parse(import_node_fs5.default.readFileSync(targetPkgPath, "utf-8"));
863
+ if (pkg.version && typeof pkg.version === "string") {
864
+ version2 = pkg.version;
865
+ }
866
+ }
867
+ }
868
+ } catch (error) {
869
+ }
870
+ }
871
+ const isDev = isSymlink(skillPath);
872
+ const devTag = isDev ? import_chalk2.default.yellow(" (dev)") : "";
873
+ const versionDisplay = version2 !== "unknown" ? import_chalk2.default.gray(`(v${version2})`) : "";
874
+ console.log(import_chalk2.default.green("\u{1F4E6}") + ` ${import_chalk2.default.bold(skill)}${versionDisplay ? " " + versionDisplay : ""}${devTag}`);
875
+ const cwd = process.cwd();
876
+ const linkedAgents = [];
877
+ for (const agent of AGENTS) {
878
+ const skillsDirs = getAgentSkillsDirs(agent, cwd);
879
+ const hasSymlink = skillsDirs.some((dir) => {
880
+ const agentSkillPath = import_node_path6.default.join(dir, skill);
881
+ if (import_node_fs5.default.existsSync(agentSkillPath) && isSymlink(agentSkillPath)) {
882
+ const target = readSymlink(agentSkillPath);
883
+ return target === skillPath;
884
+ }
885
+ return false;
886
+ });
887
+ if (hasSymlink) {
888
+ linkedAgents.push(agent.displayName);
889
+ }
890
+ }
891
+ if (linkedAgents.length > 0) {
892
+ console.log(import_chalk2.default.gray(` \u2192 Linked to: ${linkedAgents.join(", ")}`));
893
+ } else {
894
+ console.log(import_chalk2.default.yellow(` \u2192 Not linked to any agent`));
895
+ }
896
+ if (isDev) {
897
+ const target = readSymlink(skillPath);
898
+ if (target) {
899
+ console.log(import_chalk2.default.gray(` \u2192 Source: ${target}`));
900
+ }
901
+ }
902
+ console.log("");
903
+ } catch (error) {
904
+ continue;
905
+ }
906
+ }
907
+ }
908
+
909
+ // src/commands/remove.ts
910
+ var import_node_fs6 = __toESM(require("fs"), 1);
911
+ async function remove(skillName, options = {}) {
912
+ const extractedName = extractSkillName(skillName);
913
+ const sharedPath = getSharedSkillPath(extractedName);
914
+ const skillExists = import_node_fs6.default.existsSync(sharedPath);
915
+ if (!skillExists) {
916
+ logger.error(`Skill not found: ${extractedName}`);
917
+ logger.info(`Location: ${sharedPath}`);
918
+ process.exit(1);
919
+ }
920
+ const cwd = process.cwd();
921
+ const installedAgents = detectInstalledAgents(cwd);
922
+ let targetAgents = installedAgents;
923
+ if (options.agent && options.agent !== "all") {
924
+ const agent = installedAgents.find((a) => a.name === options.agent);
925
+ if (!agent) {
926
+ logger.error(`Agent not installed: ${options.agent}`);
927
+ logger.info(`
928
+ Installed agents: ${installedAgents.map((a) => a.name).join(", ")}`);
929
+ process.exit(1);
930
+ }
931
+ targetAgents = [agent];
932
+ }
933
+ if (targetAgents.length > 0) {
934
+ logger.info("Removing symlinks from agents...");
935
+ let removedCount = 0;
936
+ for (const agent of targetAgents) {
937
+ if (removeSymlink(extractedName, agent, cwd)) {
938
+ removedCount++;
939
+ }
940
+ }
941
+ logger.info(`Removed ${removedCount}/${targetAgents.length} symlinks`);
942
+ }
943
+ if (options.agent && options.agent !== "all") {
944
+ logger.success(`\u2705 Removed symlinks for ${options.agent} only`);
945
+ logger.info(`Skill remains in shared directory: ${sharedPath}`);
946
+ return;
947
+ }
948
+ logger.info("Removing skill from shared directory...");
949
+ try {
950
+ const stats = import_node_fs6.default.lstatSync(sharedPath);
951
+ if (stats.isSymbolicLink()) {
952
+ import_node_fs6.default.unlinkSync(sharedPath);
953
+ logger.success("Removed symlink from shared directory");
954
+ } else {
955
+ import_node_fs6.default.rmSync(sharedPath, { recursive: true, force: true });
956
+ logger.success("Removed skill from shared directory");
957
+ }
958
+ } catch (error) {
959
+ logger.error(`Failed to remove skill: ${error.message}`);
960
+ process.exit(1);
961
+ }
962
+ const remainingSymlinks = [];
963
+ for (const agent of AGENTS) {
964
+ const agentSkillPaths = getAgentSkillPaths(agent.name, extractedName, cwd);
965
+ const hasSymlink = agentSkillPaths.some((path7) => {
966
+ return import_node_fs6.default.existsSync(path7) && isSymlink(path7);
967
+ });
968
+ if (hasSymlink) {
969
+ remainingSymlinks.push(agent.displayName);
970
+ }
971
+ }
972
+ if (remainingSymlinks.length > 0) {
973
+ logger.warn(`
974
+ \u26A0\uFE0F Warning: Found remaining symlinks in:`);
975
+ for (const agentName of remainingSymlinks) {
976
+ logger.info(` - ${agentName}`);
977
+ }
978
+ logger.info("\nYou may need to manually remove these symlinks");
979
+ } else {
980
+ logger.success(`\u2705 Skill "${extractedName}" removed successfully!`);
981
+ }
982
+ }
983
+
984
+ // src/commands/agents.ts
985
+ var import_chalk3 = __toESM(require("chalk"), 1);
986
+ var import_node_fs7 = __toESM(require("fs"), 1);
987
+ var import_node_os3 = __toESM(require("os"), 1);
988
+ function agents() {
989
+ const cwd = process.cwd();
990
+ console.log(import_chalk3.default.bold("\n\u{1F4CB} Supported AI Agents:\n"));
991
+ for (const agent of AGENTS) {
992
+ const skillsDirs = getAgentSkillsDirs(agent, cwd);
993
+ const dirsInfo = skillsDirs.length > 1 ? ` (${skillsDirs.length} directories)` : "";
994
+ console.log(import_chalk3.default.bold(agent.displayName));
995
+ console.log(import_chalk3.default.gray(` Name: ${agent.name}`));
996
+ if (skillsDirs.length === 0) {
997
+ console.log(import_chalk3.default.yellow(" \u26A0\uFE0F No directories configured"));
998
+ } else {
999
+ console.log(import_chalk3.default.gray(` Directories${dirsInfo}:`));
1000
+ for (const dir of skillsDirs) {
1001
+ const exists = import_node_fs7.default.existsSync(dir);
1002
+ const status = exists ? import_chalk3.default.green("\u2713") : import_chalk3.default.gray("\u25CB");
1003
+ const pathColor = exists ? import_chalk3.default.white : import_chalk3.default.gray;
1004
+ console.log(` ${status} ${pathColor(dir)}`);
1005
+ }
1006
+ }
1007
+ console.log("");
1008
+ }
1009
+ console.log(import_chalk3.default.bold("\u{1F4E6} Shared Skills Directory:"));
1010
+ const sharedExists = import_node_fs7.default.existsSync(SHARED_SKILLS_DIR);
1011
+ const sharedStatus = sharedExists ? import_chalk3.default.green("\u2713") : import_chalk3.default.gray("\u25CB");
1012
+ const sharedPathColor = sharedExists ? import_chalk3.default.white : import_chalk3.default.gray;
1013
+ console.log(` ${sharedStatus} ${sharedPathColor(SHARED_SKILLS_DIR)}`);
1014
+ console.log("");
1015
+ const home = import_node_os3.default.homedir();
1016
+ if (cwd !== home) {
1017
+ console.log(import_chalk3.default.gray(`Current working directory: ${cwd}`));
1018
+ console.log(import_chalk3.default.gray("(Project-level directories are shown above)\n"));
1019
+ }
1020
+ }
1021
+
1022
+ // src/index.ts
1023
+ var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
1024
+ var __dirname = (0, import_path.dirname)(__filename2);
1025
+ var packageJsonPath = (0, import_path.join)(__dirname, "..", "package.json");
1026
+ var packageJson = JSON.parse((0, import_fs.readFileSync)(packageJsonPath, "utf-8"));
1027
+ var version = packageJson.version;
1028
+ var program = new import_commander.Command();
1029
+ program.name("eskill").description("Unified CLI tool for managing AI agent skills").version(version);
1030
+ program.command("install <skill>").alias("add").description("Install a skill from NPM, Git URL, or local directory").option("-a, --agent <name>", "Install for specific agent (claude, cursor, windsurf, or all)").option("-l, --link", "Dev mode: symlink from local directory").option("-f, --force", "Force reinstall if already exists").option("-r, --registry <url>", "NPM registry URL (overrides .npmrc and global config)").option("-t, --timeout <ms>", "Timeout in milliseconds (default: 180000 for npm, 120000 for git)", (value) => parseInt(value, 10)).action(async (skill, options) => {
1031
+ try {
1032
+ await install(skill, options);
1033
+ } catch (error) {
1034
+ console.error("Error:", error.message);
1035
+ process.exit(1);
1036
+ }
1037
+ });
1038
+ program.command("list").alias("ls").description("List installed skills").action(() => {
1039
+ try {
1040
+ list();
1041
+ } catch (error) {
1042
+ console.error("Error:", error.message);
1043
+ process.exit(1);
1044
+ }
1045
+ });
1046
+ program.command("remove <skill>").alias("rm").alias("uninstall").description("Remove an installed skill").option("-a, --agent <name>", "Remove symlink for specific agent only (keeps skill in shared directory)").action(async (skill, options) => {
1047
+ try {
1048
+ await remove(skill, options);
1049
+ } catch (error) {
1050
+ console.error("Error:", error.message);
1051
+ process.exit(1);
1052
+ }
1053
+ });
1054
+ program.command("agents").alias("list-agents").description("List all supported AI agents and their skills directories").action(() => {
1055
+ try {
1056
+ agents();
1057
+ } catch (error) {
1058
+ console.error("Error:", error.message);
1059
+ process.exit(1);
1060
+ }
1061
+ });
1062
+ program.parse();
1063
+ //# sourceMappingURL=index.cjs.map