itismyskillmarket 1.2.1 → 1.2.3

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/src/cli.ts CHANGED
@@ -14,10 +14,11 @@
14
14
  * - skm ls 列出可用的 skills
15
15
  * - skm ls --installed 列出已安装的 skills
16
16
  * - skm info <skill> 显示 skill 详情
17
- * - skm install <skill> 安装 skill
18
- * - skm uninstall <skill> 卸载 skill
17
+ * - skm install <skill> 安装 skill(支持 --platform)
18
+ * - skm uninstall <skill> 卸载 skill(支持 --platform)
19
19
  * - skm update [skill] 更新 skill(s)
20
20
  * - skm sync 同步平台链接
21
+ * - skm platforms 显示可用平台
21
22
  *
22
23
  * @module cli
23
24
  */
@@ -37,6 +38,7 @@ import { installSkill } from './commands/install.js'; // 安装命令
37
38
  import { syncPlatformLinks } from './commands/sync.js'; // 同步命令
38
39
  import { updateSkill } from './commands/update.js'; // 更新命令
39
40
  import { uninstallSkill } from './commands/uninstall.js'; // 卸载命令
41
+ import { detectPlatforms, getAllAdapters, OpenCodeAdapter, ClaudeAdapter, VSCodeAdapter } from './adapters/index.js'; // 平台适配器
40
42
 
41
43
  // -----------------------------------------------------------------------------
42
44
  // 创建命令程序实例
@@ -63,7 +65,7 @@ const program = new Command();
63
65
  program
64
66
  .name('skm')
65
67
  .description('SkillMarket - Cross-platform skill manager for AI coding tools')
66
- .version('1.2.0');
68
+ .version('1.0.0');
67
69
 
68
70
  // -----------------------------------------------------------------------------
69
71
  // 帮助命令 (-h, --help)
@@ -74,38 +76,43 @@ program
74
76
  *
75
77
  * 显示详细的使用说明和命令示例
76
78
  */
77
- const helpCmd = program.command('help').description('Display help information');
78
- helpCmd.action(() => {
79
- console.log(`
79
+ program
80
+ .hook('preAction', (thisCommand) => {
81
+ if (thisCommand.opts().help) {
82
+ console.log(`
80
83
  SkillMarket CLI
81
84
 
82
85
  Usage: skm <command> [options]
83
86
 
84
87
  Commands:
85
- --help, -h Display this help message
86
- --ls [options] List available skills
87
- --installed Show only installed skills
88
- --updates Check for updates
89
- --info <skill-id> Display skill information
90
- --install <skill> Install a skill (e.g., skm --install brainstorming)
91
- @version Install specific version
92
- --all Install all available skills
93
- --uninstall <skill> Remove an installed skill
94
- --update [options] Update skills
95
- --all Update all skills
96
- --sync Synchronize platform links
97
- --platform <name> Set target platform (${PLATFORMS.join(', ')})
88
+ ls [options] List available skills
89
+ --installed Show only installed skills
90
+ --updates Check for updates
91
+ info <skill-id> Display skill information
92
+ install <skill> Install a skill
93
+ @version Install specific version
94
+ --platform Target platforms (opencode,claude,vscode)
95
+ --force Overwrite if already installed
96
+ uninstall <skill> Remove an installed skill
97
+ --platform Target platforms
98
+ update [options] Update skills
99
+ --all Update all skills
100
+ sync Synchronize platform links
101
+ platforms Show available platforms
98
102
 
99
103
  Examples:
100
- skm --ls List all available skills
101
- skm --ls --installed Show installed skills only
102
- skm --info brainstorming View skill details
103
- skm --install brainstorming Install a skill
104
- skm --install brainstorming@1.0.0 Install specific version
105
- skm --update --all Update all installed skills
106
- skm --sync Sync platform links
107
- `);
108
- });
104
+ skm ls List all available skills
105
+ skm ls --installed Show installed skills only
106
+ skm info brainstorming View skill details
107
+ skm install brainstorming Install to all platforms
108
+ skm install brainstorming --platform opencode Install to OpenCode only
109
+ skm install brainstorming --platform claude,vscode Install to multiple
110
+ skm uninstall brainstorming
111
+ skm platforms Show available platforms
112
+ `);
113
+ process.exit(0);
114
+ }
115
+ });
109
116
 
110
117
  // -----------------------------------------------------------------------------
111
118
  // 列表命令 (skm ls)
@@ -157,25 +164,36 @@ infoCmd
157
164
  /**
158
165
  * 安装命令
159
166
  *
160
- * 从 npm 安装指定的 skill 到本地
167
+ * 从 npm 安装指定的 skill 到本地和跨平台目录
161
168
  *
162
169
  * 用法:
163
- * - skm install <skill> 安装最新版本
170
+ * - skm install <skill> 安装到所有检测到的平台
164
171
  * - skm install <skill>@<ver> 安装指定版本
165
- * - skm install --all 安装所有可用 skills(预留)
172
+ * - skm install --platform opencode 安装到特定平台
173
+ * - skm install --platform claude,vscode 安装到多个平台
174
+ * - skm install --force 强制覆盖
166
175
  *
167
176
  * @example
168
177
  * skm install brainstorming
169
178
  * skm install brainstorming@1.0.0
179
+ * skm install brainstorming --platform opencode
170
180
  */
171
- const installCmd = program.command('install').description('Install a skill');
181
+ const installCmd = program.command('install').description('Install a skill to local and platform directories');
172
182
  installCmd
173
183
  .argument('<skill>', 'Skill ID to install (e.g., brainstorming or @scope/name)')
174
- .option('--all', 'Install all available skills')
175
- .option('-p, --platform <platform>', `Target platform (${PLATFORMS.join(', ')})`)
184
+ .option('-p, --platform <platforms>', 'Target platforms (comma-separated: opencode,claude,vscode)')
185
+ .option('-f, --force', 'Overwrite if already installed')
186
+ .option('-v, --version <version>', 'Specific version to install')
176
187
  .action(async (skill, opts) => {
177
188
  try {
178
- await installSkill(skill, undefined, opts.platform);
189
+ const platforms = opts.platform
190
+ ? opts.platform.split(',').map((p: string) => p.trim())
191
+ : undefined;
192
+
193
+ await installSkill(skill, opts.version, {
194
+ platforms,
195
+ force: opts.force
196
+ });
179
197
  } catch (err) {
180
198
  console.error('Installation failed:', err);
181
199
  process.exit(1);
@@ -189,19 +207,27 @@ installCmd
189
207
  /**
190
208
  * 卸载命令
191
209
  *
192
- * 移除本地已安装的 skill
210
+ * 移除本地已安装的 skill 及各平台的文件
193
211
  *
194
- * 用法: skm uninstall <skill-id>
212
+ * 用法:
213
+ * - skm uninstall <skill> 卸载所有平台
214
+ * - skm uninstall <skill> --platform opencode 卸载特定平台
195
215
  *
196
216
  * @example
197
217
  * skm uninstall brainstorming
218
+ * skm uninstall brainstorming --platform claude
198
219
  */
199
- const uninstallCmd = program.command('uninstall').description('Remove an installed skill');
220
+ const uninstallCmd = program.command('uninstall').description('Remove an installed skill from local and platform directories');
200
221
  uninstallCmd
201
222
  .argument('<skill>', 'Skill ID to uninstall')
202
- .action(async (skill) => {
223
+ .option('-p, --platform <platforms>', 'Target platforms (comma-separated)')
224
+ .action(async (skill, opts) => {
203
225
  try {
204
- await uninstallSkill(skill);
226
+ const platforms = opts.platform
227
+ ? opts.platform.split(',').map((p: string) => p.trim())
228
+ : undefined;
229
+
230
+ await uninstallSkill(skill, { platforms });
205
231
  } catch (err) {
206
232
  console.error('Uninstall failed:', err);
207
233
  process.exit(1);
@@ -268,21 +294,46 @@ program
268
294
  });
269
295
 
270
296
  // -----------------------------------------------------------------------------
271
- // 平台命令 (skm platform)
297
+ // 平台命令 (skm platforms)
272
298
  // -----------------------------------------------------------------------------
273
299
 
274
300
  /**
275
- * 平台命令(预留)
301
+ * 平台命令
276
302
  *
277
- * 设置默认目标平台
303
+ * 显示所有支持的平台及其状态
278
304
  *
279
- * 用法: skm platform <name>
305
+ * 用法: skm platforms
280
306
  */
281
- const platformCmd = program.command('platform').description('Set target platform');
282
- platformCmd
283
- .argument('<name>', 'Platform name')
284
- .action((name) => {
285
- console.log('Platform command - name:', name);
307
+ const platformsCmd = program.command('platforms').description('Show available platforms');
308
+ platformsCmd
309
+ .action(async () => {
310
+ try {
311
+ const available = await detectPlatforms();
312
+
313
+ console.log('\n📍 Available Platforms:\n');
314
+
315
+ const allPlatforms = [
316
+ { name: 'OpenCode', adapter: new OpenCodeAdapter() },
317
+ { name: 'Claude Code', adapter: new ClaudeAdapter() },
318
+ { name: 'VSCode', adapter: new VSCodeAdapter() },
319
+ ];
320
+
321
+ for (const { name, adapter } of allPlatforms) {
322
+ const isAvailable = available.find(a => a.id === adapter.id);
323
+ const installed = await adapter.listInstalled();
324
+
325
+ if (isAvailable) {
326
+ console.log(`${name.padEnd(12)} ✅ Available (${installed.length} skills installed)`);
327
+ } else {
328
+ console.log(`${name.padEnd(12)} ❌ Not detected`);
329
+ }
330
+ }
331
+
332
+ console.log('');
333
+ } catch (err) {
334
+ console.error('Failed to list platforms:', err);
335
+ process.exit(1);
336
+ }
286
337
  });
287
338
 
288
339
  // -----------------------------------------------------------------------------
@@ -19,7 +19,7 @@
19
19
  // 导入依赖
20
20
  // -----------------------------------------------------------------------------
21
21
 
22
- import { fetchNpmPackage } from './npm.js'; // npm registry 查询
22
+ import { fetchSkillPackage } from './npm.js'; // npm registry 查询
23
23
  import {
24
24
  isSkillInstalled, // 检查 skill 是否已安装
25
25
  getInstalledSkills // 获取已安装 skills 列表
@@ -49,25 +49,14 @@ export async function showSkillInfo(skillId: string): Promise<void> {
49
49
  // 处理包名格式
50
50
  // -------------------------------------------------------------------------
51
51
 
52
- /**
53
- * 转换 skillId 为完整的 npm 包名格式
54
- *
55
- * 支持两种输入格式:
56
- * 1. 短格式: "brainstorming" → "@skillmarket/brainstorming"
57
- * 2. 完整格式: "@custom/skill" → "@custom/skill"
58
- */
59
- const packageName = skillId.startsWith('@')
60
- ? skillId // 已经是 scoped 包名
61
- : `@skillmarket/${skillId}`; // 转换为 scoped 包名
62
-
63
- console.log(`Fetching info for: ${packageName}\n`);
52
+ console.log(`Fetching info for: ${skillId}\n`);
64
53
 
65
54
  try {
66
55
  // -------------------------------------------------------------------------
67
- // 从 npm 获取包信息
56
+ // 从 npm 获取包信息(自动尝试多个可能的 scope)
68
57
  // -------------------------------------------------------------------------
69
58
 
70
- const info = await fetchNpmPackage(packageName);
59
+ const info = await fetchSkillPackage(skillId);
71
60
 
72
61
  // 包不存在
73
62
  if (!info) {
@@ -3,7 +3,7 @@
3
3
  * SkillMarket 安装命令模块
4
4
  * =============================================================================
5
5
  *
6
- * 本模块实现 `skm install` 命令,用于安装 skill 到本地。
6
+ * 本模块实现 `skm install` 命令,用于安装 skill 到本地和跨平台目录。
7
7
  *
8
8
  * 安装流程:
9
9
  * 1. 确保目录结构存在
@@ -11,7 +11,8 @@
11
11
  * 3. 下载包到缓存
12
12
  * 4. 解压并复制到 skills 目录
13
13
  * 5. 创建 latest 软链接
14
- * 6. 更新本地注册表
14
+ * 6. 安装到目标平台(OpenCode/Claude Code/VSCode)
15
+ * 7. 更新本地注册表
15
16
  *
16
17
  * 安装后的目录结构:
17
18
  * ~/.skillmarket/
@@ -23,6 +24,11 @@
23
24
  * │ └── metadata.json
24
25
  * └── ...
25
26
  *
27
+ * 跨平台安装:
28
+ * - OpenCode: ~/.config/opencode/skills/<skillId>/SKILL.md
29
+ * - Claude Code: ~/.claude/skills/<skillId>/SKILL.md
30
+ * - VSCode: ~/.copilot/skills/<skillId>/SKILL.md
31
+ *
26
32
  * @module commands/install
27
33
  */
28
34
 
@@ -36,17 +42,29 @@ import { exec } from 'child_process'; // 执行 shell 命令
36
42
  import { promisify } from 'util'; // Promise 化工具
37
43
 
38
44
  // 模块导入
39
- import { fetchNpmPackage } from './npm.js'; // npm 查询
45
+ import { fetchSkillPackage } from './npm.js'; // npm 查询
40
46
  import { loadRegistry, saveRegistry } from './registry.js'; // 注册表操作
41
47
  import { getCacheDir, getSkillsDir, ensureMarketDirs } from '../utils/dirs.js'; // 目录工具
42
- import { detectPlatform, getPlatformFromInput, isValidPlatform, type Platform } from '../utils/platform.js'; // 平台检测
43
- import { syncPlatformLinks } from './sync.js'; // 同步命令
44
- import { LATEST_LINK, PLATFORMS } from '../constants.js'; // 常量
48
+ import { detectPlatforms, getAdapterByPlatform } from '../adapters/index.js'; // 平台适配器
49
+ import { LATEST_LINK } from '../constants.js'; // 常量
45
50
  import type { InstalledSkill } from '../types.js'; // 类型定义
51
+ import type { Platform } from '../constants.js';
52
+ import type { PlatformAdapter } from '../types.js';
46
53
 
47
54
  // 将 exec 转为 Promise 形式
48
55
  const execAsync = promisify(exec);
49
56
 
57
+ // -----------------------------------------------------------------------------
58
+ // 安装选项接口
59
+ // -----------------------------------------------------------------------------
60
+
61
+ export interface InstallOptions {
62
+ /** 目标平台列表(留空则安装到所有可用平台) */
63
+ platforms?: string[];
64
+ /** 强制覆盖已安装的 skill */
65
+ force?: boolean;
66
+ }
67
+
50
68
  // -----------------------------------------------------------------------------
51
69
  // 安装函数
52
70
  // -----------------------------------------------------------------------------
@@ -56,7 +74,7 @@ const execAsync = promisify(exec);
56
74
  *
57
75
  * @param {string} skillId - Skill 标识符(支持短格式或 scoped 格式)
58
76
  * @param {string} [version] - 指定版本号(可选,不指定则安装最新版本)
59
- * @param {string} [targetPlatform] - 目标平台(可选,不指定则使用当前检测到的平台)
77
+ * @param {InstallOptions} [options] - 安装选项
60
78
  * @returns {Promise<void>}
61
79
  *
62
80
  * @example
@@ -66,16 +84,16 @@ const execAsync = promisify(exec);
66
84
  * // 安装指定版本
67
85
  * await installSkill('brainstorming', '1.0.0');
68
86
  *
69
- * // 安装到指定平台
70
- * await installSkill('brainstorming', undefined, 'opencode');
87
+ * // 安装到特定平台
88
+ * await installSkill('brainstorming', undefined, { platforms: ['opencode'] });
71
89
  *
72
- * // 安装 scoped 包
73
- * await installSkill('@custom/skill');
90
+ * // 强制覆盖
91
+ * await installSkill('brainstorming', undefined, { force: true });
74
92
  */
75
93
  export async function installSkill(
76
94
  skillId: string,
77
95
  version?: string,
78
- targetPlatform?: string
96
+ options?: InstallOptions
79
97
  ): Promise<void> {
80
98
  // ==========================================================================
81
99
  // 步骤 0: 准备
@@ -84,23 +102,21 @@ export async function installSkill(
84
102
  // 确保所有必要的目录都已创建
85
103
  await ensureMarketDirs();
86
104
 
87
- // 转换包名格式
88
- const packageName = skillId.startsWith('@')
89
- ? skillId
90
- : `@skillmarket/${skillId}`;
91
-
92
- console.log(`Installing ${packageName}${version ? `@${version}` : ''}...`);
105
+ console.log(`Installing ${skillId}${version ? `@${version}` : ''}...`);
93
106
 
94
107
  // ==========================================================================
95
108
  // 步骤 1: 获取包信息
96
109
  // ==========================================================================
97
110
 
98
- // 从 npm 查询包的元信息
99
- const pkgInfo = await fetchNpmPackage(packageName);
111
+ // 从 npm 查询包的元信息(自动尝试多个可能的 scope)
112
+ const pkgInfo = await fetchSkillPackage(skillId);
100
113
  if (!pkgInfo) {
101
- throw new Error(`Package ${packageName} not found`);
114
+ throw new Error(`Package ${skillId} not found`);
102
115
  }
103
116
 
117
+ // 获取实际找到的包名
118
+ const packageName = pkgInfo.name;
119
+
104
120
  // 确定要安装的版本(用户指定版本 > 最新版本)
105
121
  const targetVersion = version || pkgInfo['dist-tags']?.latest;
106
122
  if (!targetVersion) {
@@ -211,56 +227,82 @@ export async function installSkill(
211
227
  }
212
228
 
213
229
  // ==========================================================================
214
- // 步骤 5: 确定目标平台
230
+ // 步骤 5: 安装到目标平台 (NEW)
215
231
  // ==========================================================================
216
232
 
217
- // 确定实际使用的平台
218
- let finalPlatform: Platform;
219
-
220
- if (targetPlatform) {
221
- // 验证用户指定的目标平台
222
- const parsed = getPlatformFromInput(targetPlatform);
223
- if (!parsed) {
224
- throw new Error(`Invalid platform: ${targetPlatform}. Valid platforms: ${PLATFORMS.join(', ')}`);
233
+ let targetAdapters: PlatformAdapter[] = [];
234
+
235
+ if (options?.platforms && options.platforms.length > 0) {
236
+ // 用户指定了平台
237
+ for (const platformStr of options.platforms) {
238
+ const platform = platformStr as Platform;
239
+ const adapter = getAdapterByPlatform(platform);
240
+ if (adapter) {
241
+ targetAdapters.push(adapter);
242
+ } else {
243
+ console.warn(`⚠️ Unknown platform: ${platformStr}`);
244
+ }
225
245
  }
226
- finalPlatform = parsed;
227
246
  } else {
228
- // 使用当前检测到的平台
229
- finalPlatform = detectPlatform();
247
+ // 自动检测可用平台
248
+ targetAdapters = await detectPlatforms();
230
249
  }
231
250
 
232
- console.log(`Target platform: ${finalPlatform}`);
251
+ if (targetAdapters.length === 0) {
252
+ console.log('No target platforms detected.');
253
+ console.log('Use --platform to specify platforms manually.');
254
+ } else {
255
+ console.log(`\nInstalling to ${targetAdapters.length} platform(s)...\n`);
256
+
257
+ // 安装到每个平台
258
+ const results: { name: string; status: 'installed' | 'skipped' | 'failed'; error?: string }[] = [];
259
+
260
+ for (const adapter of targetAdapters) {
261
+ try {
262
+ const isInstalled = await adapter.isInstalled(skillId);
263
+
264
+ if (isInstalled && !options?.force) {
265
+ console.log(`${adapter.name.padEnd(12)} ⚠️ Already installed (use --force to overwrite)`);
266
+ results.push({ name: adapter.name, status: 'skipped' });
267
+ continue;
268
+ }
269
+
270
+ // 安装 skill 到平台目录
271
+ await adapter.install(skillId, skillVersionDir);
272
+ console.log(`${adapter.name.padEnd(12)} ✅ Installed successfully`);
273
+ results.push({ name: adapter.name, status: 'installed' });
274
+ } catch (error) {
275
+ console.log(`${adapter.name.padEnd(12)} ❌ Failed: ${error}`);
276
+ results.push({ name: adapter.name, status: 'failed', error: String(error) });
277
+ }
278
+ }
279
+
280
+ // 显示摘要
281
+ const installed = results.filter(r => r.status === 'installed').length;
282
+ const skipped = results.filter(r => r.status === 'skipped').length;
283
+ const failed = results.filter(r => r.status === 'failed').length;
284
+
285
+ console.log(`\n📊 Summary: ${installed} installed, ${skipped} skipped, ${failed} failed`);
286
+ }
233
287
 
234
288
  // ==========================================================================
235
289
  // 步骤 6: 更新注册表
236
290
  // ==========================================================================
237
291
 
238
292
  const registry = await loadRegistry();
239
-
240
- // 获取现有平台列表(如果 skill 已安装)
241
- const existingSkill = registry.skills[skillId];
242
- const existingPlatforms = existingSkill?.platforms || [];
293
+ const installedPlatforms = targetAdapters.map(a => a.id);
243
294
 
244
295
  // 添加/更新注册表中的 skill 记录
245
296
  registry.skills[skillId] = {
246
297
  id: skillId,
247
298
  version: targetVersion,
248
299
  installedAt: new Date().toISOString(),
249
- platforms: existingPlatforms.includes(finalPlatform as string)
250
- ? existingPlatforms
251
- : [...existingPlatforms, finalPlatform as string]
300
+ platforms: installedPlatforms
252
301
  } as InstalledSkill;
253
302
 
254
303
  // 保存注册表
255
304
  await saveRegistry(registry);
256
305
 
257
- // ==========================================================================
258
- // 步骤 7: 同步到指定平台
259
- // ==========================================================================
260
-
261
- console.log(`Syncing to ${finalPlatform}...`);
262
- await syncPlatformLinks(finalPlatform);
263
-
264
306
  // ==========================================================================
265
307
  // 完成
266
308
  // ==========================================================================
@@ -117,22 +117,49 @@ export async function listSkills(options: LsOptions): Promise<void> {
117
117
 
118
118
  // 遍历每个包,获取详细信息并显示
119
119
  for (const pkgName of packages) {
120
- const info = await fetchNpmPackage(pkgName);
121
-
122
- if (info && info['dist-tags']?.latest) {
123
- const latestVersion = info['dist-tags'].latest;
120
+ try {
121
+ const info = await fetchNpmPackage(pkgName);
122
+
123
+ if (!info) {
124
+ // 如果获取失败,仍然显示包名
125
+ console.log(`📦 ${pkgName} (信息获取失败)`);
126
+ console.log();
127
+ continue;
128
+ }
129
+
130
+ // 获取最新版本号
131
+ const latestVersion = info['dist-tags']?.latest || 'unknown';
124
132
 
125
133
  // 获取该版本的详细信息
126
134
  const pkg = info.versions?.[latestVersion];
127
135
 
136
+ // 获取 skillmarket 元数据
137
+ const skillMeta = pkg?.skillmarket;
138
+
128
139
  // 打印包名和版本
129
- console.log(` ${info.name}@${latestVersion}`);
140
+ console.log(`📦 ${info.name}@${latestVersion}`);
130
141
 
131
- // 打印描述(如果有的话)
132
- console.log(` ${pkg?.description || 'No description'}`);
142
+ // 打印显示名称
143
+ const displayName = skillMeta?.displayName || info.name;
144
+ console.log(` 名称: ${displayName}`);
145
+
146
+ // 打印描述
147
+ console.log(` 描述: ${pkg?.description || 'N/A'}`);
148
+
149
+ // 打印支持平台
150
+ const platforms = skillMeta?.platforms || [];
151
+ console.log(` 平台: ${platforms.length > 0 ? platforms.join(', ') : 'N/A'}`);
152
+
153
+ // 打印 npm 链接
154
+ const npmLink = pkg?.links?.npm || `https://www.npmjs.com/package/${info.name}`;
155
+ console.log(` 链接: ${npmLink}`);
133
156
 
134
157
  // 空行分隔
135
158
  console.log();
159
+ } catch (e) {
160
+ // 错误时仍显示包名
161
+ console.log(`📦 ${pkgName} (获取失败: ${e})`);
162
+ console.log();
136
163
  }
137
164
  }
138
165
  } catch (error) {
@@ -40,6 +40,14 @@ interface NpmPackage {
40
40
  /** 包描述信息 */
41
41
  description?: string;
42
42
 
43
+ /** npm 链接 */
44
+ links?: {
45
+ npm?: string;
46
+ homepage?: string;
47
+ repository?: string;
48
+ bugs?: string;
49
+ };
50
+
43
51
  /**
44
52
  * SkillMarket 特有的元数据
45
53
  *
@@ -146,7 +154,7 @@ export async function fetchNpmPackage(packageName: string): Promise<NpmRegistryR
146
154
 
147
155
  // 收集响应数据
148
156
  res.on('data', chunk => { data += chunk; });
149
-
157
+
150
158
  res.on('end', () => {
151
159
  try {
152
160
  // 解析 JSON 响应
@@ -158,7 +166,7 @@ export async function fetchNpmPackage(packageName: string): Promise<NpmRegistryR
158
166
  resolve(null);
159
167
  return;
160
168
  }
161
-
169
+
162
170
  // 成功解析,返回包信息
163
171
  resolve(parsed);
164
172
  } catch {
@@ -179,6 +187,58 @@ export async function fetchNpmPackage(packageName: string): Promise<NpmRegistryR
179
187
  });
180
188
  }
181
189
 
190
+ // -----------------------------------------------------------------------------
191
+ // 兼容的 Scope 列表
192
+ // -----------------------------------------------------------------------------
193
+
194
+ /**
195
+ * SkillMarket 支持的 npm scope 列表
196
+ * 按优先级排序,先尝试的 scope 排在前面
197
+ */
198
+ const SKILL_SCOPES = [
199
+ '@wanxuchen', // 原作者 scope
200
+ '@itismyskillmarket', // 当前包名 scope
201
+ '@thisisskillmarket', // 曾用 scope
202
+ '@this-is-skillmarket', // 曾用 scope (带横线)
203
+ '@skillmarket', // 通用 scope
204
+ ];
205
+
206
+ /**
207
+ * 将 skillId 转换为可能的包名列表
208
+ */
209
+ function getPossiblePackageNames(skillId: string): string[] {
210
+ if (skillId.startsWith('@')) {
211
+ // 已经是 scoped 包,直接返回
212
+ return [skillId];
213
+ }
214
+
215
+ // 生成所有可能的包名
216
+ return SKILL_SCOPES.map(scope => `${scope}/${skillId}`);
217
+ }
218
+
219
+ /**
220
+ * 尝试获取 npm 包信息,自动尝试多个可能的 scope
221
+ *
222
+ * @param skillId - Skill ID(可以是短格式或完整格式)
223
+ * @returns 包信息,失败返回 null
224
+ */
225
+ export async function fetchSkillPackage(skillId: string): Promise<NpmRegistryResponse | null> {
226
+ const packageNames = getPossiblePackageNames(skillId);
227
+
228
+ for (const packageName of packageNames) {
229
+ try {
230
+ const info = await fetchNpmPackage(packageName);
231
+ if (info) {
232
+ return info;
233
+ }
234
+ } catch {
235
+ // 继续尝试下一个 scope
236
+ }
237
+ }
238
+
239
+ return null;
240
+ }
241
+
182
242
  // -----------------------------------------------------------------------------
183
243
  // 搜索包
184
244
  // -----------------------------------------------------------------------------
@@ -225,9 +285,11 @@ export async function searchSkillmarketPackages(): Promise<string[]> {
225
285
 
226
286
  // 提取所有匹配的包名
227
287
  // npm search 返回结构: { objects: [{ package: { name: "..." } }] }
228
- for (const item of result.objects || []) {
229
- if (item?.package?.name) {
230
- packages.push(item.package.name);
288
+ if (result.objects) {
289
+ for (const item of result.objects) {
290
+ if (item?.package?.name) {
291
+ packages.push(item.package.name);
292
+ }
231
293
  }
232
294
  }
233
295