@cannbot-ai/install-helper 0.0.2-beta.8 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -145,251 +145,6 @@ 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
- function detectTraeVariant() {
149
- const home = homedir();
150
- if (existsSync(join(home, ".trae-cn"))) return "ide";
151
- if (existsSync(join(home, ".marscode"))) return "plugin";
152
- if (existsSync(join(home, ".traecli"))) return "cli";
153
- return "unknown";
154
- }
155
- function getConfigRoot(tool, level, base) {
156
- const home = homedir();
157
- if (level === "global") {
158
- switch (tool) {
159
- case "opencode":
160
- return join(home, ".config", "opencode");
161
- case "claude":
162
- return join(home, ".claude");
163
- case "trae": {
164
- const variant = detectTraeVariant();
165
- switch (variant) {
166
- case "plugin":
167
- return join(home, ".marscode");
168
- case "cli":
169
- return join(home, ".traecli");
170
- default:
171
- return join(home, ".trae-cn");
172
- }
173
- }
174
- case "cursor":
175
- return join(home, ".cursor");
176
- case "copilot":
177
- return join(home, ".copilot");
178
- }
179
- }
180
- const baseDir = base || process.cwd();
181
- switch (tool) {
182
- case "opencode":
183
- return join(baseDir, ".opencode");
184
- case "claude":
185
- return join(baseDir, ".claude");
186
- case "trae": {
187
- const variant = detectTraeVariant();
188
- switch (variant) {
189
- case "plugin":
190
- return join(baseDir, ".marscode");
191
- case "cli":
192
- return join(baseDir, ".traecli");
193
- default:
194
- return join(baseDir, ".trae");
195
- }
196
- }
197
- case "cursor":
198
- return join(baseDir, ".cursor");
199
- case "copilot":
200
- return join(baseDir, ".github");
201
- }
202
- }
203
- function getConfigFileName(tool) {
204
- return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
205
- }
206
- function getSkillsDir(configRoot) {
207
- return join(configRoot, "skills");
208
- }
209
- function getAgentsDir(configRoot) {
210
- return join(configRoot, "agents");
211
- }
212
- function getManifestPath(configRoot) {
213
- return join(configRoot, "cannbot-manifest.json");
214
- }
215
- function getCannbotConfigDir() {
216
- return join(homedir(), ".cannbot");
217
- }
218
- function getCannbotRepoPath() {
219
- return join(getCannbotConfigDir(), "repo");
220
- }
221
-
222
- // src/core/detector.ts
223
- async function getCommandVersion(cmd, args = ["--version"]) {
224
- try {
225
- const result = await execa(cmd, args, { timeout: 5e3 });
226
- const output = result.stdout.trim();
227
- const match = output.match(/(\d+\.\d+\.\d+)/);
228
- return match ? match[1] : output.split("\n")[0];
229
- } catch {
230
- return void 0;
231
- }
232
- }
233
- async function getCommandPath(cmd) {
234
- try {
235
- const result = await execa("sh", ["-c", `command -v ${cmd}`], { timeout: 5e3 });
236
- return result.stdout.trim();
237
- } catch {
238
- return void 0;
239
- }
240
- }
241
- async function detectOpenCode() {
242
- const path = await getCommandPath("opencode");
243
- if (!path) return void 0;
244
- const version = await getCommandVersion("opencode");
245
- return { name: "opencode", version, path };
246
- }
247
- async function detectClaude() {
248
- const path = await getCommandPath("claude");
249
- if (!path) return void 0;
250
- const version = await getCommandVersion("claude");
251
- return { name: "claude", version, path };
252
- }
253
- async function detectTrae() {
254
- const home = homedir2();
255
- const variant = detectTraeVariant();
256
- let path;
257
- if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
258
- path = join2(home, ".trae-cn");
259
- } else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
260
- path = join2(home, ".marscode");
261
- } else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
262
- path = join2(home, ".traecli");
263
- } else if (variant === "unknown") {
264
- const cmdPath = await getCommandPath("trae");
265
- if (cmdPath) path = cmdPath;
266
- }
267
- if (!path) return void 0;
268
- return { name: "trae", version: variant, path };
269
- }
270
- async function detectCursor() {
271
- const path = await getCommandPath("cursor");
272
- if (path) {
273
- const version = await getCommandVersion("cursor");
274
- return { name: "cursor", version, path };
275
- }
276
- if (process.platform === "darwin") {
277
- const appPath = "/Applications/Cursor.app";
278
- if (existsSync2(appPath)) {
279
- return { name: "cursor", path: appPath };
280
- }
281
- }
282
- return void 0;
283
- }
284
- async function detectCopilot() {
285
- const path = await getCommandPath("gh");
286
- if (!path) return void 0;
287
- try {
288
- await execa("gh", ["extension", "list"], { timeout: 5e3 });
289
- return { name: "copilot", path };
290
- } catch {
291
- return void 0;
292
- }
293
- }
294
- async function detectTools() {
295
- const detectors = [
296
- detectOpenCode,
297
- detectClaude,
298
- detectTrae,
299
- detectCursor,
300
- detectCopilot
301
- ];
302
- const results = await Promise.allSettled(detectors.map((d) => d()));
303
- const tools = [];
304
- for (const result of results) {
305
- if (result.status === "fulfilled" && result.value) {
306
- tools.push(result.value);
307
- }
308
- }
309
- return tools;
310
- }
311
- function getToolDisplayName(tool) {
312
- const names = {
313
- opencode: "OpenCode",
314
- claude: "Claude Code",
315
- trae: "Trae",
316
- cursor: "Cursor",
317
- copilot: "GitHub Copilot"
318
- };
319
- return names[tool];
320
- }
321
- function getAllTools() {
322
- return ["opencode", "claude", "trae", "cursor", "copilot"];
323
- }
324
-
325
- // src/core/manifest.ts
326
- import { existsSync as existsSync3, readFileSync } from "fs";
327
- import { join as join3 } from "path";
328
- function readManifest(configRoot) {
329
- const manifestPath = getManifestPath(configRoot);
330
- if (existsSync3(manifestPath)) {
331
- try {
332
- const content = readFileSync(manifestPath, "utf-8");
333
- return JSON.parse(content);
334
- } catch {
335
- }
336
- }
337
- return null;
338
- }
339
- function readAllManifests(configRoot) {
340
- const manifests = [];
341
- const plugins = getAllPlugins();
342
- const standardPath = getManifestPath(configRoot);
343
- if (existsSync3(standardPath)) {
344
- try {
345
- const content = readFileSync(standardPath, "utf-8");
346
- manifests.push(JSON.parse(content));
347
- } catch {
348
- }
349
- }
350
- for (const plugin of plugins) {
351
- const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
352
- if (existsSync3(pluginManifestPath)) {
353
- try {
354
- const content = readFileSync(pluginManifestPath, "utf-8");
355
- manifests.push(JSON.parse(content));
356
- } catch {
357
- }
358
- }
359
- }
360
- return manifests;
361
- }
362
- function scanInstalled() {
363
- const plugins = getAllPlugins();
364
- const installed = [];
365
- const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
366
- const levels = ["project", "global"];
367
- for (const tool of tools) {
368
- for (const level of levels) {
369
- const configRoot = getConfigRoot(tool, level);
370
- const manifests = readAllManifests(configRoot);
371
- for (const manifest of manifests) {
372
- const plugin = plugins.find((p) => p.id === manifest.team);
373
- if (!plugin) continue;
374
- const alreadyAdded = installed.some(
375
- (p) => p.id === plugin.id && p.tool === tool && p.level === level
376
- );
377
- if (alreadyAdded) continue;
378
- installed.push({
379
- id: plugin.id,
380
- displayName: plugin.displayName,
381
- tool,
382
- level,
383
- skillsCount: manifest.installed_skills?.length || 0,
384
- agentsCount: manifest.installed_agents?.length || 0,
385
- installTime: manifest.install_time,
386
- configRoot
387
- });
388
- }
389
- }
390
- }
391
- return installed;
392
- }
393
148
 
394
149
  // src/utils/i18n.ts
395
150
  var zh_CN = {
@@ -454,6 +209,8 @@ var zh_CN = {
454
209
  error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
455
210
  error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
456
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}",
457
214
  quick_start: "\u542F\u52A8\u547D\u4EE4",
458
215
  quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
459
216
  skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
@@ -508,7 +265,55 @@ var zh_CN = {
508
265
  backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
509
266
  backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
510
267
  backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
511
- backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD"
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
+ skill_count_format: "{count} \u4E2A Skills",
287
+ result_success_format: "{count} \u6210\u529F",
288
+ result_failed_format: "{count} \u5931\u8D25",
289
+ uninstall_remove_label: "\u79FB\u9664",
290
+ uninstall_remove_file_format: "\u79FB\u9664 {count} \u4E2A\u6587\u4EF6",
291
+ uninstall_remove_dir_format: "{count} \u4E2A\u76EE\u5F55",
292
+ uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
293
+ uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
294
+ available_plugins: "\u53EF\u7528\u63D2\u4EF6",
295
+ available_skills: "\u53EF\u7528 Skills",
296
+ skill_total_count: "\u5171 {count} \u4E2A Skills",
297
+ wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
298
+ wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
299
+ update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
300
+ hint_select_move: "\u79FB\u52A8",
301
+ hint_select_space: "\u7A7A\u683C",
302
+ hint_select_check: "\u52FE\u9009",
303
+ hint_select_confirm: "\u786E\u8BA4",
304
+ hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
305
+ doctor_not_installed: "\u672A\u5B89\u88C5",
306
+ doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
307
+ doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
308
+ doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
309
+ doctor_not_exist: "\u4E0D\u5B58\u5728",
310
+ doctor_created: "\u5DF2\u521B\u5EFA {path}",
311
+ doctor_config_exists: "\u5B58\u5728",
312
+ doctor_config_not_exist: "\u4E0D\u5B58\u5728",
313
+ doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF, {fixes} \u9879\u5DF2\u4FEE\u590D",
314
+ doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF",
315
+ count_suffix: "\u4E2A",
316
+ try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
512
317
  };
513
318
  var en_US = {
514
319
  wizard_title: "CANNBot Install Helper",
@@ -572,6 +377,8 @@ var en_US = {
572
377
  error_tool_not_found: "No AI coding tool detected",
573
378
  error_plugin_not_found: "Plugin not found",
574
379
  error_repo_not_found: "Repository not found",
380
+ error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
381
+ error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
575
382
  quick_start: "Launch command",
576
383
  quick_start_hint: "Try: help me develop an Abs operator",
577
384
  skill_select_category: "Select Skill category",
@@ -626,14 +433,364 @@ var en_US = {
626
433
  backup_only_uninstall: "Uninstall only (no restore)",
627
434
  backup_select_action: "Please select an action",
628
435
  backup_restore_success: "Configuration restored",
629
- backup_created: "Backup created"
436
+ backup_created: "Backup created",
437
+ loading_skills_list: "Loading Skills list...",
438
+ loading_skills_list_complete: "Skills list loaded",
439
+ loading_plugin_data: "Loading plugin data...",
440
+ loading_plugin_data_complete: "Plugin data loaded",
441
+ loading_skills_list_failed: "Failed to load Skills list, using built-in data",
442
+ install_no_skill_selected: "No Skill selected",
443
+ install_to: "Install to",
444
+ start_to_use: "Start {tool} to use",
445
+ wizard_step_1_title: "[1/4] Select AI coding tool",
446
+ wizard_step_2_title: "[2/4] Select installation location",
447
+ wizard_step_3_title: "[3/4] Select plugins to install",
448
+ wizard_step_4_title: "[4/4] Confirm installation",
449
+ wizard_step_3_skill_title: "[3/4] Select Skill category",
450
+ wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
451
+ install_level_project: "Install to current directory (recommended, current project only)",
452
+ install_level_global: "Install globally (all projects)",
453
+ wizard_select_mode_title: "Select installation type",
454
+ skill_count_format: "{count} Skills",
455
+ result_success_format: "{count} succeeded",
456
+ result_failed_format: "{count} failed",
457
+ uninstall_remove_label: "Removed",
458
+ uninstall_remove_file_format: "Removed {count} files",
459
+ uninstall_remove_dir_format: "{count} directories",
460
+ uninstall_clean_empty_dir: "Cleaned empty directory",
461
+ uninstall_skill_not_installed: "Not installed",
462
+ available_plugins: "Available plugins",
463
+ available_skills: "Available Skills",
464
+ skill_total_count: "Total {count} Skills",
465
+ wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
466
+ wizard_install_path: "Install path",
467
+ update_install_hint: "Use {cmd} to install plugins",
468
+ hint_select_move: "Navigate",
469
+ hint_select_space: "Space",
470
+ hint_select_check: "Toggle",
471
+ hint_select_confirm: "Confirm",
472
+ hint_checkbox_suffix: "Press enter without selection to go back",
473
+ doctor_not_installed: "Not installed",
474
+ doctor_links_valid: "{count} symlinks all valid",
475
+ doctor_broken_links: "{count} broken links",
476
+ doctor_fixed: "Cleaned {count} broken links",
477
+ doctor_not_exist: "Does not exist",
478
+ doctor_created: "Created {path}",
479
+ doctor_config_exists: "Exists",
480
+ doctor_config_not_exist: "Does not exist",
481
+ doctor_result_with_fix: "{warnings} warnings, {errors} errors, {fixes} fixed",
482
+ doctor_result_without_fix: "{warnings} warnings, {errors} errors",
483
+ count_suffix: "",
484
+ try_prompt: "help me develop an Abs operator"
630
485
  };
631
486
  var currentLang = "zh_CN";
632
487
  function setLanguage(lang) {
633
488
  currentLang = lang;
634
489
  }
635
- function t(key) {
636
- return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
490
+ function t(key) {
491
+ return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
492
+ }
493
+
494
+ // src/utils/paths.ts
495
+ var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
496
+ var VALID_LEVELS = ["project", "global"];
497
+ function validateTool(tool) {
498
+ if (!VALID_TOOLS.includes(tool)) {
499
+ throw new Error(
500
+ t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
501
+ );
502
+ }
503
+ return tool;
504
+ }
505
+ function validateLevel(level) {
506
+ if (!VALID_LEVELS.includes(level)) {
507
+ throw new Error(
508
+ t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
509
+ );
510
+ }
511
+ return level;
512
+ }
513
+ function detectTraeVariant() {
514
+ const home = homedir();
515
+ if (existsSync(join(home, ".trae-cn"))) return "ide";
516
+ if (existsSync(join(home, ".marscode"))) return "plugin";
517
+ if (existsSync(join(home, ".traecli"))) return "cli";
518
+ return "unknown";
519
+ }
520
+ function getConfigRoot(tool, level, base) {
521
+ const home = homedir();
522
+ if (level === "global") {
523
+ switch (tool) {
524
+ case "opencode":
525
+ return join(home, ".config", "opencode");
526
+ case "claude":
527
+ return join(home, ".claude");
528
+ case "trae": {
529
+ const variant = detectTraeVariant();
530
+ switch (variant) {
531
+ case "plugin":
532
+ return join(home, ".marscode");
533
+ case "cli":
534
+ return join(home, ".traecli");
535
+ default:
536
+ return join(home, ".trae-cn");
537
+ }
538
+ }
539
+ case "cursor":
540
+ return join(home, ".cursor");
541
+ case "copilot":
542
+ return join(home, ".copilot");
543
+ default:
544
+ throw new Error(
545
+ t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
546
+ );
547
+ }
548
+ }
549
+ const baseDir = base || process.cwd();
550
+ switch (tool) {
551
+ case "opencode":
552
+ return join(baseDir, ".opencode");
553
+ case "claude":
554
+ return join(baseDir, ".claude");
555
+ case "trae": {
556
+ const variant = detectTraeVariant();
557
+ switch (variant) {
558
+ case "plugin":
559
+ return join(baseDir, ".marscode");
560
+ case "cli":
561
+ return join(baseDir, ".traecli");
562
+ default:
563
+ return join(baseDir, ".trae");
564
+ }
565
+ }
566
+ case "cursor":
567
+ return join(baseDir, ".cursor");
568
+ case "copilot":
569
+ return join(baseDir, ".github");
570
+ default:
571
+ throw new Error(
572
+ t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
573
+ );
574
+ }
575
+ }
576
+ function getConfigFileName(tool) {
577
+ return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
578
+ }
579
+ function getSkillsDir(configRoot) {
580
+ return join(configRoot, "skills");
581
+ }
582
+ function getAgentsDir(configRoot) {
583
+ return join(configRoot, "agents");
584
+ }
585
+ function getManifestPath(configRoot) {
586
+ return join(configRoot, "cannbot-manifest.json");
587
+ }
588
+ function getCannbotConfigDir() {
589
+ return join(homedir(), ".cannbot");
590
+ }
591
+ function getCannbotRepoPath() {
592
+ return join(getCannbotConfigDir(), "repo");
593
+ }
594
+
595
+ // src/core/detector.ts
596
+ async function getCommandVersion(cmd, args = ["--version"]) {
597
+ try {
598
+ const result = await execa(cmd, args, { timeout: 5e3 });
599
+ const output = result.stdout.trim();
600
+ const match = output.match(/(\d+\.\d+\.\d+)/);
601
+ return match ? match[1] : output.split("\n")[0];
602
+ } catch {
603
+ return void 0;
604
+ }
605
+ }
606
+ async function getCommandPath(cmd) {
607
+ try {
608
+ const result = await execa("which", [cmd], { timeout: 5e3 });
609
+ return result.stdout.trim();
610
+ } catch {
611
+ return void 0;
612
+ }
613
+ }
614
+ async function detectOpenCode() {
615
+ const path = await getCommandPath("opencode");
616
+ if (!path) return void 0;
617
+ const version = await getCommandVersion("opencode");
618
+ return { name: "opencode", version, path };
619
+ }
620
+ async function detectClaude() {
621
+ const path = await getCommandPath("claude");
622
+ if (!path) return void 0;
623
+ const version = await getCommandVersion("claude");
624
+ return { name: "claude", version, path };
625
+ }
626
+ async function detectTrae() {
627
+ const home = homedir2();
628
+ const variant = detectTraeVariant();
629
+ let path;
630
+ if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
631
+ path = join2(home, ".trae-cn");
632
+ } else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
633
+ path = join2(home, ".marscode");
634
+ } else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
635
+ path = join2(home, ".traecli");
636
+ } else if (variant === "unknown") {
637
+ const cmdPath = await getCommandPath("trae");
638
+ if (cmdPath) path = cmdPath;
639
+ }
640
+ if (!path) return void 0;
641
+ return { name: "trae", version: variant, path };
642
+ }
643
+ async function detectCursor() {
644
+ const path = await getCommandPath("cursor");
645
+ if (path) {
646
+ const version = await getCommandVersion("cursor");
647
+ return { name: "cursor", version, path };
648
+ }
649
+ if (process.platform === "darwin") {
650
+ const appPath = "/Applications/Cursor.app";
651
+ if (existsSync2(appPath)) {
652
+ return { name: "cursor", path: appPath };
653
+ }
654
+ }
655
+ return void 0;
656
+ }
657
+ async function detectCopilot() {
658
+ const path = await getCommandPath("gh");
659
+ if (!path) return void 0;
660
+ try {
661
+ await execa("gh", ["extension", "list"], { timeout: 5e3 });
662
+ return { name: "copilot", path };
663
+ } catch {
664
+ return void 0;
665
+ }
666
+ }
667
+ async function detectTools() {
668
+ const detectors = [
669
+ detectOpenCode,
670
+ detectClaude,
671
+ detectTrae,
672
+ detectCursor,
673
+ detectCopilot
674
+ ];
675
+ const results = await Promise.allSettled(detectors.map((d) => d()));
676
+ const tools = [];
677
+ for (const result of results) {
678
+ if (result.status === "fulfilled" && result.value) {
679
+ tools.push(result.value);
680
+ }
681
+ }
682
+ return tools;
683
+ }
684
+ function getToolDisplayName(tool) {
685
+ const names = {
686
+ opencode: "OpenCode",
687
+ claude: "Claude Code",
688
+ trae: "Trae",
689
+ cursor: "Cursor",
690
+ copilot: "GitHub Copilot"
691
+ };
692
+ return names[tool];
693
+ }
694
+ function getAllTools() {
695
+ return ["opencode", "claude", "trae", "cursor", "copilot"];
696
+ }
697
+
698
+ // src/core/manifest.ts
699
+ import { existsSync as existsSync3, readFileSync } from "fs";
700
+ import { join as join3 } from "path";
701
+ function readManifest(configRoot) {
702
+ const manifestPath = getManifestPath(configRoot);
703
+ if (existsSync3(manifestPath)) {
704
+ try {
705
+ const content = readFileSync(manifestPath, "utf-8");
706
+ return JSON.parse(content);
707
+ } catch {
708
+ }
709
+ }
710
+ return null;
711
+ }
712
+ function readAllManifests(configRoot) {
713
+ const manifests = [];
714
+ const plugins = getAllPlugins();
715
+ const standardPath = getManifestPath(configRoot);
716
+ if (existsSync3(standardPath)) {
717
+ try {
718
+ const content = readFileSync(standardPath, "utf-8");
719
+ manifests.push(JSON.parse(content));
720
+ } catch {
721
+ }
722
+ }
723
+ for (const plugin of plugins) {
724
+ const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
725
+ if (existsSync3(pluginManifestPath)) {
726
+ try {
727
+ const content = readFileSync(pluginManifestPath, "utf-8");
728
+ manifests.push(JSON.parse(content));
729
+ } catch {
730
+ }
731
+ }
732
+ }
733
+ return manifests;
734
+ }
735
+ function scanInstalled() {
736
+ const plugins = getAllPlugins();
737
+ const installed = [];
738
+ const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
739
+ const levels = ["project", "global"];
740
+ for (const tool of tools) {
741
+ for (const level of levels) {
742
+ const configRoot = getConfigRoot(tool, level);
743
+ const manifests = readAllManifests(configRoot);
744
+ for (const manifest of manifests) {
745
+ const plugin = plugins.find((p) => p.id === manifest.team);
746
+ if (!plugin) continue;
747
+ if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
748
+ continue;
749
+ }
750
+ const alreadyAdded = installed.some(
751
+ (p) => p.id === plugin.id && p.tool === tool && p.level === level
752
+ );
753
+ if (alreadyAdded) continue;
754
+ installed.push({
755
+ id: plugin.id,
756
+ displayName: plugin.displayName,
757
+ tool,
758
+ level,
759
+ skillsCount: manifest.installed_skills?.length || 0,
760
+ agentsCount: manifest.installed_agents?.length || 0,
761
+ installTime: manifest.install_time,
762
+ configRoot
763
+ });
764
+ }
765
+ }
766
+ }
767
+ return installed;
768
+ }
769
+ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
770
+ const skillsDir = join3(configRoot, "skills");
771
+ const agentsDir = join3(configRoot, "agents");
772
+ if (manifest.installed_skills && manifest.installed_skills.length > 0) {
773
+ const hasAnySkill = manifest.installed_skills.some((skillId) => {
774
+ const skillPath = join3(skillsDir, skillId);
775
+ return existsSync3(skillPath);
776
+ });
777
+ if (!hasAnySkill) return false;
778
+ }
779
+ if (manifest.installed_agents && manifest.installed_agents.length > 0) {
780
+ const hasAnyAgent = manifest.installed_agents.some((agentId) => {
781
+ const agentPath = join3(agentsDir, agentId);
782
+ return existsSync3(agentPath);
783
+ });
784
+ if (!hasAnyAgent) return false;
785
+ }
786
+ let agentsMdPath;
787
+ if (tool === "opencode" && level === "project") {
788
+ agentsMdPath = join3(configRoot, "..", "AGENTS.md");
789
+ } else {
790
+ agentsMdPath = join3(configRoot, "AGENTS.md");
791
+ }
792
+ if (!existsSync3(agentsMdPath)) return false;
793
+ return true;
637
794
  }
638
795
 
639
796
  // src/utils/logger.ts
@@ -690,10 +847,10 @@ function printBoxTitle(title, width = 65) {
690
847
  }
691
848
  function showOperationHints(isCheckbox = false) {
692
849
  if (isCheckbox) {
693
- 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
850
+ console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m${t("hint_select_space")}\x1B[0m ${t("hint_select_check")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}\uFF08${t("hint_checkbox_suffix")}\uFF09
694
851
  `);
695
852
  } else {
696
- console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4
853
+ console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
697
854
  `);
698
855
  }
699
856
  }
@@ -859,7 +1016,7 @@ async function runWizard() {
859
1016
  }
860
1017
  }
861
1018
  async function stepTool(detectedTools, lastTool) {
862
- printBoxTitle("[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177");
1019
+ printBoxTitle(t("wizard_step_1_title"));
863
1020
  if (detectedTools.length === 0) {
864
1021
  logger.error(t("error_tool_not_found"));
865
1022
  const choices2 = [
@@ -934,10 +1091,10 @@ async function stepToolManual(lastTool) {
934
1091
  return result;
935
1092
  }
936
1093
  async function stepLevel(lastLevel) {
937
- printBoxTitle("[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E");
1094
+ printBoxTitle(t("wizard_step_2_title"));
938
1095
  const choices = [
939
- { name: "> project \u2014 \u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09", value: "project" },
940
- { name: "> global \u2014 \u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09", value: "global" },
1096
+ { name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
1097
+ { name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
941
1098
  new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
942
1099
  { name: "<- " + t("wizard_back"), value: BACK },
943
1100
  { name: "x " + t("wizard_cancel"), value: CANCEL }
@@ -955,7 +1112,7 @@ async function stepLevel(lastLevel) {
955
1112
  return result;
956
1113
  }
957
1114
  async function stepPlugins(tool, level) {
958
- printBoxTitle("[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6");
1115
+ printBoxTitle(t("wizard_step_3_title"));
959
1116
  const plugins = getAllPlugins();
960
1117
  const configRoot = getConfigRoot(tool, level);
961
1118
  const manifests = readAllManifests(configRoot);
@@ -996,14 +1153,14 @@ async function stepPlugins(tool, level) {
996
1153
  return pluginIds;
997
1154
  }
998
1155
  async function stepConfirm(tool, level, plugins) {
999
- printBoxTitle("[4/4] \u786E\u8BA4\u5B89\u88C5");
1156
+ printBoxTitle(t("wizard_step_4_title"));
1000
1157
  const allPlugins = getAllPlugins();
1001
1158
  const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
1002
1159
  const displayPath = getConfigRoot(tool, level);
1003
1160
  const toolName = getToolDisplayName(tool);
1004
1161
  const levelText = level === "project" ? "project" : "global";
1005
- 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`);
1006
- logger.info(`\u5B89\u88C5\u8DEF\u5F84: ${chalk2.cyan(displayPath)}`);
1162
+ logger.info(t("wizard_confirm_install_format").replace("{count}", chalk2.bold(String(plugins.length))).replace("{tool}", chalk2.green(toolName)).replace("{level}", chalk2.cyan(levelText)));
1163
+ logger.info(`${t("wizard_install_path")}: ${chalk2.cyan(displayPath)}`);
1007
1164
  for (const plugin of selectedPlugins) {
1008
1165
  if (plugin) {
1009
1166
  logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
@@ -1030,7 +1187,7 @@ import { execa as execa2 } from "execa";
1030
1187
 
1031
1188
  // src/core/scanner.ts
1032
1189
  import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1033
- import { join as join5 } from "path";
1190
+ import { join as join5, dirname } from "path";
1034
1191
  import { parse as parseYaml } from "yaml";
1035
1192
  var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
1036
1193
  "node_modules",
@@ -1168,8 +1325,7 @@ function writeScanCache(cache) {
1168
1325
  }
1169
1326
  }
1170
1327
  function getCachePath() {
1171
- const homeDir = process.env.HOME || process.env.USERPROFILE || "/tmp";
1172
- return join5(homeDir, ".cannbot", "scan-cache.json");
1328
+ return join5(getCannbotConfigDir(), "scan-cache.json");
1173
1329
  }
1174
1330
  function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1175
1331
  const selfSkillMd = join5(dirPath, "SKILL.md");
@@ -1638,7 +1794,6 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1638
1794
  files.push(repoLinkInConfig);
1639
1795
  }
1640
1796
  }
1641
- directories.push(configRoot);
1642
1797
  return {
1643
1798
  pluginId,
1644
1799
  displayName,
@@ -1715,11 +1870,17 @@ function removeSkillsFromRecord(skillIds, tool, level, installPath) {
1715
1870
  }
1716
1871
  function getInstalledSkills(tool, level, installPath) {
1717
1872
  const record = readSkillRecord();
1718
- return record[tool]?.[level]?.[installPath]?.skills || [];
1873
+ const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
1874
+ const configRoot = getConfigRoot(tool, level);
1875
+ const skillsDir = join7(configRoot, "skills");
1876
+ return recordedSkills.filter((skillId) => {
1877
+ const skillPath = join7(skillsDir, skillId);
1878
+ return existsSync7(skillPath) || isSymlink(skillPath);
1879
+ });
1719
1880
  }
1720
1881
 
1721
1882
  // src/core/backup.ts
1722
- import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
1883
+ import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
1723
1884
  import { join as join8 } from "path";
1724
1885
  function getAgentsFileName(tool) {
1725
1886
  return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
@@ -1777,7 +1938,7 @@ function findBackups(configRoot) {
1777
1938
  if (!existsSync8(configRoot)) {
1778
1939
  return [];
1779
1940
  }
1780
- const files = readdirSync3(configRoot);
1941
+ const files = readdirSync2(configRoot);
1781
1942
  const backups = [];
1782
1943
  for (const file of files) {
1783
1944
  if (file.includes(".cannbot-backup.")) {
@@ -2016,7 +2177,7 @@ async function installPlugin(opts) {
2016
2177
  }
2017
2178
 
2018
2179
  // src/core/skill-installer.ts
2019
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
2180
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
2020
2181
  import { join as join10 } from "path";
2021
2182
  import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
2022
2183
  import chalk4 from "chalk";
@@ -2071,10 +2232,10 @@ async function uninstallSkills(skillIds, tool, level) {
2071
2232
  unlinkSync3(targetPath);
2072
2233
  results.push({ skillId, success: true });
2073
2234
  removedIds.push(skillId);
2074
- logger.step(` \u79FB\u9664: ${skillId}`);
2235
+ logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
2075
2236
  } else {
2076
2237
  results.push({ skillId, success: false, error: "Not installed" });
2077
- logger.warn(`${skillId} \u672A\u5B89\u88C5`);
2238
+ logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
2078
2239
  }
2079
2240
  } catch (error) {
2080
2241
  results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
@@ -2085,27 +2246,17 @@ async function uninstallSkills(skillIds, tool, level) {
2085
2246
  }
2086
2247
  if (existsSync10(skillsDir)) {
2087
2248
  try {
2088
- const entries = readdirSync4(skillsDir);
2249
+ const entries = readdirSync3(skillsDir);
2089
2250
  if (entries.length === 0) {
2090
2251
  rmdirSync(skillsDir);
2091
- logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: skills/`);
2092
- }
2093
- } catch {
2094
- }
2095
- }
2096
- if (existsSync10(configRoot)) {
2097
- try {
2098
- const entries = readdirSync4(configRoot);
2099
- if (entries.length === 0) {
2100
- rmdirSync(configRoot);
2101
- logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
2252
+ logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
2102
2253
  }
2103
2254
  } catch {
2104
2255
  }
2105
2256
  }
2106
2257
  return results;
2107
2258
  }
2108
- async function interactiveSkillSelect() {
2259
+ async function interactiveSkillSelect(tool, level) {
2109
2260
  const BACK3 = "__back__";
2110
2261
  const CANCEL3 = "__cancel__";
2111
2262
  const categories = getAllCategories();
@@ -2114,7 +2265,7 @@ async function interactiveSkillSelect() {
2114
2265
  while (true) {
2115
2266
  switch (step) {
2116
2267
  case 0: {
2117
- printBoxTitle("[3/4] \u9009\u62E9 Skill \u7C7B\u522B");
2268
+ printBoxTitle(t("wizard_step_3_skill_title"));
2118
2269
  const categoryChoices = [
2119
2270
  ...categories.map((cat) => ({
2120
2271
  name: `> ${cat.name} (${cat.skills.length} skills)`,
@@ -2140,16 +2291,9 @@ async function interactiveSkillSelect() {
2140
2291
  case 1: {
2141
2292
  const category = categories.find((c) => c.id === selectedCategoryId);
2142
2293
  if (!category) return [];
2143
- printBoxTitle(`[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 ${category.name}`);
2144
- const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
2145
- const levels = ["project", "global"];
2146
- const installedSkills = [];
2147
- for (const tk of tools) {
2148
- for (const l of levels) {
2149
- const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
2150
- installedSkills.push(...getInstalledSkills(tk, l, path));
2151
- }
2152
- }
2294
+ printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
2295
+ const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
2296
+ const installedSkills = getInstalledSkills(tool, level, installPath);
2153
2297
  const installedSet = new Set(installedSkills);
2154
2298
  const skillChoices = category.skills.map((skill) => {
2155
2299
  const isInstalled = installedSet.has(skill.id);
@@ -2214,7 +2358,7 @@ function listAllSkills() {
2214
2358
  console.log(table.toString());
2215
2359
  }
2216
2360
  console.log();
2217
- console.log(chalk4.dim(` \u5171 ${getAllSkills().length} \u4E2A Skills`));
2361
+ console.log(chalk4.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
2218
2362
  console.log();
2219
2363
  }
2220
2364
  function isSymlink2(path) {
@@ -2251,7 +2395,8 @@ function printPluginList(plugins, installed) {
2251
2395
  ]);
2252
2396
  });
2253
2397
  console.log();
2254
- console.log(chalk5.bold(` ${t("list_title")} (${plugins.length} \u4E2A)`));
2398
+ const countSuffix = t("count_suffix");
2399
+ console.log(chalk5.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
2255
2400
  console.log(table.toString());
2256
2401
  console.log();
2257
2402
  }
@@ -2284,15 +2429,16 @@ function printEnhancedSummary(results, tool, configRoot) {
2284
2429
  return;
2285
2430
  }
2286
2431
  const toolName = getToolDisplayName(tool);
2432
+ const countSuffix = t("count_suffix");
2287
2433
  const lines = [
2288
2434
  chalk5.bold(` ${t("install_done")}!`),
2289
2435
  "",
2290
2436
  ` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
2291
- ` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills} \u4E2A ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents} \u4E2A`,
2437
+ ` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
2292
2438
  "",
2293
2439
  chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
2294
2440
  ` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
2295
- ` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50")}`,
2441
+ ` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green(t("try_prompt"))}`,
2296
2442
  ` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
2297
2443
  ` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
2298
2444
  ""
@@ -2330,14 +2476,14 @@ async function initCommand() {
2330
2476
  return;
2331
2477
  }
2332
2478
  printBanner(t("wizard_title"));
2333
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2479
+ const spinner = createSpinner(t("loading_skills_list"));
2334
2480
  spinner.start();
2335
2481
  try {
2336
2482
  const repoManager = createRepositoryManager();
2337
2483
  await repoManager.ensureRepoAndScan();
2338
- spinner.succeed("Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210");
2484
+ spinner.succeed(t("loading_skills_list_complete"));
2339
2485
  } catch {
2340
- spinner.warn("Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E");
2486
+ spinner.warn(t("loading_skills_list_failed"));
2341
2487
  }
2342
2488
  let step = 0;
2343
2489
  let mode = "plugin";
@@ -2371,7 +2517,7 @@ async function initCommand() {
2371
2517
  }
2372
2518
  }
2373
2519
  async function selectMode() {
2374
- printBoxTitle("\u9009\u62E9\u5B89\u88C5\u7C7B\u578B");
2520
+ printBoxTitle(t("wizard_select_mode_title"));
2375
2521
  const CANCEL3 = "__cancel__";
2376
2522
  const choices = [
2377
2523
  {
@@ -2403,10 +2549,10 @@ async function pluginInstallFlow() {
2403
2549
  return "done";
2404
2550
  }
2405
2551
  const repoManager = createRepositoryManager();
2406
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...");
2552
+ const spinner = createSpinner(t("loading_plugin_data"));
2407
2553
  spinner.start();
2408
2554
  const repoPath = await repoManager.ensureRepo();
2409
- spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
2555
+ spinner.succeed(t("loading_plugin_data_complete"));
2410
2556
  const allPlugins = getAllPlugins();
2411
2557
  const selectedPlugins = answers.plugins.map(
2412
2558
  (id) => allPlugins.find((p) => p.id === id)
@@ -2482,7 +2628,7 @@ async function skillInstallFlow() {
2482
2628
  break;
2483
2629
  }
2484
2630
  case 2: {
2485
- const selectedSkills = await interactiveSkillSelect();
2631
+ const selectedSkills = await interactiveSkillSelect(tool, level);
2486
2632
  if (selectedSkills === "back") {
2487
2633
  step = 1;
2488
2634
  break;
@@ -2492,15 +2638,15 @@ async function skillInstallFlow() {
2492
2638
  return "done";
2493
2639
  }
2494
2640
  if (selectedSkills.length === 0) {
2495
- logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Skill");
2641
+ logger.info(t("install_no_skill_selected"));
2496
2642
  return "done";
2497
2643
  }
2498
2644
  const repoManager = createRepositoryManager();
2499
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...");
2645
+ const spinner = createSpinner(t("loading_plugin_data"));
2500
2646
  spinner.start();
2501
2647
  const repoPath = await repoManager.ensureRepo();
2502
- spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
2503
- logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)} \u4E2A Skills...`);
2648
+ spinner.succeed(t("loading_plugin_data_complete"));
2649
+ logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk6.bold(String(selectedSkills.length)))}...`);
2504
2650
  const results = await installSkills(selectedSkills, tool, level, repoPath);
2505
2651
  let successCount = 0;
2506
2652
  let failCount = 0;
@@ -2518,10 +2664,10 @@ async function skillInstallFlow() {
2518
2664
  }
2519
2665
  const configRoot = getConfigRoot(tool, level);
2520
2666
  logger.blank();
2521
- logger.success(`${t("skill_install_done")}: ${chalk6.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk6.red(failCount + " \u5931\u8D25") : chalk6.dim(failCount + " \u5931\u8D25")}`);
2667
+ logger.success(`${t("skill_install_done")}: ${chalk6.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk6.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk6.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
2522
2668
  logger.blank();
2523
- logger.info(`\u5B89\u88C5\u5230: ${chalk6.cyan(configRoot + "/skills/")}`);
2524
- logger.info(`\u542F\u52A8 ${chalk6.green(tool)} \u5373\u53EF\u4F7F\u7528`);
2669
+ logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
2670
+ logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
2525
2671
  logger.blank();
2526
2672
  return "done";
2527
2673
  }
@@ -2544,7 +2690,7 @@ async function listCommand() {
2544
2690
 
2545
2691
  // src/commands/doctor.ts
2546
2692
  import chalk7 from "chalk";
2547
- import { existsSync as existsSync12, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2693
+ import { existsSync as existsSync12, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2548
2694
  import { join as join12 } from "path";
2549
2695
  async function doctorCommand(options = {}) {
2550
2696
  console.log();
@@ -2564,7 +2710,7 @@ async function doctorCommand(options = {}) {
2564
2710
  chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
2565
2711
  );
2566
2712
  } else {
2567
- console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 \u672A\u5B89\u88C5`);
2713
+ console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
2568
2714
  }
2569
2715
  }
2570
2716
  console.log();
@@ -2579,7 +2725,7 @@ async function doctorCommand(options = {}) {
2579
2725
  chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
2580
2726
  );
2581
2727
  } else {
2582
- console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 \u672A\u5B89\u88C5`);
2728
+ console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
2583
2729
  }
2584
2730
  }
2585
2731
  console.log();
@@ -2591,45 +2737,45 @@ async function doctorCommand(options = {}) {
2591
2737
  if (existsSync12(skillsDir)) {
2592
2738
  const brokenLinks = checkBrokenLinks(skillsDir);
2593
2739
  if (brokenLinks === 0) {
2594
- const count = readdirSync5(skillsDir).length;
2595
- console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2740
+ const count = readdirSync4(skillsDir).length;
2741
+ console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
2596
2742
  } else {
2597
- console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2743
+ console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
2598
2744
  warnings++;
2599
2745
  if (options.fix) {
2600
2746
  const fixed = fixBrokenLinks(skillsDir);
2601
2747
  fixes += fixed;
2602
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2748
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
2603
2749
  }
2604
2750
  }
2605
2751
  } else {
2606
- console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 \u4E0D\u5B58\u5728`);
2752
+ console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
2607
2753
  if (options.fix) {
2608
2754
  mkdirSync6(skillsDir, { recursive: true });
2609
2755
  fixes++;
2610
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${skillsDir}`);
2756
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
2611
2757
  }
2612
2758
  }
2613
2759
  if (existsSync12(agentsDir)) {
2614
2760
  const brokenLinks = checkBrokenLinks(agentsDir);
2615
2761
  if (brokenLinks === 0) {
2616
- const count = readdirSync5(agentsDir).length;
2617
- console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2762
+ const count = readdirSync4(agentsDir).length;
2763
+ console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
2618
2764
  } else {
2619
- console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2765
+ console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
2620
2766
  warnings++;
2621
2767
  if (options.fix) {
2622
2768
  const fixed = fixBrokenLinks(agentsDir);
2623
2769
  fixes += fixed;
2624
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2770
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
2625
2771
  }
2626
2772
  }
2627
2773
  } else {
2628
- console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 \u4E0D\u5B58\u5728`);
2774
+ console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
2629
2775
  if (options.fix) {
2630
2776
  mkdirSync6(agentsDir, { recursive: true });
2631
2777
  fixes++;
2632
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${agentsDir}`);
2778
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
2633
2779
  }
2634
2780
  }
2635
2781
  console.log();
@@ -2637,19 +2783,19 @@ async function doctorCommand(options = {}) {
2637
2783
  const configFile = getConfigFileName(primaryTool);
2638
2784
  const configPath = join12(process.cwd(), configFile);
2639
2785
  if (existsSync12(configPath)) {
2640
- console.log(chalk7.green(" \u2713") + ` ${configFile} \u5B58\u5728`);
2786
+ console.log(chalk7.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
2641
2787
  } else {
2642
- console.log(chalk7.dim(" \u2014") + ` ${configFile} \u4E0D\u5B58\u5728`);
2788
+ console.log(chalk7.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
2643
2789
  }
2644
2790
  console.log();
2645
2791
  console.log(chalk7.dim(" " + "\u2500".repeat(46)));
2646
2792
  if (options.fix && fixes > 0) {
2647
2793
  console.log(
2648
- ` ${t("doctor_result")}: ${warnings} \u4E2A\u8B66\u544A, ${errors} \u4E2A\u9519\u8BEF, ${fixes} \u9879\u5DF2\u4FEE\u590D`
2794
+ ` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
2649
2795
  );
2650
2796
  } else {
2651
2797
  console.log(
2652
- ` ${t("doctor_result")}: ${warnings} \u4E2A\u8B66\u544A, ${errors} \u4E2A\u9519\u8BEF`
2798
+ ` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
2653
2799
  );
2654
2800
  }
2655
2801
  console.log();
@@ -2657,7 +2803,7 @@ async function doctorCommand(options = {}) {
2657
2803
  function checkBrokenLinks(dir) {
2658
2804
  let broken = 0;
2659
2805
  try {
2660
- const entries = readdirSync5(dir);
2806
+ const entries = readdirSync4(dir);
2661
2807
  for (const entry of entries) {
2662
2808
  const fullPath = join12(dir, entry);
2663
2809
  try {
@@ -2678,7 +2824,7 @@ function checkBrokenLinks(dir) {
2678
2824
  function fixBrokenLinks(dir) {
2679
2825
  let fixed = 0;
2680
2826
  try {
2681
- const entries = readdirSync5(dir);
2827
+ const entries = readdirSync4(dir);
2682
2828
  for (const entry of entries) {
2683
2829
  const fullPath = join12(dir, entry);
2684
2830
  try {
@@ -2739,46 +2885,47 @@ async function installCommand(names, options) {
2739
2885
  return;
2740
2886
  }
2741
2887
  if (names.length === 0) {
2742
- const scanSpinner2 = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2888
+ const scanSpinner2 = createSpinner(t("loading_skills_list"));
2743
2889
  scanSpinner2.start();
2744
2890
  try {
2745
2891
  const scanRepoManager = createRepositoryManager();
2746
2892
  await scanRepoManager.ensureRepoAndScan();
2747
- scanSpinner2.succeed("Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210");
2893
+ scanSpinner2.succeed(t("loading_skills_list_complete"));
2748
2894
  } catch {
2749
- scanSpinner2.warn("Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E");
2895
+ scanSpinner2.warn(t("loading_skills_list_failed"));
2750
2896
  }
2751
2897
  let tool2;
2752
2898
  if (options.tool) {
2753
- tool2 = options.tool;
2899
+ tool2 = validateTool(options.tool);
2754
2900
  } else {
2755
2901
  const detected = await selectToolWithDetection();
2756
- if (detected === "back") return;
2902
+ if (detected === "back" || detected === "cancel") return;
2757
2903
  tool2 = detected;
2758
2904
  }
2905
+ const level2 = options.level ? validateLevel(options.level) : "project";
2759
2906
  let step = 1;
2760
2907
  while (true) {
2761
2908
  switch (step) {
2762
2909
  case 1: {
2763
- const selectedSkills = await interactiveSkillSelect();
2910
+ const selectedSkills = await interactiveSkillSelect(tool2, level2);
2764
2911
  if (selectedSkills === "back") {
2765
2912
  const redetected = await selectToolWithDetection();
2766
- if (redetected === "back") return;
2913
+ if (redetected === "back" || redetected === "cancel") return;
2767
2914
  tool2 = redetected;
2768
2915
  break;
2769
2916
  }
2917
+ if (selectedSkills === "cancel") return;
2770
2918
  if (selectedSkills.length === 0) {
2771
- logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Skill");
2919
+ logger.info(t("install_no_skill_selected"));
2772
2920
  return;
2773
2921
  }
2774
2922
  names = selectedSkills;
2775
2923
  const repoManager = createRepositoryManager();
2776
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...");
2924
+ const spinner = createSpinner(t("loading_plugin_data"));
2777
2925
  spinner.start();
2778
2926
  const repoPath = await repoManager.ensureRepo();
2779
- spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
2780
- logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)} \u4E2A Skills...`);
2781
- const level2 = options.level || "project";
2927
+ spinner.succeed(t("loading_plugin_data_complete"));
2928
+ logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(names.length)))}...`);
2782
2929
  const results = await installSkills(names, tool2, level2, repoPath);
2783
2930
  let successCount = 0;
2784
2931
  let failCount = 0;
@@ -2796,24 +2943,24 @@ async function installCommand(names, options) {
2796
2943
  }
2797
2944
  const configRoot = getConfigRoot(tool2, level2);
2798
2945
  logger.blank();
2799
- logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
2946
+ logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
2800
2947
  logger.blank();
2801
- logger.info(`\u5B89\u88C5\u5230: ${chalk9.cyan(configRoot + "/skills/")}`);
2802
- logger.info(`\u542F\u52A8 ${chalk9.green(tool2)} \u5373\u53EF\u4F7F\u7528`);
2948
+ logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
2949
+ logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
2803
2950
  logger.blank();
2804
2951
  return;
2805
2952
  }
2806
2953
  }
2807
2954
  }
2808
2955
  }
2809
- const scanSpinner = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2956
+ const scanSpinner = createSpinner(t("loading_skills_list"));
2810
2957
  scanSpinner.start();
2811
2958
  try {
2812
2959
  const scanRepoManager = createRepositoryManager();
2813
2960
  await scanRepoManager.ensureRepoAndScan();
2814
- scanSpinner.succeed("Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210");
2961
+ scanSpinner.succeed(t("loading_skills_list_complete"));
2815
2962
  } catch {
2816
- scanSpinner.warn("Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E");
2963
+ scanSpinner.warn(t("loading_skills_list_failed"));
2817
2964
  }
2818
2965
  const plugins = [];
2819
2966
  const skills = [];
@@ -2830,22 +2977,22 @@ async function installCommand(names, options) {
2830
2977
  }
2831
2978
  logger.error(`${t("error_plugin_not_found")}: ${name}`);
2832
2979
  const allPlugins = getAllPlugins();
2833
- logger.info("\u53EF\u7528\u63D2\u4EF6:");
2980
+ logger.info(t("available_plugins") + ":");
2834
2981
  for (const p of allPlugins) {
2835
2982
  logger.step(` ${p.id} (${p.aliases.join(", ")})`);
2836
2983
  }
2837
- logger.info(`\u53EF\u7528 Skills: ${chalk9.cyan("install-helper install --list")}`);
2984
+ logger.info(`${t("available_skills")}: ${chalk9.cyan("install-helper install --list")}`);
2838
2985
  return;
2839
2986
  }
2840
2987
  let tool;
2841
2988
  if (options.tool) {
2842
- tool = options.tool;
2989
+ tool = validateTool(options.tool);
2843
2990
  } else {
2844
2991
  const detected = await selectToolWithDetection();
2845
- if (detected === "back") return;
2992
+ if (detected === "back" || detected === "cancel") return;
2846
2993
  tool = detected;
2847
2994
  }
2848
- const level = options.level || "project";
2995
+ const level = options.level ? validateLevel(options.level) : "project";
2849
2996
  if (plugins.length > 0) {
2850
2997
  const configRoot = getConfigRoot(tool, level);
2851
2998
  const manifests = readAllManifests(configRoot);
@@ -2873,10 +3020,10 @@ async function installCommand(names, options) {
2873
3020
  }
2874
3021
  if (pluginsToInstall.length > 0) {
2875
3022
  const repoManager = createRepositoryManager();
2876
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...");
3023
+ const spinner = createSpinner(t("loading_plugin_data"));
2877
3024
  spinner.start();
2878
3025
  const repoPath = await repoManager.ensureRepo();
2879
- spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
3026
+ spinner.succeed(t("loading_plugin_data_complete"));
2880
3027
  const allPlugins = getAllPlugins();
2881
3028
  const results = await installPlugins(
2882
3029
  pluginsToInstall,
@@ -2902,11 +3049,11 @@ async function installCommand(names, options) {
2902
3049
  }
2903
3050
  if (skills.length > 0) {
2904
3051
  const repoManager = createRepositoryManager();
2905
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...");
3052
+ const spinner = createSpinner(t("loading_plugin_data"));
2906
3053
  spinner.start();
2907
3054
  const repoPath = await repoManager.ensureRepo();
2908
- spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
2909
- logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)} \u4E2A Skills...`);
3055
+ spinner.succeed(t("loading_plugin_data_complete"));
3056
+ logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(skills.length)))}...`);
2910
3057
  const results = await installSkills(skills, tool, level, repoPath);
2911
3058
  let successCount = 0;
2912
3059
  let failCount = 0;
@@ -2923,10 +3070,10 @@ async function installCommand(names, options) {
2923
3070
  }
2924
3071
  }
2925
3072
  logger.blank();
2926
- logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
3073
+ logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
2927
3074
  logger.blank();
2928
- logger.info(`\u5B89\u88C5\u5230: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
2929
- logger.info(`\u542F\u52A8 ${chalk9.green(tool)} \u5373\u53EF\u4F7F\u7528`);
3075
+ logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
3076
+ logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
2930
3077
  logger.blank();
2931
3078
  }
2932
3079
  }
@@ -2958,11 +3105,11 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
2958
3105
  }
2959
3106
 
2960
3107
  // src/commands/uninstall.ts
2961
- import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
3108
+ import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
2962
3109
  import chalk10 from "chalk";
2963
3110
  async function uninstallCommand(names, options) {
2964
- const tool = options.tool || "opencode";
2965
- const level = options.level || "project";
3111
+ const tool = options.tool ? validateTool(options.tool) : "opencode";
3112
+ const level = options.level ? validateLevel(options.level) : "project";
2966
3113
  const plugins = [];
2967
3114
  const skills = [];
2968
3115
  for (const name of names) {
@@ -2995,21 +3142,25 @@ async function uninstallCommand(names, options) {
2995
3142
  if (existsSync13(filePath) || isSymlink3(filePath)) {
2996
3143
  unlinkSync5(filePath);
2997
3144
  const name = filePath.split("/").pop() || filePath;
2998
- logger.step(` \u79FB\u9664: ${name}`);
3145
+ logger.step(` ${t("uninstall_remove_label")}: ${name}`);
2999
3146
  removedFiles++;
3000
3147
  }
3001
3148
  } catch {
3002
3149
  }
3003
3150
  }
3151
+ const configRoot = getConfigRoot(record.tool, record.level);
3004
3152
  const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
3005
3153
  for (const dirPath of sortedDirs) {
3006
3154
  try {
3155
+ if (dirPath === configRoot) {
3156
+ continue;
3157
+ }
3007
3158
  if (existsSync13(dirPath)) {
3008
- const entries = readdirSync6(dirPath);
3159
+ const entries = readdirSync5(dirPath);
3009
3160
  if (entries.length === 0) {
3010
3161
  rmdirSync2(dirPath);
3011
3162
  const name = dirPath.split("/").pop() || dirPath;
3012
- logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${name}/`);
3163
+ logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
3013
3164
  removedDirs++;
3014
3165
  }
3015
3166
  }
@@ -3018,7 +3169,6 @@ async function uninstallCommand(names, options) {
3018
3169
  }
3019
3170
  deleteRecord(plugin.id);
3020
3171
  removeInstalledPlugin(plugin.id);
3021
- const configRoot = getConfigRoot(record.tool, record.level);
3022
3172
  const backups = findBackups(configRoot);
3023
3173
  const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
3024
3174
  if (otherBackups.length > 0) {
@@ -3038,13 +3188,13 @@ async function uninstallCommand(names, options) {
3038
3188
  }
3039
3189
  const totalRemoved = removedFiles + removedDirs;
3040
3190
  if (totalRemoved > 0) {
3041
- logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08\u79FB\u9664 ${removedFiles} \u4E2A\u6587\u4EF6\uFF0C${removedDirs} \u4E2A\u76EE\u5F55\uFF09`);
3191
+ logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08${t("uninstall_remove_file_format").replace("{count}", String(removedFiles))}\uFF0C${t("uninstall_remove_dir_format").replace("{count}", String(removedDirs))}\uFF09`);
3042
3192
  } else {
3043
3193
  logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
3044
3194
  }
3045
3195
  }
3046
3196
  if (skills.length > 0) {
3047
- logger.info(`${t("uninstall_skills_in_progress")} ${skills.length} \u4E2A Skills...`);
3197
+ logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
3048
3198
  const results = await uninstallSkills(skills, tool, level);
3049
3199
  let successCount = 0;
3050
3200
  let failCount = 0;
@@ -3056,7 +3206,7 @@ async function uninstallCommand(names, options) {
3056
3206
  }
3057
3207
  }
3058
3208
  logger.blank();
3059
- logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk10.red(failCount + " \u5931\u8D25") : chalk10.dim(failCount + " \u5931\u8D25")}`);
3209
+ logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
3060
3210
  logger.blank();
3061
3211
  }
3062
3212
  }
@@ -3073,13 +3223,13 @@ import chalk11 from "chalk";
3073
3223
  async function updateCommand(pluginNames, options) {
3074
3224
  let tool;
3075
3225
  if (options.tool) {
3076
- tool = options.tool;
3226
+ tool = validateTool(options.tool);
3077
3227
  } else {
3078
3228
  const detected = await selectToolWithDetection();
3079
- if (detected === "back") return;
3229
+ if (detected === "back" || detected === "cancel") return;
3080
3230
  tool = detected;
3081
3231
  }
3082
- const level = options.level || "project";
3232
+ const level = options.level ? validateLevel(options.level) : "project";
3083
3233
  let pluginsToUpdate = [];
3084
3234
  if (pluginNames.length === 0) {
3085
3235
  const configRoot = getConfigRoot(tool, level);
@@ -3087,7 +3237,7 @@ async function updateCommand(pluginNames, options) {
3087
3237
  pluginsToUpdate = manifests.map((m) => m.team);
3088
3238
  if (pluginsToUpdate.length === 0) {
3089
3239
  logger.info(t("update_no_plugins"));
3090
- logger.info(`\u4F7F\u7528 ${chalk11.cyan("install-helper install <plugin>")} \u5B89\u88C5\u63D2\u4EF6`);
3240
+ logger.info(t("update_install_hint").replace("{cmd}", chalk11.cyan("install-helper install <plugin>")));
3091
3241
  return;
3092
3242
  }
3093
3243
  } else {
@@ -3150,7 +3300,7 @@ async function updateCommand(pluginNames, options) {
3150
3300
 
3151
3301
  // src/commands/info.ts
3152
3302
  import chalk12 from "chalk";
3153
- import { existsSync as existsSync14, readdirSync as readdirSync7 } from "fs";
3303
+ import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
3154
3304
  import { join as join13 } from "path";
3155
3305
  async function infoCommand(pluginName) {
3156
3306
  const plugin = findPlugin(pluginName);
@@ -3181,7 +3331,7 @@ async function infoCommand(pluginName) {
3181
3331
  }
3182
3332
  const agentsDir = join13(repoPath, plugin.dir, "agents");
3183
3333
  if (existsSync14(agentsDir)) {
3184
- const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
3334
+ const agentFiles = readdirSync6(agentsDir).filter((f) => f.endsWith(".md"));
3185
3335
  if (agentFiles.length > 0) {
3186
3336
  console.log();
3187
3337
  console.log(` ${chalk12.bold("Agents:")}`);
@@ -3238,7 +3388,7 @@ function createCLI() {
3238
3388
  setLanguage(config.language);
3239
3389
  }
3240
3390
  const program2 = new Command();
3241
- program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.8");
3391
+ program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("1.0.2");
3242
3392
  program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
3243
3393
  await initCommand();
3244
3394
  });
@@ -3275,7 +3425,7 @@ function createCLI() {
3275
3425
  // src/index.ts
3276
3426
  process.on("uncaughtException", (error) => {
3277
3427
  if (error.name === "ExitPromptError") {
3278
- console.log("\n\u5DF2\u53D6\u6D88\u5B89\u88C5");
3428
+ console.log("\n" + t("init_cancelled"));
3279
3429
  process.exit(0);
3280
3430
  }
3281
3431
  throw error;