rushangle-cli 0.1.1 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rushangle-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "SkillHub CLI - 数智凯航技能市场命令行工具",
5
5
  "bin": {
6
6
  "rushangle": "./bin/rushangle.js"
@@ -13,14 +13,17 @@
13
13
  "scripts": {
14
14
  "start": "node bin/rushangle.js"
15
15
  },
16
- "keywords": ["rushangle", "skillhub", "cli", "skills"],
16
+ "keywords": [
17
+ "rushangle",
18
+ "skillhub",
19
+ "cli",
20
+ "skills"
21
+ ],
17
22
  "license": "MIT",
18
23
  "dependencies": {
19
24
  "commander": "^12.0.0",
20
25
  "chalk": "^4.1.2",
21
- "ora": "^8.0.0",
22
26
  "open": "^10.0.0",
23
- "conf": "^12.0.0",
24
27
  "cli-table3": "^0.6.5"
25
28
  }
26
29
  }
package/src/api.js CHANGED
@@ -87,4 +87,7 @@ module.exports = {
87
87
 
88
88
  // Search
89
89
  search(query) { return apiCall('GET', `/api/search?q=${encodeURIComponent(query)}`); },
90
+
91
+ // Categories (no auth required)
92
+ getCategories() { return apiCall('GET', '/api/skills/categories'); },
90
93
  };
@@ -1,6 +1,5 @@
1
1
  const { Command } = require('commander');
2
2
  const chalk = require('chalk');
3
- const open = require('open');
4
3
  const http = require('http');
5
4
  const auth = require('../auth');
6
5
  const { SITE_URL } = require('../api');
@@ -65,7 +64,12 @@ p{color:#666;margin:4px 0}
65
64
 
66
65
  // Open browser to DingTalk OAuth login with CLI callback params
67
66
  const loginUrl = `${SITE_URL}/api/auth/dingtalk/login?cli=1&cli_port=${CLI_PORT}`;
68
- await open(loginUrl);
67
+ try {
68
+ const open = (await import('open')).default;
69
+ await open(loginUrl);
70
+ } catch {
71
+ // open module unavailable, fall through to manual URL prompt
72
+ }
69
73
  console.log(chalk.gray('请在浏览器中完成钉钉授权...'));
70
74
  console.log(chalk.yellow('等待授权完成... (如浏览器未打开,请手动访问: ' + loginUrl + ')'));
71
75
  });
@@ -2,11 +2,70 @@ const { Command } = require('commander');
2
2
  const chalk = require('chalk');
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const readline = require('readline');
5
6
  const auth = require('../auth');
6
7
  const api = require('../api');
7
8
 
8
9
  const VALID_TYPES = ['skill', 'mcp', 'code', 'doc'];
9
10
 
11
+ async function askQuestion(query) {
12
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
13
+ return new Promise(resolve => rl.question(query, ans => { rl.close(); resolve(ans.trim()); }));
14
+ }
15
+
16
+ /**
17
+ * Fetch preset categories from server and attempt to match/resolve user's category.
18
+ * @returns {{ categories: string[], resolved: string }} - resolved is the final category to use
19
+ */
20
+ async function resolveCategory(type, userCategory, apiModule) {
21
+ const typeMap = { skill: 'skill', mcp: 'mcp', code: 'code', doc: 'doc' };
22
+ const t = typeMap[type] || 'skill';
23
+
24
+ let serverCategories = [];
25
+ try {
26
+ const data = await apiModule.getCategories();
27
+ serverCategories = data[t] || data.skill || [];
28
+ } catch {
29
+ // Server unreachable — just use whatever user provided
30
+ return { categories: [], resolved: userCategory };
31
+ }
32
+
33
+ if (!serverCategories.length) {
34
+ return { categories: [], resolved: userCategory };
35
+ }
36
+
37
+ // Case 1: user already specified a category that matches → use it
38
+ if (userCategory) {
39
+ const exact = serverCategories.find(c => c.toLowerCase() === userCategory.toLowerCase());
40
+ if (exact) {
41
+ console.log(chalk.gray(` 分类: ${exact}`));
42
+ return { categories: serverCategories, resolved: exact };
43
+ }
44
+ // Case 2: user specified but doesn't match → show mismatch, let them pick
45
+ console.log(chalk.yellow(` ⚠ 指定的分类 "${userCategory}" 不在预设列表中,请从以下选择:`));
46
+ }
47
+
48
+ // Show numbered list
49
+ console.log(chalk.cyan(`\n 请选择 ${type} 分类:`));
50
+ serverCategories.forEach((c, i) => {
51
+ console.log(chalk.white(` ${chalk.bold(String(i + 1))}. ${c}`));
52
+ });
53
+ console.log(chalk.gray(` ${serverCategories.length + 1}. 自定义(输入文本)`));
54
+
55
+ const answer = await askQuestion(chalk.green('\n 输入编号或分类名: '));
56
+ const num = parseInt(answer, 10);
57
+ if (num >= 1 && num <= serverCategories.length) {
58
+ console.log(chalk.gray(` 分类: ${serverCategories[num - 1]}`));
59
+ return { categories: serverCategories, resolved: serverCategories[num - 1] };
60
+ }
61
+ if (answer) {
62
+ console.log(chalk.gray(` 分类: ${answer}`));
63
+ return { categories: serverCategories, resolved: answer };
64
+ }
65
+ console.log(chalk.gray(` 分类: ${serverCategories[0]}`));
66
+ return { categories: serverCategories, resolved: serverCategories[0] };
67
+ }
68
+
10
69
  module.exports = new Command('publish')
11
70
  .description('发布技能/MCP配置/代码段/文档到 SkillHub 市场')
12
71
  .argument('[dir]', '项目目录路径', '.')
@@ -52,10 +111,12 @@ module.exports = new Command('publish')
52
111
  const name = opts.name || meta.name || path.basename(absDir);
53
112
  const description = opts.description || meta.description || '';
54
113
  const version = opts.version || meta.version || '0.1.0';
55
- const category = opts.category || meta.category || '';
56
114
  const tags = opts.tags ? opts.tags.split(',').map(t => t.trim()).filter(Boolean)
57
115
  : (meta.tags || []);
58
116
 
117
+ // Resolve category via server presets
118
+ const { resolved: category } = await resolveCategory(type, opts.category || meta.category || '', api);
119
+
59
120
  // Read README if exists
60
121
  let readme = '';
61
122
  for (const f of ['README.md', 'readme.md', 'README', 'readme']) {