itismyskillmarket 1.2.2 → 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/.github/workflows/publish-npm.yml +54 -0
- package/.github/workflows/publish-skill.yml +70 -0
- package/5e51cb7aa8b8e60d49d86f4689f5d4d1.png +0 -0
- package/DEVELOPMENT.md +376 -0
- package/README.md +70 -4
- package/SKILLMARKET-GUIDE.md +277 -0
- package/dist/index.js +436 -164
- package/docs/plans/2026-04-01-skillmarket-design.md +267 -0
- package/docs/plans/2026-04-01-skillmarket-implementation.md +1031 -0
- package/docs/plans/2026-04-15-cross-platform-adapter-design.md +416 -0
- package/docs/plans/2026-04-15-cross-platform-adapter-plan.md +833 -0
- package/package.json +1 -6
- package/skills/README.md +52 -0
- package/skills/test-skill/SKILL.md +25 -0
- package/skills/test-skill/index.js +66 -0
- package/skills/test-skill/metadata.json +9 -0
- package/skills/test-skill/package.json +19 -0
- package/src/adapters/base.ts +87 -0
- package/src/adapters/claude.ts +31 -0
- package/src/adapters/index.ts +9 -0
- package/src/adapters/opencode.ts +40 -0
- package/src/adapters/registry.ts +77 -0
- package/src/adapters/vscode.ts +62 -0
- package/src/cli.ts +100 -49
- package/src/commands/info.ts +4 -15
- package/src/commands/install.ts +91 -52
- package/src/commands/ls.ts +34 -7
- package/src/commands/npm.ts +67 -5
- package/src/commands/sync.ts +6 -27
- package/src/commands/uninstall.ts +60 -7
- package/src/commands/update.ts +2 -2
- package/src/index.ts +27 -0
- package/src/types.ts +35 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +22 -0
- package/wanxuchen-skillmarket-1.0.1.tgz +0 -0
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.
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
101
|
-
skm
|
|
102
|
-
skm
|
|
103
|
-
skm
|
|
104
|
-
skm
|
|
105
|
-
skm
|
|
106
|
-
skm
|
|
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 --
|
|
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('--
|
|
175
|
-
.option('-
|
|
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
|
-
|
|
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
|
-
* 用法:
|
|
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
|
-
.
|
|
223
|
+
.option('-p, --platform <platforms>', 'Target platforms (comma-separated)')
|
|
224
|
+
.action(async (skill, opts) => {
|
|
203
225
|
try {
|
|
204
|
-
|
|
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
|
|
297
|
+
// 平台命令 (skm platforms)
|
|
272
298
|
// -----------------------------------------------------------------------------
|
|
273
299
|
|
|
274
300
|
/**
|
|
275
|
-
*
|
|
301
|
+
* 平台命令
|
|
276
302
|
*
|
|
277
|
-
*
|
|
303
|
+
* 显示所有支持的平台及其状态
|
|
278
304
|
*
|
|
279
|
-
* 用法: skm
|
|
305
|
+
* 用法: skm platforms
|
|
280
306
|
*/
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
.
|
|
284
|
-
|
|
285
|
-
|
|
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
|
// -----------------------------------------------------------------------------
|
package/src/commands/info.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// 导入依赖
|
|
20
20
|
// -----------------------------------------------------------------------------
|
|
21
21
|
|
|
22
|
-
import {
|
|
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
|
|
59
|
+
const info = await fetchSkillPackage(skillId);
|
|
71
60
|
|
|
72
61
|
// 包不存在
|
|
73
62
|
if (!info) {
|
package/src/commands/install.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
43
|
-
import {
|
|
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 {
|
|
77
|
+
* @param {InstallOptions} [options] - 安装选项
|
|
60
78
|
* @returns {Promise<void>}
|
|
61
79
|
*
|
|
62
80
|
* @example
|
|
@@ -66,44 +84,39 @@ 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
|
-
* //
|
|
73
|
-
* await installSkill('
|
|
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
|
-
|
|
96
|
+
options?: InstallOptions
|
|
79
97
|
): Promise<void> {
|
|
80
98
|
// ==========================================================================
|
|
81
99
|
// 步骤 0: 准备
|
|
82
100
|
// ==========================================================================
|
|
83
101
|
|
|
84
|
-
// 确保所有必要的目录都已创建
|
|
102
|
+
// 确保所有必要的目录都已创建
|
|
85
103
|
await ensureMarketDirs();
|
|
86
|
-
|
|
87
|
-
// 转换包名格式
|
|
88
|
-
// 用户可以直接指定 @scope/package 或不带前缀的 short name
|
|
89
|
-
// short name 会被添加 @skillmarket/ 前缀尝试
|
|
90
|
-
// 如果是 scoped 包 (@scope/name),直接使用
|
|
91
|
-
const isScoped = skillId.startsWith('@');
|
|
92
|
-
const packageName = isScoped ? skillId : `@skillmarket/${skillId}`;
|
|
93
|
-
const shortName = skillId; // 用于本地目录名
|
|
94
104
|
|
|
95
|
-
console.log(`Installing ${
|
|
105
|
+
console.log(`Installing ${skillId}${version ? `@${version}` : ''}...`);
|
|
96
106
|
|
|
97
107
|
// ==========================================================================
|
|
98
108
|
// 步骤 1: 获取包信息
|
|
99
109
|
// ==========================================================================
|
|
100
110
|
|
|
101
|
-
// 从 npm
|
|
102
|
-
const pkgInfo = await
|
|
111
|
+
// 从 npm 查询包的元信息(自动尝试多个可能的 scope)
|
|
112
|
+
const pkgInfo = await fetchSkillPackage(skillId);
|
|
103
113
|
if (!pkgInfo) {
|
|
104
|
-
throw new Error(`Package ${
|
|
114
|
+
throw new Error(`Package ${skillId} not found`);
|
|
105
115
|
}
|
|
106
116
|
|
|
117
|
+
// 获取实际找到的包名
|
|
118
|
+
const packageName = pkgInfo.name;
|
|
119
|
+
|
|
107
120
|
// 确定要安装的版本(用户指定版本 > 最新版本)
|
|
108
121
|
const targetVersion = version || pkgInfo['dist-tags']?.latest;
|
|
109
122
|
if (!targetVersion) {
|
|
@@ -214,56 +227,82 @@ export async function installSkill(
|
|
|
214
227
|
}
|
|
215
228
|
|
|
216
229
|
// ==========================================================================
|
|
217
|
-
// 步骤 5:
|
|
230
|
+
// 步骤 5: 安装到目标平台 (NEW)
|
|
218
231
|
// ==========================================================================
|
|
219
232
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
+
}
|
|
228
245
|
}
|
|
229
|
-
finalPlatform = parsed;
|
|
230
246
|
} else {
|
|
231
|
-
//
|
|
232
|
-
|
|
247
|
+
// 自动检测可用平台
|
|
248
|
+
targetAdapters = await detectPlatforms();
|
|
233
249
|
}
|
|
234
250
|
|
|
235
|
-
|
|
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
|
+
}
|
|
236
287
|
|
|
237
288
|
// ==========================================================================
|
|
238
289
|
// 步骤 6: 更新注册表
|
|
239
290
|
// ==========================================================================
|
|
240
291
|
|
|
241
292
|
const registry = await loadRegistry();
|
|
242
|
-
|
|
243
|
-
// 获取现有平台列表(如果 skill 已安装)
|
|
244
|
-
const existingSkill = registry.skills[skillId];
|
|
245
|
-
const existingPlatforms = existingSkill?.platforms || [];
|
|
293
|
+
const installedPlatforms = targetAdapters.map(a => a.id);
|
|
246
294
|
|
|
247
295
|
// 添加/更新注册表中的 skill 记录
|
|
248
296
|
registry.skills[skillId] = {
|
|
249
297
|
id: skillId,
|
|
250
298
|
version: targetVersion,
|
|
251
299
|
installedAt: new Date().toISOString(),
|
|
252
|
-
platforms:
|
|
253
|
-
? existingPlatforms
|
|
254
|
-
: [...existingPlatforms, finalPlatform as string]
|
|
300
|
+
platforms: installedPlatforms
|
|
255
301
|
} as InstalledSkill;
|
|
256
302
|
|
|
257
303
|
// 保存注册表
|
|
258
304
|
await saveRegistry(registry);
|
|
259
305
|
|
|
260
|
-
// ==========================================================================
|
|
261
|
-
// 步骤 7: 同步到指定平台
|
|
262
|
-
// ==========================================================================
|
|
263
|
-
|
|
264
|
-
console.log(`Syncing to ${finalPlatform}...`);
|
|
265
|
-
await syncPlatformLinks(finalPlatform);
|
|
266
|
-
|
|
267
306
|
// ==========================================================================
|
|
268
307
|
// 完成
|
|
269
308
|
// ==========================================================================
|
package/src/commands/ls.ts
CHANGED
|
@@ -117,22 +117,49 @@ export async function listSkills(options: LsOptions): Promise<void> {
|
|
|
117
117
|
|
|
118
118
|
// 遍历每个包,获取详细信息并显示
|
|
119
119
|
for (const pkgName of packages) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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(
|
|
140
|
+
console.log(`📦 ${info.name}@${latestVersion}`);
|
|
130
141
|
|
|
131
|
-
//
|
|
132
|
-
|
|
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) {
|
package/src/commands/npm.ts
CHANGED
|
@@ -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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|