eskill 1.0.23 → 1.0.27

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 +48 -7
  2. package/lib/installer.js +102 -73
  3. package/package.json +1 -1
package/cli.js CHANGED
@@ -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.23');
16
+ .version('1.0.27');
17
17
 
18
18
  // 安装命令
19
19
  program
@@ -95,11 +95,15 @@ program
95
95
  program
96
96
  .command('update')
97
97
  .description('更新技能')
98
- .argument('[name]', '技能名称(不指定则更新所有)')
98
+ .argument('[name]', '技能名称(不指定或 "all" 则更新所有)')
99
99
  .option('-f, --force', '强制更新(即使版本相同)', false)
100
100
  .action(async (name, options) => {
101
101
  try {
102
- if (name) {
102
+ // 支持显式使用 "all" 参数
103
+ if (name === 'all' || name === 'All' || name === 'ALL') {
104
+ // 更新所有技能
105
+ await updateAllSkills(options);
106
+ } else if (name) {
103
107
  // 更新单个技能
104
108
  console.log(`\n检查技能: ${name}`);
105
109
  const result = await updateSkill(name, options);
@@ -114,7 +118,7 @@ program
114
118
  console.log(` 新版本: ${result.newVersion}\n`);
115
119
  }
116
120
  } else {
117
- // 更新所有技能
121
+ // 更新所有技能(不指定参数时)
118
122
  await updateAllSkills(options);
119
123
  }
120
124
  } catch (error) {
@@ -160,9 +164,9 @@ program
160
164
  }
161
165
 
162
166
  // 从 API 返回的数据结构中提取技能和分页信息
163
- const skills = result.data?.skills || result.data || result.skills || result.results || [];
164
- const pagination = result.data?.pagination || result.pagination || {};
165
- const total = pagination.total || result.total || skills.length;
167
+ let skills = result.data?.skills || result.data || result.skills || result.results || [];
168
+ let pagination = result.data?.pagination || result.pagination || {};
169
+ let total = pagination.total || result.total || skills.length;
166
170
 
167
171
  // 确保 skills 是数组
168
172
  if (!Array.isArray(skills)) {
@@ -171,6 +175,43 @@ program
171
175
  process.exit(1);
172
176
  }
173
177
 
178
+ // 智能搜索:如果没有结果且是 name@author 格式
179
+ if (skills.length === 0) {
180
+ const match = query.match(/^([^@]+)@([^@]+)$/);
181
+ if (match) {
182
+ const [, skillName, author] = match;
183
+ console.log(`\n💡 未找到 "${query}",尝试按技能名 "${skillName}" 和作者 "${author}" 搜索...\n`);
184
+
185
+ // 重新搜索技能名
186
+ const newResult = await searchSkills(skillName, {
187
+ page: 1,
188
+ limit: 100,
189
+ sortBy: options.sort,
190
+ ai: options.ai
191
+ });
192
+
193
+ const allSkills = newResult.data?.skills || newResult.data || newResult.skills || newResult.results || [];
194
+
195
+ // 筛选匹配作者的技能(不区分大小写)
196
+ skills = allSkills.filter(skill => {
197
+ const skillAuthor = (skill.author || skill.owner || '').toLowerCase();
198
+ return skillAuthor === author.toLowerCase();
199
+ });
200
+
201
+ total = skills.length;
202
+
203
+ if (skills.length > 0) {
204
+ console.log(`✓ 找到 ${total} 个匹配的技能\n`);
205
+ } else {
206
+ console.log(`❌ 仍然未找到匹配的技能\n`);
207
+ console.log('提示:');
208
+ console.log(` - 尝试只搜索技能名: eskill search ${skillName}`);
209
+ console.log(` - 尝试只搜索作者: eskill search ${author}\n`);
210
+ process.exit(0);
211
+ }
212
+ }
213
+ }
214
+
174
215
  console.log(formatSkillList(skills));
175
216
 
176
217
  // 显示分页信息和提示
package/lib/installer.js CHANGED
@@ -200,7 +200,7 @@ async function handleSkillAtAuthorFormat(input) {
200
200
 
201
201
  const [, skillName, author] = match;
202
202
  console.log(`\n检测到技能名称格式: ${skillName}@${author}`);
203
- console.log(`正在搜索 "${skillName}"...\n`);
203
+ console.log(`正在搜索 "${skillName}" 和作者 "${author}"...\n`);
204
204
 
205
205
  try {
206
206
  // 搜索技能
@@ -212,17 +212,28 @@ async function handleSkillAtAuthorFormat(input) {
212
212
 
213
213
  const skills = result.data?.skills || result.data || result.skills || result.results || [];
214
214
 
215
- // 筛选出匹配作者的技能
215
+ // 筛选出匹配作者的技能(不区分大小写)
216
216
  const matchedSkills = skills.filter(skill => {
217
- const skillAuthor = skill.author || skill.owner || '';
218
- return skillAuthor.toLowerCase() === author.toLowerCase();
217
+ const skillAuthor = (skill.author || skill.owner || '').toLowerCase();
218
+ return skillAuthor === author.toLowerCase();
219
219
  });
220
220
 
221
221
  if (matchedSkills.length === 0) {
222
222
  // 没有找到匹配的作者
223
223
  console.log(`❌ 未找到作者 "${author}" 的技能 "${skillName}"\n`);
224
- console.log('搜索结果如下:\n');
225
- console.log(formatSkillList(skills));
224
+
225
+ // 显示找到的同名技能(如果有)
226
+ if (skills.length > 0) {
227
+ console.log(`找到 ${skills.length} 个名为 "${skillName}" 的技能,但作者不匹配:\n`);
228
+ console.log(formatSkillList(skills));
229
+ }
230
+
231
+ // 给出建议
232
+ console.log(`💡 建议:`);
233
+ console.log(` - 尝试搜索其他作者: eskill search ${skillName}`);
234
+ console.log(` - 尝试搜索该作者的其他技能: eskill search ${author}`);
235
+ console.log(` - 或直接使用 GitHub URL 安装\n`);
236
+
226
237
  throw new Error(`未找到匹配的技能`);
227
238
  }
228
239
 
@@ -234,7 +245,7 @@ async function handleSkillAtAuthorFormat(input) {
234
245
  throw new Error('技能信息中没有 GitHub URL');
235
246
  }
236
247
 
237
- console.log(`\n✓ 找到匹配的技能:`);
248
+ console.log(`✓ 找到匹配的技能:`);
238
249
  console.log(` 名称: ${skillName}@${author}`);
239
250
  console.log(` Stars: ⭐ ${matchedSkill.stars || 'N/A'}`);
240
251
  console.log(` GitHub: ${githubUrl}\n`);
@@ -484,6 +495,23 @@ export function getSkillsDir() {
484
495
  return getSkillsStorageDir();
485
496
  }
486
497
 
498
+ /**
499
+ * 获取远程仓库的 commit hash(不克隆)
500
+ */
501
+ function getRemoteCommitHash(gitUrl, branch) {
502
+ try {
503
+ const result = execSync(
504
+ `git ls-remote "${gitUrl}" refs/heads/${branch}`,
505
+ { encoding: 'utf-8' }
506
+ );
507
+ // 输出格式: "commitHash\trefs/heads/branch"
508
+ const match = result.trim().split('\t');
509
+ return match[0] || null;
510
+ } catch (error) {
511
+ return null;
512
+ }
513
+ }
514
+
487
515
  /**
488
516
  * 更新单个技能
489
517
  */
@@ -507,83 +535,84 @@ export async function updateSkill(skillName, options = {}) {
507
535
 
508
536
  const currentVersion = meta.version || meta.commitHash?.substring(0, 7) || 'unknown';
509
537
 
510
- // 重新安装技能(使用内部的 installFromGitUrl,但不打印)
511
- try {
512
- const parsed = parseGitUrl(meta.gitUrl);
538
+ // 解析 Git URL
539
+ const parsed = parseGitUrl(meta.gitUrl);
513
540
 
514
- // 确定技能名称
515
- const skillNameFromUrl = parsed.path ? basename(parsed.path) : parsed.repo;
541
+ // 先检查远程 commit hash(不下载)
542
+ console.log(` 检查远程版本...`);
543
+ const remoteCommitHash = getRemoteCommitHash(parsed.cloneUrl, parsed.branch);
516
544
 
517
- // 创建临时目录
518
- const tempDir = join(tmpdir(), `eskill-update-${Date.now()}`);
545
+ if (!remoteCommitHash) {
546
+ throw new Error('无法获取远程版本信息');
547
+ }
519
548
 
520
- try {
521
- // 克隆仓库(静默模式)
522
- const cloneUrl = parsed.cloneUrl;
523
- execSync(
524
- `git clone --depth 1 --branch ${parsed.branch} --single-branch "${cloneUrl}" "${tempDir}"`,
525
- { stdio: 'inherit' }
526
- );
527
-
528
- // 确定源路径
529
- const sourcePath = parsed.path ? join(tempDir, parsed.path) : tempDir;
530
-
531
- // 验证源路径存在
532
- if (!existsSync(sourcePath)) {
533
- throw new Error(`路径不存在: ${sourcePath}`);
534
- }
549
+ // 检查是否有更新
550
+ if (!force && remoteCommitHash === meta.commitHash) {
551
+ return { success: true, skill: skillName, updated: false, currentVersion };
552
+ }
535
553
 
536
- // 检查 SKILL.md 是否存在
537
- const skillManifestPath = join(sourcePath, 'SKILL.md');
538
- if (!existsSync(skillManifestPath)) {
539
- throw new Error(`无效的技能包:未找到 SKILL.md 文件`);
540
- }
554
+ // 有更新或强制更新,才下载代码
555
+ console.log(` 发现更新,正在下载...`);
541
556
 
542
- // 获取新版本信息
543
- const newCommitHash = getGitCommitHash(sourcePath);
544
- const newVersion = getGitVersion(sourcePath);
545
- const newVersionDisplay = newVersion || newCommitHash?.substring(0, 7) || 'unknown';
557
+ // 创建临时目录
558
+ const tempDir = join(tmpdir(), `eskill-update-${Date.now()}`);
546
559
 
547
- // 检查是否有更新
548
- if (!force && newCommitHash === meta.commitHash) {
549
- // 删除临时目录
550
- rmSync(tempDir, { recursive: true, force: true });
551
- return { success: true, skill: skillName, updated: false, currentVersion };
552
- }
560
+ try {
561
+ // 克隆仓库(静默模式)
562
+ execSync(
563
+ `git clone --depth 1 --branch ${parsed.branch} --single-branch "${parsed.cloneUrl}" "${tempDir}"`,
564
+ { stdio: 'inherit' }
565
+ );
553
566
 
554
- // 删除旧技能
555
- rmSync(targetPath, { recursive: true, force: true });
567
+ // 确定源路径
568
+ const sourcePath = parsed.path ? join(tempDir, parsed.path) : tempDir;
556
569
 
557
- // 复制新技能
558
- if (process.platform === 'win32') {
559
- execSync(`xcopy "${sourcePath}" "${targetPath}" /E /I /H /Y`, { stdio: 'inherit' });
560
- } else {
561
- execSync(`cp -r "${sourcePath}" "${targetPath}"`, { stdio: 'inherit' });
562
- }
570
+ // 验证源路径存在
571
+ if (!existsSync(sourcePath)) {
572
+ throw new Error(`路径不存在: ${sourcePath}`);
573
+ }
563
574
 
564
- // 更新元数据
565
- saveSkillMeta(skillName, {
566
- ...meta,
567
- commitHash: newCommitHash,
568
- version: newVersion,
569
- updatedAt: new Date().toISOString()
570
- });
575
+ // 检查 SKILL.md 是否存在
576
+ const skillManifestPath = join(sourcePath, 'SKILL.md');
577
+ if (!existsSync(skillManifestPath)) {
578
+ throw new Error(`无效的技能包:未找到 SKILL.md 文件`);
579
+ }
571
580
 
572
- return {
573
- success: true,
574
- skill: skillName,
575
- updated: true,
576
- currentVersion,
577
- newVersion: newVersionDisplay
578
- };
579
- } finally {
580
- // 清理临时目录
581
- if (existsSync(tempDir)) {
582
- rmSync(tempDir, { recursive: true, force: true });
583
- }
581
+ // 获取新版本信息
582
+ const newCommitHash = getGitCommitHash(sourcePath);
583
+ const newVersion = getGitVersion(sourcePath);
584
+ const newVersionDisplay = newVersion || newCommitHash?.substring(0, 7) || 'unknown';
585
+
586
+ // 删除旧技能
587
+ rmSync(targetPath, { recursive: true, force: true });
588
+
589
+ // 复制新技能
590
+ if (process.platform === 'win32') {
591
+ execSync(`xcopy "${sourcePath}" "${targetPath}" /E /I /H /Y`, { stdio: 'inherit' });
592
+ } else {
593
+ execSync(`cp -r "${sourcePath}" "${targetPath}"`, { stdio: 'inherit' });
594
+ }
595
+
596
+ // 更新元数据
597
+ saveSkillMeta(skillName, {
598
+ ...meta,
599
+ commitHash: newCommitHash,
600
+ version: newVersion,
601
+ updatedAt: new Date().toISOString()
602
+ });
603
+
604
+ return {
605
+ success: true,
606
+ skill: skillName,
607
+ updated: true,
608
+ currentVersion,
609
+ newVersion: newVersionDisplay
610
+ };
611
+ } finally {
612
+ // 清理临时目录
613
+ if (existsSync(tempDir)) {
614
+ rmSync(tempDir, { recursive: true, force: true });
584
615
  }
585
- } catch (error) {
586
- throw new Error(`更新失败: ${error.message}`);
587
616
  }
588
617
  }
589
618
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eskill",
3
- "version": "1.0.23",
3
+ "version": "1.0.27",
4
4
  "description": "Unified AI Agent Skills Management - Install skills from Git URLs",
5
5
  "main": "index.js",
6
6
  "type": "module",