itismyskillmarket 1.2.2 → 1.2.4
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 +72 -0
- package/5e51cb7aa8b8e60d49d86f4689f5d4d1.png +0 -0
- package/CHANGELOG.md +143 -0
- package/DEVELOPMENT.md +376 -0
- package/README.md +70 -4
- package/SKILLMARKET-GUIDE.md +277 -0
- package/dist/index.js +478 -177
- 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/skills/test-skill-1/SKILL.md +24 -0
- package/skills/test-skill-1/index.js +13 -0
- package/skills/test-skill-1/metadata.json +9 -0
- package/skills/test-skill-1/package.json +16 -0
- package/skills/test-skill-2/SKILL.md +25 -0
- package/skills/test-skill-2/index.js +13 -0
- package/skills/test-skill-2/metadata.json +9 -0
- package/skills/test-skill-2/package.json +16 -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 +113 -49
- package/src/commands/info.ts +4 -15
- package/src/commands/install.ts +93 -54
- package/src/commands/ls.ts +69 -13
- package/src/commands/npm.ts +87 -12
- 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/commands/ls.ts
CHANGED
|
@@ -37,6 +37,12 @@ interface LsOptions {
|
|
|
37
37
|
|
|
38
38
|
/** 检查更新(预留功能) */
|
|
39
39
|
updates?: boolean;
|
|
40
|
+
|
|
41
|
+
/** 页码(从 1 开始) */
|
|
42
|
+
page?: number;
|
|
43
|
+
|
|
44
|
+
/** 每页数量 */
|
|
45
|
+
limit?: number;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
// -----------------------------------------------------------------------------
|
|
@@ -60,25 +66,33 @@ interface LsOptions {
|
|
|
60
66
|
* await listSkills({ installed: true });
|
|
61
67
|
*/
|
|
62
68
|
export async function listSkills(options: LsOptions): Promise<void> {
|
|
63
|
-
const { installed, updates } = options;
|
|
69
|
+
const { installed, updates, page = 1, limit = 20 } = options;
|
|
64
70
|
|
|
65
71
|
// -------------------------------------------------------------------------
|
|
66
72
|
// 模式1: 显示已安装的 skills
|
|
67
73
|
// -------------------------------------------------------------------------
|
|
68
74
|
if (installed) {
|
|
69
75
|
const skills = await getInstalledSkills();
|
|
76
|
+
const total = skills.length;
|
|
77
|
+
const totalPages = Math.ceil(total / limit) || 1;
|
|
78
|
+
const currentPage = Math.min(Math.max(1, page), totalPages);
|
|
70
79
|
|
|
71
80
|
// 无已安装 skills 时给出提示
|
|
72
81
|
if (skills.length === 0) {
|
|
73
|
-
console.log('No skills installed yet. Run "skm
|
|
82
|
+
console.log('No skills installed yet. Run "skm ls" to see available skills.');
|
|
74
83
|
return;
|
|
75
84
|
}
|
|
76
85
|
|
|
86
|
+
// 计算分页范围
|
|
87
|
+
const start = (currentPage - 1) * limit;
|
|
88
|
+
const end = Math.min(start + limit, total);
|
|
89
|
+
const pageSkills = skills.slice(start, end);
|
|
90
|
+
|
|
77
91
|
// 打印表头
|
|
78
|
-
console.log(
|
|
92
|
+
console.log(`Installed Skills (${total}):\n`);
|
|
79
93
|
|
|
80
94
|
// 遍历并打印每个 skill 的详细信息
|
|
81
|
-
for (const skill of
|
|
95
|
+
for (const skill of pageSkills) {
|
|
82
96
|
// skill 名称和版本
|
|
83
97
|
console.log(` ${skill.id}@${skill.version}`);
|
|
84
98
|
|
|
@@ -92,6 +106,9 @@ export async function listSkills(options: LsOptions): Promise<void> {
|
|
|
92
106
|
console.log();
|
|
93
107
|
}
|
|
94
108
|
|
|
109
|
+
// 打印分页信息
|
|
110
|
+
console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
|
|
111
|
+
|
|
95
112
|
return;
|
|
96
113
|
}
|
|
97
114
|
|
|
@@ -103,8 +120,17 @@ export async function listSkills(options: LsOptions): Promise<void> {
|
|
|
103
120
|
console.log('Searching npm registry...\n');
|
|
104
121
|
|
|
105
122
|
try {
|
|
123
|
+
// 计算分页偏移量
|
|
124
|
+
const offset = (page - 1) * limit;
|
|
125
|
+
|
|
106
126
|
// 调用 npm search API 搜索 skillmarket 相关包
|
|
107
|
-
const packages = await searchSkillmarketPackages(
|
|
127
|
+
const { packages, total } = await searchSkillmarketPackages({
|
|
128
|
+
from: offset,
|
|
129
|
+
size: limit
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const totalPages = Math.ceil(total / limit) || 1;
|
|
133
|
+
const currentPage = Math.min(Math.max(1, page), totalPages);
|
|
108
134
|
|
|
109
135
|
// 无搜索结果时
|
|
110
136
|
if (packages.length === 0) {
|
|
@@ -113,28 +139,58 @@ export async function listSkills(options: LsOptions): Promise<void> {
|
|
|
113
139
|
}
|
|
114
140
|
|
|
115
141
|
// 打印找到的包数量
|
|
116
|
-
console.log(`Found ${
|
|
142
|
+
console.log(`Found ${total} skill(s):\n`);
|
|
117
143
|
|
|
118
144
|
// 遍历每个包,获取详细信息并显示
|
|
119
145
|
for (const pkgName of packages) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
146
|
+
try {
|
|
147
|
+
const info = await fetchNpmPackage(pkgName);
|
|
148
|
+
|
|
149
|
+
if (!info) {
|
|
150
|
+
// 如果获取失败,仍然显示包名
|
|
151
|
+
console.log(`📦 ${pkgName} (信息获取失败)`);
|
|
152
|
+
console.log();
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 获取最新版本号
|
|
157
|
+
const latestVersion = info['dist-tags']?.latest || 'unknown';
|
|
124
158
|
|
|
125
159
|
// 获取该版本的详细信息
|
|
126
160
|
const pkg = info.versions?.[latestVersion];
|
|
127
161
|
|
|
162
|
+
// 获取 skillmarket 元数据
|
|
163
|
+
const skillMeta = pkg?.skillmarket;
|
|
164
|
+
|
|
128
165
|
// 打印包名和版本
|
|
129
|
-
console.log(
|
|
166
|
+
console.log(`📦 ${info.name}@${latestVersion}`);
|
|
167
|
+
|
|
168
|
+
// 打印显示名称
|
|
169
|
+
const displayName = skillMeta?.displayName || info.name;
|
|
170
|
+
console.log(` 名称: ${displayName}`);
|
|
171
|
+
|
|
172
|
+
// 打印描述
|
|
173
|
+
console.log(` 描述: ${pkg?.description || 'N/A'}`);
|
|
130
174
|
|
|
131
|
-
//
|
|
132
|
-
|
|
175
|
+
// 打印支持平台
|
|
176
|
+
const platforms = skillMeta?.platforms || [];
|
|
177
|
+
console.log(` 平台: ${platforms.length > 0 ? platforms.join(', ') : 'N/A'}`);
|
|
178
|
+
|
|
179
|
+
// 打印 npm 链接
|
|
180
|
+
const npmLink = pkg?.links?.npm || `https://www.npmjs.com/package/${info.name}`;
|
|
181
|
+
console.log(` 链接: ${npmLink}`);
|
|
133
182
|
|
|
134
183
|
// 空行分隔
|
|
135
184
|
console.log();
|
|
185
|
+
} catch (e) {
|
|
186
|
+
// 错误时仍显示包名
|
|
187
|
+
console.log(`📦 ${pkgName} (获取失败: ${e})`);
|
|
188
|
+
console.log();
|
|
136
189
|
}
|
|
137
190
|
}
|
|
191
|
+
|
|
192
|
+
// 打印分页信息
|
|
193
|
+
console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
|
|
138
194
|
} catch (error) {
|
|
139
195
|
// 网络错误处理
|
|
140
196
|
console.log(`Error fetching skills: ${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
|
// -----------------------------------------------------------------------------
|
|
@@ -190,17 +250,25 @@ export async function fetchNpmPackage(packageName: string): Promise<NpmRegistryR
|
|
|
190
250
|
*
|
|
191
251
|
* API 端点: GET https://registry.npmjs.org/-/v1/search
|
|
192
252
|
*
|
|
193
|
-
* @
|
|
253
|
+
* @param options - 搜索选项
|
|
254
|
+
* @param options.from - 起始位置(分页用)
|
|
255
|
+
* @param options.size - 返回结果数量
|
|
256
|
+
* @returns {Promise<{packages: string[], total: number}>} 匹配的包名数组和总数
|
|
194
257
|
*
|
|
195
258
|
* @example
|
|
196
|
-
* const packages = await searchSkillmarketPackages();
|
|
197
|
-
* console.log(`找到 ${
|
|
259
|
+
* const { packages, total } = await searchSkillmarketPackages();
|
|
260
|
+
* console.log(`找到 ${total} 个 skill 包`);
|
|
198
261
|
* packages.forEach(name => {
|
|
199
262
|
* console.log(`- ${name}`);
|
|
200
263
|
* });
|
|
201
264
|
*/
|
|
202
|
-
export async function searchSkillmarketPackages(
|
|
265
|
+
export async function searchSkillmarketPackages(options: {
|
|
266
|
+
from?: number;
|
|
267
|
+
size?: number;
|
|
268
|
+
} = {}): Promise<{ packages: string[]; total: number }> {
|
|
269
|
+
const { from = 0, size = 100 } = options;
|
|
203
270
|
const packages: string[] = [];
|
|
271
|
+
let total = 0;
|
|
204
272
|
|
|
205
273
|
return new Promise((resolve, reject) => {
|
|
206
274
|
// 构建 search API URL
|
|
@@ -209,8 +277,10 @@ export async function searchSkillmarketPackages(): Promise<string[]> {
|
|
|
209
277
|
// 设置搜索参数
|
|
210
278
|
// text: 搜索关键字
|
|
211
279
|
// size: 返回结果数量上限
|
|
280
|
+
// from: 起始位置(分页用)
|
|
212
281
|
url.searchParams.set('text', 'keywords:skillmarket');
|
|
213
|
-
url.searchParams.set('size',
|
|
282
|
+
url.searchParams.set('size', String(size));
|
|
283
|
+
url.searchParams.set('from', String(from));
|
|
214
284
|
|
|
215
285
|
const req = https.get(url.toString(), { timeout: 10000 }, (res) => {
|
|
216
286
|
let data = '';
|
|
@@ -223,18 +293,23 @@ export async function searchSkillmarketPackages(): Promise<string[]> {
|
|
|
223
293
|
// 解析搜索结果
|
|
224
294
|
const result = JSON.parse(data);
|
|
225
295
|
|
|
296
|
+
// 获取总数
|
|
297
|
+
total = result.total || 0;
|
|
298
|
+
|
|
226
299
|
// 提取所有匹配的包名
|
|
227
300
|
// npm search 返回结构: { objects: [{ package: { name: "..." } }] }
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
301
|
+
if (result.objects) {
|
|
302
|
+
for (const item of result.objects) {
|
|
303
|
+
if (item?.package?.name) {
|
|
304
|
+
packages.push(item.package.name);
|
|
305
|
+
}
|
|
231
306
|
}
|
|
232
307
|
}
|
|
233
308
|
|
|
234
|
-
resolve(packages);
|
|
309
|
+
resolve({ packages, total });
|
|
235
310
|
} catch {
|
|
236
311
|
// 解析失败返回空数组
|
|
237
|
-
resolve([]);
|
|
312
|
+
resolve({ packages: [], total: 0 });
|
|
238
313
|
}
|
|
239
314
|
});
|
|
240
315
|
});
|
package/src/commands/sync.ts
CHANGED
|
@@ -43,7 +43,6 @@ import {
|
|
|
43
43
|
|
|
44
44
|
import { loadRegistry } from './registry.js';
|
|
45
45
|
|
|
46
|
-
import { getPlatformFromInput, type Platform } from '../utils/platform.js';
|
|
47
46
|
import { PLATFORMS, LATEST_LINK } from '../constants.js';
|
|
48
47
|
|
|
49
48
|
// -----------------------------------------------------------------------------
|
|
@@ -51,22 +50,18 @@ import { PLATFORMS, LATEST_LINK } from '../constants.js';
|
|
|
51
50
|
// -----------------------------------------------------------------------------
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
|
-
*
|
|
53
|
+
* 同步所有平台的软链接
|
|
55
54
|
*
|
|
56
|
-
* 为每个已安装的 skill
|
|
55
|
+
* 为每个已安装的 skill 在各平台目录下创建软链接,
|
|
57
56
|
* 指向 skills 目录中对应的平台特定文件。
|
|
58
57
|
*
|
|
59
|
-
* @param {string | Platform} [targetPlatform] - 目标平台(可选,不指定则同步所有平台)
|
|
60
58
|
* @returns {Promise<void>}
|
|
61
59
|
*
|
|
62
60
|
* @example
|
|
63
61
|
* // 同步所有平台链接
|
|
64
62
|
* await syncPlatformLinks();
|
|
65
|
-
*
|
|
66
|
-
* // 仅同步到 opencode
|
|
67
|
-
* await syncPlatformLinks('opencode');
|
|
68
63
|
*/
|
|
69
|
-
export async function syncPlatformLinks(
|
|
64
|
+
export async function syncPlatformLinks(): Promise<void> {
|
|
70
65
|
// ==========================================================================
|
|
71
66
|
// 步骤 1: 准备
|
|
72
67
|
// ==========================================================================
|
|
@@ -81,29 +76,13 @@ export async function syncPlatformLinks(targetPlatform?: string): Promise<void>
|
|
|
81
76
|
// 加载注册表获取已安装的 skills
|
|
82
77
|
const registry = await loadRegistry();
|
|
83
78
|
|
|
84
|
-
|
|
85
|
-
let platformsToSync: Platform[];
|
|
86
|
-
|
|
87
|
-
if (targetPlatform) {
|
|
88
|
-
const parsed = getPlatformFromInput(targetPlatform);
|
|
89
|
-
if (!parsed) {
|
|
90
|
-
console.warn(`Invalid platform: ${targetPlatform}, syncing all platforms`);
|
|
91
|
-
platformsToSync = PLATFORMS;
|
|
92
|
-
} else {
|
|
93
|
-
platformsToSync = [parsed];
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
platformsToSync = PLATFORMS;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const targetDesc = targetPlatform ? targetPlatform : 'all platforms';
|
|
100
|
-
console.log(`Syncing platform links to ${targetDesc}...\n`);
|
|
79
|
+
console.log('Syncing platform links...\n');
|
|
101
80
|
|
|
102
81
|
// ==========================================================================
|
|
103
|
-
// 步骤 2:
|
|
82
|
+
// 步骤 2: 遍历所有平台
|
|
104
83
|
// ==========================================================================
|
|
105
84
|
|
|
106
|
-
for (const platform of
|
|
85
|
+
for (const platform of PLATFORMS) {
|
|
107
86
|
// 创建平台的 skills 目录
|
|
108
87
|
// 例如: ~/.skillmarket/platform-links/cursor/skills/
|
|
109
88
|
const platformDir = path.join(platformLinksDir, platform, 'skills');
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 卸载流程:
|
|
9
9
|
* 1. 检查 skill 是否已安装
|
|
10
|
-
* 2.
|
|
11
|
-
* 3.
|
|
12
|
-
* 4.
|
|
10
|
+
* 2. 从目标平台卸载
|
|
11
|
+
* 3. 删除 skill 主目录(包含所有版本和软链接)
|
|
12
|
+
* 4. 删除各平台的软链接
|
|
13
|
+
* 5. 从注册表中移除记录
|
|
13
14
|
*
|
|
14
15
|
* @module commands/uninstall
|
|
15
16
|
*/
|
|
@@ -23,6 +24,18 @@ import path from 'path'; // 路径处理
|
|
|
23
24
|
import { loadRegistry, saveRegistry } from './registry.js'; // 注册表操作
|
|
24
25
|
import { getSkillsDir, getPlatformLinksDir } from '../utils/dirs.js'; // 目录工具
|
|
25
26
|
import { PLATFORMS } from '../constants.js'; // 平台常量
|
|
27
|
+
import { detectPlatforms, getAdapterByPlatform } from '../adapters/index.js'; // 平台适配器
|
|
28
|
+
import type { Platform } from '../constants.js';
|
|
29
|
+
import type { PlatformAdapter } from '../types.js';
|
|
30
|
+
|
|
31
|
+
// -----------------------------------------------------------------------------
|
|
32
|
+
// 卸载选项接口
|
|
33
|
+
// -----------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
export interface UninstallOptions {
|
|
36
|
+
/** 目标平台列表(留空则卸载所有平台) */
|
|
37
|
+
platforms?: string[];
|
|
38
|
+
}
|
|
26
39
|
|
|
27
40
|
// -----------------------------------------------------------------------------
|
|
28
41
|
// 卸载函数
|
|
@@ -32,13 +45,20 @@ import { PLATFORMS } from '../constants.js'; // 平台常量
|
|
|
32
45
|
* 卸载指定的 skill
|
|
33
46
|
*
|
|
34
47
|
* @param {string} skillId - Skill 标识符
|
|
48
|
+
* @param {UninstallOptions} [options] - 卸载选项
|
|
35
49
|
* @returns {Promise<void>}
|
|
36
50
|
*
|
|
37
51
|
* @example
|
|
38
52
|
* // 卸载 brainstorming
|
|
39
53
|
* await uninstallSkill('brainstorming');
|
|
54
|
+
*
|
|
55
|
+
* // 从特定平台卸载
|
|
56
|
+
* await uninstallSkill('brainstorming', { platforms: ['opencode'] });
|
|
40
57
|
*/
|
|
41
|
-
export async function uninstallSkill(
|
|
58
|
+
export async function uninstallSkill(
|
|
59
|
+
skillId: string,
|
|
60
|
+
options?: UninstallOptions
|
|
61
|
+
): Promise<void> {
|
|
42
62
|
// ==========================================================================
|
|
43
63
|
// 步骤 1: 检查是否已安装
|
|
44
64
|
// ==========================================================================
|
|
@@ -57,7 +77,40 @@ export async function uninstallSkill(skillId: string): Promise<void> {
|
|
|
57
77
|
console.log(`Uninstalling ${skillId}@${skillInfo.version}...`);
|
|
58
78
|
|
|
59
79
|
// ==========================================================================
|
|
60
|
-
// 步骤 2:
|
|
80
|
+
// 步骤 2: 从平台卸载 (NEW)
|
|
81
|
+
// ==========================================================================
|
|
82
|
+
|
|
83
|
+
let targetAdapters: (PlatformAdapter | undefined)[] = [];
|
|
84
|
+
|
|
85
|
+
if (options?.platforms && options.platforms.length > 0) {
|
|
86
|
+
// 用户指定了平台
|
|
87
|
+
for (const platformStr of options.platforms) {
|
|
88
|
+
const platform = platformStr as Platform;
|
|
89
|
+
targetAdapters.push(getAdapterByPlatform(platform));
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
// 自动检测可用平台
|
|
93
|
+
targetAdapters = await detectPlatforms();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 过滤掉 undefined(无效平台)
|
|
97
|
+
const validAdapters = targetAdapters.filter((a): a is PlatformAdapter => a !== undefined);
|
|
98
|
+
|
|
99
|
+
if (validAdapters.length > 0) {
|
|
100
|
+
console.log(`\nUninstalling from ${validAdapters.length} platform(s)...\n`);
|
|
101
|
+
|
|
102
|
+
for (const adapter of validAdapters) {
|
|
103
|
+
try {
|
|
104
|
+
await adapter.uninstall(skillId);
|
|
105
|
+
console.log(`${adapter.name.padEnd(12)} ✅ Uninstalled`);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.log(`${adapter.name.padEnd(12)} ❌ Failed: ${error}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ==========================================================================
|
|
113
|
+
// 步骤 3: 删除 skill 主目录
|
|
61
114
|
// ==========================================================================
|
|
62
115
|
|
|
63
116
|
// skill 主目录包含所有版本和 latest 软链接
|
|
@@ -68,7 +121,7 @@ export async function uninstallSkill(skillId: string): Promise<void> {
|
|
|
68
121
|
await fs.remove(skillDir);
|
|
69
122
|
|
|
70
123
|
// ==========================================================================
|
|
71
|
-
// 步骤
|
|
124
|
+
// 步骤 4: 删除平台软链接
|
|
72
125
|
// ==========================================================================
|
|
73
126
|
|
|
74
127
|
const platformLinksDir = getPlatformLinksDir();
|
|
@@ -85,7 +138,7 @@ export async function uninstallSkill(skillId: string): Promise<void> {
|
|
|
85
138
|
}
|
|
86
139
|
|
|
87
140
|
// ==========================================================================
|
|
88
|
-
// 步骤
|
|
141
|
+
// 步骤 5: 更新注册表
|
|
89
142
|
// ==========================================================================
|
|
90
143
|
|
|
91
144
|
// 从注册表中删除该 skill 的记录
|
package/src/commands/update.ts
CHANGED
|
@@ -47,7 +47,7 @@ export async function updateSkill(skillId?: string): Promise<void> {
|
|
|
47
47
|
|
|
48
48
|
if (skillId) {
|
|
49
49
|
// 查询 npm 获取最新版本
|
|
50
|
-
const pkgInfo = await fetchNpmPackage(`@
|
|
50
|
+
const pkgInfo = await fetchNpmPackage(`@itismyskillmarket/${skillId}`);
|
|
51
51
|
|
|
52
52
|
if (pkgInfo) {
|
|
53
53
|
const latestVersion = pkgInfo['dist-tags']?.latest;
|
|
@@ -81,7 +81,7 @@ export async function updateSkill(skillId?: string): Promise<void> {
|
|
|
81
81
|
// 遍历每个已安装的 skill
|
|
82
82
|
for (const skill of installed) {
|
|
83
83
|
// 查询 npm 获取最新版本信息
|
|
84
|
-
const pkgInfo = await fetchNpmPackage(`@
|
|
84
|
+
const pkgInfo = await fetchNpmPackage(`@wanxuchen/${skill.id}`);
|
|
85
85
|
|
|
86
86
|
if (pkgInfo) {
|
|
87
87
|
const latestVersion = pkgInfo['dist-tags']?.latest;
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
*
|
|
6
6
|
* 本文件是 SkillMarket CLI 工具的入口点。
|
|
7
7
|
*
|
|
8
|
+
* Shebang (#!) 说明:
|
|
9
|
+
* - #!/usr/bin/env node 表示使用系统中的 node 解释器执行
|
|
10
|
+
* - 这使得脚本可以作为可执行文件直接运行
|
|
11
|
+
*
|
|
12
|
+
* 包配置 (package.json):
|
|
13
|
+
* {
|
|
14
|
+
* "bin": {
|
|
15
|
+
* "skm": "./dist/index.js"
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* 安装后:
|
|
20
|
+
* - 全局安装: npm install -g skillmarket
|
|
21
|
+
* 会将 skm 命令链接到系统 PATH
|
|
22
|
+
*
|
|
23
|
+
* - 本地运行: npx skillmarket
|
|
24
|
+
* 使用 npx 直接运行而不安装
|
|
25
|
+
*
|
|
8
26
|
* 执行流程:
|
|
9
27
|
* 1. Node.js 根据 shebang 启动
|
|
10
28
|
* 2. 导入 cli.ts 中的命令行解析器
|
|
@@ -22,6 +40,15 @@
|
|
|
22
40
|
* node dist/index.js --help
|
|
23
41
|
*/
|
|
24
42
|
|
|
43
|
+
// -----------------------------------------------------------------------------
|
|
44
|
+
// Shebang - 在 tsup.config.ts 的 banner 中配置
|
|
45
|
+
// -----------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 注意: shebang (#!/usr/bin/env node) 现在由 tsup.config.ts 的 banner 配置添加,
|
|
49
|
+
* 不再直接写在此文件中。这确保了跨平台兼容性。
|
|
50
|
+
*/
|
|
51
|
+
|
|
25
52
|
// -----------------------------------------------------------------------------
|
|
26
53
|
// 导入 CLI 模块
|
|
27
54
|
// -----------------------------------------------------------------------------
|
package/src/types.ts
CHANGED
|
@@ -135,3 +135,38 @@ export interface RegistryData {
|
|
|
135
135
|
*/
|
|
136
136
|
lastUpdated: string;
|
|
137
137
|
}
|
|
138
|
+
|
|
139
|
+
// -----------------------------------------------------------------------------
|
|
140
|
+
// Platform Adapter 接口
|
|
141
|
+
// -----------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Platform adapter interface for cross-platform skill installation
|
|
145
|
+
*
|
|
146
|
+
* @interface PlatformAdapter
|
|
147
|
+
*/
|
|
148
|
+
export interface PlatformAdapter {
|
|
149
|
+
/** Unique platform identifier */
|
|
150
|
+
readonly id: string;
|
|
151
|
+
|
|
152
|
+
/** Human-readable platform name */
|
|
153
|
+
readonly name: string;
|
|
154
|
+
|
|
155
|
+
/** Platform's skill directory path */
|
|
156
|
+
readonly skillDir: string;
|
|
157
|
+
|
|
158
|
+
/** Check if this platform is available on the current system */
|
|
159
|
+
isAvailable(): Promise<boolean>;
|
|
160
|
+
|
|
161
|
+
/** Check if a skill is installed on this platform */
|
|
162
|
+
isInstalled(skillId: string): Promise<boolean>;
|
|
163
|
+
|
|
164
|
+
/** Install a skill to this platform */
|
|
165
|
+
install(skillId: string, sourceDir: string): Promise<void>;
|
|
166
|
+
|
|
167
|
+
/** Uninstall a skill from this platform */
|
|
168
|
+
uninstall(skillId: string): Promise<void>;
|
|
169
|
+
|
|
170
|
+
/** List all skills installed on this platform */
|
|
171
|
+
listInstalled(): Promise<string[]>;
|
|
172
|
+
}
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
format: ['esm'],
|
|
6
|
+
dts: true,
|
|
7
|
+
banner: {
|
|
8
|
+
/**
|
|
9
|
+
* 添加 shebang 到编译后的输出
|
|
10
|
+
*
|
|
11
|
+
* 注意: tsup 的 banner 选项会在模块内容前添加字符串,
|
|
12
|
+
* 这里用 JSON 格式传递以确保正确处理
|
|
13
|
+
*/
|
|
14
|
+
js: '#!/usr/bin/env node'
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* 禁用 shims 以避免潜在的兼容性问题
|
|
18
|
+
* shims 会自动为某些 Node.js 模块提供垫片,
|
|
19
|
+
* 但可能与我们的场景不兼容
|
|
20
|
+
*/
|
|
21
|
+
shims: false
|
|
22
|
+
});
|
|
Binary file
|