rushangle-cli 0.2.1 → 0.2.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.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "SkillHub CLI - 数智凯航技能市场命令行工具",
5
5
  "bin": {
6
6
  "rushangle": "./bin/rushangle.js"
@@ -11,7 +11,7 @@ const INSTALL_DIR = path.join(os.homedir(), '.workbuddy', 'skills');
11
11
  module.exports = new Command('install')
12
12
  .description('安装技能/MCP/代码段')
13
13
  .argument('[skill]', '技能名称或 ID')
14
- .option('-t, --type <type>', '类型: skill | mcp | code', 'skill')
14
+ .option('-t, --type <type>', '类型: skill | mcp | code | doc', 'skill')
15
15
  .option('-g, --global', '全局安装(默认)', true)
16
16
  .option('--register-only', '仅注册到服务器,不写入本地文件(适用于受限环境)')
17
17
  .action(async (nameOrId, opts) => {
@@ -149,7 +149,7 @@ module.exports = new Command('install')
149
149
  installDir = path.join(INSTALL_DIR, 'mcp', item.id);
150
150
  fs.mkdirSync(installDir, { recursive: true });
151
151
  if (item.code) {
152
- const ext = item.language === 'python' ? '.py'
152
+ const ext = item.language === 'python' || item.language === 'py' ? '.py'
153
153
  : item.language === 'javascript' ? '.js'
154
154
  : item.language === 'typescript' ? '.ts'
155
155
  : item.language === 'json' ? '.json'
@@ -206,7 +206,7 @@ module.exports = new Command('install')
206
206
  installDir = path.join(INSTALL_DIR, 'code', item.id);
207
207
  fs.mkdirSync(installDir, { recursive: true });
208
208
  if (item.code) {
209
- const extMap = { python: '.py', javascript: '.js', typescript: '.ts', go: '.go', rust: '.rs', java: '.java', c: '.c', cpp: '.cpp', shell: '.sh', bash: '.sh', json: '.json', yaml: '.yml', toml: '.toml', sql: '.sql', ruby: '.rb', php: '.php', swift: '.swift', kotlin: '.kt', scala: '.scala', r: '.r' };
209
+ const extMap = { python: '.py', py: '.py', javascript: '.js', typescript: '.ts', go: '.go', rust: '.rs', java: '.java', c: '.c', cpp: '.cpp', shell: '.sh', bash: '.sh', json: '.json', yaml: '.yml', toml: '.toml', sql: '.sql', ruby: '.rb', php: '.php', swift: '.swift', kotlin: '.kt', scala: '.scala', r: '.r' };
210
210
  const ext = extMap[item.language] || `.${item.language}` || '.txt';
211
211
  fs.writeFileSync(path.join(installDir, `snippet${ext}`), item.code);
212
212
  }
@@ -227,6 +227,63 @@ module.exports = new Command('install')
227
227
  console.log(chalk.gray(` 语言: ${item.language}`));
228
228
  if (showLocalPath) console.log(chalk.gray(` 本地目录: ${installDir}`));
229
229
 
230
+ } else if (type === 'doc') {
231
+ // Docs: Try by ID first, then search by name
232
+ try {
233
+ item = await api.getDoc(nameOrId);
234
+ } catch {
235
+ const listData = await api.listDocs({ search: nameOrId });
236
+ if (listData.items && listData.items.length > 0) {
237
+ const match = listData.items.find(
238
+ s => s.name === nameOrId || s.id === nameOrId
239
+ );
240
+ if (match) {
241
+ // Re-fetch full data (list data is sanitized)
242
+ item = await api.getDoc(match.id);
243
+ }
244
+ }
245
+ }
246
+
247
+ if (!item) {
248
+ console.log(chalk.red(`未找到文档: ${nameOrId}`));
249
+ return;
250
+ }
251
+
252
+ // Local file install (best-effort)
253
+ let showLocalPath = false;
254
+ let installDir = '';
255
+ if (!opts.registerOnly) {
256
+ try {
257
+ installDir = path.join(INSTALL_DIR, 'docs', item.id);
258
+ fs.mkdirSync(installDir, { recursive: true });
259
+ // Write document content as markdown
260
+ if (item.content) {
261
+ fs.writeFileSync(path.join(installDir, 'document.md'), item.content);
262
+ }
263
+ // Write attached file list if any
264
+ if (item.files && item.files.length > 0) {
265
+ fs.writeFileSync(
266
+ path.join(installDir, 'files.json'),
267
+ JSON.stringify(item.files, null, 2)
268
+ );
269
+ console.log(chalk.gray(` 附带文件: ${item.files.map(f => f.name || f).join(', ')}`));
270
+ }
271
+ fs.writeFileSync(
272
+ path.join(installDir, 'rushangle.json'),
273
+ JSON.stringify({ type: 'doc', ...item, installedAt: new Date().toISOString() }, null, 2)
274
+ );
275
+ showLocalPath = true;
276
+ } catch (fileErr) {
277
+ console.log(chalk.yellow(` 本地文件写入跳过(${fileErr.code || fileErr.message})`));
278
+ if (fileErr.code === 'EPERM' || fileErr.code === 'EACCES') {
279
+ console.log(chalk.gray(` 提示: 使用 --register-only 跳过本地文件操作`));
280
+ }
281
+ }
282
+ }
283
+
284
+ console.log(chalk.green(`✓ 安装成功: ${item.name}`));
285
+ if (showLocalPath) console.log(chalk.gray(` 本地目录: ${installDir}`));
286
+
230
287
  } else {
231
288
  console.log(chalk.red(`不支持的类型: ${type}`));
232
289
  }