@empjs/skill 1.0.6 → 1.0.8

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 CHANGED
@@ -7,14 +7,16 @@ import { dirname, join } from "path";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  // src/commands/agents.ts
10
- import fs from "fs";
10
+ import fs2 from "fs";
11
11
  import os2 from "os";
12
12
  import chalk from "chalk";
13
13
 
14
14
  // src/config/agents.ts
15
+ import fs from "fs";
15
16
  import os from "os";
16
17
  import path from "path";
17
18
  var HOME = os.homedir();
19
+ var CONFIG_HOME = process.env.XDG_CONFIG_HOME || path.join(HOME, ".config");
18
20
  function getAgentSkillsDirs(agent, cwd) {
19
21
  if (agent.skillsDirs) {
20
22
  if (typeof agent.skillsDirs === "function") {
@@ -29,21 +31,44 @@ function getAgentSkillsDirs(agent, cwd) {
29
31
  }
30
32
  var AGENTS = [
31
33
  {
32
- name: "claude",
33
- displayName: "Claude Code",
34
- skillsDir: path.join(HOME, ".claude", "skills"),
34
+ name: "amp",
35
+ displayName: "AMP",
36
+ skillsDirs: (cwd) => {
37
+ const dirs = [path.join(CONFIG_HOME, "agents", "skills")];
38
+ if (cwd) dirs.push(path.join(cwd, ".agents", "skills"));
39
+ return dirs;
40
+ },
35
41
  enabled: true
36
42
  },
37
43
  {
38
- name: "cursor",
39
- displayName: "Cursor",
40
- skillsDir: path.join(HOME, ".cursor", "skills"),
44
+ name: "antigravity",
45
+ displayName: "Antigravity",
46
+ skillsDirs: (cwd) => {
47
+ const dirs = [path.join(HOME, ".gemini", "antigravity", "skills")];
48
+ if (cwd) dirs.push(path.join(cwd, ".agent", "skills"));
49
+ if (cwd) dirs.push(path.join(cwd, ".shared", "skills"));
50
+ return dirs;
51
+ },
41
52
  enabled: true
42
53
  },
43
54
  {
44
- name: "windsurf",
45
- displayName: "Windsurf",
46
- skillsDir: path.join(HOME, ".windsurf", "skills"),
55
+ name: "claude",
56
+ displayName: "Claude Code",
57
+ skillsDir: path.join(process.env.CLAUDE_CONFIG_DIR?.trim() || path.join(HOME, ".claude"), "skills"),
58
+ enabled: true
59
+ },
60
+ {
61
+ name: "clawdbot",
62
+ displayName: "ClawdBot",
63
+ skillsDirs: () => {
64
+ const openclaw = path.join(HOME, ".openclaw", "skills");
65
+ const clawdbot = path.join(HOME, ".clawdbot", "skills");
66
+ const moltbot = path.join(HOME, ".moltbot", "skills");
67
+ if (fs.existsSync(path.join(HOME, ".openclaw"))) return [openclaw];
68
+ if (fs.existsSync(path.join(HOME, ".clawdbot"))) return [clawdbot];
69
+ if (fs.existsSync(path.join(HOME, ".moltbot"))) return [moltbot];
70
+ return [openclaw];
71
+ },
47
72
  enabled: true
48
73
  },
49
74
  {
@@ -52,9 +77,29 @@ var AGENTS = [
52
77
  skillsDir: path.join(HOME, ".cline", "skills"),
53
78
  enabled: true
54
79
  },
80
+ {
81
+ name: "codex",
82
+ displayName: "Codex",
83
+ skillsDir: path.join(process.env.CODEX_HOME?.trim() || path.join(HOME, ".codex"), "skills"),
84
+ enabled: true
85
+ },
86
+ {
87
+ name: "cursor",
88
+ displayName: "Cursor",
89
+ skillsDir: path.join(HOME, ".cursor", "skills"),
90
+ enabled: true,
91
+ /** Cursor does not follow symlinks to discover skills (known bug). Use copy instead. */
92
+ useCopyInsteadOfSymlink: true
93
+ },
94
+ {
95
+ name: "droid",
96
+ displayName: "Droid",
97
+ skillsDir: path.join(HOME, ".factory", "skills"),
98
+ enabled: true
99
+ },
55
100
  {
56
101
  name: "gemini",
57
- displayName: "Gemini Code",
102
+ displayName: "Gemini",
58
103
  skillsDir: path.join(HOME, ".gemini", "skills"),
59
104
  enabled: true
60
105
  },
@@ -65,47 +110,31 @@ var AGENTS = [
65
110
  enabled: true
66
111
  },
67
112
  {
68
- name: "opencode",
69
- displayName: "OpenCode",
70
- skillsDir: path.join(HOME, ".opencode", "skills"),
113
+ name: "goose",
114
+ displayName: "Goose",
115
+ skillsDir: path.join(CONFIG_HOME, "goose", "skills"),
71
116
  enabled: true
72
117
  },
73
118
  {
74
- name: "antigravity",
75
- displayName: "Antigravity",
76
- skillsDirs: (cwd) => {
77
- const dirs = [];
78
- dirs.push(path.join(HOME, ".gemini", "antigravity", "skills"));
79
- if (cwd) {
80
- dirs.push(path.join(cwd, ".agent", "skills"));
81
- }
82
- if (cwd) {
83
- dirs.push(path.join(cwd, ".shared", "skills"));
84
- }
85
- return dirs;
86
- },
119
+ name: "kilo",
120
+ displayName: "Kilo Code",
121
+ skillsDir: path.join(HOME, ".kilocode", "skills"),
87
122
  enabled: true
88
123
  },
89
124
  {
90
125
  name: "kiro",
91
- displayName: "Kiro",
126
+ displayName: "Kiro CLI",
92
127
  skillsDir: path.join(HOME, ".kiro", "skills"),
93
128
  enabled: true
94
129
  },
95
130
  {
96
- name: "codex",
97
- displayName: "Codex CLI",
98
- skillsDir: path.join(HOME, ".codex", "skills"),
99
- enabled: true
100
- },
101
- {
102
- name: "qoder",
103
- displayName: "Qoder",
104
- skillsDir: path.join(HOME, ".qoder", "skills"),
131
+ name: "opencode",
132
+ displayName: "OpenCode",
133
+ skillsDir: path.join(CONFIG_HOME, "opencode", "skills"),
105
134
  enabled: true
106
135
  },
107
136
  {
108
- name: "roocode",
137
+ name: "roo",
109
138
  displayName: "Roo Code",
110
139
  skillsDir: path.join(HOME, ".roo", "skills"),
111
140
  enabled: true
@@ -116,6 +145,24 @@ var AGENTS = [
116
145
  skillsDir: path.join(HOME, ".trae", "skills"),
117
146
  enabled: true
118
147
  },
148
+ {
149
+ name: "windsurf",
150
+ displayName: "Windsurf",
151
+ skillsDirs: () => {
152
+ const dirs = [];
153
+ dirs.push(path.join(HOME, ".windsurf", "skills"));
154
+ dirs.push(path.join(HOME, ".codeium", "windsurf", "skills"));
155
+ return dirs;
156
+ },
157
+ enabled: true
158
+ },
159
+ // Additional agents
160
+ {
161
+ name: "qoder",
162
+ displayName: "Qoder",
163
+ skillsDir: path.join(HOME, ".qoder", "skills"),
164
+ enabled: true
165
+ },
119
166
  {
120
167
  name: "continue",
121
168
  displayName: "Continue",
@@ -141,7 +188,7 @@ function agents() {
141
188
  } else {
142
189
  console.log(chalk.gray(` Directories${dirsInfo}:`));
143
190
  for (const dir of skillsDirs) {
144
- const exists = fs.existsSync(dir);
191
+ const exists = fs2.existsSync(dir);
145
192
  const status = exists ? chalk.green("\u2713") : chalk.gray("\u25CB");
146
193
  const pathColor = exists ? chalk.white : chalk.gray;
147
194
  console.log(` ${status} ${pathColor(dir)}`);
@@ -150,7 +197,7 @@ function agents() {
150
197
  console.log("");
151
198
  }
152
199
  console.log(chalk.bold("\u{1F4E6} Shared Skills Directory:"));
153
- const sharedExists = fs.existsSync(SHARED_SKILLS_DIR);
200
+ const sharedExists = fs2.existsSync(SHARED_SKILLS_DIR);
154
201
  const sharedStatus = sharedExists ? chalk.green("\u2713") : chalk.gray("\u25CB");
155
202
  const sharedPathColor = sharedExists ? chalk.white : chalk.gray;
156
203
  console.log(` ${sharedStatus} ${sharedPathColor(SHARED_SKILLS_DIR)}`);
@@ -164,7 +211,7 @@ function agents() {
164
211
 
165
212
  // src/commands/install.ts
166
213
  import { exec as exec2 } from "child_process";
167
- import fs5 from "fs";
214
+ import fs6 from "fs";
168
215
  import os3 from "os";
169
216
  import path5 from "path";
170
217
  import { promisify as promisify2 } from "util";
@@ -296,6 +343,10 @@ var Logger = class {
296
343
  if (this.spinner) this.spinner.stop();
297
344
  console.log(chalk2.red("\u2717"), message);
298
345
  }
346
+ dim(message) {
347
+ if (this.spinner) this.spinner.stop();
348
+ console.log(chalk2.dim(" " + message));
349
+ }
299
350
  start(message) {
300
351
  if (this.spinner) this.spinner.stop();
301
352
  this.spinner = ora(message).start();
@@ -332,7 +383,7 @@ var Logger = class {
332
383
  var logger = new Logger();
333
384
 
334
385
  // src/utils/paths.ts
335
- import fs2 from "fs";
386
+ import fs3 from "fs";
336
387
  import path2 from "path";
337
388
  function getSharedSkillPath(skillName) {
338
389
  return path2.join(SHARED_SKILLS_DIR, skillName);
@@ -346,8 +397,8 @@ function getAgentSkillPaths(agentName, skillName, cwd) {
346
397
  return skillsDirs.map((dir) => path2.join(dir, skillName));
347
398
  }
348
399
  function ensureSharedDir() {
349
- if (!fs2.existsSync(SHARED_SKILLS_DIR)) {
350
- fs2.mkdirSync(SHARED_SKILLS_DIR, { recursive: true });
400
+ if (!fs3.existsSync(SHARED_SKILLS_DIR)) {
401
+ fs3.mkdirSync(SHARED_SKILLS_DIR, { recursive: true });
351
402
  }
352
403
  }
353
404
  function detectInstalledAgents(cwd) {
@@ -355,7 +406,7 @@ function detectInstalledAgents(cwd) {
355
406
  try {
356
407
  const skillsDirs = getAgentSkillsDirs(agent, cwd);
357
408
  return skillsDirs.some((dir) => {
358
- return fs2.existsSync(dir) || fs2.existsSync(path2.dirname(dir));
409
+ return fs3.existsSync(dir) || fs3.existsSync(path2.dirname(dir));
359
410
  });
360
411
  } catch {
361
412
  return false;
@@ -376,7 +427,7 @@ function extractSkillName(nameOrPath) {
376
427
 
377
428
  // src/utils/registry.ts
378
429
  import { exec } from "child_process";
379
- import fs3 from "fs";
430
+ import fs4 from "fs";
380
431
  import path3 from "path";
381
432
  import { promisify } from "util";
382
433
  var execAsync = promisify(exec);
@@ -384,7 +435,7 @@ function findNpmrc(startDir) {
384
435
  let currentDir = path3.resolve(startDir);
385
436
  while (currentDir !== path3.dirname(currentDir)) {
386
437
  const npmrcPath = path3.join(currentDir, ".npmrc");
387
- if (fs3.existsSync(npmrcPath)) {
438
+ if (fs4.existsSync(npmrcPath)) {
388
439
  return npmrcPath;
389
440
  }
390
441
  currentDir = path3.dirname(currentDir);
@@ -393,7 +444,7 @@ function findNpmrc(startDir) {
393
444
  }
394
445
  function parseNpmrc(npmrcPath) {
395
446
  try {
396
- const content = fs3.readFileSync(npmrcPath, "utf-8");
447
+ const content = fs4.readFileSync(npmrcPath, "utf-8");
397
448
  const lines = content.split("\n");
398
449
  for (const line of lines) {
399
450
  const trimmed = line.trim();
@@ -436,33 +487,50 @@ async function getRegistry(cwd = process.cwd()) {
436
487
  }
437
488
 
438
489
  // src/utils/symlink.ts
439
- import fs4 from "fs";
490
+ import fs5 from "fs";
440
491
  import path4 from "path";
492
+ function copyDir(src, dest) {
493
+ fs5.mkdirSync(dest, { recursive: true });
494
+ const entries = fs5.readdirSync(src, { withFileTypes: true });
495
+ for (const entry of entries) {
496
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
497
+ const srcPath = path4.join(src, entry.name);
498
+ const destPath = path4.join(dest, entry.name);
499
+ if (entry.isDirectory()) {
500
+ copyDir(srcPath, destPath);
501
+ } else {
502
+ fs5.copyFileSync(srcPath, destPath);
503
+ }
504
+ }
505
+ }
441
506
  function createSymlink(skillName, agent, cwd) {
442
507
  const source = getSharedSkillPath(skillName);
443
- if (!fs4.existsSync(source)) {
508
+ if (!fs5.existsSync(source)) {
444
509
  logger.error(`Skill not found: ${source}`);
445
510
  return false;
446
511
  }
447
512
  const targets = getAgentSkillPaths(agent.name, skillName, cwd);
513
+ const useCopy = agent.useCopyInsteadOfSymlink === true;
448
514
  let successCount = 0;
449
515
  for (const target of targets) {
450
516
  const targetDir = path4.dirname(target);
451
- if (!fs4.existsSync(targetDir)) {
517
+ if (!fs5.existsSync(targetDir)) {
452
518
  try {
453
- fs4.mkdirSync(targetDir, { recursive: true });
519
+ fs5.mkdirSync(targetDir, { recursive: true });
454
520
  } catch (error) {
455
521
  logger.error(`Failed to create directory ${targetDir}: ${error.message}`);
456
522
  continue;
457
523
  }
458
524
  }
459
- if (fs4.existsSync(target)) {
525
+ if (fs5.existsSync(target)) {
460
526
  try {
461
- const stats = fs4.lstatSync(target);
527
+ const stats = fs5.lstatSync(target);
462
528
  if (stats.isSymbolicLink()) {
463
- fs4.unlinkSync(target);
529
+ fs5.unlinkSync(target);
530
+ } else if (stats.isDirectory()) {
531
+ fs5.rmSync(target, { recursive: true, force: true });
464
532
  } else {
465
- logger.warn(`Target exists but is not a symlink, skipping: ${target}`);
533
+ logger.warn(`Target exists but is not a symlink/dir, skipping: ${target}`);
466
534
  continue;
467
535
  }
468
536
  } catch (error) {
@@ -471,10 +539,14 @@ function createSymlink(skillName, agent, cwd) {
471
539
  }
472
540
  }
473
541
  try {
474
- fs4.symlinkSync(source, target, "dir");
542
+ if (useCopy) {
543
+ copyDir(source, target);
544
+ } else {
545
+ fs5.symlinkSync(source, target, "dir");
546
+ }
475
547
  successCount++;
476
548
  } catch (error) {
477
- logger.error(`Failed to create symlink at ${target}: ${error.message}`);
549
+ logger.error(`Failed to ${useCopy ? "copy" : "symlink"} at ${target}: ${error.message}`);
478
550
  }
479
551
  }
480
552
  if (successCount > 0) {
@@ -488,19 +560,22 @@ function removeSymlink(skillName, agent, cwd) {
488
560
  const targets = getAgentSkillPaths(agent.name, skillName, cwd);
489
561
  let removedCount = 0;
490
562
  for (const target of targets) {
491
- if (!fs4.existsSync(target)) {
563
+ if (!fs5.existsSync(target)) {
492
564
  continue;
493
565
  }
494
566
  try {
495
- const stats = fs4.lstatSync(target);
567
+ const stats = fs5.lstatSync(target);
496
568
  if (stats.isSymbolicLink()) {
497
- fs4.unlinkSync(target);
569
+ fs5.unlinkSync(target);
570
+ removedCount++;
571
+ } else if (stats.isDirectory()) {
572
+ fs5.rmSync(target, { recursive: true, force: true });
498
573
  removedCount++;
499
574
  } else {
500
- logger.warn(`Not a symlink: ${target}`);
575
+ logger.warn(`Not a symlink or directory: ${target}`);
501
576
  }
502
577
  } catch (error) {
503
- logger.error(`Failed to remove symlink at ${target}: ${error.message}`);
578
+ logger.error(`Failed to remove at ${target}: ${error.message}`);
504
579
  }
505
580
  }
506
581
  if (removedCount > 0) {
@@ -512,7 +587,7 @@ function removeSymlink(skillName, agent, cwd) {
512
587
  }
513
588
  function isSymlink(filePath) {
514
589
  try {
515
- const stats = fs4.lstatSync(filePath);
590
+ const stats = fs5.lstatSync(filePath);
516
591
  return stats.isSymbolicLink();
517
592
  } catch {
518
593
  return false;
@@ -520,7 +595,7 @@ function isSymlink(filePath) {
520
595
  }
521
596
  function readSymlink(filePath) {
522
597
  try {
523
- return fs4.readlinkSync(filePath);
598
+ return fs5.readlinkSync(filePath);
524
599
  } catch {
525
600
  return null;
526
601
  }
@@ -550,11 +625,20 @@ async function execWithTimeout(command, timeout = 12e4, env) {
550
625
  }
551
626
  }
552
627
  async function install(skillNameOrPath, options = {}) {
553
- logger.info(`Installing skill: ${skillNameOrPath}`);
628
+ const isGit = isGitUrl(skillNameOrPath);
629
+ if (isGit) {
630
+ logger.info("Installing from Git URL");
631
+ logger.dim(skillNameOrPath);
632
+ } else {
633
+ logger.info(`Installing skill: ${skillNameOrPath}`);
634
+ }
554
635
  ensureSharedDir();
555
636
  let skillPath;
556
637
  let skillName;
557
- if (isGitUrl(skillNameOrPath)) {
638
+ if (process.env.DEBUG_ESKILL) {
639
+ logger.dim(`[DEBUG] isGitUrl=${isGit}, parseGitUrl=${parseGitUrl(skillNameOrPath) ? "ok" : "null"}`);
640
+ }
641
+ if (isGit) {
558
642
  const gitInfo = parseGitUrl(skillNameOrPath);
559
643
  if (!gitInfo) {
560
644
  logger.error(`Invalid git URL: ${skillNameOrPath}`);
@@ -566,21 +650,17 @@ async function install(skillNameOrPath, options = {}) {
566
650
  const cloneDir = path5.join(tempDir, "repo");
567
651
  try {
568
652
  const timeout = options.timeout || 12e4;
569
- const spinner = logger.start(`Cloning ${gitInfo.gitUrl}...`);
570
- logger.infoWithoutStop(`Repository: ${gitInfo.gitUrl}`);
571
- if (gitInfo.branch) {
572
- logger.infoWithoutStop(`Branch: ${gitInfo.branch}`);
573
- }
574
- if (gitInfo.path) {
575
- logger.infoWithoutStop(`Path: ${gitInfo.path}`);
576
- }
577
- logger.infoWithoutStop(`Timeout: ${timeout / 1e3}s`);
578
- fs5.mkdirSync(tempDir, { recursive: true });
653
+ const gitDetails = [`${gitInfo.gitUrl}`];
654
+ if (gitInfo.branch) gitDetails.push(`branch: ${gitInfo.branch}`);
655
+ if (gitInfo.path) gitDetails.push(`path: ${gitInfo.path}`);
656
+ logger.dim(gitDetails.join(" \xB7 "));
657
+ const spinner = logger.start(`Cloning...`);
658
+ fs6.mkdirSync(tempDir, { recursive: true });
579
659
  const branchFlag = gitInfo.branch ? `-b ${gitInfo.branch}` : "";
580
660
  const cloneCommand = branchFlag ? `git clone ${branchFlag} ${gitInfo.gitUrl} ${cloneDir} --depth 1 --quiet` : `git clone ${gitInfo.gitUrl} ${cloneDir} --depth 1 --quiet`;
581
661
  try {
582
662
  await execWithTimeout(cloneCommand, timeout);
583
- spinner.succeed(`Repository cloned successfully`);
663
+ spinner.succeed(`Cloned successfully`);
584
664
  } catch (error) {
585
665
  spinner.fail("Clone failed");
586
666
  if (error.message.includes("timeout")) {
@@ -597,7 +677,7 @@ async function install(skillNameOrPath, options = {}) {
597
677
  }
598
678
  if (gitInfo.path) {
599
679
  skillPath = path5.join(cloneDir, gitInfo.path);
600
- if (!fs5.existsSync(skillPath)) {
680
+ if (!fs6.existsSync(skillPath)) {
601
681
  logger.error(`Path not found in repository: ${gitInfo.path}`);
602
682
  logger.info(`Repository cloned to: ${cloneDir}`);
603
683
  process.exit(1);
@@ -606,7 +686,7 @@ async function install(skillNameOrPath, options = {}) {
606
686
  skillPath = cloneDir;
607
687
  }
608
688
  const skillMdPath = path5.join(skillPath, "SKILL.md");
609
- if (!fs5.existsSync(skillMdPath)) {
689
+ if (!fs6.existsSync(skillMdPath)) {
610
690
  logger.warn(`Warning: SKILL.md not found in ${skillPath}`);
611
691
  logger.info("The directory may not be a valid skill package");
612
692
  }
@@ -628,16 +708,16 @@ Tried to clone: ${gitInfo.gitUrl}`);
628
708
  }
629
709
  process.exit(1);
630
710
  }
631
- } else if (options.link || fs5.existsSync(skillNameOrPath)) {
711
+ } else if (options.link || fs6.existsSync(skillNameOrPath)) {
632
712
  skillPath = path5.resolve(skillNameOrPath);
633
- if (!fs5.existsSync(skillPath)) {
713
+ if (!fs6.existsSync(skillPath)) {
634
714
  logger.error(`Path not found: ${skillPath}`);
635
715
  process.exit(1);
636
716
  }
637
717
  const pkgPath = path5.join(skillPath, "package.json");
638
- if (fs5.existsSync(pkgPath)) {
718
+ if (fs6.existsSync(pkgPath)) {
639
719
  try {
640
- const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
720
+ const pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
641
721
  skillName = extractSkillName(pkg.name);
642
722
  } catch {
643
723
  skillName = extractSkillName(path5.basename(skillPath));
@@ -654,12 +734,12 @@ Tried to clone: ${gitInfo.gitUrl}`);
654
734
  const spinner = logger.start(`Installing ${skillNameOrPath}...`);
655
735
  logger.infoWithoutStop(`Registry: ${registry}`);
656
736
  logger.infoWithoutStop(`Timeout: ${timeout / 1e3}s`);
657
- fs5.mkdirSync(tempDir, { recursive: true });
737
+ fs6.mkdirSync(tempDir, { recursive: true });
658
738
  logger.updateSpinner(`Downloading ${skillNameOrPath} from ${registry}...`);
659
739
  const homeDir = process.env.HOME || process.env.USERPROFILE || os3.homedir();
660
740
  const npmCacheDir = path5.join(homeDir, ".npm");
661
741
  const npmConfigPrefix = path5.join(homeDir, ".npm-global");
662
- fs5.mkdirSync(npmCacheDir, { recursive: true });
742
+ fs6.mkdirSync(npmCacheDir, { recursive: true });
663
743
  const installCommand = `npm install ${skillNameOrPath} --prefix ${tempDir} --registry=${registry} --no-save --silent --no-bin-links --prefer-offline`;
664
744
  const env = {
665
745
  ...process.env,
@@ -740,7 +820,7 @@ Tried to clone: ${gitInfo.gitUrl}`);
740
820
  process.exit(1);
741
821
  }
742
822
  skillPath = path5.join(tempDir, "node_modules", skillNameOrPath);
743
- if (!fs5.existsSync(skillPath)) {
823
+ if (!fs6.existsSync(skillPath)) {
744
824
  logger.error(`Failed to download package: ${skillNameOrPath}`);
745
825
  process.exit(1);
746
826
  }
@@ -750,21 +830,24 @@ Tried to clone: ${gitInfo.gitUrl}`);
750
830
  }
751
831
  }
752
832
  const targetPath = getSharedSkillPath(skillName);
753
- if (fs5.existsSync(targetPath)) {
833
+ const sourceIsTarget = path5.resolve(skillPath) === path5.resolve(targetPath);
834
+ const alreadyExists = fs6.existsSync(targetPath) && !sourceIsTarget;
835
+ if (alreadyExists) {
754
836
  if (options.force) {
755
837
  logger.warn("Removing existing installation...");
756
- fs5.rmSync(targetPath, { recursive: true, force: true });
838
+ fs6.rmSync(targetPath, { recursive: true, force: true });
757
839
  } else {
758
- logger.error(`Skill already exists: ${targetPath}`);
759
- logger.info("Use --force to overwrite");
760
- process.exit(1);
840
+ logger.info(`Skill already exists, updating agent links...`);
761
841
  }
762
842
  }
763
- if (options.link) {
764
- fs5.symlinkSync(skillPath, targetPath, "dir");
843
+ if (sourceIsTarget) {
844
+ logger.info(`Skill already in shared directory, updating agent links...`);
845
+ } else if (alreadyExists && !options.force) {
846
+ } else if (options.link) {
847
+ fs6.symlinkSync(skillPath, targetPath, "dir");
765
848
  logger.success(`Linked to shared directory (dev mode)`);
766
849
  } else {
767
- copyDir(skillPath, targetPath);
850
+ copyDir2(skillPath, targetPath);
768
851
  logger.success(`Installed to shared directory`);
769
852
  }
770
853
  logger.info(`\u{1F4CD} Location: ${targetPath}`);
@@ -775,21 +858,25 @@ Tried to clone: ${gitInfo.gitUrl}`);
775
858
  logger.info("Skill installed to shared directory, but not linked to any agent");
776
859
  logger.info("");
777
860
  logger.info("Supported agents:");
778
- logger.info(" - Claude Code (~/.claude/skills)");
779
- logger.info(" - Cursor (~/.cursor/skills)");
780
- logger.info(" - Windsurf (~/.windsurf/skills)");
861
+ logger.info(" AMP, Antigravity, Claude Code, ClawdBot, Cline, Codex, Cursor, Droid,");
862
+ logger.info(" Gemini, GitHub Copilot, Goose, Kilo, Kiro CLI, OpenCode, Roo, Trae, Windsurf");
863
+ logger.info("");
864
+ logger.info('Run "eskill agents" to see all agent directories');
781
865
  return;
782
866
  }
783
867
  let targetAgents = installedAgents;
784
868
  if (options.agent && options.agent !== "all") {
785
- const agent = installedAgents.find((a) => a.name === options.agent);
869
+ const agent = AGENTS.find((a) => a.name === options.agent && a.enabled);
786
870
  if (!agent) {
787
- logger.error(`Agent not installed: ${options.agent}`);
871
+ logger.error(`Unknown agent: ${options.agent}`);
788
872
  logger.info(`
789
- Installed agents: ${installedAgents.map((a) => a.name).join(", ")}`);
873
+ Supported agents: ${AGENTS.filter((a) => a.enabled).map((a) => a.name).join(", ")}`);
790
874
  process.exit(1);
791
875
  }
792
876
  targetAgents = [agent];
877
+ if (!installedAgents.find((a) => a.name === options.agent)) {
878
+ logger.info(`Note: ${agent.displayName} directory will be created if not exists`);
879
+ }
793
880
  }
794
881
  logger.info("\nCreating symlinks...");
795
882
  let successCount = 0;
@@ -806,9 +893,9 @@ Linked to ${successCount}/${targetAgents.length} agents`);
806
893
  logger.info("\n\u{1F4A1} Dev mode: changes to source files will reflect immediately");
807
894
  }
808
895
  }
809
- function copyDir(src, dest) {
810
- fs5.mkdirSync(dest, { recursive: true });
811
- const entries = fs5.readdirSync(src, { withFileTypes: true });
896
+ function copyDir2(src, dest) {
897
+ fs6.mkdirSync(dest, { recursive: true });
898
+ const entries = fs6.readdirSync(src, { withFileTypes: true });
812
899
  for (const entry of entries) {
813
900
  const srcPath = path5.join(src, entry.name);
814
901
  const destPath = path5.join(dest, entry.name);
@@ -816,25 +903,25 @@ function copyDir(src, dest) {
816
903
  continue;
817
904
  }
818
905
  if (entry.isDirectory()) {
819
- copyDir(srcPath, destPath);
906
+ copyDir2(srcPath, destPath);
820
907
  } else {
821
- fs5.copyFileSync(srcPath, destPath);
908
+ fs6.copyFileSync(srcPath, destPath);
822
909
  }
823
910
  }
824
911
  }
825
912
 
826
913
  // src/commands/list.ts
827
- import fs6 from "fs";
914
+ import fs7 from "fs";
828
915
  import path6 from "path";
829
916
  import chalk3 from "chalk";
830
917
  function list() {
831
- if (!fs6.existsSync(SHARED_SKILLS_DIR)) {
918
+ if (!fs7.existsSync(SHARED_SKILLS_DIR)) {
832
919
  logger.info("No skills installed");
833
920
  logger.info(`
834
921
  To install a skill, run: ${chalk3.cyan("eskill install <skill-name>")}`);
835
922
  return;
836
923
  }
837
- const skills = fs6.readdirSync(SHARED_SKILLS_DIR);
924
+ const skills = fs7.readdirSync(SHARED_SKILLS_DIR);
838
925
  if (skills.length === 0) {
839
926
  logger.info("No skills installed");
840
927
  logger.info(`
@@ -847,15 +934,15 @@ Installed skills in ${SHARED_SKILLS_DIR}:
847
934
  for (const skill of skills) {
848
935
  const skillPath = path6.join(SHARED_SKILLS_DIR, skill);
849
936
  try {
850
- const stats = fs6.lstatSync(skillPath);
937
+ const stats = fs7.lstatSync(skillPath);
851
938
  if (!stats.isDirectory() && !stats.isSymbolicLink()) {
852
939
  continue;
853
940
  }
854
941
  let version2 = "unknown";
855
942
  const pkgPath = path6.join(skillPath, "package.json");
856
- if (fs6.existsSync(pkgPath)) {
943
+ if (fs7.existsSync(pkgPath)) {
857
944
  try {
858
- const pkgContent = fs6.readFileSync(pkgPath, "utf-8");
945
+ const pkgContent = fs7.readFileSync(pkgPath, "utf-8");
859
946
  const pkg = JSON.parse(pkgContent);
860
947
  if (pkg.version && typeof pkg.version === "string") {
861
948
  version2 = pkg.version;
@@ -865,9 +952,9 @@ Installed skills in ${SHARED_SKILLS_DIR}:
865
952
  }
866
953
  if (version2 === "unknown") {
867
954
  const skillMdPath = path6.join(skillPath, "SKILL.md");
868
- if (fs6.existsSync(skillMdPath)) {
955
+ if (fs7.existsSync(skillMdPath)) {
869
956
  try {
870
- const skillMdContent = fs6.readFileSync(skillMdPath, "utf-8");
957
+ const skillMdContent = fs7.readFileSync(skillMdPath, "utf-8");
871
958
  const frontmatterMatch = skillMdContent.match(/^---\s*\n([\s\S]*?)\n---/);
872
959
  if (frontmatterMatch) {
873
960
  const frontmatter = frontmatterMatch[1];
@@ -885,8 +972,8 @@ Installed skills in ${SHARED_SKILLS_DIR}:
885
972
  const targetPath = readSymlink(skillPath);
886
973
  if (targetPath) {
887
974
  const targetPkgPath = path6.join(targetPath, "package.json");
888
- if (fs6.existsSync(targetPkgPath)) {
889
- const pkg = JSON.parse(fs6.readFileSync(targetPkgPath, "utf-8"));
975
+ if (fs7.existsSync(targetPkgPath)) {
976
+ const pkg = JSON.parse(fs7.readFileSync(targetPkgPath, "utf-8"));
890
977
  if (pkg.version && typeof pkg.version === "string") {
891
978
  version2 = pkg.version;
892
979
  }
@@ -903,15 +990,19 @@ Installed skills in ${SHARED_SKILLS_DIR}:
903
990
  const linkedAgents = [];
904
991
  for (const agent of AGENTS) {
905
992
  const skillsDirs = getAgentSkillsDirs(agent, cwd);
906
- const hasSymlink = skillsDirs.some((dir) => {
993
+ const hasRef = skillsDirs.some((dir) => {
907
994
  const agentSkillPath = path6.join(dir, skill);
908
- if (fs6.existsSync(agentSkillPath) && isSymlink(agentSkillPath)) {
995
+ if (!fs7.existsSync(agentSkillPath)) return false;
996
+ if (isSymlink(agentSkillPath)) {
909
997
  const target = readSymlink(agentSkillPath);
910
998
  return target === skillPath;
911
999
  }
1000
+ if (agent.useCopyInsteadOfSymlink) {
1001
+ return fs7.statSync(agentSkillPath).isDirectory();
1002
+ }
912
1003
  return false;
913
1004
  });
914
- if (hasSymlink) {
1005
+ if (hasRef) {
915
1006
  linkedAgents.push(agent.displayName);
916
1007
  }
917
1008
  }
@@ -933,11 +1024,11 @@ Installed skills in ${SHARED_SKILLS_DIR}:
933
1024
  }
934
1025
 
935
1026
  // src/commands/remove.ts
936
- import fs7 from "fs";
1027
+ import fs8 from "fs";
937
1028
  async function remove(skillName, options = {}) {
938
1029
  const extractedName = extractSkillName(skillName);
939
1030
  const sharedPath = getSharedSkillPath(extractedName);
940
- const skillExists = fs7.existsSync(sharedPath);
1031
+ const skillExists = fs8.existsSync(sharedPath);
941
1032
  if (!skillExists) {
942
1033
  logger.error(`Skill not found: ${extractedName}`);
943
1034
  logger.info(`Location: ${sharedPath}`);
@@ -947,11 +1038,11 @@ async function remove(skillName, options = {}) {
947
1038
  const installedAgents = detectInstalledAgents(cwd);
948
1039
  let targetAgents = installedAgents;
949
1040
  if (options.agent && options.agent !== "all") {
950
- const agent = installedAgents.find((a) => a.name === options.agent);
1041
+ const agent = AGENTS.find((a) => a.name === options.agent && a.enabled);
951
1042
  if (!agent) {
952
- logger.error(`Agent not installed: ${options.agent}`);
1043
+ logger.error(`Unknown agent: ${options.agent}`);
953
1044
  logger.info(`
954
- Installed agents: ${installedAgents.map((a) => a.name).join(", ")}`);
1045
+ Supported agents: ${AGENTS.filter((a) => a.enabled).map((a) => a.name).join(", ")}`);
955
1046
  process.exit(1);
956
1047
  }
957
1048
  targetAgents = [agent];
@@ -973,35 +1064,33 @@ Installed agents: ${installedAgents.map((a) => a.name).join(", ")}`);
973
1064
  }
974
1065
  logger.info("Removing skill from shared directory...");
975
1066
  try {
976
- const stats = fs7.lstatSync(sharedPath);
1067
+ const stats = fs8.lstatSync(sharedPath);
977
1068
  if (stats.isSymbolicLink()) {
978
- fs7.unlinkSync(sharedPath);
1069
+ fs8.unlinkSync(sharedPath);
979
1070
  logger.success("Removed symlink from shared directory");
980
1071
  } else {
981
- fs7.rmSync(sharedPath, { recursive: true, force: true });
1072
+ fs8.rmSync(sharedPath, { recursive: true, force: true });
982
1073
  logger.success("Removed skill from shared directory");
983
1074
  }
984
1075
  } catch (error) {
985
1076
  logger.error(`Failed to remove skill: ${error.message}`);
986
1077
  process.exit(1);
987
1078
  }
988
- const remainingSymlinks = [];
1079
+ const remainingRefs = [];
989
1080
  for (const agent of AGENTS) {
990
1081
  const agentSkillPaths = getAgentSkillPaths(agent.name, extractedName, cwd);
991
- const hasSymlink = agentSkillPaths.some((path7) => {
992
- return fs7.existsSync(path7) && isSymlink(path7);
993
- });
994
- if (hasSymlink) {
995
- remainingSymlinks.push(agent.displayName);
1082
+ const hasRef = agentSkillPaths.some((p) => fs8.existsSync(p));
1083
+ if (hasRef) {
1084
+ remainingRefs.push(agent.displayName);
996
1085
  }
997
1086
  }
998
- if (remainingSymlinks.length > 0) {
1087
+ if (remainingRefs.length > 0) {
999
1088
  logger.warn(`
1000
- \u26A0\uFE0F Warning: Found remaining symlinks in:`);
1001
- for (const agentName of remainingSymlinks) {
1089
+ \u26A0\uFE0F Warning: Found remaining references in:`);
1090
+ for (const agentName of remainingRefs) {
1002
1091
  logger.info(` - ${agentName}`);
1003
1092
  }
1004
- logger.info("\nYou may need to manually remove these symlinks");
1093
+ logger.info("\nYou may need to manually remove these");
1005
1094
  } else {
1006
1095
  logger.success(`\u2705 Skill "${extractedName}" removed successfully!`);
1007
1096
  }