itismyskillmarket 1.2.3 → 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.
@@ -19,6 +19,8 @@ jobs:
19
19
  steps:
20
20
  - name: Checkout
21
21
  uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 0
22
24
 
23
25
  - name: Debug inputs
24
26
  run: |
package/CHANGELOG.md ADDED
@@ -0,0 +1,143 @@
1
+ # SkillMarket v1.2.3 发布总结
2
+
3
+ **日期**: 2026-04-15
4
+ **版本**: 1.2.3
5
+
6
+ ---
7
+
8
+ ## 🎉 新功能:跨平台 Skill 安装
9
+
10
+ ### 支持的平台
11
+
12
+ | 平台 | Skill 目录 | 状态 |
13
+ |------|-----------|------|
14
+ | OpenCode | `~/.config/opencode/skills/` | ✅ |
15
+ | Claude Code | `~/.claude/skills/` | ✅ |
16
+ | VSCode | `~/.copilot/skills/` | ✅ |
17
+
18
+ ### 新增命令
19
+
20
+ ```bash
21
+ # 查看可用平台
22
+ skm platforms
23
+
24
+ # 安装到所有检测到的平台
25
+ skm install <skill>
26
+
27
+ # 安装到指定平台
28
+ skm install <skill> --platform opencode
29
+ skm install <skill> --platform opencode,claude,vscode
30
+
31
+ # 卸载
32
+ skm uninstall <skill>
33
+ skm uninstall <skill> --platform claude
34
+ ```
35
+
36
+ ### 安装输出示例
37
+
38
+ ```
39
+ skm install test-skill-1
40
+
41
+ Installing test-skill-1...
42
+ Downloading package...
43
+ Setting up skill...
44
+
45
+ Installing to 3 platform(s)...
46
+
47
+ OpenCode ✅ Installed successfully
48
+ Claude Code ✅ Installed successfully
49
+ VSCode ✅ Installed successfully
50
+
51
+ 📊 Summary: 3 installed, 0 skipped, 0 failed
52
+
53
+ ✅ test-skill-1@1.1.0 installed successfully!
54
+ ```
55
+
56
+ ### 平台状态查看
57
+
58
+ ```
59
+ skm platforms
60
+
61
+ 📍 Available Platforms:
62
+
63
+ OpenCode ✅ Available (3 skills installed)
64
+ Claude Code ✅ Available (2 skills installed)
65
+ VSCode ✅ Available (3 skills installed)
66
+ ```
67
+
68
+ ---
69
+
70
+ ## 🐛 Bug 修复
71
+
72
+ 1. **npm scope 兼容问题**
73
+ - 修复了 `@wanxuchen/`、`@itismyskillmarket/` 等多个 scope 的自动检测
74
+ - 现在安装 `test-skill-1` 会自动查找正确的 npm 包
75
+
76
+ 2. **tarball 文件名匹配**
77
+ - 修复了 scoped 包文件名匹配逻辑
78
+ - 之前 `@scope/package` 被错误匹配为 `@scope-package`
79
+
80
+ ---
81
+
82
+ ## 📦 Skill 包列表
83
+
84
+ | Skill | npm 包名 | 用途 |
85
+ |-------|---------|------|
86
+ | test-skill | @wanxuchen/test-skill | 通用测试 |
87
+ | test-skill-1 | @wanxuchen/test-skill-1 | 测试安装和 info 功能 |
88
+ | test-skill-2 | @wanxuchen/test-skill-2 | 测试卸载和更新功能 |
89
+
90
+ ---
91
+
92
+ ## 🔧 技术实现
93
+
94
+ ### 架构
95
+
96
+ ```
97
+ src/adapters/
98
+ ├── base.ts # 平台适配器基类
99
+ ├── opencode.ts # OpenCode 适配器
100
+ ├── claude.ts # Claude Code 适配器
101
+ ├── vscode.ts # VSCode 适配器
102
+ ├── registry.ts # 平台注册和检测
103
+ └── index.ts # 导出
104
+ ```
105
+
106
+ ### 平台适配器接口
107
+
108
+ ```typescript
109
+ interface PlatformAdapter {
110
+ readonly id: string;
111
+ readonly name: string;
112
+ readonly skillDir: string;
113
+
114
+ isAvailable(): Promise<boolean>;
115
+ isInstalled(skillId: string): Promise<boolean>;
116
+ install(skillId: string, sourceDir: string): Promise<void>;
117
+ uninstall(skillId: string): Promise<void>;
118
+ listInstalled(): Promise<string[]>;
119
+ }
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 📚 文档更新
125
+
126
+ - `README.md` - 更新使用说明
127
+ - `docs/plans/2026-04-15-cross-platform-adapter-design.md` - 设计文档
128
+ - `docs/plans/2026-04-15-cross-platform-adapter-plan.md` - 实现计划
129
+
130
+ ---
131
+
132
+ ## 🚀 下一步
133
+
134
+ - [ ] 发布到 VSCode Marketplace
135
+ - [ ] 发布 Claude Code 插件市场
136
+ - [ ] 创建 skill 市场网站
137
+ - [ ] 添加 skill 搜索功能
138
+
139
+ ---
140
+
141
+ ## 贡献者
142
+
143
+ - wxc2004 (wanxuchen)
package/dist/index.js CHANGED
@@ -170,12 +170,15 @@ async function fetchSkillPackage(skillId) {
170
170
  }
171
171
  return null;
172
172
  }
173
- async function searchSkillmarketPackages() {
173
+ async function searchSkillmarketPackages(options = {}) {
174
+ const { from = 0, size = 100 } = options;
174
175
  const packages = [];
176
+ let total = 0;
175
177
  return new Promise((resolve, reject) => {
176
178
  const url = new URL("https://registry.npmjs.org/-/v1/search");
177
179
  url.searchParams.set("text", "keywords:skillmarket");
178
- url.searchParams.set("size", "100");
180
+ url.searchParams.set("size", String(size));
181
+ url.searchParams.set("from", String(from));
179
182
  const req = https.get(url.toString(), { timeout: 1e4 }, (res) => {
180
183
  let data = "";
181
184
  res.on("data", (chunk) => {
@@ -184,6 +187,7 @@ async function searchSkillmarketPackages() {
184
187
  res.on("end", () => {
185
188
  try {
186
189
  const result = JSON.parse(data);
190
+ total = result.total || 0;
187
191
  if (result.objects) {
188
192
  for (const item of result.objects) {
189
193
  if (item?.package?.name) {
@@ -191,9 +195,9 @@ async function searchSkillmarketPackages() {
191
195
  }
192
196
  }
193
197
  }
194
- resolve(packages);
198
+ resolve({ packages, total });
195
199
  } catch {
196
- resolve([]);
200
+ resolve({ packages: [], total: 0 });
197
201
  }
198
202
  });
199
203
  });
@@ -207,30 +211,44 @@ async function searchSkillmarketPackages() {
207
211
 
208
212
  // src/commands/ls.ts
209
213
  async function listSkills(options) {
210
- const { installed, updates } = options;
214
+ const { installed, updates, page = 1, limit = 20 } = options;
211
215
  if (installed) {
212
216
  const skills = await getInstalledSkills();
217
+ const total = skills.length;
218
+ const totalPages = Math.ceil(total / limit) || 1;
219
+ const currentPage = Math.min(Math.max(1, page), totalPages);
213
220
  if (skills.length === 0) {
214
- console.log('No skills installed yet. Run "skm --ls" to see available skills.');
221
+ console.log('No skills installed yet. Run "skm ls" to see available skills.');
215
222
  return;
216
223
  }
217
- console.log("Installed Skills:\n");
218
- for (const skill of skills) {
224
+ const start = (currentPage - 1) * limit;
225
+ const end = Math.min(start + limit, total);
226
+ const pageSkills = skills.slice(start, end);
227
+ console.log(`Installed Skills (${total}):
228
+ `);
229
+ for (const skill of pageSkills) {
219
230
  console.log(` ${skill.id}@${skill.version}`);
220
231
  console.log(` Platforms: ${skill.platforms.join(", ")}`);
221
232
  console.log(` Installed: ${skill.installedAt}`);
222
233
  console.log();
223
234
  }
235
+ console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
224
236
  return;
225
237
  }
226
238
  console.log("Searching npm registry...\n");
227
239
  try {
228
- const packages = await searchSkillmarketPackages();
240
+ const offset = (page - 1) * limit;
241
+ const { packages, total } = await searchSkillmarketPackages({
242
+ from: offset,
243
+ size: limit
244
+ });
245
+ const totalPages = Math.ceil(total / limit) || 1;
246
+ const currentPage = Math.min(Math.max(1, page), totalPages);
229
247
  if (packages.length === 0) {
230
248
  console.log("No skills found. Check back later!");
231
249
  return;
232
250
  }
233
- console.log(`Found ${packages.length} skill(s):
251
+ console.log(`Found ${total} skill(s):
234
252
  `);
235
253
  for (const pkgName of packages) {
236
254
  try {
@@ -257,6 +275,7 @@ async function listSkills(options) {
257
275
  console.log();
258
276
  }
259
277
  }
278
+ console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
260
279
  } catch (error) {
261
280
  console.log(`Error fetching skills: ${error}`);
262
281
  }
@@ -511,7 +530,7 @@ async function installSkill(skillId, version, options) {
511
530
  await execAsync(`npm pack ${packageName}@${targetVersion} --pack-destination ${cacheDir}`);
512
531
  const files = await fs7.readdir(cacheDir);
513
532
  const tarball = files.find(
514
- (f) => f.endsWith(".tgz") && f.includes(packageName.replace("/", "-"))
533
+ (f) => f.endsWith(".tgz") && f.includes(packageName.replace(/^@/, "").replace("/", "-"))
515
534
  );
516
535
  if (tarball) {
517
536
  await execAsync(`tar -xzf "${path6.join(cacheDir, tarball)}" -C "${cacheDir}"`);
@@ -744,23 +763,28 @@ Usage: skm <command> [options]
744
763
 
745
764
  Commands:
746
765
  ls [options] List available skills
747
- --installed Show only installed skills
748
- --updates Check for updates
766
+ --installed Show only installed skills
767
+ --updates Check for updates
768
+ --page <n> Page number (default: 1)
769
+ --limit <n> Items per page (default: 20)
749
770
  info <skill-id> Display skill information
750
771
  install <skill> Install a skill
751
- @version Install specific version
752
- --platform Target platforms (opencode,claude,vscode)
753
- --force Overwrite if already installed
772
+ @version Install specific version
773
+ --platform Target platforms (opencode,claude,vscode)
774
+ --force Overwrite if already installed
754
775
  uninstall <skill> Remove an installed skill
755
- --platform Target platforms
776
+ --platform Target platforms
756
777
  update [options] Update skills
757
- --all Update all skills
778
+ --all Update all skills
758
779
  sync Synchronize platform links
759
780
  platforms Show available platforms
760
781
 
761
782
  Examples:
762
- skm ls List all available skills
783
+ skm ls List all available skills (page 1)
784
+ skm ls --page 2 Go to page 2
785
+ skm ls --limit 10 Show 10 items per page
763
786
  skm ls --installed Show installed skills only
787
+ skm ls --installed --page 2
764
788
  skm info brainstorming View skill details
765
789
  skm install brainstorming Install to all platforms
766
790
  skm install brainstorming --platform opencode Install to OpenCode only
@@ -772,8 +796,13 @@ Examples:
772
796
  }
773
797
  });
774
798
  var lsCmd = program.command("ls").description("List available skills");
775
- lsCmd.option("--installed", "Show only installed skills").option("--updates", "Check for updates").action((opts) => {
776
- listSkills(opts);
799
+ lsCmd.option("--installed", "Show only installed skills").option("--updates", "Check for updates").option("-p, --page <number>", "Page number (default: 1)", parseInt).option("-l, --limit <number>", "Items per page (default: 20)", parseInt).action((opts) => {
800
+ const options = {
801
+ ...opts,
802
+ page: opts.page ?? 1,
803
+ limit: opts.limit ?? 20
804
+ };
805
+ listSkills(options);
777
806
  });
778
807
  var infoCmd = program.command("info").description("Display skill information");
779
808
  infoCmd.argument("<skill-id>", "Skill ID to show info").action((skillId) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itismyskillmarket",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Cross-platform skill manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,24 @@
1
+ # Test Skill 1
2
+
3
+ 用于测试 SkillMarket 安装和 info 功能的 skill。
4
+
5
+ ## 功能
6
+
7
+ - 验证 skm install 功能
8
+ - 验证 skm info 功能
9
+
10
+ ## 使用方法
11
+
12
+ ```bash
13
+ skm install test-skill-1
14
+ skm info test-skill-1
15
+ ```
16
+
17
+ ## 平台支持
18
+
19
+ - OpenCode
20
+ - Cursor
21
+ - VSCode
22
+ - Claude Code
23
+ - Codex
24
+ - Antigravity
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Test Skill 1 - 用于测试 SkillMarket 安装和 info 功能
3
+ */
4
+
5
+ export default async function TestSkill1() {
6
+ console.log("✅ Test Skill 1 加载成功!");
7
+
8
+ return {
9
+ name: "test-skill-1",
10
+ version: "1.0.3",
11
+ status: "installed"
12
+ };
13
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "test-skill-1",
3
+ "displayName": "测试技能1",
4
+ "description": "用于测试 SkillMarket 安装和 info 功能",
5
+ "version": "1.0.3",
6
+ "author": "SkillMarket",
7
+ "platforms": ["opencode", "cursor", "vscode", "claude", "codex", "antigravity"],
8
+ "tags": ["test", "install", "info"]
9
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@wanxuchen/test-skill-1",
3
+ "version": "1.0.3",
4
+ "description": "Test Skill 1 - 用于测试 SkillMarket 安装流程",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "keywords": ["skillmarket", "test"],
8
+ "author": "SkillMarket",
9
+ "license": "MIT",
10
+ "skillmarket": {
11
+ "id": "test-skill-1",
12
+ "displayName": "测试技能1",
13
+ "description": "用于测试 SkillMarket 安装和 info 功能",
14
+ "platforms": ["opencode", "cursor", "vscode", "claude", "codex", "antigravity"]
15
+ }
16
+ }
@@ -0,0 +1,25 @@
1
+ # Test Skill 2
2
+
3
+ 用于测试 SkillMarket 卸载和更新流程的 skill。
4
+
5
+ ## 功能
6
+
7
+ - 验证 skm uninstall 功能
8
+ - 验证 skm update 功能
9
+
10
+ ## 使用方法
11
+
12
+ ```bash
13
+ skm install test-skill-2
14
+ skm update test-skill-2
15
+ skm uninstall test-skill-2
16
+ ```
17
+
18
+ ## 平台支持
19
+
20
+ - OpenCode
21
+ - Cursor
22
+ - VSCode
23
+ - Claude Code
24
+ - Codex
25
+ - Antigravity
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Test Skill 2 - 用于测试 SkillMarket 卸载和更新功能
3
+ */
4
+
5
+ export default async function TestSkill2() {
6
+ console.log("✅ Test Skill 2 加载成功!");
7
+
8
+ return {
9
+ name: "test-skill-2",
10
+ version: "1.0.3",
11
+ status: "installed"
12
+ };
13
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "test-skill-2",
3
+ "displayName": "测试技能2",
4
+ "description": "用于测试 SkillMarket 卸载和更新功能",
5
+ "version": "1.0.3",
6
+ "author": "SkillMarket",
7
+ "platforms": ["opencode", "cursor", "vscode", "claude", "codex", "antigravity"],
8
+ "tags": ["test", "uninstall", "update"]
9
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@wanxuchen/test-skill-2",
3
+ "version": "1.0.3",
4
+ "description": "Test Skill 2 - 用于测试 SkillMarket 卸载和更新流程",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "keywords": ["skillmarket", "test"],
8
+ "author": "SkillMarket",
9
+ "license": "MIT",
10
+ "skillmarket": {
11
+ "id": "test-skill-2",
12
+ "displayName": "测试技能2",
13
+ "description": "用于测试 SkillMarket 卸载和更新功能",
14
+ "platforms": ["opencode", "cursor", "vscode", "claude", "codex", "antigravity"]
15
+ }
16
+ }
package/src/cli.ts CHANGED
@@ -79,30 +79,35 @@ program
79
79
  program
80
80
  .hook('preAction', (thisCommand) => {
81
81
  if (thisCommand.opts().help) {
82
- console.log(`
82
+ console.log(`
83
83
  SkillMarket CLI
84
84
 
85
85
  Usage: skm <command> [options]
86
86
 
87
87
  Commands:
88
88
  ls [options] List available skills
89
- --installed Show only installed skills
90
- --updates Check for updates
89
+ --installed Show only installed skills
90
+ --updates Check for updates
91
+ --page <n> Page number (default: 1)
92
+ --limit <n> Items per page (default: 20)
91
93
  info <skill-id> Display skill information
92
94
  install <skill> Install a skill
93
- @version Install specific version
94
- --platform Target platforms (opencode,claude,vscode)
95
- --force Overwrite if already installed
95
+ @version Install specific version
96
+ --platform Target platforms (opencode,claude,vscode)
97
+ --force Overwrite if already installed
96
98
  uninstall <skill> Remove an installed skill
97
- --platform Target platforms
99
+ --platform Target platforms
98
100
  update [options] Update skills
99
- --all Update all skills
101
+ --all Update all skills
100
102
  sync Synchronize platform links
101
103
  platforms Show available platforms
102
104
 
103
105
  Examples:
104
- skm ls List all available skills
106
+ skm ls List all available skills (page 1)
107
+ skm ls --page 2 Go to page 2
108
+ skm ls --limit 10 Show 10 items per page
105
109
  skm ls --installed Show installed skills only
110
+ skm ls --installed --page 2
106
111
  skm info brainstorming View skill details
107
112
  skm install brainstorming Install to all platforms
108
113
  skm install brainstorming --platform opencode Install to OpenCode only
@@ -132,8 +137,16 @@ const lsCmd = program.command('ls').description('List available skills');
132
137
  lsCmd
133
138
  .option('--installed', 'Show only installed skills')
134
139
  .option('--updates', 'Check for updates')
140
+ .option('-p, --page <number>', 'Page number (default: 1)', parseInt)
141
+ .option('-l, --limit <number>', 'Items per page (default: 20)', parseInt)
135
142
  .action((opts) => {
136
- listSkills(opts);
143
+ // Ensure numeric options have default values if not provided
144
+ const options = {
145
+ ...opts,
146
+ page: opts.page ?? 1,
147
+ limit: opts.limit ?? 20
148
+ };
149
+ listSkills(options);
137
150
  });
138
151
 
139
152
  // -----------------------------------------------------------------------------
@@ -147,10 +147,10 @@ export async function installSkill(
147
147
  const files = await fs.readdir(cacheDir);
148
148
 
149
149
  // npm pack 生成的文件名格式: <package-name>-<version>.tgz
150
- // scoped 包格式: @scope-package-name-<version>.tgz
150
+ // scoped 包格式: @scope-package-name-<version>.tgz (注意:@ 不在文件名中)
151
151
  const tarball = files.find(f =>
152
152
  f.endsWith('.tgz') &&
153
- f.includes(packageName.replace('/', '-'))
153
+ f.includes(packageName.replace(/^@/, '').replace('/', '-'))
154
154
  );
155
155
 
156
156
  if (tarball) {
@@ -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 --ls" to see available skills.');
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('Installed Skills:\n');
92
+ console.log(`Installed Skills (${total}):\n`);
79
93
 
80
94
  // 遍历并打印每个 skill 的详细信息
81
- for (const skill of skills) {
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,7 +139,7 @@ export async function listSkills(options: LsOptions): Promise<void> {
113
139
  }
114
140
 
115
141
  // 打印找到的包数量
116
- console.log(`Found ${packages.length} skill(s):\n`);
142
+ console.log(`Found ${total} skill(s):\n`);
117
143
 
118
144
  // 遍历每个包,获取详细信息并显示
119
145
  for (const pkgName of packages) {
@@ -162,6 +188,9 @@ export async function listSkills(options: LsOptions): Promise<void> {
162
188
  console.log();
163
189
  }
164
190
  }
191
+
192
+ // 打印分页信息
193
+ console.log(`Page ${currentPage}/${totalPages} (${limit} per page) | Use --page N to navigate`);
165
194
  } catch (error) {
166
195
  // 网络错误处理
167
196
  console.log(`Error fetching skills: ${error}`);
@@ -250,17 +250,25 @@ export async function fetchSkillPackage(skillId: string): Promise<NpmRegistryRes
250
250
  *
251
251
  * API 端点: GET https://registry.npmjs.org/-/v1/search
252
252
  *
253
- * @returns {Promise<string[]>} 匹配的包名数组
253
+ * @param options - 搜索选项
254
+ * @param options.from - 起始位置(分页用)
255
+ * @param options.size - 返回结果数量
256
+ * @returns {Promise<{packages: string[], total: number}>} 匹配的包名数组和总数
254
257
  *
255
258
  * @example
256
- * const packages = await searchSkillmarketPackages();
257
- * console.log(`找到 ${packages.length} 个 skill 包`);
259
+ * const { packages, total } = await searchSkillmarketPackages();
260
+ * console.log(`找到 ${total} 个 skill 包`);
258
261
  * packages.forEach(name => {
259
262
  * console.log(`- ${name}`);
260
263
  * });
261
264
  */
262
- export async function searchSkillmarketPackages(): Promise<string[]> {
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;
263
270
  const packages: string[] = [];
271
+ let total = 0;
264
272
 
265
273
  return new Promise((resolve, reject) => {
266
274
  // 构建 search API URL
@@ -269,8 +277,10 @@ export async function searchSkillmarketPackages(): Promise<string[]> {
269
277
  // 设置搜索参数
270
278
  // text: 搜索关键字
271
279
  // size: 返回结果数量上限
280
+ // from: 起始位置(分页用)
272
281
  url.searchParams.set('text', 'keywords:skillmarket');
273
- url.searchParams.set('size', '100');
282
+ url.searchParams.set('size', String(size));
283
+ url.searchParams.set('from', String(from));
274
284
 
275
285
  const req = https.get(url.toString(), { timeout: 10000 }, (res) => {
276
286
  let data = '';
@@ -283,6 +293,9 @@ export async function searchSkillmarketPackages(): Promise<string[]> {
283
293
  // 解析搜索结果
284
294
  const result = JSON.parse(data);
285
295
 
296
+ // 获取总数
297
+ total = result.total || 0;
298
+
286
299
  // 提取所有匹配的包名
287
300
  // npm search 返回结构: { objects: [{ package: { name: "..." } }] }
288
301
  if (result.objects) {
@@ -293,10 +306,10 @@ export async function searchSkillmarketPackages(): Promise<string[]> {
293
306
  }
294
307
  }
295
308
 
296
- resolve(packages);
309
+ resolve({ packages, total });
297
310
  } catch {
298
311
  // 解析失败返回空数组
299
- resolve([]);
312
+ resolve({ packages: [], total: 0 });
300
313
  }
301
314
  });
302
315
  });