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.
Files changed (49) hide show
  1. package/.github/workflows/publish-npm.yml +59 -0
  2. package/.github/workflows/publish-skill.yml +72 -0
  3. package/5e51cb7aa8b8e60d49d86f4689f5d4d1.png +0 -0
  4. package/CHANGELOG.md +410 -0
  5. package/DEVELOPMENT.md +376 -0
  6. package/README.md +75 -6
  7. package/SKILLMARKET-GUIDE.md +288 -0
  8. package/dist/index.js +733 -212
  9. package/docs/WEEKLY-UPDATE-2026-04-23.md +43 -0
  10. package/docs/plans/2026-04-01-skillmarket-design.md +267 -0
  11. package/docs/plans/2026-04-01-skillmarket-implementation.md +1031 -0
  12. package/docs/plans/2026-04-15-cross-platform-adapter-design.md +416 -0
  13. package/docs/plans/2026-04-15-cross-platform-adapter-plan.md +833 -0
  14. package/docs/plans/2026-04-16-keyword-search-design.md +143 -0
  15. package/docs/plans/2026-04-29-weekly-update.md +57 -0
  16. package/package.json +1 -6
  17. package/skills/README.md +54 -0
  18. package/skills/test-skill/SKILL.md +25 -0
  19. package/skills/test-skill/index.js +66 -0
  20. package/skills/test-skill/metadata.json +9 -0
  21. package/skills/test-skill/package.json +19 -0
  22. package/skills/test-skill-1/SKILL.md +24 -0
  23. package/skills/test-skill-1/index.js +13 -0
  24. package/skills/test-skill-1/metadata.json +9 -0
  25. package/skills/test-skill-1/package.json +16 -0
  26. package/skills/test-skill-2/SKILL.md +25 -0
  27. package/skills/test-skill-2/index.js +13 -0
  28. package/skills/test-skill-2/metadata.json +9 -0
  29. package/skills/test-skill-2/package.json +16 -0
  30. package/src/adapters/base.ts +87 -0
  31. package/src/adapters/claude.ts +31 -0
  32. package/src/adapters/index.ts +9 -0
  33. package/src/adapters/opencode.ts +40 -0
  34. package/src/adapters/registry.ts +77 -0
  35. package/src/adapters/vscode.ts +62 -0
  36. package/src/cli.ts +189 -75
  37. package/src/commands/info.ts +4 -15
  38. package/src/commands/install.ts +93 -54
  39. package/src/commands/ls.ts +182 -17
  40. package/src/commands/npm.ts +118 -16
  41. package/src/commands/search.ts +12 -7
  42. package/src/commands/sync.ts +6 -27
  43. package/src/commands/uninstall.ts +313 -15
  44. package/src/commands/update.ts +2 -2
  45. package/src/index.ts +27 -0
  46. package/src/types.ts +35 -0
  47. package/tsconfig.json +10 -0
  48. package/tsup.config.ts +22 -0
  49. package/wanxuchen-skillmarket-1.0.1.tgz +0 -0
@@ -7,9 +7,16 @@
7
7
  *
8
8
  * 卸载流程:
9
9
  * 1. 检查 skill 是否已安装
10
- * 2. 删除 skill 主目录(包含所有版本和软链接)
11
- * 3. 删除各平台的软链接
12
- * 4. 从注册表中移除记录
10
+ * 2. 从目标平台卸载
11
+ * 3. 删除 skill 主目录(包含所有版本和软链接)
12
+ * 4. 删除各平台的软链接
13
+ * 5. 从注册表中移除记录
14
+ *
15
+ * 新增功能 (v1.4.0):
16
+ * - 支持 --all 卸载所有 skills
17
+ * - 支持 --dry-run 预览删除内容
18
+ * - 添加确认提示(--all 时强制确认)
19
+ * - 改进错误处理:平台卸载失败时暂停本地删除
13
20
  *
14
21
  * @module commands/uninstall
15
22
  */
@@ -20,25 +27,126 @@
20
27
 
21
28
  import fs from 'fs-extra'; // 文件系统操作
22
29
  import path from 'path'; // 路径处理
23
- import { loadRegistry, saveRegistry } from './registry.js'; // 注册表操作
30
+ import readline from 'readline'; // 用户交互确认
31
+ import { loadRegistry, saveRegistry, getInstalledSkills } from './registry.js'; // 注册表操作
24
32
  import { getSkillsDir, getPlatformLinksDir } from '../utils/dirs.js'; // 目录工具
25
33
  import { PLATFORMS } from '../constants.js'; // 平台常量
34
+ import { detectPlatforms, getAdapterByPlatform } from '../adapters/index.js'; // 平台适配器
35
+ import type { Platform } from '../constants.js';
36
+ import type { PlatformAdapter, InstalledSkill } from '../types.js';
37
+
38
+ // -----------------------------------------------------------------------------
39
+ // 卸载选项接口
40
+ // -----------------------------------------------------------------------------
41
+
42
+ export interface UninstallOptions {
43
+ /** 目标平台列表(留空则卸载所有平台) */
44
+ platforms?: string[];
45
+ /** 卸载所有已安装的 skills */
46
+ all?: boolean;
47
+ /** 预览模式:不实际删除,只显示将要删除的内容 */
48
+ dryRun?: boolean;
49
+ /** 跳过确认提示 */
50
+ yes?: boolean;
51
+ }
52
+
53
+ // -----------------------------------------------------------------------------
54
+ // 工具函数:用户确认提示
55
+ // -----------------------------------------------------------------------------
56
+
57
+ /**
58
+ * 请求用户确认
59
+ *
60
+ * @param {string} message - 确认提示信息
61
+ * @returns {Promise<boolean>} 用户是否确认
62
+ */
63
+ async function askConfirmation(message: string): Promise<boolean> {
64
+ const rl = readline.createInterface({
65
+ input: process.stdin,
66
+ output: process.stdout
67
+ });
68
+
69
+ return new Promise((resolve) => {
70
+ rl.question(`${message} (y/N): `, (answer) => {
71
+ rl.close();
72
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
73
+ });
74
+ });
75
+ }
26
76
 
27
77
  // -----------------------------------------------------------------------------
28
- // 卸载函数
78
+ // 工具函数:收集 skill 删除预览信息
79
+ // -----------------------------------------------------------------------------
80
+
81
+ /**
82
+ * 收集指定 skill 的删除预览信息
83
+ *
84
+ * @param {string} skillId - Skill 标识符
85
+ * @param {UninstallOptions} options - 卸载选项
86
+ * @returns {Promise<{skillInfo: InstalledSkill, platforms: string[], localFiles: string[]}>} 预览信息
87
+ */
88
+ async function getUninstallPreview(
89
+ skillId: string,
90
+ options?: UninstallOptions
91
+ ): Promise<{
92
+ skillInfo: InstalledSkill;
93
+ platforms: string[];
94
+ localPath: string;
95
+ platformLinks: string[];
96
+ }> {
97
+ const registry = await loadRegistry();
98
+ const skillInfo = registry.skills[skillId];
99
+
100
+ // 收集目标平台
101
+ let platformNames: string[] = [];
102
+
103
+ if (options?.platforms && options.platforms.length > 0) {
104
+ platformNames = options.platforms;
105
+ } else {
106
+ const adapters = await detectPlatforms();
107
+ platformNames = adapters.map(a => a.name);
108
+ }
109
+
110
+ // 本地文件路径
111
+ const skillsDir = getSkillsDir();
112
+ const localPath = path.join(skillsDir, skillId);
113
+
114
+ // 平台软链接路径
115
+ const platformLinksDir = getPlatformLinksDir();
116
+ const platformLinks: string[] = [];
117
+
118
+ for (const platform of PLATFORMS) {
119
+ const linkPath = path.join(platformLinksDir, platform, 'skills', skillId);
120
+ if (await fs.pathExists(linkPath)) {
121
+ platformLinks.push(linkPath);
122
+ }
123
+ }
124
+
125
+ return { skillInfo, platforms: platformNames, localPath, platformLinks };
126
+ }
127
+
128
+ // -----------------------------------------------------------------------------
129
+ // 卸载单个 skill
29
130
  // -----------------------------------------------------------------------------
30
131
 
31
132
  /**
32
133
  * 卸载指定的 skill
33
134
  *
34
135
  * @param {string} skillId - Skill 标识符
35
- * @returns {Promise<void>}
136
+ * @param {UninstallOptions} [options] - 卸载选项
137
+ * @returns {Promise<boolean>} 是否成功卸载
36
138
  *
37
139
  * @example
38
140
  * // 卸载 brainstorming
39
141
  * await uninstallSkill('brainstorming');
142
+ *
143
+ * // 从特定平台卸载
144
+ * await uninstallSkill('brainstorming', { platforms: ['opencode'] });
40
145
  */
41
- export async function uninstallSkill(skillId: string): Promise<void> {
146
+ export async function uninstallSkill(
147
+ skillId: string,
148
+ options?: UninstallOptions
149
+ ): Promise<boolean> {
42
150
  // ==========================================================================
43
151
  // 步骤 1: 检查是否已安装
44
152
  // ==========================================================================
@@ -47,31 +155,121 @@ export async function uninstallSkill(skillId: string): Promise<void> {
47
155
 
48
156
  // 检查注册表中是否存在该 skill
49
157
  if (!(skillId in registry.skills)) {
50
- console.log(`Skill "${skillId}" is not installed.`);
51
- return;
158
+ console.log(`❌ Skill "${skillId}" is not installed.`);
159
+ return false;
52
160
  }
53
161
 
54
162
  // 获取 skill 信息(用于显示)
55
163
  const skillInfo = registry.skills[skillId];
56
164
 
57
- console.log(`Uninstalling ${skillId}@${skillInfo.version}...`);
165
+ // ==========================================================================
166
+ // 步骤 2: Dry-run 模式 - 只显示预览
167
+ // ==========================================================================
168
+
169
+ if (options?.dryRun) {
170
+ const preview = await getUninstallPreview(skillId, options);
171
+
172
+ console.log(`\n📋 Uninstall Preview for "${skillId}":`);
173
+ console.log(` Version: ${skillInfo.version}`);
174
+ console.log(` Installed: ${skillInfo.installedAt}`);
175
+ console.log(` Platforms (from registry): ${preview.platforms.join(', ') || 'none'}`);
176
+ console.log(`\n Local files to remove:`);
177
+ console.log(` - ${preview.localPath}`);
178
+
179
+ if (preview.platformLinks.length > 0) {
180
+ console.log(`\n Platform links to remove:`);
181
+ for (const link of preview.platformLinks) {
182
+ console.log(` - ${link}`);
183
+ }
184
+ }
185
+
186
+ console.log(`\n⚠️ This was a dry-run. No files were actually deleted.`);
187
+ return true;
188
+ }
189
+
190
+ // ==========================================================================
191
+ // 步骤 3: 确认提示(除非使用 --yes)
192
+ // ==========================================================================
193
+
194
+ if (!options?.yes) {
195
+ const confirmed = await askConfirmation(`Are you sure you want to uninstall "${skillId}"?`);
196
+ if (!confirmed) {
197
+ console.log('Uninstall cancelled.');
198
+ return false;
199
+ }
200
+ }
201
+
202
+ console.log(`\nUninstalling ${skillId}@${skillInfo.version}...`);
58
203
 
59
204
  // ==========================================================================
60
- // 步骤 2: 删除 skill 主目录
205
+ // 步骤 4: 从平台卸载
206
+ // ==========================================================================
207
+
208
+ let targetAdapters: (PlatformAdapter | undefined)[] = [];
209
+ let platformUninstallErrors: { name: string; error: string }[] = [];
210
+
211
+ if (options?.platforms && options.platforms.length > 0) {
212
+ // 用户指定了平台
213
+ for (const platformStr of options.platforms) {
214
+ const platform = platformStr as Platform;
215
+ targetAdapters.push(getAdapterByPlatform(platform));
216
+ }
217
+ } else {
218
+ // 自动检测可用平台
219
+ targetAdapters = await detectPlatforms();
220
+ }
221
+
222
+ // 过滤掉 undefined(无效平台)
223
+ const validAdapters = targetAdapters.filter((a): a is PlatformAdapter => a !== undefined);
224
+
225
+ if (validAdapters.length > 0) {
226
+ console.log(`\nUninstalling from ${validAdapters.length} platform(s)...\n`);
227
+
228
+ for (const adapter of validAdapters) {
229
+ try {
230
+ await adapter.uninstall(skillId);
231
+ console.log(`${adapter.name.padEnd(12)} ✅ Uninstalled`);
232
+ } catch (error) {
233
+ const errorMsg = error instanceof Error ? error.message : String(error);
234
+ console.log(`${adapter.name.padEnd(12)} ❌ Failed: ${errorMsg}`);
235
+ platformUninstallErrors.push({ name: adapter.name, error: errorMsg });
236
+ }
237
+ }
238
+ }
239
+
240
+ // ==========================================================================
241
+ // 步骤 5: 如果有平台卸载失败,询问是否继续
242
+ // ==========================================================================
243
+
244
+ if (platformUninstallErrors.length > 0 && !options?.yes) {
245
+ const continueAnyway = await askConfirmation(
246
+ `⚠️ ${platformUninstallErrors.length} platform(s) failed to uninstall. Continue with local cleanup?`
247
+ );
248
+ if (!continueAnyway) {
249
+ console.log('Uninstall aborted. Platform files may still exist.');
250
+ return false;
251
+ }
252
+ }
253
+
254
+ // ==========================================================================
255
+ // 步骤 6: 删除 skill 主目录
61
256
  // ==========================================================================
62
257
 
63
258
  // skill 主目录包含所有版本和 latest 软链接
64
259
  const skillsDir = getSkillsDir();
65
260
  const skillDir = path.join(skillsDir, skillId);
66
261
 
67
- // 递归删除整个目录
68
- await fs.remove(skillDir);
262
+ if (await fs.pathExists(skillDir)) {
263
+ await fs.remove(skillDir);
264
+ console.log(`✅ Removed local files: ${skillDir}`);
265
+ }
69
266
 
70
267
  // ==========================================================================
71
- // 步骤 3: 删除平台软链接
268
+ // 步骤 7: 删除平台软链接
72
269
  // ==========================================================================
73
270
 
74
271
  const platformLinksDir = getPlatformLinksDir();
272
+ let removedLinks = 0;
75
273
 
76
274
  // 遍历所有平台,删除对应的软链接
77
275
  for (const platform of PLATFORMS) {
@@ -81,11 +279,16 @@ export async function uninstallSkill(skillId: string): Promise<void> {
81
279
  // 如果软链接存在则删除
82
280
  if (await fs.pathExists(linkPath)) {
83
281
  await fs.remove(linkPath);
282
+ removedLinks++;
84
283
  }
85
284
  }
86
285
 
286
+ if (removedLinks > 0) {
287
+ console.log(`✅ Removed ${removedLinks} platform link(s)`);
288
+ }
289
+
87
290
  // ==========================================================================
88
- // 步骤 4: 更新注册表
291
+ // 步骤 8: 更新注册表
89
292
  // ==========================================================================
90
293
 
91
294
  // 从注册表中删除该 skill 的记录
@@ -93,10 +296,105 @@ export async function uninstallSkill(skillId: string): Promise<void> {
93
296
 
94
297
  // 保存更新后的注册表
95
298
  await saveRegistry(registry);
299
+ console.log(`✅ Registry updated`);
96
300
 
97
301
  // ==========================================================================
98
302
  // 完成
99
303
  // ==========================================================================
100
304
 
101
305
  console.log(`\n✅ ${skillId} uninstalled successfully!`);
306
+ return true;
307
+ }
308
+
309
+ // -----------------------------------------------------------------------------
310
+ // 卸载所有已安装的 skills
311
+ // -----------------------------------------------------------------------------
312
+
313
+ /**
314
+ * 卸载所有已安装的 skills
315
+ *
316
+ * @param {UninstallOptions} [options] - 卸载选项
317
+ * @returns {Promise<{success: number, failed: number}>} 卸载结果统计
318
+ *
319
+ * @example
320
+ * // 卸载所有,带确认提示
321
+ * await uninstallAll({ yes: false });
322
+ *
323
+ * // 强制卸载所有,跳过确认
324
+ * await uninstallAll({ yes: true });
325
+ */
326
+ export async function uninstallAll(
327
+ options?: UninstallOptions
328
+ ): Promise<{ success: number; failed: number }> {
329
+ const registry = await loadRegistry();
330
+ const installedSkills = Object.keys(registry.skills);
331
+
332
+ if (installedSkills.length === 0) {
333
+ console.log('No skills installed.');
334
+ return { success: 0, failed: 0 };
335
+ }
336
+
337
+ console.log(`\nFound ${installedSkills.length} installed skill(s):`);
338
+ for (const skillId of installedSkills) {
339
+ const info = registry.skills[skillId];
340
+ console.log(` - ${skillId}@${info.version}`);
341
+ }
342
+
343
+ // ==========================================================================
344
+ // Dry-run 模式
345
+ // ==========================================================================
346
+
347
+ if (options?.dryRun) {
348
+ console.log(`\n📋 Dry-run: Would uninstall ${installedSkills.length} skill(s).`);
349
+ console.log(`⚠️ No files were actually deleted.`);
350
+ return { success: installedSkills.length, failed: 0 };
351
+ }
352
+
353
+ // ==========================================================================
354
+ // 确认提示(--all 时强制要求确认,除非使用 --yes)
355
+ // ==========================================================================
356
+
357
+ if (!options?.yes) {
358
+ const confirmed = await askConfirmation(
359
+ `\n⚠️ Are you sure you want to uninstall ALL ${installedSkills.length} skill(s)? This action cannot be undone.`
360
+ );
361
+ if (!confirmed) {
362
+ console.log('Uninstall cancelled.');
363
+ return { success: 0, failed: 0 };
364
+ }
365
+ }
366
+
367
+ // ==========================================================================
368
+ // 逐个卸载
369
+ // ==========================================================================
370
+
371
+ console.log(`\nUninstalling all skills...\n`);
372
+
373
+ let successCount = 0;
374
+ let failedCount = 0;
375
+
376
+ for (const skillId of installedSkills) {
377
+ try {
378
+ const success = await uninstallSkill(skillId, { ...options, yes: true }); // 已经确认过,跳过子确认
379
+ if (success) {
380
+ successCount++;
381
+ } else {
382
+ failedCount++;
383
+ }
384
+ } catch (error) {
385
+ console.log(`❌ Failed to uninstall ${skillId}: ${error}`);
386
+ failedCount++;
387
+ }
388
+ }
389
+
390
+ // ==========================================================================
391
+ // 完成统计
392
+ // ==========================================================================
393
+
394
+ console.log(`\n📊 Summary:`);
395
+ console.log(` ✅ Success: ${successCount}`);
396
+ console.log(` ❌ Failed: ${failedCount}`);
397
+ console.log(` 📦 Total: ${installedSkills.length}`);
398
+
399
+ return { success: successCount, failed: failedCount };
102
400
  }
@@ -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(`@skillmarket/${skillId}`);
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(`@skillmarket/${skill.id}`);
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
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "outDir": "dist",
8
+ "rootDir": "src"
9
+ }
10
+ }
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