eskill 1.0.33 → 1.2.0
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/cli.js +21 -2
- package/lib/installer.js +128 -14
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
-
import { installFromGitUrl, listSkills, removeSkill, cleanupAllSkills, cleanupAll, updateSkill, updateAllSkills } from './lib/installer.js';
|
|
4
|
+
import { installFromGitUrl, listSkills, removeSkill, linkSkill, cleanupAllSkills, cleanupAll, updateSkill, updateAllSkills } from './lib/installer.js';
|
|
5
5
|
import { AGENTS, getDefaultAgent } from './lib/agent-config.js';
|
|
6
6
|
import { bashCompletionScript, zshCompletionScript, listSkillsForCompletion } from './lib/completion.js';
|
|
7
7
|
import { searchSkills, formatSkillList } from './lib/search.js';
|
|
@@ -13,7 +13,7 @@ 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
|
|
16
|
+
.version('1.2.0')
|
|
17
17
|
.option('-g, --global', '使用全局技能目录(~/.eskill/skills/),否则使用当前目录(./.claude/skills/)', false);
|
|
18
18
|
|
|
19
19
|
// 安装命令
|
|
@@ -105,6 +105,25 @@ program
|
|
|
105
105
|
}
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
// 链接命令
|
|
109
|
+
program
|
|
110
|
+
.command('link')
|
|
111
|
+
.description('将全局技能软链接到本地')
|
|
112
|
+
.argument('<name>', '技能名称(不需要 @作者)')
|
|
113
|
+
.action(async (name) => {
|
|
114
|
+
try {
|
|
115
|
+
const result = await linkSkill(name);
|
|
116
|
+
|
|
117
|
+
// 如果用户取消链接,正常退出
|
|
118
|
+
if (result && result.cancelled) {
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(`\n❌ 链接失败: ${error.message}`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
108
127
|
// 更新命令
|
|
109
128
|
program
|
|
110
129
|
.command('update')
|
package/lib/installer.js
CHANGED
|
@@ -250,9 +250,25 @@ async function handleSkillAtAuthorFormat(input, global = false) {
|
|
|
250
250
|
const globalSkillPath = join(globalSkillsDir, skillName);
|
|
251
251
|
|
|
252
252
|
if (existsSync(globalSkillPath)) {
|
|
253
|
-
//
|
|
253
|
+
// 全局仓库中找到,检查本地是否已存在
|
|
254
254
|
console.log(`✓ 在全局仓库中找到技能: ${skillName}`);
|
|
255
|
-
console.log(` 位置: ~/.eskill/skills/${skillName}
|
|
255
|
+
console.log(` 位置: ~/.eskill/skills/${skillName}`);
|
|
256
|
+
|
|
257
|
+
const localSkillsDir = getSkillsDir(false);
|
|
258
|
+
const localSkillPath = join(localSkillsDir, skillName);
|
|
259
|
+
|
|
260
|
+
if (existsSync(localSkillPath)) {
|
|
261
|
+
console.log(`\n⚠️ 本地已存在同名技能: ${skillName}`);
|
|
262
|
+
console.log(` 位置: ${localSkillPath}\n`);
|
|
263
|
+
|
|
264
|
+
const overwrite = await confirmAction('是否覆盖本地已存在的技能?');
|
|
265
|
+
if (!overwrite) {
|
|
266
|
+
console.log('\n已取消安装\n');
|
|
267
|
+
throw new Error('用户取消安装');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
console.log();
|
|
256
272
|
|
|
257
273
|
// 返回特殊标记,表示需要从全局复制
|
|
258
274
|
return `GLOBAL:${skillName}`;
|
|
@@ -284,9 +300,25 @@ async function handleSkillAtAuthorFormat(input, global = false) {
|
|
|
284
300
|
const globalSkillPath = join(globalSkillsDir, skillName);
|
|
285
301
|
|
|
286
302
|
if (existsSync(globalSkillPath)) {
|
|
287
|
-
//
|
|
303
|
+
// 全局仓库中找到,检查本地是否已存在
|
|
288
304
|
console.log(`✓ 在全局仓库中找到技能: ${skillName}`);
|
|
289
|
-
console.log(` 位置: ~/.eskill/skills/${skillName}
|
|
305
|
+
console.log(` 位置: ~/.eskill/skills/${skillName}`);
|
|
306
|
+
|
|
307
|
+
const localSkillsDir = getSkillsDir(false);
|
|
308
|
+
const localSkillPath = join(localSkillsDir, skillName);
|
|
309
|
+
|
|
310
|
+
if (existsSync(localSkillPath)) {
|
|
311
|
+
console.log(`\n⚠️ 本地已存在同名技能: ${skillName}`);
|
|
312
|
+
console.log(` 位置: ${localSkillPath}\n`);
|
|
313
|
+
|
|
314
|
+
const overwrite = await confirmAction('是否覆盖本地已存在的技能?');
|
|
315
|
+
if (!overwrite) {
|
|
316
|
+
console.log('\n已取消安装\n');
|
|
317
|
+
throw new Error('用户取消安装');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
console.log();
|
|
290
322
|
|
|
291
323
|
// 返回特殊标记,表示需要从全局复制
|
|
292
324
|
return `GLOBAL:${skillName}`;
|
|
@@ -366,10 +398,17 @@ async function copyFromGlobal(skillName, options = {}) {
|
|
|
366
398
|
const sourcePath = join(globalSkillsDir, skillName);
|
|
367
399
|
const targetPath = join(localSkillsDir, skillName);
|
|
368
400
|
|
|
369
|
-
//
|
|
401
|
+
// 检查本地是否已存在同名技能
|
|
370
402
|
if (existsSync(targetPath)) {
|
|
371
403
|
if (!force) {
|
|
372
|
-
|
|
404
|
+
console.log(`\n⚠️ 本地已存在技能: ${skillName}`);
|
|
405
|
+
console.log(` 位置: ${targetPath}\n`);
|
|
406
|
+
|
|
407
|
+
const overwrite = await confirmAction('是否强制覆盖已存在的技能?');
|
|
408
|
+
if (!overwrite) {
|
|
409
|
+
console.log('\n已取消安装\n');
|
|
410
|
+
return { success: false, cancelled: true };
|
|
411
|
+
}
|
|
373
412
|
}
|
|
374
413
|
console.log(`删除本地已存在的技能: ${targetPath}`);
|
|
375
414
|
rmSync(targetPath, { recursive: true, force: true });
|
|
@@ -418,13 +457,21 @@ function confirmAction(message) {
|
|
|
418
457
|
export async function installFromGitUrl(gitUrl, options = {}) {
|
|
419
458
|
const { agent = 'claude', link = false, force = false, global = false } = options;
|
|
420
459
|
|
|
421
|
-
|
|
422
|
-
|
|
460
|
+
try {
|
|
461
|
+
// 检测并处理 name@author 格式
|
|
462
|
+
let actualUrl = await handleSkillAtAuthorFormat(gitUrl, global);
|
|
423
463
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
464
|
+
// 如果是从全局复制
|
|
465
|
+
if (actualUrl.startsWith('GLOBAL:')) {
|
|
466
|
+
const skillName = actualUrl.replace('GLOBAL:', '');
|
|
467
|
+
return await copyFromGlobal(skillName, options);
|
|
468
|
+
}
|
|
469
|
+
} catch (error) {
|
|
470
|
+
// 用户取消安装
|
|
471
|
+
if (error.message === '用户取消安装') {
|
|
472
|
+
return { success: false, cancelled: true };
|
|
473
|
+
}
|
|
474
|
+
throw error;
|
|
428
475
|
}
|
|
429
476
|
|
|
430
477
|
// 解析 Git URL
|
|
@@ -462,10 +509,17 @@ export async function installFromGitUrl(gitUrl, options = {}) {
|
|
|
462
509
|
mkdirSync(skillsDir, { recursive: true });
|
|
463
510
|
}
|
|
464
511
|
|
|
465
|
-
//
|
|
512
|
+
// 检查是否已存在同名技能
|
|
466
513
|
if (existsSync(targetPath)) {
|
|
467
514
|
if (!force) {
|
|
468
|
-
|
|
515
|
+
console.log(`\n⚠️ 技能已存在: ${skillName}`);
|
|
516
|
+
console.log(` 位置: ${targetPath}\n`);
|
|
517
|
+
|
|
518
|
+
const overwrite = await confirmAction('是否强制覆盖已存在的技能?');
|
|
519
|
+
if (!overwrite) {
|
|
520
|
+
console.log('\n已取消安装\n');
|
|
521
|
+
return { success: false, cancelled: true };
|
|
522
|
+
}
|
|
469
523
|
}
|
|
470
524
|
console.log(`删除已存在的技能: ${targetPath}`);
|
|
471
525
|
rmSync(targetPath, { recursive: true, force: true });
|
|
@@ -596,6 +650,66 @@ export function removeSkill(skillName, agent = 'claude', global = false) {
|
|
|
596
650
|
console.log(`✓ 已删除技能: ${skillName}`);
|
|
597
651
|
}
|
|
598
652
|
|
|
653
|
+
/**
|
|
654
|
+
* 链接全局技能到本地
|
|
655
|
+
*/
|
|
656
|
+
export async function linkSkill(skillName) {
|
|
657
|
+
const globalSkillsDir = getSkillsDir(true);
|
|
658
|
+
const localSkillsDir = getSkillsDir(false);
|
|
659
|
+
|
|
660
|
+
const globalSkillPath = join(globalSkillsDir, skillName);
|
|
661
|
+
const localSkillPath = join(localSkillsDir, skillName);
|
|
662
|
+
|
|
663
|
+
// 检查全局是否存在该技能
|
|
664
|
+
if (!existsSync(globalSkillPath)) {
|
|
665
|
+
console.log(`\n❌ 全局仓库中不存在技能: ${skillName}`);
|
|
666
|
+
console.log(` 位置: ~/.eskill/skills/${skillName}\n`);
|
|
667
|
+
console.log(`💡 提示:`);
|
|
668
|
+
console.log(` - 使用 "eskill install -g ${skillName}" 先安装到全局仓库`);
|
|
669
|
+
console.log(` - 或使用 "eskill install" 直接安装到本地\n`);
|
|
670
|
+
throw new Error(`全局仓库中不存在技能: ${skillName}`);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// 确保本地技能目录存在
|
|
674
|
+
if (!existsSync(localSkillsDir)) {
|
|
675
|
+
mkdirSync(localSkillsDir, { recursive: true });
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// 检查本地是否已存在同名技能
|
|
679
|
+
if (existsSync(localSkillPath)) {
|
|
680
|
+
console.log(`\n⚠️ 本地已存在同名技能: ${skillName}`);
|
|
681
|
+
console.log(` 位置: ${localSkillPath}\n`);
|
|
682
|
+
|
|
683
|
+
const overwrite = await confirmAction('是否删除本地技能并创建软链接?');
|
|
684
|
+
if (!overwrite) {
|
|
685
|
+
console.log('\n已取消链接\n');
|
|
686
|
+
return { success: false, cancelled: true };
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
console.log(`删除本地已存在的技能: ${localSkillPath}`);
|
|
690
|
+
rmSync(localSkillPath, { recursive: true, force: true });
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// 创建软链接
|
|
694
|
+
console.log(`\n创建软链接:`);
|
|
695
|
+
console.log(` 源: ${globalSkillPath}`);
|
|
696
|
+
console.log(` 目标: ${localSkillPath}\n`);
|
|
697
|
+
|
|
698
|
+
try {
|
|
699
|
+
symlinkSync(globalSkillPath, localSkillPath, 'dir');
|
|
700
|
+
|
|
701
|
+
console.log(`✓ 软链接创建成功`);
|
|
702
|
+
console.log(` 技能: ${skillName}`);
|
|
703
|
+
console.log(` 本地路径: ${localSkillPath}`);
|
|
704
|
+
console.log(` 全局路径: ${globalSkillPath}`);
|
|
705
|
+
console.log(` 说明: 本地技能指向全局仓库,全局更新会自动同步\n`);
|
|
706
|
+
|
|
707
|
+
return { success: true, path: localSkillPath };
|
|
708
|
+
} catch (error) {
|
|
709
|
+
throw new Error(`创建软链接失败: ${error.message}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
599
713
|
/**
|
|
600
714
|
* 清理所有技能(用于卸载)
|
|
601
715
|
*/
|