itismyskillmarket 1.3.0 → 1.3.2
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 +59 -0
- package/.github/workflows/publish-skill.yml +72 -0
- package/5e51cb7aa8b8e60d49d86f4689f5d4d1.png +0 -0
- package/CHANGELOG.md +410 -0
- package/DEVELOPMENT.md +376 -0
- package/README.md +75 -6
- package/SKILLMARKET-GUIDE.md +288 -0
- package/dist/index.js +733 -212
- package/docs/WEEKLY-UPDATE-2026-04-23.md +43 -0
- 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/docs/plans/2026-04-16-keyword-search-design.md +143 -0
- package/docs/plans/2026-04-29-weekly-update.md +57 -0
- package/package.json +1 -6
- package/skills/README.md +54 -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 +189 -75
- package/src/commands/info.ts +4 -15
- package/src/commands/install.ts +93 -54
- package/src/commands/ls.ts +182 -17
- package/src/commands/npm.ts +118 -16
- package/src/commands/search.ts +12 -7
- package/src/commands/sync.ts +6 -27
- package/src/commands/uninstall.ts +313 -15
- 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/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,27 +250,37 @@ 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
|
+
keyword?: string;
|
|
269
|
+
} = {}): Promise<{ packages: string[]; total: number }> {
|
|
270
|
+
const { from = 0, size = 100, keyword } = options;
|
|
203
271
|
const packages: string[] = [];
|
|
272
|
+
let total = 0;
|
|
204
273
|
|
|
205
274
|
return new Promise((resolve, reject) => {
|
|
206
275
|
// 构建 search API URL
|
|
207
276
|
const url = new URL('https://registry.npmjs.org/-/v1/search');
|
|
208
277
|
|
|
209
278
|
// 设置搜索参数
|
|
210
|
-
//
|
|
211
|
-
//
|
|
279
|
+
// 始终搜索 skillmarket 关键字的包
|
|
280
|
+
// 然后在本地进行精确过滤
|
|
212
281
|
url.searchParams.set('text', 'keywords:skillmarket');
|
|
213
|
-
url.searchParams.set('size',
|
|
282
|
+
url.searchParams.set('size', String(Math.max(size, 100))); // 获取更多结果用于本地过滤
|
|
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,50 @@ export async function searchSkillmarketPackages(): Promise<string[]> {
|
|
|
223
293
|
// 解析搜索结果
|
|
224
294
|
const result = JSON.parse(data);
|
|
225
295
|
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
296
|
+
// 获取总数(基于过滤后的结果)
|
|
297
|
+
let filteredTotal = 0;
|
|
298
|
+
|
|
299
|
+
// 提取所有匹配的包名,并进行本地精确过滤
|
|
300
|
+
// npm search 返回结构: { objects: [{ package: { name: "...", description: "..." } }] }
|
|
301
|
+
if (result.objects) {
|
|
302
|
+
for (const item of result.objects) {
|
|
303
|
+
if (item?.package?.name) {
|
|
304
|
+
const pkgName = item.package.name;
|
|
305
|
+
const pkgDesc = item.package.description || '';
|
|
306
|
+
|
|
307
|
+
// 本地精确过滤:如果有关键字,检查包名或描述是否包含
|
|
308
|
+
if (keyword) {
|
|
309
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
310
|
+
// 检查包名(处理 scoped 包名)
|
|
311
|
+
const shortName = pkgName.includes('/')
|
|
312
|
+
? pkgName.split('/')[1]
|
|
313
|
+
: pkgName;
|
|
314
|
+
|
|
315
|
+
const nameMatch = shortName.toLowerCase().includes(lowerKeyword) ||
|
|
316
|
+
pkgName.toLowerCase().includes(lowerKeyword);
|
|
317
|
+
const descMatch = pkgDesc.toLowerCase().includes(lowerKeyword);
|
|
318
|
+
|
|
319
|
+
// 关键词匹配:包名或描述中包含搜索词
|
|
320
|
+
if (nameMatch || descMatch) {
|
|
321
|
+
packages.push(pkgName);
|
|
322
|
+
filteredTotal++;
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
// 无关键字,返回所有
|
|
326
|
+
packages.push(pkgName);
|
|
327
|
+
filteredTotal++;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
231
330
|
}
|
|
232
331
|
}
|
|
233
332
|
|
|
234
|
-
|
|
333
|
+
// 使用过滤后的总数
|
|
334
|
+
total = filteredTotal;
|
|
335
|
+
|
|
336
|
+
resolve({ packages, total });
|
|
235
337
|
} catch {
|
|
236
338
|
// 解析失败返回空数组
|
|
237
|
-
resolve([]);
|
|
339
|
+
resolve({ packages: [], total: 0 });
|
|
238
340
|
}
|
|
239
341
|
});
|
|
240
342
|
});
|
package/src/commands/search.ts
CHANGED
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
* search 命令选项接口
|
|
28
28
|
*/
|
|
29
29
|
interface SearchOptions {
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/** 最大结果数量 */
|
|
31
|
+
limit?: number;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// -----------------------------------------------------------------------------
|
|
@@ -47,15 +47,17 @@ interface SearchOptions {
|
|
|
47
47
|
*
|
|
48
48
|
* @example
|
|
49
49
|
* // 搜索包含 "test" 的 skills
|
|
50
|
-
* await searchSkills('test', {});
|
|
50
|
+
* await searchSkills('test', { limit: 20 });
|
|
51
51
|
*/
|
|
52
|
-
export async function searchSkills(keyword: string): Promise<void> {
|
|
52
|
+
export async function searchSkills(keyword: string, options: SearchOptions = {}): Promise<void> {
|
|
53
|
+
const limit = options.limit ?? 20;
|
|
54
|
+
|
|
53
55
|
// 提示用户正在搜索
|
|
54
56
|
console.log(`Searching for "${keyword}"...\n`);
|
|
55
57
|
|
|
56
58
|
try {
|
|
57
59
|
// 调用 npm search API 搜索 skillmarket 相关包
|
|
58
|
-
const packages = await searchSkillmarketPackages();
|
|
60
|
+
const { packages } = await searchSkillmarketPackages({ keyword });
|
|
59
61
|
|
|
60
62
|
// 精确匹配:只返回包名包含关键词的 skill
|
|
61
63
|
const filtered = packages.filter(pkg =>
|
|
@@ -68,11 +70,14 @@ export async function searchSkills(keyword: string): Promise<void> {
|
|
|
68
70
|
return;
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
// 限制结果数量
|
|
74
|
+
const results = filtered.slice(0, limit);
|
|
75
|
+
|
|
71
76
|
// 打印找到的包数量
|
|
72
|
-
console.log(`Found ${filtered.length} skill(s):\n`);
|
|
77
|
+
console.log(`Found ${filtered.length} skill(s) (showing ${results.length}):\n`);
|
|
73
78
|
|
|
74
79
|
// 遍历每个包,获取详细信息并显示
|
|
75
|
-
for (const pkgName of
|
|
80
|
+
for (const pkgName of results) {
|
|
76
81
|
const info = await fetchNpmPackage(pkgName);
|
|
77
82
|
|
|
78
83
|
if (info && info['dist-tags']?.latest) {
|
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');
|