@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/README.md +37 -7
- package/dist/index.cjs +226 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +226 -137
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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: "
|
|
33
|
-
displayName: "
|
|
34
|
-
|
|
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: "
|
|
39
|
-
displayName: "
|
|
40
|
-
|
|
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: "
|
|
45
|
-
displayName: "
|
|
46
|
-
skillsDir: path.join(HOME, ".
|
|
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
|
|
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: "
|
|
69
|
-
displayName: "
|
|
70
|
-
skillsDir: path.join(
|
|
113
|
+
name: "goose",
|
|
114
|
+
displayName: "Goose",
|
|
115
|
+
skillsDir: path.join(CONFIG_HOME, "goose", "skills"),
|
|
71
116
|
enabled: true
|
|
72
117
|
},
|
|
73
118
|
{
|
|
74
|
-
name: "
|
|
75
|
-
displayName: "
|
|
76
|
-
|
|
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: "
|
|
97
|
-
displayName: "
|
|
98
|
-
skillsDir: path.join(
|
|
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: "
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 (!
|
|
350
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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 =
|
|
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
|
|
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 (!
|
|
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 (!
|
|
517
|
+
if (!fs5.existsSync(targetDir)) {
|
|
452
518
|
try {
|
|
453
|
-
|
|
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 (
|
|
525
|
+
if (fs5.existsSync(target)) {
|
|
460
526
|
try {
|
|
461
|
-
const stats =
|
|
527
|
+
const stats = fs5.lstatSync(target);
|
|
462
528
|
if (stats.isSymbolicLink()) {
|
|
463
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
563
|
+
if (!fs5.existsSync(target)) {
|
|
492
564
|
continue;
|
|
493
565
|
}
|
|
494
566
|
try {
|
|
495
|
-
const stats =
|
|
567
|
+
const stats = fs5.lstatSync(target);
|
|
496
568
|
if (stats.isSymbolicLink()) {
|
|
497
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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
|
|
570
|
-
|
|
571
|
-
if (gitInfo.
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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(`
|
|
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 (!
|
|
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 (!
|
|
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 ||
|
|
711
|
+
} else if (options.link || fs6.existsSync(skillNameOrPath)) {
|
|
632
712
|
skillPath = path5.resolve(skillNameOrPath);
|
|
633
|
-
if (!
|
|
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 (
|
|
718
|
+
if (fs6.existsSync(pkgPath)) {
|
|
639
719
|
try {
|
|
640
|
-
const pkg = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
838
|
+
fs6.rmSync(targetPath, { recursive: true, force: true });
|
|
757
839
|
} else {
|
|
758
|
-
logger.
|
|
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 (
|
|
764
|
-
|
|
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
|
-
|
|
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("
|
|
779
|
-
logger.info("
|
|
780
|
-
logger.info("
|
|
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 =
|
|
869
|
+
const agent = AGENTS.find((a) => a.name === options.agent && a.enabled);
|
|
786
870
|
if (!agent) {
|
|
787
|
-
logger.error(`
|
|
871
|
+
logger.error(`Unknown agent: ${options.agent}`);
|
|
788
872
|
logger.info(`
|
|
789
|
-
|
|
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
|
|
810
|
-
|
|
811
|
-
const entries =
|
|
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
|
-
|
|
906
|
+
copyDir2(srcPath, destPath);
|
|
820
907
|
} else {
|
|
821
|
-
|
|
908
|
+
fs6.copyFileSync(srcPath, destPath);
|
|
822
909
|
}
|
|
823
910
|
}
|
|
824
911
|
}
|
|
825
912
|
|
|
826
913
|
// src/commands/list.ts
|
|
827
|
-
import
|
|
914
|
+
import fs7 from "fs";
|
|
828
915
|
import path6 from "path";
|
|
829
916
|
import chalk3 from "chalk";
|
|
830
917
|
function list() {
|
|
831
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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 (
|
|
943
|
+
if (fs7.existsSync(pkgPath)) {
|
|
857
944
|
try {
|
|
858
|
-
const pkgContent =
|
|
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 (
|
|
955
|
+
if (fs7.existsSync(skillMdPath)) {
|
|
869
956
|
try {
|
|
870
|
-
const skillMdContent =
|
|
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 (
|
|
889
|
-
const pkg = JSON.parse(
|
|
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
|
|
993
|
+
const hasRef = skillsDirs.some((dir) => {
|
|
907
994
|
const agentSkillPath = path6.join(dir, skill);
|
|
908
|
-
if (
|
|
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 (
|
|
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
|
|
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 =
|
|
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 =
|
|
1041
|
+
const agent = AGENTS.find((a) => a.name === options.agent && a.enabled);
|
|
951
1042
|
if (!agent) {
|
|
952
|
-
logger.error(`
|
|
1043
|
+
logger.error(`Unknown agent: ${options.agent}`);
|
|
953
1044
|
logger.info(`
|
|
954
|
-
|
|
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 =
|
|
1067
|
+
const stats = fs8.lstatSync(sharedPath);
|
|
977
1068
|
if (stats.isSymbolicLink()) {
|
|
978
|
-
|
|
1069
|
+
fs8.unlinkSync(sharedPath);
|
|
979
1070
|
logger.success("Removed symlink from shared directory");
|
|
980
1071
|
} else {
|
|
981
|
-
|
|
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
|
|
1079
|
+
const remainingRefs = [];
|
|
989
1080
|
for (const agent of AGENTS) {
|
|
990
1081
|
const agentSkillPaths = getAgentSkillPaths(agent.name, extractedName, cwd);
|
|
991
|
-
const
|
|
992
|
-
|
|
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 (
|
|
1087
|
+
if (remainingRefs.length > 0) {
|
|
999
1088
|
logger.warn(`
|
|
1000
|
-
\u26A0\uFE0F Warning: Found remaining
|
|
1001
|
-
for (const agentName of
|
|
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
|
|
1093
|
+
logger.info("\nYou may need to manually remove these");
|
|
1005
1094
|
} else {
|
|
1006
1095
|
logger.success(`\u2705 Skill "${extractedName}" removed successfully!`);
|
|
1007
1096
|
}
|