eskill 1.0.27 → 1.0.28

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.
Files changed (3) hide show
  1. package/cli.js +33 -10
  2. package/lib/installer.js +63 -40
  3. package/package.json +1 -1
package/cli.js CHANGED
@@ -13,7 +13,8 @@ const program = new Command();
13
13
  program
14
14
  .name('eskill')
15
15
  .description('Unified AI Agent Skills Management - Install skills from Git URLs')
16
- .version('1.0.27');
16
+ .version('1.0.28')
17
+ .option('-g, --global', '使用全局技能目录(~/.eskill/skills/),否则使用当前目录(./.claude/skills/)', false);
17
18
 
18
19
  // 安装命令
19
20
  program
@@ -26,7 +27,11 @@ program
26
27
  .option('-f, --force', '强制覆盖已存在的技能', false)
27
28
  .action(async (url, options) => {
28
29
  try {
29
- const result = await installFromGitUrl(url, options);
30
+ // 获取全局选项
31
+ const globalOptions = program.opts();
32
+ const fullOptions = { ...options, global: globalOptions.global };
33
+
34
+ const result = await installFromGitUrl(url, fullOptions);
30
35
 
31
36
  // 如果用户取消安装,正常退出
32
37
  if (result && result.cancelled) {
@@ -49,14 +54,19 @@ program
49
54
  .option('-a, --agent <name>', '目标 agent', getDefaultAgent())
50
55
  .action(async (options) => {
51
56
  try {
52
- const skills = await listSkills(options.agent);
57
+ // 获取全局选项
58
+ const globalOptions = program.opts();
59
+ const global = globalOptions.global;
60
+
61
+ const skills = await listSkills(options.agent, global);
53
62
 
54
63
  if (skills.length === 0) {
55
64
  console.log('未安装任何技能');
56
65
  return;
57
66
  }
58
67
 
59
- console.log(`\n已安装的技能:\n`);
68
+ const location = global ? '~/.eskill/skills/' : './.claude/skills/';
69
+ console.log(`\n已安装的技能 (${location}):\n`);
60
70
  skills.forEach(skill => {
61
71
  const displayName = skill.author ? `${skill.name}@${skill.author}` : skill.name;
62
72
  const versionInfo = skill.version || (skill.commitHash ? `#${skill.commitHash.substring(0, 7)}` : '');
@@ -84,7 +94,11 @@ program
84
94
  .option('-a, --agent <name>', '目标 agent', getDefaultAgent())
85
95
  .action(async (name, options) => {
86
96
  try {
87
- removeSkill(name, options.agent);
97
+ // 获取全局选项
98
+ const globalOptions = program.opts();
99
+ const global = globalOptions.global;
100
+
101
+ removeSkill(name, options.agent, global);
88
102
  } catch (error) {
89
103
  console.error(`错误: ${error.message}`);
90
104
  process.exit(1);
@@ -99,14 +113,18 @@ program
99
113
  .option('-f, --force', '强制更新(即使版本相同)', false)
100
114
  .action(async (name, options) => {
101
115
  try {
116
+ // 获取全局选项
117
+ const globalOptions = program.opts();
118
+ const fullOptions = { ...options, global: globalOptions.global };
119
+
102
120
  // 支持显式使用 "all" 参数
103
121
  if (name === 'all' || name === 'All' || name === 'ALL') {
104
122
  // 更新所有技能
105
- await updateAllSkills(options);
123
+ await updateAllSkills(fullOptions);
106
124
  } else if (name) {
107
125
  // 更新单个技能
108
126
  console.log(`\n检查技能: ${name}`);
109
- const result = await updateSkill(name, options);
127
+ const result = await updateSkill(name, fullOptions);
110
128
 
111
129
  if (result.local) {
112
130
  console.log('\n⚠️ 本地技能无法自动更新,请手动更新\n');
@@ -119,7 +137,7 @@ program
119
137
  }
120
138
  } else {
121
139
  // 更新所有技能(不指定参数时)
122
- await updateAllSkills(options);
140
+ await updateAllSkills(fullOptions);
123
141
  }
124
142
  } catch (error) {
125
143
  console.error(`\n❌ 更新失败: ${error.message}\n`);
@@ -289,6 +307,10 @@ program
289
307
  .option('-a, --all', '清理所有数据(包括配置和技能)', false)
290
308
  .action(async (options) => {
291
309
  try {
310
+ // 获取全局选项
311
+ const globalOptions = program.opts();
312
+ const global = globalOptions.global;
313
+
292
314
  if (options.all) {
293
315
  console.log('\n⚠️ 警告:这将删除所有 eskill 数据(包括 API Key 配置和已安装的技能)');
294
316
 
@@ -306,8 +328,9 @@ program
306
328
  }
307
329
  });
308
330
  } else {
309
- console.log('\n清理所有已安装的技能(保留配置)...\n');
310
- cleanupAllSkills();
331
+ const location = global ? '~/.eskill/skills/' : './.claude/skills/';
332
+ console.log(`\n清理 ${location} 中的所有技能...\n`);
333
+ cleanupAllSkills(global);
311
334
  }
312
335
  } catch (error) {
313
336
  console.error(`\n❌ 清理失败: ${error.message}\n`);
package/lib/installer.js CHANGED
@@ -58,21 +58,29 @@ function getEskillPackageDir() {
58
58
  }
59
59
 
60
60
  /**
61
- * 获取技能存储目录(用户主目录下,不受 npm 更新影响)
61
+ * 获取技能存储目录
62
+ * @param {boolean} global - 是否使用全局目录(~/.eskill/skills/),否则使用当前目录(./.claude/skills/)
62
63
  */
63
- function getSkillsStorageDir() {
64
- return join(homedir(), '.eskill', 'skills');
64
+ function getSkillsStorageDir(global = false) {
65
+ if (global) {
66
+ // 全局模式:使用 ~/.eskill/skills/
67
+ return join(homedir(), '.eskill', 'skills');
68
+ } else {
69
+ // 本地模式:使用当前目录的 .claude/skills/
70
+ return join(process.cwd(), '.claude', 'skills');
71
+ }
65
72
  }
66
73
 
67
74
  /**
68
75
  * 迁移旧位置的技能到新位置
76
+ * @param {boolean} global - 是否全局模式
69
77
  */
70
- function migrateOldSkills() {
78
+ function migrateOldSkills(global = false) {
71
79
  const __filename = fileURLToPath(import.meta.url);
72
80
  const __dirname = dirname(__filename);
73
81
  const pkgDir = dirname(__dirname);
74
82
  const oldDir = join(pkgDir, 'skills-storage');
75
- const newDir = getSkillsStorageDir();
83
+ const newDir = getSkillsStorageDir(global);
76
84
 
77
85
  // 如果旧位置不存在,无需迁移
78
86
  if (!existsSync(oldDir)) {
@@ -84,6 +92,11 @@ function migrateOldSkills() {
84
92
  return;
85
93
  }
86
94
 
95
+ // 只在全局模式下迁移
96
+ if (!global) {
97
+ return;
98
+ }
99
+
87
100
  try {
88
101
  // 创建新目录
89
102
  mkdirSync(newDir, { recursive: true });
@@ -119,9 +132,10 @@ function migrateOldSkills() {
119
132
 
120
133
  /**
121
134
  * 读取所有技能元数据
135
+ * @param {boolean} global - 是否全局模式
122
136
  */
123
- function getAllSkillsMeta() {
124
- const skillDir = getSkillsStorageDir();
137
+ function getAllSkillsMeta(global = false) {
138
+ const skillDir = getSkillsStorageDir(global);
125
139
  const metaPath = join(skillDir, '.eskill-meta.json');
126
140
 
127
141
  if (!existsSync(metaPath)) {
@@ -138,9 +152,11 @@ function getAllSkillsMeta() {
138
152
 
139
153
  /**
140
154
  * 保存所有技能元数据
155
+ * @param {metaArray} metaArray - 元数据数组
156
+ * @param {boolean} global - 是否全局模式
141
157
  */
142
- function saveAllSkillsMeta(metaArray) {
143
- const skillDir = getSkillsStorageDir();
158
+ function saveAllSkillsMeta(metaArray, global = false) {
159
+ const skillDir = getSkillsStorageDir(global);
144
160
 
145
161
  // 确保目录存在
146
162
  if (!existsSync(skillDir)) {
@@ -153,9 +169,12 @@ function saveAllSkillsMeta(metaArray) {
153
169
 
154
170
  /**
155
171
  * 保存单个技能元数据
172
+ * @param {string} skillName - 技能名称
173
+ * @param {object} meta - 元数据
174
+ * @param {boolean} global - 是否全局模式
156
175
  */
157
- function saveSkillMeta(skillName, meta) {
158
- const allMeta = getAllSkillsMeta();
176
+ function saveSkillMeta(skillName, meta, global = false) {
177
+ const allMeta = getAllSkillsMeta(global);
159
178
 
160
179
  // 查找并更新或添加
161
180
  const index = allMeta.findIndex(m => m.name === skillName);
@@ -165,24 +184,28 @@ function saveSkillMeta(skillName, meta) {
165
184
  allMeta.push(meta);
166
185
  }
167
186
 
168
- saveAllSkillsMeta(allMeta);
187
+ saveAllSkillsMeta(allMeta, global);
169
188
  }
170
189
 
171
190
  /**
172
191
  * 读取单个技能元数据
192
+ * @param {string} skillName - 技能名称
193
+ * @param {boolean} global - 是否全局模式
173
194
  */
174
- function getSkillMeta(skillName) {
175
- const allMeta = getAllSkillsMeta();
195
+ function getSkillMeta(skillName, global = false) {
196
+ const allMeta = getAllSkillsMeta(global);
176
197
  return allMeta.find(m => m.name === skillName) || null;
177
198
  }
178
199
 
179
200
  /**
180
201
  * 删除技能元数据
202
+ * @param {string} skillName - 技能名称
203
+ * @param {boolean} global - 是否全局模式
181
204
  */
182
- function removeSkillMeta(skillName) {
183
- const allMeta = getAllSkillsMeta();
205
+ function removeSkillMeta(skillName, global = false) {
206
+ const allMeta = getAllSkillsMeta(global);
184
207
  const newMeta = allMeta.filter(m => m.name !== skillName);
185
- saveAllSkillsMeta(newMeta);
208
+ saveAllSkillsMeta(newMeta, global);
186
209
  }
187
210
 
188
211
  /**
@@ -280,7 +303,7 @@ function confirmAction(message) {
280
303
  * 从 Git URL 安装技能
281
304
  */
282
305
  export async function installFromGitUrl(gitUrl, options = {}) {
283
- const { agent = 'claude', link = false, force = false } = options;
306
+ const { agent = 'claude', link = false, force = false, global = false } = options;
284
307
 
285
308
  // 检测并处理 name@author 格式
286
309
  let actualUrl = await handleSkillAtAuthorFormat(gitUrl);
@@ -304,7 +327,7 @@ export async function installFromGitUrl(gitUrl, options = {}) {
304
327
  }
305
328
 
306
329
  // 获取技能存储目录
307
- const skillDir = getSkillsStorageDir();
330
+ const skillDir = getSkillsStorageDir(global);
308
331
 
309
332
  // 确保技能目录存在
310
333
  if (!existsSync(skillDir)) {
@@ -385,7 +408,7 @@ export async function installFromGitUrl(gitUrl, options = {}) {
385
408
  version,
386
409
  installedAt: new Date().toISOString(),
387
410
  updatedAt: new Date().toISOString()
388
- });
411
+ }, global);
389
412
 
390
413
  return { success: true, path: targetPath };
391
414
  } finally {
@@ -400,11 +423,11 @@ export async function installFromGitUrl(gitUrl, options = {}) {
400
423
  /**
401
424
  * 列出已安装的技能
402
425
  */
403
- export function listSkills(agent = 'claude') {
426
+ export function listSkills(agent = 'claude', global = false) {
404
427
  // 自动迁移旧位置的技能
405
- migrateOldSkills();
428
+ migrateOldSkills(global);
406
429
 
407
- const skillDir = getSkillsStorageDir();
430
+ const skillDir = getSkillsStorageDir(global);
408
431
 
409
432
  if (!existsSync(skillDir)) {
410
433
  return [];
@@ -415,7 +438,7 @@ export function listSkills(agent = 'claude') {
415
438
  .filter(dirent => dirent.isDirectory());
416
439
 
417
440
  // 读取所有元数据
418
- const allMeta = getAllSkillsMeta();
441
+ const allMeta = getAllSkillsMeta(global);
419
442
 
420
443
  return skillDirs.map(dirent => {
421
444
  const skillPath = join(skillDir, dirent.name);
@@ -435,8 +458,8 @@ export function listSkills(agent = 'claude') {
435
458
  /**
436
459
  * 删除技能
437
460
  */
438
- export function removeSkill(skillName, agent = 'claude') {
439
- const skillDir = getSkillsStorageDir();
461
+ export function removeSkill(skillName, agent = 'claude', global = false) {
462
+ const skillDir = getSkillsStorageDir(global);
440
463
  const targetPath = join(skillDir, skillName);
441
464
 
442
465
  if (!existsSync(targetPath)) {
@@ -444,15 +467,15 @@ export function removeSkill(skillName, agent = 'claude') {
444
467
  }
445
468
 
446
469
  rmSync(targetPath, { recursive: true, force: true });
447
- removeSkillMeta(skillName);
470
+ removeSkillMeta(skillName, global);
448
471
  console.log(`✓ 已删除技能: ${skillName}`);
449
472
  }
450
473
 
451
474
  /**
452
475
  * 清理所有技能(用于卸载)
453
476
  */
454
- export function cleanupAllSkills() {
455
- const skillDir = getSkillsStorageDir();
477
+ export function cleanupAllSkills(global = false) {
478
+ const skillDir = getSkillsStorageDir(global);
456
479
 
457
480
  if (!existsSync(skillDir)) {
458
481
  console.log('没有需要清理的技能');
@@ -491,8 +514,8 @@ export function cleanupAll() {
491
514
  /**
492
515
  * 获取技能存储目录路径
493
516
  */
494
- export function getSkillsDir() {
495
- return getSkillsStorageDir();
517
+ export function getSkillsDir(global = false) {
518
+ return getSkillsStorageDir(global);
496
519
  }
497
520
 
498
521
  /**
@@ -516,8 +539,8 @@ function getRemoteCommitHash(gitUrl, branch) {
516
539
  * 更新单个技能
517
540
  */
518
541
  export async function updateSkill(skillName, options = {}) {
519
- const { force = false } = options;
520
- const skillDir = getSkillsStorageDir();
542
+ const { force = false, global = false } = options;
543
+ const skillDir = getSkillsStorageDir(global);
521
544
  const targetPath = join(skillDir, skillName);
522
545
 
523
546
  // 检查技能是否存在
@@ -526,7 +549,7 @@ export async function updateSkill(skillName, options = {}) {
526
549
  }
527
550
 
528
551
  // 读取元数据
529
- const meta = getSkillMeta(skillName);
552
+ const meta = getSkillMeta(skillName, global);
530
553
 
531
554
  // 如果没有 gitUrl,说明是本地技能,无法更新
532
555
  if (!meta || !meta.gitUrl) {
@@ -599,7 +622,7 @@ export async function updateSkill(skillName, options = {}) {
599
622
  commitHash: newCommitHash,
600
623
  version: newVersion,
601
624
  updatedAt: new Date().toISOString()
602
- });
625
+ }, global);
603
626
 
604
627
  return {
605
628
  success: true,
@@ -620,12 +643,12 @@ export async function updateSkill(skillName, options = {}) {
620
643
  * 更新所有技能
621
644
  */
622
645
  export async function updateAllSkills(options = {}) {
623
- const { force = false } = options;
646
+ const { force = false, global = false } = options;
624
647
 
625
648
  // 自动迁移旧位置的技能
626
- migrateOldSkills();
649
+ migrateOldSkills(global);
627
650
 
628
- const skillDir = getSkillsStorageDir();
651
+ const skillDir = getSkillsStorageDir(global);
629
652
 
630
653
  if (!existsSync(skillDir)) {
631
654
  console.log('未安装任何技能');
@@ -637,7 +660,7 @@ export async function updateAllSkills(options = {}) {
637
660
  .filter(dirent => dirent.isDirectory())
638
661
  .map(dirent => {
639
662
  const skillPath = join(skillDir, dirent.name);
640
- const meta = getSkillMeta(dirent.name);
663
+ const meta = getSkillMeta(dirent.name, global);
641
664
  return {
642
665
  name: dirent.name,
643
666
  path: skillPath,
@@ -668,7 +691,7 @@ export async function updateAllSkills(options = {}) {
668
691
  }
669
692
 
670
693
  // 更新技能
671
- const result = await updateSkill(skill.name, { force });
694
+ const result = await updateSkill(skill.name, { force, global });
672
695
 
673
696
  if (result.success && result.updated) {
674
697
  console.log(`✓ ${skill.name} - ${result.currentVersion} → ${result.newVersion}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eskill",
3
- "version": "1.0.27",
3
+ "version": "1.0.28",
4
4
  "description": "Unified AI Agent Skills Management - Install skills from Git URLs",
5
5
  "main": "index.js",
6
6
  "type": "module",