rushangle-cli 0.2.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rushangle-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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) => {
@@ -129,7 +129,10 @@ module.exports = new Command('install')
129
129
  const match = listData.items.find(
130
130
  s => s.name === nameOrId || s.id === nameOrId
131
131
  );
132
- if (match) item = match;
132
+ if (match) {
133
+ // Re-fetch full data (list data is sanitized)
134
+ item = await api.getMcp(match.id);
135
+ }
133
136
  }
134
137
  }
135
138
 
@@ -183,7 +186,10 @@ module.exports = new Command('install')
183
186
  const match = listData.items.find(
184
187
  s => s.name === nameOrId || s.id === nameOrId
185
188
  );
186
- if (match) item = match;
189
+ if (match) {
190
+ // Re-fetch full data (list data is sanitized)
191
+ item = await api.getCode(match.id);
192
+ }
187
193
  }
188
194
  }
189
195
 
@@ -221,6 +227,63 @@ module.exports = new Command('install')
221
227
  console.log(chalk.gray(` 语言: ${item.language}`));
222
228
  if (showLocalPath) console.log(chalk.gray(` 本地目录: ${installDir}`));
223
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
+
224
287
  } else {
225
288
  console.log(chalk.red(`不支持的类型: ${type}`));
226
289
  }