eskill 1.2.3 → 1.3.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 +114 -5
- 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, linkSkill, cleanupAllSkills, cleanupAll, updateSkill, updateAllSkills } from './lib/installer.js';
|
|
4
|
+
import { installFromGitUrl, listSkills, removeSkill, linkSkill, uploadSkill, 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.3.0')
|
|
17
17
|
.option('-g, --global', '使用全局技能目录(~/.eskill/skills/),否则使用当前目录(./.claude/skills/)', false);
|
|
18
18
|
|
|
19
19
|
// 安装命令
|
|
@@ -141,6 +141,25 @@ program
|
|
|
141
141
|
}
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
// 上传命令
|
|
145
|
+
program
|
|
146
|
+
.command('upload')
|
|
147
|
+
.description('上传本地技能到全局仓库')
|
|
148
|
+
.argument('<name>', '技能名称(支持 name@author 格式)')
|
|
149
|
+
.action(async (name) => {
|
|
150
|
+
try {
|
|
151
|
+
const result = await uploadSkill(name);
|
|
152
|
+
|
|
153
|
+
// 如果用户取消上传,正常退出
|
|
154
|
+
if (result && result.cancelled) {
|
|
155
|
+
process.exit(0);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error(`\n❌ 上传失败: ${error.message}`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
144
163
|
// 更新命令
|
|
145
164
|
program
|
|
146
165
|
.command('update')
|
package/lib/installer.js
CHANGED
|
@@ -434,7 +434,12 @@ async function copyFromGlobal(skillName, options = {}) {
|
|
|
434
434
|
console.log(` 源: ${sourcePath}`);
|
|
435
435
|
console.log(` 目标: ${targetPath}\n`);
|
|
436
436
|
|
|
437
|
-
|
|
437
|
+
// 使用 -L 选项解引用符号链接,复制实际文件内容
|
|
438
|
+
if (process.platform === 'win32') {
|
|
439
|
+
execSync(`xcopy "${sourcePath}" "${targetPath}" /E /I /H /Y`, { stdio: 'inherit' });
|
|
440
|
+
} else {
|
|
441
|
+
execSync(`cp -rL "${sourcePath}" "${targetPath}"`, { stdio: 'inherit' });
|
|
442
|
+
}
|
|
438
443
|
|
|
439
444
|
// 复制元数据,并标记来源
|
|
440
445
|
const globalMeta = getSkillMeta(skillName, true);
|
|
@@ -597,12 +602,13 @@ export async function installFromGitUrl(gitUrl, options = {}) {
|
|
|
597
602
|
console.log(`\n创建符号链接: ${sourcePath} -> ${targetPath}`);
|
|
598
603
|
symlinkSync(sourcePath, targetPath, 'dir');
|
|
599
604
|
} else {
|
|
600
|
-
//
|
|
605
|
+
// 复制文件(使用 -L 选项跟随符号链接,复制实际文件而非链接本身)
|
|
601
606
|
console.log(`\n复制文件到: ${targetPath}`);
|
|
602
607
|
if (process.platform === 'win32') {
|
|
603
608
|
execSync(`xcopy "${sourcePath}" "${targetPath}" /E /I /H /Y`, { stdio: 'inherit' });
|
|
604
609
|
} else {
|
|
605
|
-
|
|
610
|
+
// 使用 -L 选项解引用符号链接,复制实际文件内容
|
|
611
|
+
execSync(`cp -rL "${sourcePath}" "${targetPath}"`, { stdio: 'inherit' });
|
|
606
612
|
}
|
|
607
613
|
}
|
|
608
614
|
|
|
@@ -836,6 +842,109 @@ export async function linkSkill(skillName) {
|
|
|
836
842
|
}
|
|
837
843
|
}
|
|
838
844
|
|
|
845
|
+
/**
|
|
846
|
+
* 上传本地技能到全局仓库
|
|
847
|
+
*/
|
|
848
|
+
export async function uploadSkill(skillName) {
|
|
849
|
+
// 解析 skill-name@author 格式,提取实际技能名
|
|
850
|
+
let actualSkillName = skillName;
|
|
851
|
+
const match = skillName.match(/^([^@]+)@([^@]+)$/);
|
|
852
|
+
if (match) {
|
|
853
|
+
actualSkillName = match[1];
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const localSkillsDir = getSkillsDir(false);
|
|
857
|
+
const globalSkillsDir = getSkillsDir(true);
|
|
858
|
+
|
|
859
|
+
const localSkillPath = join(localSkillsDir, actualSkillName);
|
|
860
|
+
const globalSkillPath = join(globalSkillsDir, actualSkillName);
|
|
861
|
+
|
|
862
|
+
// 检查本地是否存在该技能
|
|
863
|
+
if (!existsSync(localSkillPath)) {
|
|
864
|
+
console.log(`\n❌ 本地不存在技能: ${actualSkillName}`);
|
|
865
|
+
console.log(` 位置: ${localSkillPath}\n`);
|
|
866
|
+
console.log(`💡 提示:`);
|
|
867
|
+
console.log(` - 使用 "eskill list" 查看本地已安装的技能`);
|
|
868
|
+
console.log(` - 使用 "eskill install" 先安装技能到本地\n`);
|
|
869
|
+
throw new Error(`本地不存在技能: ${actualSkillName}`);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// 检查是否为符号链接
|
|
873
|
+
try {
|
|
874
|
+
const stat = lstatSync(localSkillPath);
|
|
875
|
+
if (stat.isSymbolicLink()) {
|
|
876
|
+
console.log(`\n❌ 无法上传软链接技能: ${actualSkillName}`);
|
|
877
|
+
console.log(` 软链接指向全局仓库,无法上传\n`);
|
|
878
|
+
console.log(`💡 提示:`);
|
|
879
|
+
console.log(` - 软链接技能已经存在于全局仓库`);
|
|
880
|
+
console.log(` - 如需更新全局技能,请直接在全局仓库中使用 eskill update\n`);
|
|
881
|
+
throw new Error(`无法上传软链接技能`);
|
|
882
|
+
}
|
|
883
|
+
} catch (error) {
|
|
884
|
+
console.error(`检测技能类型失败: ${error.message}`);
|
|
885
|
+
throw error;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// 确保全局技能目录存在
|
|
889
|
+
if (!existsSync(globalSkillsDir)) {
|
|
890
|
+
mkdirSync(globalSkillsDir, { recursive: true });
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// 检查全局是否已存在同名技能
|
|
894
|
+
if (existsSync(globalSkillPath)) {
|
|
895
|
+
console.log(`\n⚠️ 全局仓库已存在同名技能: ${actualSkillName}`);
|
|
896
|
+
console.log(` 位置: ${globalSkillPath}\n`);
|
|
897
|
+
|
|
898
|
+
const overwrite = await confirmAction('是否覆盖全局仓库中的技能?');
|
|
899
|
+
if (!overwrite) {
|
|
900
|
+
console.log('\n已取消上传\n');
|
|
901
|
+
return { success: false, cancelled: true };
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
console.log(`删除全局仓库中已存在的技能: ${globalSkillPath}`);
|
|
905
|
+
rmSync(globalSkillPath, { recursive: true, force: true });
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// 复制技能到全局仓库
|
|
909
|
+
console.log(`\n上传技能到全局仓库: ${actualSkillName}`);
|
|
910
|
+
console.log(` 源: ${localSkillPath}`);
|
|
911
|
+
console.log(` 目标: ${globalSkillPath}\n`);
|
|
912
|
+
|
|
913
|
+
// 使用 -L 选项解引用符号链接
|
|
914
|
+
if (process.platform === 'win32') {
|
|
915
|
+
execSync(`xcopy "${localSkillPath}" "${globalSkillPath}" /E /I /H /Y`, { stdio: 'inherit' });
|
|
916
|
+
} else {
|
|
917
|
+
execSync(`cp -rL "${localSkillPath}" "${globalSkillPath}"`, { stdio: 'inherit' });
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// 读取本地元数据
|
|
921
|
+
const localMeta = getSkillMeta(actualSkillName, false);
|
|
922
|
+
|
|
923
|
+
// 保存/更新全局元数据
|
|
924
|
+
if (localMeta) {
|
|
925
|
+
saveSkillMeta(actualSkillName, {
|
|
926
|
+
...localMeta,
|
|
927
|
+
source: 'github', // 上传到全局后,来源标记为 github
|
|
928
|
+
updatedAt: new Date().toISOString()
|
|
929
|
+
}, true);
|
|
930
|
+
} else {
|
|
931
|
+
// 如果本地没有元数据,创建基础元数据
|
|
932
|
+
saveSkillMeta(actualSkillName, {
|
|
933
|
+
name: actualSkillName,
|
|
934
|
+
source: 'github',
|
|
935
|
+
installedAt: new Date().toISOString(),
|
|
936
|
+
updatedAt: new Date().toISOString()
|
|
937
|
+
}, true);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
console.log(`\n✓ 技能已上传到全局仓库`);
|
|
941
|
+
console.log(` 技能: ${actualSkillName}`);
|
|
942
|
+
console.log(` 全局位置: ${globalSkillPath}`);
|
|
943
|
+
console.log(` 说明: 技现已可在全局仓库中使用\n`);
|
|
944
|
+
|
|
945
|
+
return { success: true, path: globalSkillPath };
|
|
946
|
+
}
|
|
947
|
+
|
|
839
948
|
/**
|
|
840
949
|
* 清理所有技能(用于卸载)
|
|
841
950
|
*/
|
|
@@ -982,11 +1091,11 @@ export async function updateSkill(skillName, options = {}) {
|
|
|
982
1091
|
// 删除旧技能
|
|
983
1092
|
rmSync(targetPath, { recursive: true, force: true });
|
|
984
1093
|
|
|
985
|
-
//
|
|
1094
|
+
// 复制新技能(使用 -L 选项解引用符号链接)
|
|
986
1095
|
if (process.platform === 'win32') {
|
|
987
1096
|
execSync(`xcopy "${sourcePath}" "${targetPath}" /E /I /H /Y`, { stdio: 'inherit' });
|
|
988
1097
|
} else {
|
|
989
|
-
execSync(`cp -
|
|
1098
|
+
execSync(`cp -rL "${sourcePath}" "${targetPath}"`, { stdio: 'inherit' });
|
|
990
1099
|
}
|
|
991
1100
|
|
|
992
1101
|
// 更新元数据
|