eskill 1.1.0 → 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 +110 -10
- 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.
|
|
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}`;
|
|
@@ -425,13 +457,21 @@ function confirmAction(message) {
|
|
|
425
457
|
export async function installFromGitUrl(gitUrl, options = {}) {
|
|
426
458
|
const { agent = 'claude', link = false, force = false, global = false } = options;
|
|
427
459
|
|
|
428
|
-
|
|
429
|
-
|
|
460
|
+
try {
|
|
461
|
+
// 检测并处理 name@author 格式
|
|
462
|
+
let actualUrl = await handleSkillAtAuthorFormat(gitUrl, global);
|
|
430
463
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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;
|
|
435
475
|
}
|
|
436
476
|
|
|
437
477
|
// 解析 Git URL
|
|
@@ -610,6 +650,66 @@ export function removeSkill(skillName, agent = 'claude', global = false) {
|
|
|
610
650
|
console.log(`✓ 已删除技能: ${skillName}`);
|
|
611
651
|
}
|
|
612
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
|
+
|
|
613
713
|
/**
|
|
614
714
|
* 清理所有技能(用于卸载)
|
|
615
715
|
*/
|