@cannbot-ai/install-helper 0.0.2-beta.1 → 0.0.2-beta.11

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/dist/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import { select as select3, Separator as Separator3 } from "@inquirer/prompts";
8
- import chalk5 from "chalk";
7
+ import { select as select4, Separator as Separator3 } from "@inquirer/prompts";
8
+ import chalk6 from "chalk";
9
9
 
10
10
  // src/ui/wizard.ts
11
11
  import { select, checkbox, Separator } from "@inquirer/prompts";
@@ -145,6 +145,309 @@ import { join as join2 } from "path";
145
145
  import { homedir } from "os";
146
146
  import { join } from "path";
147
147
  import { existsSync } from "fs";
148
+
149
+ // src/utils/i18n.ts
150
+ var zh_CN = {
151
+ wizard_title: "CANNBot Install Helper",
152
+ wizard_detect: "\u6B63\u5728\u68C0\u6D4B\u5DF2\u5B89\u88C5\u7684 AI \u7F16\u7A0B\u5DE5\u5177...",
153
+ wizard_select_tool: "\u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
154
+ wizard_select_level: "\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
155
+ wizard_select_plugins: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
156
+ wizard_confirm: "\u786E\u8BA4\u5B89\u88C5",
157
+ wizard_detected_tool: "\u68C0\u6D4B\u5230",
158
+ wizard_confirm_tool: "\u4F7F\u7528",
159
+ wizard_last_used: "\u4E0A\u6B21\u4F7F\u7528",
160
+ wizard_already_installed: "\u5DF2\u5B89\u88C5",
161
+ install_cloning: "\u6B63\u5728\u514B\u9686 cannbot-skills \u4ED3\u5E93...",
162
+ install_repo_ready: "\u4ED3\u5E93\u5C31\u7EEA",
163
+ install_progress: "\u6B63\u5728\u5B89\u88C5",
164
+ install_done: "\u5B89\u88C5\u5B8C\u6210",
165
+ install_success: "\u6210\u529F",
166
+ install_failed: "\u5931\u8D25",
167
+ install_already_installed: "\u5DF2\u5B89\u88C5",
168
+ install_reinstall_confirm: "\u662F\u5426\u91CD\u65B0\u5B89\u88C5\uFF08\u8986\u76D6\uFF09",
169
+ install_skip: "\u8DF3\u8FC7",
170
+ install_enhanced_next_steps: "\u4E0B\u4E00\u6B65",
171
+ install_enhanced_launch: "\u542F\u52A8",
172
+ install_enhanced_try: "\u8BD5\u8BD5",
173
+ install_enhanced_more: "\u66F4\u591A",
174
+ install_enhanced_check: "\u68C0\u67E5",
175
+ install_enhanced_docs: "\u6587\u6863",
176
+ update_title: "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 Skills",
177
+ update_updating: "\u6B63\u5728\u66F4\u65B0",
178
+ update_done: "\u66F4\u65B0\u5B8C\u6210",
179
+ update_no_plugins: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
180
+ doctor_title: "CANNBot Doctor",
181
+ doctor_tools: "AI \u5DE5\u5177",
182
+ doctor_plugins: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
183
+ doctor_links: "\u94FE\u63A5\u5B8C\u6574\u6027",
184
+ doctor_config: "\u914D\u7F6E\u6587\u4EF6",
185
+ doctor_result: "\u7ED3\u679C",
186
+ doctor_fix_title: "\u81EA\u52A8\u4FEE\u590D",
187
+ doctor_fix_cleaning: "\u6E05\u7406\u5931\u6548\u94FE\u63A5",
188
+ doctor_fix_rebuilding: "\u91CD\u5EFA\u76EE\u5F55",
189
+ doctor_fix_done: "\u4FEE\u590D\u5B8C\u6210",
190
+ info_title: "\u63D2\u4EF6\u8BE6\u60C5",
191
+ info_description: "\u63CF\u8FF0",
192
+ info_skills: "Skills",
193
+ info_agents: "Agents",
194
+ info_status: "\u72B6\u6001",
195
+ info_quickstart: "\u5FEB\u901F\u5F00\u59CB",
196
+ info_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
197
+ lang_title: "\u8BED\u8A00\u8BBE\u7F6E",
198
+ lang_current: "\u5F53\u524D\u8BED\u8A00",
199
+ lang_set: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A",
200
+ lang_invalid: "\u65E0\u6548\u7684\u8BED\u8A00\uFF0C\u652F\u6301: zh_CN, en_US",
201
+ list_title: "\u53EF\u7528\u573A\u666F",
202
+ list_id: "\u5E8F\u53F7",
203
+ list_name: "\u573A\u666F\u540D",
204
+ list_status: "\u72B6\u6001",
205
+ list_skills: "Skills",
206
+ list_agents: "Agents",
207
+ status_installed: "\u5DF2\u88C5",
208
+ status_not_installed: "\u672A\u88C5",
209
+ error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
210
+ error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
211
+ error_repo_not_found: "\u4ED3\u5E93\u672A\u627E\u5230",
212
+ error_invalid_tool: "\u6682\u4E0D\u652F\u6301\u7684 AI \u5DE5\u5177: {tool}\n\u652F\u6301\u7684\u5DE5\u5177: {tools}",
213
+ error_invalid_level: "\u6682\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u7EA7\u522B: {level}\n\u652F\u6301\u7684\u7EA7\u522B: {levels}",
214
+ quick_start: "\u542F\u52A8\u547D\u4EE4",
215
+ quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
216
+ skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
217
+ skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
218
+ skill_already_installed: "\u5DF2\u5B89\u88C5",
219
+ skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
220
+ skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
221
+ skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
222
+ skill_list_title: "\u53EF\u7528 Skills",
223
+ wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
224
+ wizard_mode_plugin: "\u5B89\u88C5 Plugin",
225
+ wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
226
+ wizard_mode_skill: "\u5B89\u88C5 Skill",
227
+ wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
228
+ wizard_back: "\u8FD4\u56DE",
229
+ wizard_cancel: "\u9000\u51FA",
230
+ wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
231
+ wizard_back_to_reselect: "\u8FD4\u56DE",
232
+ status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
233
+ status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
234
+ status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
235
+ status_install_time: "\u5B89\u88C5\u65F6\u95F4",
236
+ install_installed_to: "\u5B89\u88C5\u5230",
237
+ install_skills_count: "Skills",
238
+ install_agents_count: "Agents",
239
+ lang_supported: "\u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US",
240
+ lang_usage: "\u7528\u6CD5: install-helper lang set <language>",
241
+ lang_specify: "\u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN",
242
+ lang_unknown_action: "\u672A\u77E5\u64CD\u4F5C",
243
+ lang_supported_actions: "\u652F\u6301\u7684\u64CD\u4F5C: show, set",
244
+ info_aliases: "\u522B\u540D",
245
+ uninstall_not_found: "\u672A\u627E\u5230",
246
+ uninstall_no_record: "\u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
247
+ uninstall_no_record_hint: "\u5982\u9700\u5F3A\u5236\u6E05\u7406\uFF0C\u8BF7\u624B\u52A8\u5220\u9664 .opencode/ \u6216 .claude/ \u76EE\u5F55\u4E2D\u7684\u76F8\u5173\u6587\u4EF6",
248
+ uninstall_in_progress: "\u6B63\u5728\u5378\u8F7D",
249
+ uninstall_removed: "\u5DF2\u5378\u8F7D",
250
+ uninstall_files_gone: "\u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55",
251
+ uninstall_skills_in_progress: "\u6B63\u5728\u5378\u8F7D",
252
+ init_pkg_dir_warn: "\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55",
253
+ init_pkg_dir_hint: "\u8BF7\u5728\u4F60\u7684\u9879\u76EE\u76EE\u5F55\u4E2D\u8FD0\u884C\u6B64\u547D\u4EE4\uFF0C\u800C\u4E0D\u662F\u5728 install-helper \u5B89\u88C5\u76EE\u5F55\u4E2D",
254
+ init_pkg_dir_example: "\u793A\u4F8B\uFF1Acd ~/my-project && install-helper",
255
+ init_cancelled: "\u5DF2\u53D6\u6D88\u5B89\u88C5",
256
+ backup_detected: "\u68C0\u6D4B\u5230\u5F53\u524D\u76EE\u5F55\u5DF2\u5B89\u88C5\u63D2\u4EF6",
257
+ backup_current: "\u5F53\u524D\u5B89\u88C5",
258
+ backup_installing: "\u5373\u5C06\u5B89\u88C5",
259
+ backup_overwrite_warning: "\u7EE7\u7EED\u5B89\u88C5\u5C06\u8986\u76D6\u73B0\u6709\u7684 AGENTS.md \u914D\u7F6E\u6587\u4EF6\u3002",
260
+ backup_auto_create: "\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u521B\u5EFA\u5907\u4EFD\uFF0C\u4EE5\u4FBF\u540E\u7EED\u6062\u590D\u3002",
261
+ backup_overwrite: "\u8986\u76D6\u5B89\u88C5\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09",
262
+ backup_cancel: "\u53D6\u6D88\u5B89\u88C5",
263
+ backup_restore_prompt: "\u68C0\u6D4B\u5230\u5907\u4EFD\u914D\u7F6E\uFF0C\u662F\u5426\u6062\u590D\uFF1F",
264
+ backup_restore: "\u6062\u590D",
265
+ backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
266
+ backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
267
+ backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
268
+ backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
269
+ loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
270
+ loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
271
+ loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
272
+ loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
273
+ loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
274
+ install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
275
+ install_to: "\u5B89\u88C5\u5230",
276
+ start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
277
+ wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
278
+ wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
279
+ wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
280
+ wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
281
+ wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
282
+ wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
283
+ install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
284
+ install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
285
+ wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B"
286
+ };
287
+ var en_US = {
288
+ wizard_title: "CANNBot Install Helper",
289
+ wizard_detect: "Detecting installed AI coding tools...",
290
+ wizard_select_tool: "Select AI coding tool",
291
+ wizard_select_level: "Select install level",
292
+ wizard_select_plugins: "Select scenarios to install",
293
+ wizard_confirm: "Confirm installation",
294
+ wizard_detected_tool: "Detected",
295
+ wizard_confirm_tool: "Confirm using",
296
+ wizard_last_used: "last used",
297
+ wizard_already_installed: "installed",
298
+ install_cloning: "Cloning cannbot-skills repository...",
299
+ install_repo_ready: "Repository ready",
300
+ install_progress: "Installing",
301
+ install_done: "Installation complete",
302
+ install_success: "success",
303
+ install_failed: "failed",
304
+ install_already_installed: "already installed",
305
+ install_reinstall_confirm: "Reinstall (overwrite)",
306
+ install_skip: "skipped",
307
+ install_enhanced_next_steps: "Next steps",
308
+ install_enhanced_launch: "Launch",
309
+ install_enhanced_try: "Try",
310
+ install_enhanced_more: "More",
311
+ install_enhanced_check: "Check",
312
+ install_enhanced_docs: "Docs",
313
+ update_title: "Update installed Skills",
314
+ update_updating: "Updating",
315
+ update_done: "Update complete",
316
+ update_no_plugins: "No installed plugins",
317
+ doctor_title: "CANNBot Doctor",
318
+ doctor_tools: "AI Tools",
319
+ doctor_plugins: "Installed Plugins",
320
+ doctor_links: "Link Integrity",
321
+ doctor_config: "Configuration",
322
+ doctor_result: "Result",
323
+ doctor_fix_title: "Auto-fix",
324
+ doctor_fix_cleaning: "Cleaning broken links",
325
+ doctor_fix_rebuilding: "Rebuilding directories",
326
+ doctor_fix_done: "Fix complete",
327
+ info_title: "Plugin Details",
328
+ info_description: "Description",
329
+ info_skills: "Skills",
330
+ info_agents: "Agents",
331
+ info_status: "Status",
332
+ info_quickstart: "Quick Start",
333
+ info_not_found: "Plugin not found",
334
+ lang_title: "Language Settings",
335
+ lang_current: "Current language",
336
+ lang_set: "Language set to",
337
+ lang_invalid: "Invalid language, supported: zh_CN, en_US",
338
+ list_title: "Available Scenarios",
339
+ list_id: "#",
340
+ list_name: "Name",
341
+ list_status: "Status",
342
+ list_skills: "Skills",
343
+ list_agents: "Agents",
344
+ status_installed: "installed",
345
+ status_not_installed: "not installed",
346
+ error_tool_not_found: "No AI coding tool detected",
347
+ error_plugin_not_found: "Plugin not found",
348
+ error_repo_not_found: "Repository not found",
349
+ error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
350
+ error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
351
+ quick_start: "Launch command",
352
+ quick_start_hint: "Try: help me develop an Abs operator",
353
+ skill_select_category: "Select Skill category",
354
+ skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
355
+ skill_already_installed: "installed",
356
+ skill_install_progress: "Installing",
357
+ skill_install_done: "Skill installation complete",
358
+ skill_uninstall_done: "Skill uninstallation complete",
359
+ skill_list_title: "Available Skills",
360
+ wizard_select_mode: "Select installation mode",
361
+ wizard_mode_plugin: "Install Plugin",
362
+ wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
363
+ wizard_mode_skill: "Install Skill",
364
+ wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
365
+ wizard_back: "Go back",
366
+ wizard_cancel: "Cancel installation",
367
+ wizard_no_selection: "No items selected, please choose an action",
368
+ wizard_back_to_reselect: "Go back",
369
+ status_title: "Installed Plugins",
370
+ status_none: "No installed plugins",
371
+ status_hint: "Run install-helper init to start",
372
+ status_install_time: "Installed at",
373
+ install_installed_to: "Installed to",
374
+ install_skills_count: "Skills",
375
+ install_agents_count: "Agents",
376
+ lang_supported: "Supported languages: zh_CN, en_US",
377
+ lang_usage: "Usage: install-helper lang set <language>",
378
+ lang_specify: "Please specify language: install-helper lang set zh_CN",
379
+ lang_unknown_action: "Unknown action",
380
+ lang_supported_actions: "Supported actions: show, set",
381
+ info_aliases: "Aliases",
382
+ uninstall_not_found: "Not found",
383
+ uninstall_no_record: "No install record found, may not be installed or record is corrupted",
384
+ uninstall_no_record_hint: "For manual cleanup, delete related files in .opencode/ or .claude/ directory",
385
+ uninstall_in_progress: "Uninstalling",
386
+ uninstall_removed: "Uninstalled",
387
+ uninstall_files_gone: " files no longer exist, install record cleaned",
388
+ uninstall_skills_in_progress: "Uninstalling",
389
+ init_pkg_dir_warn: "Current directory appears to be the install-helper package directory",
390
+ init_pkg_dir_hint: "Please run this command in your project directory, not in the install-helper package directory",
391
+ init_pkg_dir_example: "Example: cd ~/my-project && install-helper",
392
+ init_cancelled: "Installation cancelled",
393
+ backup_detected: "Plugin already installed in current directory",
394
+ backup_current: "Currently installed",
395
+ backup_installing: "About to install",
396
+ backup_overwrite_warning: "Continuing will overwrite existing AGENTS.md configuration.",
397
+ backup_auto_create: "System will automatically create backup for future restoration.",
398
+ backup_overwrite: "Overwrite (auto backup)",
399
+ backup_cancel: "Cancel installation",
400
+ backup_restore_prompt: "Backup configuration detected, restore?",
401
+ backup_restore: "Restore",
402
+ backup_only_uninstall: "Uninstall only (no restore)",
403
+ backup_select_action: "Please select an action",
404
+ backup_restore_success: "Configuration restored",
405
+ backup_created: "Backup created",
406
+ loading_skills_list: "Loading Skills list...",
407
+ loading_skills_list_complete: "Skills list loaded",
408
+ loading_plugin_data: "Loading plugin data...",
409
+ loading_plugin_data_complete: "Plugin data loaded",
410
+ loading_skills_list_failed: "Failed to load Skills list, using built-in data",
411
+ install_no_skill_selected: "No Skill selected",
412
+ install_to: "Install to",
413
+ start_to_use: "Start {tool} to use",
414
+ wizard_step_1_title: "[1/4] Select AI coding tool",
415
+ wizard_step_2_title: "[2/4] Select installation location",
416
+ wizard_step_3_title: "[3/4] Select plugins to install",
417
+ wizard_step_4_title: "[4/4] Confirm installation",
418
+ wizard_step_3_skill_title: "[3/4] Select Skill category",
419
+ wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
420
+ install_level_project: "Install to current directory (recommended, current project only)",
421
+ install_level_global: "Install globally (all projects)",
422
+ wizard_select_mode_title: "Select installation type"
423
+ };
424
+ var currentLang = "zh_CN";
425
+ function setLanguage(lang) {
426
+ currentLang = lang;
427
+ }
428
+ function t(key) {
429
+ return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
430
+ }
431
+
432
+ // src/utils/paths.ts
433
+ var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
434
+ var VALID_LEVELS = ["project", "global"];
435
+ function validateTool(tool) {
436
+ if (!VALID_TOOLS.includes(tool)) {
437
+ throw new Error(
438
+ t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
439
+ );
440
+ }
441
+ return tool;
442
+ }
443
+ function validateLevel(level) {
444
+ if (!VALID_LEVELS.includes(level)) {
445
+ throw new Error(
446
+ t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
447
+ );
448
+ }
449
+ return level;
450
+ }
148
451
  function detectTraeVariant() {
149
452
  const home = homedir();
150
453
  if (existsSync(join(home, ".trae-cn"))) return "ide";
@@ -175,6 +478,10 @@ function getConfigRoot(tool, level, base) {
175
478
  return join(home, ".cursor");
176
479
  case "copilot":
177
480
  return join(home, ".copilot");
481
+ default:
482
+ throw new Error(
483
+ t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
484
+ );
178
485
  }
179
486
  }
180
487
  const baseDir = base || process.cwd();
@@ -198,6 +505,10 @@ function getConfigRoot(tool, level, base) {
198
505
  return join(baseDir, ".cursor");
199
506
  case "copilot":
200
507
  return join(baseDir, ".github");
508
+ default:
509
+ throw new Error(
510
+ t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
511
+ );
201
512
  }
202
513
  }
203
514
  function getConfigFileName(tool) {
@@ -232,7 +543,7 @@ async function getCommandVersion(cmd, args = ["--version"]) {
232
543
  }
233
544
  async function getCommandPath(cmd) {
234
545
  try {
235
- const result = await execa("sh", ["-c", `command -v ${cmd}`], { timeout: 5e3 });
546
+ const result = await execa("which", [cmd], { timeout: 5e3 });
236
547
  return result.stdout.trim();
237
548
  } catch {
238
549
  return void 0;
@@ -391,185 +702,6 @@ function scanInstalled() {
391
702
  return installed;
392
703
  }
393
704
 
394
- // src/utils/i18n.ts
395
- var zh_CN = {
396
- wizard_title: "CANNBot Install Helper",
397
- wizard_detect: "\u6B63\u5728\u68C0\u6D4B\u5DF2\u5B89\u88C5\u7684 AI \u7F16\u7A0B\u5DE5\u5177...",
398
- wizard_select_tool: "\u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
399
- wizard_select_level: "\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
400
- wizard_select_plugins: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
401
- wizard_confirm: "\u786E\u8BA4\u5B89\u88C5",
402
- wizard_detected_tool: "\u68C0\u6D4B\u5230",
403
- wizard_confirm_tool: "\u4F7F\u7528",
404
- wizard_last_used: "\u4E0A\u6B21\u4F7F\u7528",
405
- wizard_already_installed: "\u5DF2\u5B89\u88C5",
406
- install_cloning: "\u6B63\u5728\u514B\u9686 cannbot-skills \u4ED3\u5E93...",
407
- install_repo_ready: "\u4ED3\u5E93\u5C31\u7EEA",
408
- install_progress: "\u6B63\u5728\u5B89\u88C5",
409
- install_done: "\u5B89\u88C5\u5B8C\u6210",
410
- install_success: "\u6210\u529F",
411
- install_failed: "\u5931\u8D25",
412
- install_already_installed: "\u5DF2\u5B89\u88C5",
413
- install_reinstall_confirm: "\u662F\u5426\u91CD\u65B0\u5B89\u88C5\uFF08\u8986\u76D6\uFF09",
414
- install_skip: "\u8DF3\u8FC7",
415
- install_enhanced_next_steps: "\u4E0B\u4E00\u6B65",
416
- install_enhanced_launch: "\u542F\u52A8",
417
- install_enhanced_try: "\u8BD5\u8BD5",
418
- install_enhanced_more: "\u66F4\u591A",
419
- install_enhanced_check: "\u68C0\u67E5",
420
- install_enhanced_docs: "\u6587\u6863",
421
- update_title: "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 Skills",
422
- update_updating: "\u6B63\u5728\u66F4\u65B0",
423
- update_done: "\u66F4\u65B0\u5B8C\u6210",
424
- update_no_plugins: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
425
- doctor_title: "CANNBot Doctor",
426
- doctor_tools: "AI \u5DE5\u5177",
427
- doctor_plugins: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
428
- doctor_links: "\u94FE\u63A5\u5B8C\u6574\u6027",
429
- doctor_config: "\u914D\u7F6E\u6587\u4EF6",
430
- doctor_result: "\u7ED3\u679C",
431
- doctor_fix_title: "\u81EA\u52A8\u4FEE\u590D",
432
- doctor_fix_cleaning: "\u6E05\u7406\u5931\u6548\u94FE\u63A5",
433
- doctor_fix_rebuilding: "\u91CD\u5EFA\u76EE\u5F55",
434
- doctor_fix_done: "\u4FEE\u590D\u5B8C\u6210",
435
- info_title: "\u63D2\u4EF6\u8BE6\u60C5",
436
- info_description: "\u63CF\u8FF0",
437
- info_skills: "Skills",
438
- info_agents: "Agents",
439
- info_status: "\u72B6\u6001",
440
- info_quickstart: "\u5FEB\u901F\u5F00\u59CB",
441
- info_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
442
- lang_title: "\u8BED\u8A00\u8BBE\u7F6E",
443
- lang_current: "\u5F53\u524D\u8BED\u8A00",
444
- lang_set: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A",
445
- lang_invalid: "\u65E0\u6548\u7684\u8BED\u8A00\uFF0C\u652F\u6301: zh_CN, en_US",
446
- list_title: "\u53EF\u7528\u573A\u666F",
447
- list_id: "\u5E8F\u53F7",
448
- list_name: "\u573A\u666F\u540D",
449
- list_status: "\u72B6\u6001",
450
- list_skills: "Skills",
451
- list_agents: "Agents",
452
- status_installed: "\u5DF2\u88C5",
453
- status_not_installed: "\u672A\u88C5",
454
- error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
455
- error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
456
- error_repo_not_found: "\u4ED3\u5E93\u672A\u627E\u5230",
457
- quick_start: "\u542F\u52A8\u547D\u4EE4",
458
- quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
459
- skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
460
- skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
461
- skill_already_installed: "\u5DF2\u5B89\u88C5",
462
- skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
463
- skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
464
- skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
465
- skill_list_title: "\u53EF\u7528 Skills",
466
- wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
467
- wizard_mode_plugin: "\u5B89\u88C5 Plugin",
468
- wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
469
- wizard_mode_skill: "\u5B89\u88C5 Skill",
470
- wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
471
- wizard_back: "\u8FD4\u56DE",
472
- wizard_cancel: "\u9000\u51FA",
473
- wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
474
- wizard_back_to_reselect: "\u8FD4\u56DE",
475
- status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
476
- status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
477
- status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
478
- status_install_time: "\u5B89\u88C5\u65F6\u95F4"
479
- };
480
- var en_US = {
481
- wizard_title: "CANNBot Skills Setup Wizard",
482
- wizard_detect: "Detecting installed AI coding tools...",
483
- wizard_select_tool: "Select AI coding tool",
484
- wizard_select_level: "Select install level",
485
- wizard_select_plugins: "Select scenarios to install",
486
- wizard_confirm: "Confirm installation",
487
- wizard_detected_tool: "Detected",
488
- wizard_confirm_tool: "Confirm using",
489
- wizard_last_used: "last used",
490
- wizard_already_installed: "installed",
491
- install_cloning: "Cloning cannbot-skills repository...",
492
- install_repo_ready: "Repository ready",
493
- install_progress: "Installing",
494
- install_done: "Installation complete",
495
- install_success: "success",
496
- install_failed: "failed",
497
- install_already_installed: "already installed",
498
- install_reinstall_confirm: "Reinstall (overwrite)",
499
- install_skip: "skipped",
500
- install_enhanced_next_steps: "Next steps",
501
- install_enhanced_launch: "Launch",
502
- install_enhanced_try: "Try",
503
- install_enhanced_more: "More",
504
- install_enhanced_check: "Check",
505
- install_enhanced_docs: "Docs",
506
- update_title: "Update installed Skills",
507
- update_updating: "Updating",
508
- update_done: "Update complete",
509
- update_no_plugins: "No installed plugins",
510
- doctor_title: "CANNBot Doctor",
511
- doctor_tools: "AI Tools",
512
- doctor_plugins: "Installed Plugins",
513
- doctor_links: "Link Integrity",
514
- doctor_config: "Configuration",
515
- doctor_result: "Result",
516
- doctor_fix_title: "Auto-fix",
517
- doctor_fix_cleaning: "Cleaning broken links",
518
- doctor_fix_rebuilding: "Rebuilding directories",
519
- doctor_fix_done: "Fix complete",
520
- info_title: "Plugin Details",
521
- info_description: "Description",
522
- info_skills: "Skills",
523
- info_agents: "Agents",
524
- info_status: "Status",
525
- info_quickstart: "Quick Start",
526
- info_not_found: "Plugin not found",
527
- lang_title: "Language Settings",
528
- lang_current: "Current language",
529
- lang_set: "Language set to",
530
- lang_invalid: "Invalid language, supported: zh_CN, en_US",
531
- list_title: "Available Scenarios",
532
- list_id: "#",
533
- list_name: "Name",
534
- list_status: "Status",
535
- list_skills: "Skills",
536
- list_agents: "Agents",
537
- status_installed: "installed",
538
- status_not_installed: "not installed",
539
- error_tool_not_found: "No AI coding tool detected",
540
- error_plugin_not_found: "Plugin not found",
541
- error_repo_not_found: "Repository not found",
542
- quick_start: "Launch command",
543
- quick_start_hint: "Try: help me develop an Abs operator",
544
- skill_select_category: "Select Skill category",
545
- skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
546
- skill_already_installed: "installed",
547
- skill_install_progress: "Installing",
548
- skill_install_done: "Skill installation complete",
549
- skill_uninstall_done: "Skill uninstallation complete",
550
- skill_list_title: "Available Skills",
551
- wizard_select_mode: "Select installation mode",
552
- wizard_mode_plugin: "Install Plugin",
553
- wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
554
- wizard_mode_skill: "Install Skill",
555
- wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
556
- wizard_back: "Go back",
557
- wizard_cancel: "Cancel installation",
558
- wizard_no_selection: "No items selected, please choose an action",
559
- wizard_back_to_reselect: "Go back",
560
- status_title: "Installed Plugins",
561
- status_none: "No installed plugins",
562
- status_hint: "Run install-helper init to start",
563
- status_install_time: "Installed at"
564
- };
565
- var currentLang = "zh_CN";
566
- function setLanguage(lang) {
567
- currentLang = lang;
568
- }
569
- function t(key) {
570
- return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
571
- }
572
-
573
705
  // src/utils/logger.ts
574
706
  import chalk from "chalk";
575
707
  import ora from "ora";
@@ -624,7 +756,7 @@ function printBoxTitle(title, width = 65) {
624
756
  }
625
757
  function showOperationHints(isCheckbox = false) {
626
758
  if (isCheckbox) {
627
- console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u7A7A\u683C\x1B[0m \u9009\u62E9 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4
759
+ console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u7A7A\u683C\x1B[0m \u52FE\u9009 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4\uFF08\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE\uFF09
628
760
  `);
629
761
  } else {
630
762
  console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4
@@ -793,7 +925,7 @@ async function runWizard() {
793
925
  }
794
926
  }
795
927
  async function stepTool(detectedTools, lastTool) {
796
- printBoxTitle("\u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177");
928
+ printBoxTitle(t("wizard_step_1_title"));
797
929
  if (detectedTools.length === 0) {
798
930
  logger.error(t("error_tool_not_found"));
799
931
  const choices2 = [
@@ -868,10 +1000,10 @@ async function stepToolManual(lastTool) {
868
1000
  return result;
869
1001
  }
870
1002
  async function stepLevel(lastLevel) {
871
- printBoxTitle("\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E");
1003
+ printBoxTitle(t("wizard_step_2_title"));
872
1004
  const choices = [
873
- { name: "> project \u2014 \u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09", value: "project" },
874
- { name: "> global \u2014 \u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09", value: "global" },
1005
+ { name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
1006
+ { name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
875
1007
  new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
876
1008
  { name: "<- " + t("wizard_back"), value: BACK },
877
1009
  { name: "x " + t("wizard_cancel"), value: CANCEL }
@@ -889,7 +1021,7 @@ async function stepLevel(lastLevel) {
889
1021
  return result;
890
1022
  }
891
1023
  async function stepPlugins(tool, level) {
892
- printBoxTitle("\u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6");
1024
+ printBoxTitle(t("wizard_step_3_title"));
893
1025
  const plugins = getAllPlugins();
894
1026
  const configRoot = getConfigRoot(tool, level);
895
1027
  const manifests = readAllManifests(configRoot);
@@ -903,20 +1035,16 @@ async function stepPlugins(tool, level) {
903
1035
  checked: false
904
1036
  };
905
1037
  });
906
- choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
907
- choices.push({ name: "<- " + t("wizard_back"), value: BACK, checked: false });
908
- choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL, checked: false });
909
1038
  showOperationHints(true);
910
1039
  const selected = await checkbox({
911
1040
  message: t("wizard_select_plugins"),
912
1041
  choices,
913
1042
  loop: false,
914
1043
  instructions: false,
915
- theme: checkboxTheme
1044
+ theme: checkboxTheme,
1045
+ pageSize: 15
916
1046
  });
917
- if (selected.includes(BACK)) return BACK;
918
- if (selected.includes(CANCEL)) return CANCEL;
919
- const pluginIds = selected.filter((v) => v !== BACK && v !== CANCEL);
1047
+ const pluginIds = selected;
920
1048
  if (pluginIds.length === 0) {
921
1049
  const action = await select({
922
1050
  message: t("wizard_no_selection"),
@@ -934,14 +1062,17 @@ async function stepPlugins(tool, level) {
934
1062
  return pluginIds;
935
1063
  }
936
1064
  async function stepConfirm(tool, level, plugins) {
937
- printBoxTitle("\u786E\u8BA4\u5B89\u88C5");
1065
+ printBoxTitle(t("wizard_step_4_title"));
938
1066
  const allPlugins = getAllPlugins();
939
1067
  const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
940
1068
  const displayPath = getConfigRoot(tool, level);
941
- logger.info(`\u5373\u5C06\u5B89\u88C5 ${chalk2.bold(plugins.length)} \u4E2A\u573A\u666F\u5230 ${chalk2.cyan(displayPath)}\uFF1A`);
1069
+ const toolName = getToolDisplayName(tool);
1070
+ const levelText = level === "project" ? "project" : "global";
1071
+ logger.info(`\u5373\u5C06\u5B89\u88C5 ${chalk2.bold(plugins.length)} \u4E2A\u63D2\u4EF6\u5230 ${chalk2.green(toolName)}\uFF08${chalk2.cyan(levelText)} \u7EA7\u522B\uFF09\uFF1A`);
1072
+ logger.info(`\u5B89\u88C5\u8DEF\u5F84: ${chalk2.cyan(displayPath)}`);
942
1073
  for (const plugin of selectedPlugins) {
943
1074
  if (plugin) {
944
- logger.step(` \u2022 ${chalk2.bold(plugin.displayName)} ${chalk2.dim(`(${plugin.skills} skills, ${plugin.agents} agents)`)}`);
1075
+ logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
945
1076
  }
946
1077
  }
947
1078
  logger.blank();
@@ -965,7 +1096,7 @@ import { execa as execa2 } from "execa";
965
1096
 
966
1097
  // src/core/scanner.ts
967
1098
  import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
968
- import { join as join5, dirname } from "path";
1099
+ import { join as join5 } from "path";
969
1100
  import { parse as parseYaml } from "yaml";
970
1101
  var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
971
1102
  "node_modules",
@@ -1103,8 +1234,7 @@ function writeScanCache(cache) {
1103
1234
  }
1104
1235
  }
1105
1236
  function getCachePath() {
1106
- const homeDir = process.env.HOME || process.env.USERPROFILE || "/tmp";
1107
- return join5(homeDir, ".cannbot", "scan-cache.json");
1237
+ return join5(getCannbotConfigDir(), "scan-cache.json");
1108
1238
  }
1109
1239
  function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1110
1240
  const selfSkillMd = join5(dirPath, "SKILL.md");
@@ -1478,8 +1608,8 @@ function createRepositoryManager(customPath) {
1478
1608
  }
1479
1609
 
1480
1610
  // src/core/installer.ts
1481
- import { existsSync as existsSync8 } from "fs";
1482
- import { join as join8 } from "path";
1611
+ import { existsSync as existsSync9 } from "fs";
1612
+ import { join as join9 } from "path";
1483
1613
  import { execa as execa3 } from "execa";
1484
1614
 
1485
1615
  // src/core/record.ts
@@ -1573,7 +1703,6 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1573
1703
  files.push(repoLinkInConfig);
1574
1704
  }
1575
1705
  }
1576
- directories.push(configRoot);
1577
1706
  return {
1578
1707
  pluginId,
1579
1708
  displayName,
@@ -1653,6 +1782,179 @@ function getInstalledSkills(tool, level, installPath) {
1653
1782
  return record[tool]?.[level]?.[installPath]?.skills || [];
1654
1783
  }
1655
1784
 
1785
+ // src/core/backup.ts
1786
+ import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
1787
+ import { join as join8 } from "path";
1788
+ function getAgentsFileName(tool) {
1789
+ return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1790
+ }
1791
+ function detectCurrentPlugin(configRoot, tool) {
1792
+ const manifest = readManifest(configRoot);
1793
+ if (manifest?.team) {
1794
+ const plugin = findPlugin(manifest.team);
1795
+ if (plugin) {
1796
+ return {
1797
+ pluginId: plugin.id,
1798
+ pluginName: plugin.displayName
1799
+ };
1800
+ }
1801
+ }
1802
+ const agentsFile = join8(configRoot, getAgentsFileName(tool));
1803
+ if (!existsSync8(agentsFile)) {
1804
+ return null;
1805
+ }
1806
+ const allRecords = readAllRecords();
1807
+ for (const record of allRecords) {
1808
+ if (record.configRoot === configRoot && record.tool === tool) {
1809
+ const plugin = findPlugin(record.pluginId);
1810
+ if (plugin) {
1811
+ return {
1812
+ pluginId: plugin.id,
1813
+ pluginName: plugin.displayName
1814
+ };
1815
+ }
1816
+ }
1817
+ }
1818
+ return null;
1819
+ }
1820
+ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
1821
+ const agentsFile = join8(configRoot, getAgentsFileName(tool));
1822
+ if (!existsSync8(agentsFile)) {
1823
+ return null;
1824
+ }
1825
+ const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
1826
+ const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
1827
+ const backupPath = join8(configRoot, backupFileName);
1828
+ try {
1829
+ copyFileSync(agentsFile, backupPath);
1830
+ return {
1831
+ filePath: backupPath,
1832
+ pluginId: fromPluginId,
1833
+ pluginName: fromPluginName,
1834
+ backupTime: timestamp
1835
+ };
1836
+ } catch {
1837
+ return null;
1838
+ }
1839
+ }
1840
+ function findBackups(configRoot) {
1841
+ if (!existsSync8(configRoot)) {
1842
+ return [];
1843
+ }
1844
+ const files = readdirSync2(configRoot);
1845
+ const backups = [];
1846
+ for (const file of files) {
1847
+ if (file.includes(".cannbot-backup.")) {
1848
+ const parts = file.split(".cannbot-backup.");
1849
+ if (parts.length === 2) {
1850
+ const pluginIdAndTime = parts[1];
1851
+ const lastDotIndex = pluginIdAndTime.lastIndexOf(".");
1852
+ if (lastDotIndex > 0) {
1853
+ const pluginId = pluginIdAndTime.substring(0, lastDotIndex);
1854
+ const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
1855
+ const plugin = findPlugin(pluginId);
1856
+ backups.push({
1857
+ filePath: join8(configRoot, file),
1858
+ pluginId,
1859
+ pluginName: plugin?.displayName || pluginId,
1860
+ backupTime: timestamp
1861
+ });
1862
+ }
1863
+ }
1864
+ }
1865
+ }
1866
+ return backups.sort((a, b) => b.backupTime.localeCompare(a.backupTime));
1867
+ }
1868
+ function restoreBackup(backupPath, configRoot, tool) {
1869
+ try {
1870
+ const agentsFile = join8(configRoot, getAgentsFileName(tool));
1871
+ copyFileSync(backupPath, agentsFile);
1872
+ return true;
1873
+ } catch {
1874
+ return false;
1875
+ }
1876
+ }
1877
+ function deleteBackup(backupPath) {
1878
+ try {
1879
+ if (existsSync8(backupPath)) {
1880
+ unlinkSync2(backupPath);
1881
+ return true;
1882
+ }
1883
+ return false;
1884
+ } catch {
1885
+ return false;
1886
+ }
1887
+ }
1888
+ function formatTimestamp(date) {
1889
+ const year = date.getFullYear();
1890
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1891
+ const day = String(date.getDate()).padStart(2, "0");
1892
+ const hours = String(date.getHours()).padStart(2, "0");
1893
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1894
+ const seconds = String(date.getSeconds()).padStart(2, "0");
1895
+ return `${year}${month}${day}-${hours}${minutes}${seconds}`;
1896
+ }
1897
+ function readAllRecords() {
1898
+ const records = [];
1899
+ const plugins = [
1900
+ "ops-direct-invoke",
1901
+ "ops-direct-invoke-flash",
1902
+ "ops-registry-invoke",
1903
+ "pypto-op-orchestrator",
1904
+ "triton-op-generator",
1905
+ "tilelang-op-orchestrator",
1906
+ "model-infer-optimize",
1907
+ "catlass-op-generator",
1908
+ "ops-code-reviewer",
1909
+ "torch-compile"
1910
+ ];
1911
+ for (const pluginId of plugins) {
1912
+ const record = readRecord(pluginId);
1913
+ if (record) {
1914
+ records.push(record);
1915
+ }
1916
+ }
1917
+ return records;
1918
+ }
1919
+
1920
+ // src/ui/backup-prompts.ts
1921
+ import { select as select2 } from "@inquirer/prompts";
1922
+ import chalk3 from "chalk";
1923
+ async function showOverwriteWarning(currentPluginName, newPluginName) {
1924
+ console.log();
1925
+ console.log(chalk3.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
1926
+ console.log();
1927
+ console.log(` ${t("backup_current")}: ${chalk3.cyan(currentPluginName)}`);
1928
+ console.log(` ${t("backup_installing")}: ${chalk3.cyan(newPluginName)}`);
1929
+ console.log();
1930
+ console.log(` ${t("backup_overwrite_warning")}`);
1931
+ console.log(` ${t("backup_auto_create")}`);
1932
+ console.log();
1933
+ const choice = await select2({
1934
+ message: t("backup_select_action"),
1935
+ choices: [
1936
+ { name: t("backup_overwrite"), value: "overwrite" },
1937
+ { name: t("backup_cancel"), value: "cancel" }
1938
+ ]
1939
+ });
1940
+ return choice;
1941
+ }
1942
+ async function showRestorePrompt(backups) {
1943
+ console.log();
1944
+ console.log(chalk3.cyan(` ${t("backup_restore_prompt")}`));
1945
+ console.log();
1946
+ const choices = backups.map((b) => ({
1947
+ name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
1948
+ value: b.filePath
1949
+ }));
1950
+ choices.push({ name: t("backup_only_uninstall"), value: "none" });
1951
+ const choice = await select2({
1952
+ message: t("backup_select_action"),
1953
+ choices
1954
+ });
1955
+ return choice;
1956
+ }
1957
+
1656
1958
  // src/core/installer.ts
1657
1959
  async function installPlugin(opts) {
1658
1960
  const plugin = getPluginById(opts.pluginId);
@@ -1666,8 +1968,8 @@ async function installPlugin(opts) {
1666
1968
  warnings: []
1667
1969
  };
1668
1970
  }
1669
- const scriptPath = join8(opts.repoPath, plugin.dir, plugin.script);
1670
- if (!existsSync8(scriptPath)) {
1971
+ const scriptPath = join9(opts.repoPath, plugin.dir, plugin.script);
1972
+ if (!existsSync9(scriptPath)) {
1671
1973
  return {
1672
1974
  success: false,
1673
1975
  pluginId: opts.pluginId,
@@ -1682,13 +1984,49 @@ async function installPlugin(opts) {
1682
1984
  args.push(opts.installPath);
1683
1985
  }
1684
1986
  const cwd = opts.installPath || process.cwd();
1987
+ const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
1988
+ const agentsFile = join9(configRoot, getAgentsFileName(opts.tool));
1989
+ let backupInfo = null;
1990
+ if (existsSync9(agentsFile)) {
1991
+ const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
1992
+ if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
1993
+ let choice = "overwrite";
1994
+ if (!opts.yes) {
1995
+ choice = await showOverwriteWarning(
1996
+ currentPlugin.pluginName,
1997
+ plugin.displayName
1998
+ );
1999
+ }
2000
+ if (choice === "cancel") {
2001
+ logger.info(t("backup_cancel"));
2002
+ return {
2003
+ success: false,
2004
+ pluginId: opts.pluginId,
2005
+ skillsCount: 0,
2006
+ agentsCount: 0,
2007
+ errors: [t("backup_cancel")],
2008
+ warnings: []
2009
+ };
2010
+ }
2011
+ backupInfo = createBackup(
2012
+ configRoot,
2013
+ opts.tool,
2014
+ currentPlugin.pluginId,
2015
+ currentPlugin.pluginName
2016
+ );
2017
+ if (backupInfo) {
2018
+ logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
2019
+ }
2020
+ }
2021
+ }
1685
2022
  try {
1686
- await execa3("bash", [scriptPath, ...args], {
2023
+ const execOptions = {
1687
2024
  cwd,
1688
2025
  timeout: 3e5,
1689
- stdio: "pipe"
1690
- });
1691
- const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
2026
+ stdio: "pipe",
2027
+ input: opts.yes ? "y\n" : void 0
2028
+ };
2029
+ await execa3("bash", [scriptPath, ...args], execOptions);
1692
2030
  const manifest = readManifest(configRoot);
1693
2031
  let skillsCount = 0;
1694
2032
  let agentsCount = 0;
@@ -1709,6 +2047,14 @@ async function installPlugin(opts) {
1709
2047
  configRoot,
1710
2048
  manifest
1711
2049
  );
2050
+ if (backupInfo) {
2051
+ record.backup = {
2052
+ filePath: backupInfo.filePath,
2053
+ fromPluginId: backupInfo.pluginId,
2054
+ fromPluginName: backupInfo.pluginName,
2055
+ backupTime: backupInfo.backupTime
2056
+ };
2057
+ }
1712
2058
  writeRecord(record);
1713
2059
  } catch {
1714
2060
  }
@@ -1734,16 +2080,16 @@ async function installPlugin(opts) {
1734
2080
  }
1735
2081
 
1736
2082
  // src/core/skill-installer.ts
1737
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync2, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
1738
- import { join as join9 } from "path";
1739
- import { select as select2, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
1740
- import chalk3 from "chalk";
2083
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
2084
+ import { join as join10 } from "path";
2085
+ import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
2086
+ import chalk4 from "chalk";
1741
2087
  import Table from "cli-table3";
1742
2088
  async function installSkills(skillIds, tool, level, repoPath) {
1743
2089
  const configRoot = getConfigRoot(tool, level);
1744
- const skillsDir = join9(configRoot, "skills");
2090
+ const skillsDir = join10(configRoot, "skills");
1745
2091
  const installPath = level === "project" ? process.cwd() : configRoot;
1746
- if (!existsSync9(skillsDir)) {
2092
+ if (!existsSync10(skillsDir)) {
1747
2093
  mkdirSync5(skillsDir, { recursive: true });
1748
2094
  }
1749
2095
  const results = [];
@@ -1754,15 +2100,15 @@ async function installSkills(skillIds, tool, level, repoPath) {
1754
2100
  results.push({ skillId, success: false, error: "Skill not found in registry" });
1755
2101
  continue;
1756
2102
  }
1757
- const sourcePath = join9(repoPath, skill.source, skill.id);
1758
- const targetPath = join9(skillsDir, skillId);
1759
- if (!existsSync9(sourcePath)) {
2103
+ const sourcePath = join10(repoPath, skill.source, skill.id);
2104
+ const targetPath = join10(skillsDir, skillId);
2105
+ if (!existsSync10(sourcePath)) {
1760
2106
  results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
1761
2107
  continue;
1762
2108
  }
1763
2109
  try {
1764
- if (existsSync9(targetPath) || isSymlink2(targetPath)) {
1765
- unlinkSync2(targetPath);
2110
+ if (existsSync10(targetPath) || isSymlink2(targetPath)) {
2111
+ unlinkSync3(targetPath);
1766
2112
  }
1767
2113
  symlinkSync(realpathSync(sourcePath), targetPath);
1768
2114
  results.push({ skillId, success: true });
@@ -1778,15 +2124,15 @@ async function installSkills(skillIds, tool, level, repoPath) {
1778
2124
  }
1779
2125
  async function uninstallSkills(skillIds, tool, level) {
1780
2126
  const configRoot = getConfigRoot(tool, level);
1781
- const skillsDir = join9(configRoot, "skills");
2127
+ const skillsDir = join10(configRoot, "skills");
1782
2128
  const installPath = level === "project" ? process.cwd() : configRoot;
1783
2129
  const results = [];
1784
2130
  const removedIds = [];
1785
2131
  for (const skillId of skillIds) {
1786
- const targetPath = join9(skillsDir, skillId);
2132
+ const targetPath = join10(skillsDir, skillId);
1787
2133
  try {
1788
- if (existsSync9(targetPath) || isSymlink2(targetPath)) {
1789
- unlinkSync2(targetPath);
2134
+ if (existsSync10(targetPath) || isSymlink2(targetPath)) {
2135
+ unlinkSync3(targetPath);
1790
2136
  results.push({ skillId, success: true });
1791
2137
  removedIds.push(skillId);
1792
2138
  logger.step(` \u79FB\u9664: ${skillId}`);
@@ -1801,7 +2147,7 @@ async function uninstallSkills(skillIds, tool, level) {
1801
2147
  if (removedIds.length > 0) {
1802
2148
  removeSkillsFromRecord(removedIds, tool, level, installPath);
1803
2149
  }
1804
- if (existsSync9(skillsDir)) {
2150
+ if (existsSync10(skillsDir)) {
1805
2151
  try {
1806
2152
  const entries = readdirSync3(skillsDir);
1807
2153
  if (entries.length === 0) {
@@ -1811,16 +2157,6 @@ async function uninstallSkills(skillIds, tool, level) {
1811
2157
  } catch {
1812
2158
  }
1813
2159
  }
1814
- if (existsSync9(configRoot)) {
1815
- try {
1816
- const entries = readdirSync3(configRoot);
1817
- if (entries.length === 0) {
1818
- rmdirSync(configRoot);
1819
- logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
1820
- }
1821
- } catch {
1822
- }
1823
- }
1824
2160
  return results;
1825
2161
  }
1826
2162
  async function interactiveSkillSelect() {
@@ -1832,7 +2168,7 @@ async function interactiveSkillSelect() {
1832
2168
  while (true) {
1833
2169
  switch (step) {
1834
2170
  case 0: {
1835
- printBoxTitle("\u9009\u62E9 Skill \u7C7B\u522B");
2171
+ printBoxTitle(t("wizard_step_3_skill_title"));
1836
2172
  const categoryChoices = [
1837
2173
  ...categories.map((cat) => ({
1838
2174
  name: `> ${cat.name} (${cat.skills.length} skills)`,
@@ -1843,11 +2179,12 @@ async function interactiveSkillSelect() {
1843
2179
  { name: "x " + t("wizard_cancel"), value: CANCEL3 }
1844
2180
  ];
1845
2181
  showOperationHints();
1846
- selectedCategoryId = await select2({
2182
+ selectedCategoryId = await select3({
1847
2183
  message: t("skill_select_category"),
1848
2184
  choices: categoryChoices,
1849
2185
  loop: false,
1850
- theme: selectTheme
2186
+ theme: selectTheme,
2187
+ pageSize: 15
1851
2188
  });
1852
2189
  if (selectedCategoryId === BACK3) return "back";
1853
2190
  if (selectedCategoryId === CANCEL3) return "cancel";
@@ -1857,7 +2194,7 @@ async function interactiveSkillSelect() {
1857
2194
  case 1: {
1858
2195
  const category = categories.find((c) => c.id === selectedCategoryId);
1859
2196
  if (!category) return [];
1860
- printBoxTitle(`\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 ${category.name}`);
2197
+ printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
1861
2198
  const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
1862
2199
  const levels = ["project", "global"];
1863
2200
  const installedSkills = [];
@@ -1877,25 +2214,18 @@ async function interactiveSkillSelect() {
1877
2214
  checked: false
1878
2215
  };
1879
2216
  });
1880
- skillChoices.push(new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1881
- skillChoices.push({ name: "<- " + t("wizard_back"), value: BACK3, checked: false });
1882
- skillChoices.push({ name: "x " + t("wizard_cancel"), value: CANCEL3, checked: false });
1883
2217
  showOperationHints(true);
1884
2218
  const selectedSkills = await checkbox2({
1885
2219
  message: t("skill_select_items"),
1886
2220
  choices: skillChoices,
1887
2221
  loop: false,
1888
2222
  instructions: false,
1889
- theme: checkboxTheme
2223
+ theme: checkboxTheme,
2224
+ pageSize: 15
1890
2225
  });
1891
- if (selectedSkills.includes(BACK3)) {
1892
- step = 0;
1893
- break;
1894
- }
1895
- if (selectedSkills.includes(CANCEL3)) return "cancel";
1896
- const skillIds = selectedSkills.filter((v) => v !== BACK3 && v !== CANCEL3);
2226
+ const skillIds = selectedSkills;
1897
2227
  if (skillIds.length === 0) {
1898
- const action = await select2({
2228
+ const action = await select3({
1899
2229
  message: t("wizard_no_selection"),
1900
2230
  choices: [
1901
2231
  new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
@@ -1919,11 +2249,11 @@ async function interactiveSkillSelect() {
1919
2249
  function listAllSkills() {
1920
2250
  const categories = getAllCategories();
1921
2251
  console.log();
1922
- console.log(chalk3.bold(` ${t("skill_list_title")}`));
1923
- console.log(chalk3.dim(" " + "\u2500".repeat(60)));
2252
+ console.log(chalk4.bold(` ${t("skill_list_title")}`));
2253
+ console.log(chalk4.dim(" " + "\u2500".repeat(60)));
1924
2254
  for (const category of categories) {
1925
2255
  console.log();
1926
- console.log(chalk3.bold(` ${category.name}`) + chalk3.dim(` (${category.skills.length} skills)`));
2256
+ console.log(chalk4.bold(` ${category.name}`) + chalk4.dim(` (${category.skills.length} skills)`));
1927
2257
  const table = new Table({
1928
2258
  style: { head: [], border: [] },
1929
2259
  colWidths: [35, 45],
@@ -1931,14 +2261,14 @@ function listAllSkills() {
1931
2261
  });
1932
2262
  for (const skill of category.skills) {
1933
2263
  table.push([
1934
- chalk3.cyan(skill.id),
2264
+ chalk4.cyan(skill.id),
1935
2265
  skill.description
1936
2266
  ]);
1937
2267
  }
1938
2268
  console.log(table.toString());
1939
2269
  }
1940
2270
  console.log();
1941
- console.log(chalk3.dim(` \u5171 ${getAllSkills().length} \u4E2A Skills`));
2271
+ console.log(chalk4.dim(` \u5171 ${getAllSkills().length} \u4E2A Skills`));
1942
2272
  console.log();
1943
2273
  }
1944
2274
  function isSymlink2(path) {
@@ -1950,22 +2280,22 @@ function isSymlink2(path) {
1950
2280
  }
1951
2281
 
1952
2282
  // src/ui/display.ts
1953
- import chalk4 from "chalk";
2283
+ import chalk5 from "chalk";
1954
2284
  import Table2 from "cli-table3";
1955
2285
  function printPluginList(plugins, installed) {
1956
2286
  const table = new Table2({
1957
2287
  head: [
1958
- chalk4.cyan(t("list_id")),
1959
- chalk4.cyan(t("list_name")),
1960
- chalk4.cyan(t("list_status")),
1961
- chalk4.cyan(t("list_skills")),
1962
- chalk4.cyan(t("list_agents"))
2288
+ chalk5.cyan(t("list_id")),
2289
+ chalk5.cyan(t("list_name")),
2290
+ chalk5.cyan(t("list_status")),
2291
+ chalk5.cyan(t("list_skills")),
2292
+ chalk5.cyan(t("list_agents"))
1963
2293
  ],
1964
2294
  style: { head: [], border: [] }
1965
2295
  });
1966
2296
  plugins.forEach((plugin, index) => {
1967
2297
  const isInstalled = installed.has(plugin.id);
1968
- const status = isInstalled ? chalk4.green(`\u2713 ${t("status_installed")}`) : chalk4.dim(`\u2014`);
2298
+ const status = isInstalled ? chalk5.green(`\u2713 ${t("status_installed")}`) : chalk5.dim(`\u2014`);
1969
2299
  table.push([
1970
2300
  String(index + 1),
1971
2301
  plugin.displayName,
@@ -1975,7 +2305,7 @@ function printPluginList(plugins, installed) {
1975
2305
  ]);
1976
2306
  });
1977
2307
  console.log();
1978
- console.log(chalk4.bold(` ${t("list_title")} (${plugins.length} \u4E2A)`));
2308
+ console.log(chalk5.bold(` ${t("list_title")} (${plugins.length} \u4E2A)`));
1979
2309
  console.log(table.toString());
1980
2310
  console.log();
1981
2311
  }
@@ -1984,7 +2314,7 @@ function printInstallSummary(results) {
1984
2314
  const totalCount = results.length;
1985
2315
  console.log();
1986
2316
  console.log(
1987
- chalk4.bold(
2317
+ chalk5.bold(
1988
2318
  ` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
1989
2319
  )
1990
2320
  );
@@ -1992,10 +2322,10 @@ function printInstallSummary(results) {
1992
2322
  for (const result of results) {
1993
2323
  if (result.success) {
1994
2324
  console.log(
1995
- chalk4.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
2325
+ chalk5.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
1996
2326
  );
1997
2327
  } else {
1998
- console.log(chalk4.red(" \u2717") + ` ${result.displayName}`);
2328
+ console.log(chalk5.red(" \u2717") + ` ${result.displayName}`);
1999
2329
  }
2000
2330
  }
2001
2331
  console.log();
@@ -2009,21 +2339,21 @@ function printEnhancedSummary(results, tool, configRoot) {
2009
2339
  }
2010
2340
  const toolName = getToolDisplayName(tool);
2011
2341
  const lines = [
2012
- chalk4.bold(` ${t("install_done")}!`),
2342
+ chalk5.bold(` ${t("install_done")}!`),
2013
2343
  "",
2014
- ` ${chalk4.dim("\u5B89\u88C5\u5230:")} ${configRoot}`,
2015
- ` ${chalk4.dim("Skills:")} ${totalSkills} \u4E2A ${chalk4.dim("|")} ${chalk4.dim("Agents:")} ${totalAgents} \u4E2A`,
2344
+ ` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
2345
+ ` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills} \u4E2A ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents} \u4E2A`,
2016
2346
  "",
2017
- chalk4.bold(` ${t("install_enhanced_next_steps")}:`),
2018
- ` ${chalk4.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk4.green(toolName.toLowerCase())}`,
2019
- ` ${chalk4.cyan("2.")} ${t("install_enhanced_try")}: ${chalk4.green("\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50")}`,
2020
- ` ${chalk4.cyan("3.")} ${t("install_enhanced_more")}: ${chalk4.green("install-helper list")}`,
2021
- ` ${chalk4.cyan("4.")} ${t("install_enhanced_check")}: ${chalk4.green("install-helper doctor")}`,
2347
+ chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
2348
+ ` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
2349
+ ` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50")}`,
2350
+ ` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
2351
+ ` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
2022
2352
  ""
2023
2353
  ];
2024
2354
  if (successResults.length === 1) {
2025
2355
  lines.push(
2026
- ` ${chalk4.dim(t("install_enhanced_docs"))}: ${chalk4.dim(`plugins-official/${successResults[0].pluginId}/quickstart.md`)}`
2356
+ ` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(`plugins-official/${successResults[0].pluginId}/quickstart.md`)}`
2027
2357
  );
2028
2358
  }
2029
2359
  console.log();
@@ -2034,34 +2364,34 @@ function printEnhancedSummary(results, tool, configRoot) {
2034
2364
  }
2035
2365
 
2036
2366
  // src/commands/init.ts
2037
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
2038
- import { join as join10 } from "path";
2367
+ import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
2368
+ import { join as join11 } from "path";
2039
2369
  var BACK2 = "__back__";
2040
2370
  var CANCEL2 = "__cancel__";
2041
2371
  async function initCommand() {
2042
2372
  const cwd = process.cwd();
2043
- if (existsSync10(join10(cwd, "package.json")) && (() => {
2373
+ if (existsSync11(join11(cwd, "package.json")) && (() => {
2044
2374
  try {
2045
- const pkg = JSON.parse(readFileSync5(join10(cwd, "package.json"), "utf-8"));
2375
+ const pkg = JSON.parse(readFileSync5(join11(cwd, "package.json"), "utf-8"));
2046
2376
  return pkg.name === "@cannbot-ai/install-helper";
2047
2377
  } catch {
2048
2378
  return false;
2049
2379
  }
2050
2380
  })()) {
2051
- logger.warn("\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55");
2052
- logger.info("\u8BF7\u5728\u4F60\u7684\u9879\u76EE\u76EE\u5F55\u4E2D\u8FD0\u884C\u6B64\u547D\u4EE4\uFF0C\u800C\u4E0D\u662F\u5728 install-helper \u5B89\u88C5\u76EE\u5F55\u4E2D");
2053
- logger.info("\u793A\u4F8B\uFF1Acd ~/my-project && install-helper");
2381
+ logger.warn(t("init_pkg_dir_warn"));
2382
+ logger.info(t("init_pkg_dir_hint"));
2383
+ logger.info(t("init_pkg_dir_example"));
2054
2384
  return;
2055
2385
  }
2056
2386
  printBanner(t("wizard_title"));
2057
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2387
+ const spinner = createSpinner(t("loading_skills_list"));
2058
2388
  spinner.start();
2059
2389
  try {
2060
2390
  const repoManager = createRepositoryManager();
2061
2391
  await repoManager.ensureRepoAndScan();
2062
- spinner.succeed("Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210");
2392
+ spinner.succeed(t("loading_skills_list_complete"));
2063
2393
  } catch {
2064
- spinner.warn("Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E");
2394
+ spinner.warn(t("loading_skills_list_failed"));
2065
2395
  }
2066
2396
  let step = 0;
2067
2397
  let mode = "plugin";
@@ -2095,7 +2425,7 @@ async function initCommand() {
2095
2425
  }
2096
2426
  }
2097
2427
  async function selectMode() {
2098
- printBoxTitle("\u9009\u62E9\u5B89\u88C5\u7C7B\u578B");
2428
+ printBoxTitle(t("wizard_select_mode_title"));
2099
2429
  const CANCEL3 = "__cancel__";
2100
2430
  const choices = [
2101
2431
  {
@@ -2110,7 +2440,7 @@ async function selectMode() {
2110
2440
  { name: "x " + t("wizard_cancel"), value: CANCEL3 }
2111
2441
  ];
2112
2442
  showOperationHints();
2113
- const result = await select3({
2443
+ const result = await select4({
2114
2444
  message: t("wizard_select_mode"),
2115
2445
  choices,
2116
2446
  loop: false,
@@ -2123,14 +2453,14 @@ async function pluginInstallFlow() {
2123
2453
  const answers = await runWizard();
2124
2454
  if (!answers.confirmed) {
2125
2455
  if (answers.back) return "back";
2126
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2456
+ logger.info(t("init_cancelled"));
2127
2457
  return "done";
2128
2458
  }
2129
2459
  const repoManager = createRepositoryManager();
2130
- const spinner = createSpinner(t("install_cloning"));
2460
+ const spinner = createSpinner(t("loading_plugin_data"));
2131
2461
  spinner.start();
2132
2462
  const repoPath = await repoManager.ensureRepo();
2133
- spinner.succeed(t("install_repo_ready"));
2463
+ spinner.succeed(t("loading_plugin_data_complete"));
2134
2464
  const allPlugins = getAllPlugins();
2135
2465
  const selectedPlugins = answers.plugins.map(
2136
2466
  (id) => allPlugins.find((p) => p.id === id)
@@ -2184,7 +2514,7 @@ async function skillInstallFlow() {
2184
2514
  const result = await selectToolWithDetection();
2185
2515
  if (result === "back") return "back";
2186
2516
  if (result === "cancel") {
2187
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2517
+ logger.info(t("init_cancelled"));
2188
2518
  return "done";
2189
2519
  }
2190
2520
  tool = result;
@@ -2198,7 +2528,7 @@ async function skillInstallFlow() {
2198
2528
  break;
2199
2529
  }
2200
2530
  if (result === CANCEL2) {
2201
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2531
+ logger.info(t("init_cancelled"));
2202
2532
  return "done";
2203
2533
  }
2204
2534
  level = result;
@@ -2212,37 +2542,40 @@ async function skillInstallFlow() {
2212
2542
  break;
2213
2543
  }
2214
2544
  if (selectedSkills === "cancel") {
2215
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2545
+ logger.info(t("init_cancelled"));
2216
2546
  return "done";
2217
2547
  }
2218
2548
  if (selectedSkills.length === 0) {
2219
- logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Skill");
2549
+ logger.info(t("install_no_skill_selected"));
2220
2550
  return "done";
2221
2551
  }
2222
2552
  const repoManager = createRepositoryManager();
2223
- const spinner = createSpinner(t("install_cloning"));
2553
+ const spinner = createSpinner(t("loading_plugin_data"));
2224
2554
  spinner.start();
2225
2555
  const repoPath = await repoManager.ensureRepo();
2226
- spinner.succeed(t("install_repo_ready"));
2227
- logger.info(`${t("skill_install_progress")} ${chalk5.bold(selectedSkills.length)} \u4E2A Skills...`);
2556
+ spinner.succeed(t("loading_plugin_data_complete"));
2557
+ logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)} \u4E2A Skills...`);
2228
2558
  const results = await installSkills(selectedSkills, tool, level, repoPath);
2229
2559
  let successCount = 0;
2230
2560
  let failCount = 0;
2231
- for (const result of results) {
2561
+ const total = results.length;
2562
+ for (let i = 0; i < results.length; i++) {
2563
+ const result = results[i];
2564
+ const progress = `[${i + 1}/${total}]`;
2232
2565
  if (result.success) {
2233
- logger.success(`${result.skillId}`);
2566
+ logger.success(`${progress} ${result.skillId}`);
2234
2567
  successCount++;
2235
2568
  } else {
2236
- logger.error(`${result.skillId}: ${result.error}`);
2569
+ logger.error(`${progress} ${result.skillId}: ${result.error}`);
2237
2570
  failCount++;
2238
2571
  }
2239
2572
  }
2240
2573
  const configRoot = getConfigRoot(tool, level);
2241
2574
  logger.blank();
2242
- logger.success(`${t("skill_install_done")}: ${chalk5.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk5.red(failCount + " \u5931\u8D25") : chalk5.dim(failCount + " \u5931\u8D25")}`);
2575
+ logger.success(`${t("skill_install_done")}: ${chalk6.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk6.red(failCount + " \u5931\u8D25") : chalk6.dim(failCount + " \u5931\u8D25")}`);
2243
2576
  logger.blank();
2244
- logger.info(`\u5B89\u88C5\u5230: ${chalk5.cyan(configRoot + "/skills/")}`);
2245
- logger.info(`\u542F\u52A8 ${chalk5.green(tool)} \u5373\u53EF\u4F7F\u7528`);
2577
+ logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
2578
+ logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
2246
2579
  logger.blank();
2247
2580
  return "done";
2248
2581
  }
@@ -2264,32 +2597,32 @@ async function listCommand() {
2264
2597
  }
2265
2598
 
2266
2599
  // src/commands/doctor.ts
2267
- import chalk6 from "chalk";
2268
- import { existsSync as existsSync11, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync3, mkdirSync as mkdirSync6 } from "fs";
2269
- import { join as join11 } from "path";
2600
+ import chalk7 from "chalk";
2601
+ import { existsSync as existsSync12, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2602
+ import { join as join12 } from "path";
2270
2603
  async function doctorCommand(options = {}) {
2271
2604
  console.log();
2272
- console.log(chalk6.bold(` ${t("doctor_title")}`));
2273
- console.log(chalk6.dim(" " + "\u2500".repeat(46)));
2605
+ console.log(chalk7.bold(` ${t("doctor_title")}`));
2606
+ console.log(chalk7.dim(" " + "\u2500".repeat(46)));
2274
2607
  let errors = 0;
2275
2608
  let warnings = 0;
2276
2609
  let fixes = 0;
2277
2610
  console.log();
2278
- console.log(chalk6.bold(` ${t("doctor_tools")}`));
2611
+ console.log(chalk7.bold(` ${t("doctor_tools")}`));
2279
2612
  const detectedTools = await detectTools();
2280
2613
  const allTools = getAllTools();
2281
2614
  for (const tool of allTools) {
2282
2615
  const detected = detectedTools.find((d) => d.name === tool);
2283
2616
  if (detected) {
2284
2617
  console.log(
2285
- chalk6.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
2618
+ chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
2286
2619
  );
2287
2620
  } else {
2288
- console.log(chalk6.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 \u672A\u5B89\u88C5`);
2621
+ console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 \u672A\u5B89\u88C5`);
2289
2622
  }
2290
2623
  }
2291
2624
  console.log();
2292
- console.log(chalk6.bold(` ${t("doctor_plugins")}`));
2625
+ console.log(chalk7.bold(` ${t("doctor_plugins")}`));
2293
2626
  const plugins = getAllPlugins();
2294
2627
  const installed = scanInstalled();
2295
2628
  const installedMap = new Map(installed.map((p) => [p.id, p]));
@@ -2297,73 +2630,73 @@ async function doctorCommand(options = {}) {
2297
2630
  const inst = installedMap.get(plugin.id);
2298
2631
  if (inst) {
2299
2632
  console.log(
2300
- chalk6.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
2633
+ chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
2301
2634
  );
2302
2635
  } else {
2303
- console.log(chalk6.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 \u672A\u5B89\u88C5`);
2636
+ console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 \u672A\u5B89\u88C5`);
2304
2637
  }
2305
2638
  }
2306
2639
  console.log();
2307
- console.log(chalk6.bold(` ${t("doctor_links")}`));
2640
+ console.log(chalk7.bold(` ${t("doctor_links")}`));
2308
2641
  const primaryTool = detectedTools[0]?.name || "opencode";
2309
2642
  const configRoot = getConfigRoot(primaryTool, "project");
2310
2643
  const skillsDir = getSkillsDir(configRoot);
2311
2644
  const agentsDir = getAgentsDir(configRoot);
2312
- if (existsSync11(skillsDir)) {
2645
+ if (existsSync12(skillsDir)) {
2313
2646
  const brokenLinks = checkBrokenLinks(skillsDir);
2314
2647
  if (brokenLinks === 0) {
2315
2648
  const count = readdirSync4(skillsDir).length;
2316
- console.log(chalk6.green(" \u2713") + ` ${skillsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2649
+ console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2317
2650
  } else {
2318
- console.log(chalk6.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2651
+ console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2319
2652
  warnings++;
2320
2653
  if (options.fix) {
2321
2654
  const fixed = fixBrokenLinks(skillsDir);
2322
2655
  fixes += fixed;
2323
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2656
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2324
2657
  }
2325
2658
  }
2326
2659
  } else {
2327
- console.log(chalk6.dim(" \u2014") + ` ${skillsDir} \u2014 \u4E0D\u5B58\u5728`);
2660
+ console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 \u4E0D\u5B58\u5728`);
2328
2661
  if (options.fix) {
2329
2662
  mkdirSync6(skillsDir, { recursive: true });
2330
2663
  fixes++;
2331
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${skillsDir}`);
2664
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${skillsDir}`);
2332
2665
  }
2333
2666
  }
2334
- if (existsSync11(agentsDir)) {
2667
+ if (existsSync12(agentsDir)) {
2335
2668
  const brokenLinks = checkBrokenLinks(agentsDir);
2336
2669
  if (brokenLinks === 0) {
2337
2670
  const count = readdirSync4(agentsDir).length;
2338
- console.log(chalk6.green(" \u2713") + ` ${agentsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2671
+ console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2339
2672
  } else {
2340
- console.log(chalk6.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2673
+ console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2341
2674
  warnings++;
2342
2675
  if (options.fix) {
2343
2676
  const fixed = fixBrokenLinks(agentsDir);
2344
2677
  fixes += fixed;
2345
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2678
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2346
2679
  }
2347
2680
  }
2348
2681
  } else {
2349
- console.log(chalk6.dim(" \u2014") + ` ${agentsDir} \u2014 \u4E0D\u5B58\u5728`);
2682
+ console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 \u4E0D\u5B58\u5728`);
2350
2683
  if (options.fix) {
2351
2684
  mkdirSync6(agentsDir, { recursive: true });
2352
2685
  fixes++;
2353
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${agentsDir}`);
2686
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${agentsDir}`);
2354
2687
  }
2355
2688
  }
2356
2689
  console.log();
2357
- console.log(chalk6.bold(` ${t("doctor_config")}`));
2690
+ console.log(chalk7.bold(` ${t("doctor_config")}`));
2358
2691
  const configFile = getConfigFileName(primaryTool);
2359
- const configPath = join11(process.cwd(), configFile);
2360
- if (existsSync11(configPath)) {
2361
- console.log(chalk6.green(" \u2713") + ` ${configFile} \u5B58\u5728`);
2692
+ const configPath = join12(process.cwd(), configFile);
2693
+ if (existsSync12(configPath)) {
2694
+ console.log(chalk7.green(" \u2713") + ` ${configFile} \u5B58\u5728`);
2362
2695
  } else {
2363
- console.log(chalk6.dim(" \u2014") + ` ${configFile} \u4E0D\u5B58\u5728`);
2696
+ console.log(chalk7.dim(" \u2014") + ` ${configFile} \u4E0D\u5B58\u5728`);
2364
2697
  }
2365
2698
  console.log();
2366
- console.log(chalk6.dim(" " + "\u2500".repeat(46)));
2699
+ console.log(chalk7.dim(" " + "\u2500".repeat(46)));
2367
2700
  if (options.fix && fixes > 0) {
2368
2701
  console.log(
2369
2702
  ` ${t("doctor_result")}: ${warnings} \u4E2A\u8B66\u544A, ${errors} \u4E2A\u9519\u8BEF, ${fixes} \u9879\u5DF2\u4FEE\u590D`
@@ -2380,11 +2713,11 @@ function checkBrokenLinks(dir) {
2380
2713
  try {
2381
2714
  const entries = readdirSync4(dir);
2382
2715
  for (const entry of entries) {
2383
- const fullPath = join11(dir, entry);
2716
+ const fullPath = join12(dir, entry);
2384
2717
  try {
2385
2718
  const stats = lstatSync3(fullPath);
2386
2719
  if (stats.isSymbolicLink()) {
2387
- if (!existsSync11(fullPath)) {
2720
+ if (!existsSync12(fullPath)) {
2388
2721
  broken++;
2389
2722
  }
2390
2723
  }
@@ -2401,12 +2734,12 @@ function fixBrokenLinks(dir) {
2401
2734
  try {
2402
2735
  const entries = readdirSync4(dir);
2403
2736
  for (const entry of entries) {
2404
- const fullPath = join11(dir, entry);
2737
+ const fullPath = join12(dir, entry);
2405
2738
  try {
2406
2739
  const stats = lstatSync3(fullPath);
2407
2740
  if (stats.isSymbolicLink()) {
2408
- if (!existsSync11(fullPath)) {
2409
- unlinkSync3(fullPath);
2741
+ if (!existsSync12(fullPath)) {
2742
+ unlinkSync4(fullPath);
2410
2743
  fixed++;
2411
2744
  }
2412
2745
  }
@@ -2419,36 +2752,36 @@ function fixBrokenLinks(dir) {
2419
2752
  }
2420
2753
 
2421
2754
  // src/commands/status.ts
2422
- import chalk7 from "chalk";
2755
+ import chalk8 from "chalk";
2423
2756
  async function statusCommand() {
2424
2757
  const plugins = getAllPlugins();
2425
2758
  const installed = scanInstalled();
2426
2759
  const installedMap = new Map(installed.map((p) => [p.id, p]));
2427
2760
  console.log();
2428
- console.log(chalk7.bold(` ${t("status_title")}`));
2761
+ console.log(chalk8.bold(` ${t("status_title")}`));
2429
2762
  console.log();
2430
2763
  if (installed.length === 0) {
2431
- console.log(chalk7.dim(` ${t("status_none")}`));
2432
- console.log(chalk7.dim(` ${t("status_hint")}`));
2764
+ console.log(chalk8.dim(` ${t("status_none")}`));
2765
+ console.log(chalk8.dim(` ${t("status_hint")}`));
2433
2766
  console.log();
2434
2767
  return;
2435
2768
  }
2436
2769
  for (const inst of installed) {
2437
2770
  console.log(
2438
- chalk7.green(" \u2713") + ` ${inst.displayName}` + chalk7.dim(` (${inst.tool}, ${inst.level})`)
2771
+ chalk8.green(" \u2713") + ` ${inst.displayName}` + chalk8.dim(` (${inst.tool}, ${inst.level})`)
2439
2772
  );
2440
2773
  console.log(
2441
- chalk7.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
2774
+ chalk8.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
2442
2775
  );
2443
- console.log(chalk7.dim(` ${inst.configRoot}`));
2444
- console.log(chalk7.dim(` ${t("status_install_time")}: ${inst.installTime}`));
2776
+ console.log(chalk8.dim(` ${inst.configRoot}`));
2777
+ console.log(chalk8.dim(` ${t("status_install_time")}: ${inst.installTime}`));
2445
2778
  console.log();
2446
2779
  }
2447
2780
  }
2448
2781
 
2449
2782
  // src/commands/install.ts
2450
2783
  import { confirm } from "@inquirer/prompts";
2451
- import chalk8 from "chalk";
2784
+ import chalk9 from "chalk";
2452
2785
  async function installCommand(names, options) {
2453
2786
  if (options.list) {
2454
2787
  try {
@@ -2460,7 +2793,7 @@ async function installCommand(names, options) {
2460
2793
  return;
2461
2794
  }
2462
2795
  if (names.length === 0) {
2463
- const scanSpinner2 = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2796
+ const scanSpinner2 = createSpinner(t("loading_skills_list"));
2464
2797
  scanSpinner2.start();
2465
2798
  try {
2466
2799
  const scanRepoManager = createRepositoryManager();
@@ -2471,7 +2804,7 @@ async function installCommand(names, options) {
2471
2804
  }
2472
2805
  let tool2;
2473
2806
  if (options.tool) {
2474
- tool2 = options.tool;
2807
+ tool2 = validateTool(options.tool);
2475
2808
  } else {
2476
2809
  const detected = await selectToolWithDetection();
2477
2810
  if (detected === "back") return;
@@ -2489,42 +2822,45 @@ async function installCommand(names, options) {
2489
2822
  break;
2490
2823
  }
2491
2824
  if (selectedSkills.length === 0) {
2492
- logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Skill");
2825
+ logger.info(t("install_no_skill_selected"));
2493
2826
  return;
2494
2827
  }
2495
2828
  names = selectedSkills;
2496
2829
  const repoManager = createRepositoryManager();
2497
- const spinner = createSpinner(t("install_cloning"));
2830
+ const spinner = createSpinner(t("loading_plugin_data"));
2498
2831
  spinner.start();
2499
2832
  const repoPath = await repoManager.ensureRepo();
2500
- spinner.succeed(t("install_repo_ready"));
2501
- logger.info(`${t("skill_install_progress")} ${chalk8.bold(names.length)} \u4E2A Skills...`);
2502
- const level2 = options.level || "project";
2833
+ spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
2834
+ logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)} \u4E2A Skills...`);
2835
+ const level2 = options.level ? validateLevel(options.level) : "project";
2503
2836
  const results = await installSkills(names, tool2, level2, repoPath);
2504
2837
  let successCount = 0;
2505
2838
  let failCount = 0;
2506
- for (const result of results) {
2839
+ const total = results.length;
2840
+ for (let i = 0; i < results.length; i++) {
2841
+ const result = results[i];
2842
+ const progress = `[${i + 1}/${total}]`;
2507
2843
  if (result.success) {
2508
- logger.success(`${result.skillId}`);
2844
+ logger.success(`${progress} ${result.skillId}`);
2509
2845
  successCount++;
2510
2846
  } else {
2511
- logger.error(`${result.skillId}: ${result.error}`);
2847
+ logger.error(`${progress} ${result.skillId}: ${result.error}`);
2512
2848
  failCount++;
2513
2849
  }
2514
2850
  }
2515
2851
  const configRoot = getConfigRoot(tool2, level2);
2516
2852
  logger.blank();
2517
- logger.success(`${t("skill_install_done")}: ${chalk8.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk8.red(failCount + " \u5931\u8D25") : chalk8.dim(failCount + " \u5931\u8D25")}`);
2853
+ logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
2518
2854
  logger.blank();
2519
- logger.info(`\u5B89\u88C5\u5230: ${chalk8.cyan(configRoot + "/skills/")}`);
2520
- logger.info(`\u542F\u52A8 ${chalk8.green(tool2)} \u5373\u53EF\u4F7F\u7528`);
2855
+ logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
2856
+ logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
2521
2857
  logger.blank();
2522
2858
  return;
2523
2859
  }
2524
2860
  }
2525
2861
  }
2526
2862
  }
2527
- const scanSpinner = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2863
+ const scanSpinner = createSpinner(t("loading_skills_list"));
2528
2864
  scanSpinner.start();
2529
2865
  try {
2530
2866
  const scanRepoManager = createRepositoryManager();
@@ -2552,18 +2888,18 @@ async function installCommand(names, options) {
2552
2888
  for (const p of allPlugins) {
2553
2889
  logger.step(` ${p.id} (${p.aliases.join(", ")})`);
2554
2890
  }
2555
- logger.info(`\u53EF\u7528 Skills: ${chalk8.cyan("install-helper install --list")}`);
2891
+ logger.info(`\u53EF\u7528 Skills: ${chalk9.cyan("install-helper install --list")}`);
2556
2892
  return;
2557
2893
  }
2558
2894
  let tool;
2559
2895
  if (options.tool) {
2560
- tool = options.tool;
2896
+ tool = validateTool(options.tool);
2561
2897
  } else {
2562
2898
  const detected = await selectToolWithDetection();
2563
2899
  if (detected === "back") return;
2564
2900
  tool = detected;
2565
2901
  }
2566
- const level = options.level || "project";
2902
+ const level = options.level ? validateLevel(options.level) : "project";
2567
2903
  if (plugins.length > 0) {
2568
2904
  const configRoot = getConfigRoot(tool, level);
2569
2905
  const manifests = readAllManifests(configRoot);
@@ -2591,16 +2927,18 @@ async function installCommand(names, options) {
2591
2927
  }
2592
2928
  if (pluginsToInstall.length > 0) {
2593
2929
  const repoManager = createRepositoryManager();
2594
- const spinner = createSpinner(t("install_cloning"));
2930
+ const spinner = createSpinner(t("loading_plugin_data"));
2595
2931
  spinner.start();
2596
2932
  const repoPath = await repoManager.ensureRepo();
2597
- spinner.succeed(t("install_repo_ready"));
2933
+ spinner.succeed(t("loading_plugin_data_complete"));
2598
2934
  const allPlugins = getAllPlugins();
2599
2935
  const results = await installPlugins(
2600
2936
  pluginsToInstall,
2601
2937
  tool,
2602
2938
  level,
2603
- repoPath
2939
+ repoPath,
2940
+ void 0,
2941
+ options.yes
2604
2942
  );
2605
2943
  const summary = results.map((result) => {
2606
2944
  const plugin = allPlugins.find((p) => p.id === result.pluginId);
@@ -2618,28 +2956,31 @@ async function installCommand(names, options) {
2618
2956
  }
2619
2957
  if (skills.length > 0) {
2620
2958
  const repoManager = createRepositoryManager();
2621
- const spinner = createSpinner(t("install_cloning"));
2959
+ const spinner = createSpinner(t("loading_plugin_data"));
2622
2960
  spinner.start();
2623
2961
  const repoPath = await repoManager.ensureRepo();
2624
- spinner.succeed(t("install_repo_ready"));
2625
- logger.info(`${t("skill_install_progress")} ${chalk8.bold(skills.length)} \u4E2A Skills...`);
2962
+ spinner.succeed(t("loading_plugin_data_complete"));
2963
+ logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)} \u4E2A Skills...`);
2626
2964
  const results = await installSkills(skills, tool, level, repoPath);
2627
2965
  let successCount = 0;
2628
2966
  let failCount = 0;
2629
- for (const result of results) {
2967
+ const total = results.length;
2968
+ for (let i = 0; i < results.length; i++) {
2969
+ const result = results[i];
2970
+ const progress = `[${i + 1}/${total}]`;
2630
2971
  if (result.success) {
2631
- logger.success(`${result.skillId}`);
2972
+ logger.success(`${progress} ${result.skillId}`);
2632
2973
  successCount++;
2633
2974
  } else {
2634
- logger.error(`${result.skillId}: ${result.error}`);
2975
+ logger.error(`${progress} ${result.skillId}: ${result.error}`);
2635
2976
  failCount++;
2636
2977
  }
2637
2978
  }
2638
2979
  logger.blank();
2639
- logger.success(`${t("skill_install_done")}: ${chalk8.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk8.red(failCount + " \u5931\u8D25") : chalk8.dim(failCount + " \u5931\u8D25")}`);
2980
+ logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
2640
2981
  logger.blank();
2641
- logger.info(`\u5B89\u88C5\u5230: ${chalk8.cyan(getConfigRoot(tool, level) + "/skills/")}`);
2642
- logger.info(`\u542F\u52A8 ${chalk8.green(tool)} \u5373\u53EF\u4F7F\u7528`);
2982
+ logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
2983
+ logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
2643
2984
  logger.blank();
2644
2985
  }
2645
2986
  }
@@ -2671,11 +3012,11 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
2671
3012
  }
2672
3013
 
2673
3014
  // src/commands/uninstall.ts
2674
- import { existsSync as existsSync12, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
2675
- import chalk9 from "chalk";
3015
+ import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
3016
+ import chalk10 from "chalk";
2676
3017
  async function uninstallCommand(names, options) {
2677
- const tool = options.tool || "opencode";
2678
- const level = options.level || "project";
3018
+ const tool = options.tool ? validateTool(options.tool) : "opencode";
3019
+ const level = options.level ? validateLevel(options.level) : "project";
2679
3020
  const plugins = [];
2680
3021
  const skills = [];
2681
3022
  for (const name of names) {
@@ -2689,24 +3030,24 @@ async function uninstallCommand(names, options) {
2689
3030
  skills.push(skill.id);
2690
3031
  continue;
2691
3032
  }
2692
- logger.error(`\u672A\u627E\u5230: ${name}`);
3033
+ logger.error(`${t("uninstall_not_found")}: ${name}`);
2693
3034
  }
2694
3035
  for (const pluginId of plugins) {
2695
3036
  const plugin = findPlugin(pluginId);
2696
3037
  if (!plugin) continue;
2697
3038
  const record = readRecord(plugin.id);
2698
3039
  if (!record) {
2699
- logger.warn(`${plugin.displayName} \u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F`);
2700
- logger.info(`\u5982\u9700\u5F3A\u5236\u6E05\u7406\uFF0C\u8BF7\u624B\u52A8\u5220\u9664 .opencode/ \u6216 .claude/ \u76EE\u5F55\u4E2D\u7684\u76F8\u5173\u6587\u4EF6`);
3040
+ logger.warn(`${plugin.displayName} ${t("uninstall_no_record")}`);
3041
+ logger.info(`${t("uninstall_no_record_hint")}`);
2701
3042
  continue;
2702
3043
  }
2703
- logger.info(`\u6B63\u5728\u5378\u8F7D ${plugin.displayName}...`);
3044
+ logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
2704
3045
  let removedFiles = 0;
2705
3046
  let removedDirs = 0;
2706
3047
  for (const filePath of record.files) {
2707
3048
  try {
2708
- if (existsSync12(filePath) || isSymlink3(filePath)) {
2709
- unlinkSync4(filePath);
3049
+ if (existsSync13(filePath) || isSymlink3(filePath)) {
3050
+ unlinkSync5(filePath);
2710
3051
  const name = filePath.split("/").pop() || filePath;
2711
3052
  logger.step(` \u79FB\u9664: ${name}`);
2712
3053
  removedFiles++;
@@ -2714,10 +3055,14 @@ async function uninstallCommand(names, options) {
2714
3055
  } catch {
2715
3056
  }
2716
3057
  }
3058
+ const configRoot = getConfigRoot(record.tool, record.level);
2717
3059
  const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
2718
3060
  for (const dirPath of sortedDirs) {
2719
3061
  try {
2720
- if (existsSync12(dirPath)) {
3062
+ if (dirPath === configRoot) {
3063
+ continue;
3064
+ }
3065
+ if (existsSync13(dirPath)) {
2721
3066
  const entries = readdirSync5(dirPath);
2722
3067
  if (entries.length === 0) {
2723
3068
  rmdirSync2(dirPath);
@@ -2731,15 +3076,32 @@ async function uninstallCommand(names, options) {
2731
3076
  }
2732
3077
  deleteRecord(plugin.id);
2733
3078
  removeInstalledPlugin(plugin.id);
3079
+ const backups = findBackups(configRoot);
3080
+ const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
3081
+ if (otherBackups.length > 0) {
3082
+ const choice = await showRestorePrompt(otherBackups);
3083
+ if (choice !== "none") {
3084
+ const restored = restoreBackup(choice, configRoot, record.tool);
3085
+ if (restored) {
3086
+ const backupInfo = otherBackups.find((b) => b.filePath === choice);
3087
+ logger.success(`${t("backup_restore_success")}: ${backupInfo?.pluginName || ""}`);
3088
+ deleteBackup(choice);
3089
+ }
3090
+ }
3091
+ }
3092
+ const currentBackups = backups.filter((b) => b.pluginId === pluginId);
3093
+ for (const backup of currentBackups) {
3094
+ deleteBackup(backup.filePath);
3095
+ }
2734
3096
  const totalRemoved = removedFiles + removedDirs;
2735
3097
  if (totalRemoved > 0) {
2736
- logger.success(`\u5DF2\u5378\u8F7D ${plugin.displayName}\uFF08\u79FB\u9664 ${removedFiles} \u4E2A\u6587\u4EF6\uFF0C${removedDirs} \u4E2A\u76EE\u5F55\uFF09`);
3098
+ logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08\u79FB\u9664 ${removedFiles} \u4E2A\u6587\u4EF6\uFF0C${removedDirs} \u4E2A\u76EE\u5F55\uFF09`);
2737
3099
  } else {
2738
- logger.warn(`${plugin.displayName} \u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55`);
3100
+ logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
2739
3101
  }
2740
3102
  }
2741
3103
  if (skills.length > 0) {
2742
- logger.info(`\u6B63\u5728\u5378\u8F7D ${skills.length} \u4E2A Skills...`);
3104
+ logger.info(`${t("uninstall_skills_in_progress")} ${skills.length} \u4E2A Skills...`);
2743
3105
  const results = await uninstallSkills(skills, tool, level);
2744
3106
  let successCount = 0;
2745
3107
  let failCount = 0;
@@ -2751,7 +3113,7 @@ async function uninstallCommand(names, options) {
2751
3113
  }
2752
3114
  }
2753
3115
  logger.blank();
2754
- logger.success(`${t("skill_uninstall_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
3116
+ logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk10.red(failCount + " \u5931\u8D25") : chalk10.dim(failCount + " \u5931\u8D25")}`);
2755
3117
  logger.blank();
2756
3118
  }
2757
3119
  }
@@ -2764,17 +3126,17 @@ function isSymlink3(path) {
2764
3126
  }
2765
3127
 
2766
3128
  // src/commands/update.ts
2767
- import chalk10 from "chalk";
3129
+ import chalk11 from "chalk";
2768
3130
  async function updateCommand(pluginNames, options) {
2769
3131
  let tool;
2770
3132
  if (options.tool) {
2771
- tool = options.tool;
3133
+ tool = validateTool(options.tool);
2772
3134
  } else {
2773
3135
  const detected = await selectToolWithDetection();
2774
3136
  if (detected === "back") return;
2775
3137
  tool = detected;
2776
3138
  }
2777
- const level = options.level || "project";
3139
+ const level = options.level ? validateLevel(options.level) : "project";
2778
3140
  let pluginsToUpdate = [];
2779
3141
  if (pluginNames.length === 0) {
2780
3142
  const configRoot = getConfigRoot(tool, level);
@@ -2782,7 +3144,7 @@ async function updateCommand(pluginNames, options) {
2782
3144
  pluginsToUpdate = manifests.map((m) => m.team);
2783
3145
  if (pluginsToUpdate.length === 0) {
2784
3146
  logger.info(t("update_no_plugins"));
2785
- logger.info(`\u4F7F\u7528 ${chalk10.cyan("install-helper install <plugin>")} \u5B89\u88C5\u63D2\u4EF6`);
3147
+ logger.info(`\u4F7F\u7528 ${chalk11.cyan("install-helper install <plugin>")} \u5B89\u88C5\u63D2\u4EF6`);
2786
3148
  return;
2787
3149
  }
2788
3150
  } else {
@@ -2844,42 +3206,42 @@ async function updateCommand(pluginNames, options) {
2844
3206
  }
2845
3207
 
2846
3208
  // src/commands/info.ts
2847
- import chalk11 from "chalk";
2848
- import { existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
2849
- import { join as join12 } from "path";
3209
+ import chalk12 from "chalk";
3210
+ import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
3211
+ import { join as join13 } from "path";
2850
3212
  async function infoCommand(pluginName) {
2851
3213
  const plugin = findPlugin(pluginName);
2852
3214
  if (!plugin) {
2853
- console.log(chalk11.red(` ${t("info_not_found")}: ${pluginName}`));
3215
+ console.log(chalk12.red(` ${t("info_not_found")}: ${pluginName}`));
2854
3216
  return;
2855
3217
  }
2856
3218
  const installed = scanInstalled();
2857
3219
  const isInstalled = installed.some((p) => p.id === plugin.id);
2858
- const statusText = isInstalled ? chalk11.green(`\u2713 ${t("status_installed")}`) : chalk11.dim(`\u2014 ${t("status_not_installed")}`);
3220
+ const statusText = isInstalled ? chalk12.green(`\u2713 ${t("status_installed")}`) : chalk12.dim(`\u2014 ${t("status_not_installed")}`);
2859
3221
  console.log();
2860
- console.log(chalk11.bold(` ${plugin.displayName}`));
2861
- console.log(chalk11.dim(" " + "\u2500".repeat(40)));
3222
+ console.log(chalk12.bold(` ${plugin.displayName}`));
3223
+ console.log(chalk12.dim(" " + "\u2500".repeat(40)));
2862
3224
  console.log();
2863
- console.log(` ${chalk11.dim(t("info_description") + ":")} ${plugin.description}`);
2864
- console.log(` ${chalk11.dim("ID:")} ${plugin.id}`);
2865
- console.log(` ${chalk11.dim(t("info_status") + ":")} ${statusText}`);
2866
- console.log(` ${chalk11.dim(t("info_skills") + ":")} ${plugin.skills}`);
2867
- console.log(` ${chalk11.dim(t("info_agents") + ":")} ${plugin.agents}`);
2868
- console.log(` ${chalk11.dim("\u522B\u540D:")} ${plugin.aliases.join(", ")}`);
3225
+ console.log(` ${chalk12.dim(t("info_description") + ":")} ${plugin.description}`);
3226
+ console.log(` ${chalk12.dim("ID:")} ${plugin.id}`);
3227
+ console.log(` ${chalk12.dim(t("info_status") + ":")} ${statusText}`);
3228
+ console.log(` ${chalk12.dim(t("info_skills") + ":")} ${plugin.skills}`);
3229
+ console.log(` ${chalk12.dim(t("info_agents") + ":")} ${plugin.agents}`);
3230
+ console.log(` ${chalk12.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
2869
3231
  console.log();
2870
3232
  try {
2871
3233
  const repoManager = createRepositoryManager();
2872
3234
  const repoPath = await repoManager.ensureRepo();
2873
- const quickstartPath = join12(repoPath, plugin.dir, "quickstart.md");
2874
- if (existsSync13(quickstartPath)) {
2875
- console.log(` ${chalk11.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
3235
+ const quickstartPath = join13(repoPath, plugin.dir, "quickstart.md");
3236
+ if (existsSync14(quickstartPath)) {
3237
+ console.log(` ${chalk12.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
2876
3238
  }
2877
- const agentsDir = join12(repoPath, plugin.dir, "agents");
2878
- if (existsSync13(agentsDir)) {
3239
+ const agentsDir = join13(repoPath, plugin.dir, "agents");
3240
+ if (existsSync14(agentsDir)) {
2879
3241
  const agentFiles = readdirSync6(agentsDir).filter((f) => f.endsWith(".md"));
2880
3242
  if (agentFiles.length > 0) {
2881
3243
  console.log();
2882
- console.log(` ${chalk11.bold("Agents:")}`);
3244
+ console.log(` ${chalk12.bold("Agents:")}`);
2883
3245
  for (const agent of agentFiles) {
2884
3246
  console.log(` \u2022 ${agent.replace(".md", "")}`);
2885
3247
  }
@@ -2891,39 +3253,39 @@ async function infoCommand(pluginName) {
2891
3253
  }
2892
3254
 
2893
3255
  // src/commands/lang.ts
2894
- import chalk12 from "chalk";
3256
+ import chalk13 from "chalk";
2895
3257
  async function langCommand(action, value) {
2896
3258
  const config = readConfig();
2897
3259
  if (!action || action === "show") {
2898
3260
  console.log();
2899
- console.log(chalk12.bold(` ${t("lang_title")}`));
2900
- console.log(chalk12.dim(" " + "\u2500".repeat(40)));
3261
+ console.log(chalk13.bold(` ${t("lang_title")}`));
3262
+ console.log(chalk13.dim(" " + "\u2500".repeat(40)));
2901
3263
  console.log();
2902
- console.log(` ${chalk12.dim(t("lang_current") + ":")} ${config.language}`);
3264
+ console.log(` ${chalk13.dim(t("lang_current") + ":")} ${config.language}`);
2903
3265
  console.log();
2904
- console.log(chalk12.dim(" \u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US"));
2905
- console.log(chalk12.dim(" \u7528\u6CD5: install-helper lang set <language>"));
3266
+ console.log(chalk13.dim(` ${t("lang_supported")}`));
3267
+ console.log(chalk13.dim(` ${t("lang_usage")}`));
2906
3268
  console.log();
2907
3269
  return;
2908
3270
  }
2909
3271
  if (action === "set") {
2910
3272
  if (!value) {
2911
- console.log(chalk12.red(" \u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN"));
3273
+ console.log(chalk13.red(` ${t("lang_specify")}`));
2912
3274
  return;
2913
3275
  }
2914
3276
  if (value !== "zh_CN" && value !== "en_US") {
2915
- console.log(chalk12.red(` ${t("lang_invalid")}`));
3277
+ console.log(chalk13.red(` ${t("lang_invalid")}`));
2916
3278
  return;
2917
3279
  }
2918
3280
  updateConfig({ language: value });
2919
3281
  setLanguage(value);
2920
3282
  console.log();
2921
- console.log(chalk12.green(` \u2713 ${t("lang_set")} ${value}`));
3283
+ console.log(chalk13.green(` \u2713 ${t("lang_set")} ${value}`));
2922
3284
  console.log();
2923
3285
  return;
2924
3286
  }
2925
- console.log(chalk12.red(` \u672A\u77E5\u64CD\u4F5C: ${action}`));
2926
- console.log(chalk12.dim(" \u652F\u6301\u7684\u64CD\u4F5C: show, set"));
3287
+ console.log(chalk13.red(` ${t("lang_unknown_action")}: ${action}`));
3288
+ console.log(chalk13.dim(` ${t("lang_supported_actions")}`));
2927
3289
  }
2928
3290
 
2929
3291
  // src/cli.ts
@@ -2933,7 +3295,7 @@ function createCLI() {
2933
3295
  setLanguage(config.language);
2934
3296
  }
2935
3297
  const program2 = new Command();
2936
- program2.name("install-helper").description("Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.1");
3298
+ program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.11");
2937
3299
  program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
2938
3300
  await initCommand();
2939
3301
  });