@cannbot-ai/install-helper 0.0.2-beta.14 → 0.0.2-beta.16

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
@@ -12,108 +12,45 @@ import { select, checkbox, Separator } from "@inquirer/prompts";
12
12
  import chalk2 from "chalk";
13
13
 
14
14
  // src/core/registry.ts
15
- var PLUGIN_REGISTRY = [
16
- {
17
- id: "ops-direct-invoke",
18
- dir: "plugins-official/ops-direct-invoke",
19
- displayName: "AscendC Kernel \u76F4\u8C03",
20
- script: "init.sh",
21
- aliases: ["ops-direct", "ascendc-direct", "direct", "kernel"],
22
- skills: 16,
23
- agents: 3,
24
- description: "Ascend C Kernel <<<>>> \u76F4\u8C03\u7B97\u5B50\u5F00\u53D1\u5168\u6D41\u7A0B"
25
- },
26
- {
27
- id: "ops-direct-invoke-flash",
28
- dir: "plugins-official/ops-direct-invoke-flash",
29
- displayName: "AscendC Kernel \u4ECE\u96F6\u6784\u5EFA",
30
- script: "init.sh",
31
- aliases: ["flash", "kernel-flash"],
32
- skills: 1,
33
- agents: 1,
34
- description: "Ascend C / Ascend950 Reg API \u6838\u51FD\u6570\u4ECE\u96F6\u6784\u5EFA"
35
- },
36
- {
37
- id: "ops-registry-invoke",
38
- dir: "plugins-official/ops-registry-invoke",
39
- displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
40
- script: "init.sh",
41
- aliases: ["ops-registry", "ascendc-registry", "registry"],
42
- skills: 12,
43
- agents: 4,
44
- description: "Ascend C \u7B97\u5B50\u6CE8\u518C\u8C03\u7528\u5F00\u53D1\u6D41\u7A0B"
45
- },
46
- {
47
- id: "pypto-op-orchestrator",
48
- dir: "plugins-official/pypto-op-orchestrator",
49
- displayName: "PyPTO \u7B97\u5B50",
50
- script: "init.sh",
51
- aliases: ["pypto", "pytorch"],
52
- skills: 8,
53
- agents: 3,
54
- description: "PyPTO \u7B97\u5B50\u7AEF\u5230\u7AEF\u5F00\u53D1\u7F16\u6392"
55
- },
56
- {
57
- id: "triton-op-generator",
58
- dir: "plugins-official/triton-op-generator",
59
- displayName: "Triton \u7B97\u5B50\u751F\u6210",
60
- script: "install.sh",
61
- aliases: ["triton"],
62
- skills: 6,
63
- agents: 0,
64
- description: "Triton-Ascend \u7B97\u5B50\u4EE3\u7801\u751F\u6210\u4E0E\u4F18\u5316"
65
- },
66
- {
67
- id: "tilelang-op-orchestrator",
68
- dir: "plugins-official/tilelang-op-orchestrator",
69
- displayName: "TileLang \u7B97\u5B50",
70
- script: "init.sh",
71
- aliases: ["tilelang"],
72
- skills: 9,
73
- agents: 3,
74
- description: "TileLang \u7B97\u5B50\u5F00\u53D1\u6D41\u7A0B"
75
- },
76
- {
77
- id: "model-infer-optimize",
78
- dir: "plugins-official/model-infer-optimize",
79
- displayName: "NPU \u63A8\u7406\u4F18\u5316",
80
- script: "init.sh",
81
- aliases: ["model-infer", "infer", "inference"],
82
- skills: 11,
83
- agents: 3,
84
- description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316"
85
- },
86
- {
87
- id: "catlass-op-generator",
88
- dir: "plugins-official/catlass-op-generator",
89
- displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
90
- script: "init.sh",
91
- aliases: ["catlass"],
92
- skills: 10,
93
- agents: 3,
94
- description: "Catlass \u7B97\u5B50\u76F4\u8C03\u5F00\u53D1"
95
- },
96
- {
97
- id: "ops-code-reviewer",
98
- dir: "plugins-official/ops-code-reviewer",
99
- displayName: "\u4EE3\u7801\u68C0\u89C6",
100
- script: "init.sh",
101
- aliases: ["code-review", "reviewer", "review"],
102
- skills: 5,
103
- agents: 1,
104
- description: "\u4EE3\u7801\u68C0\u89C6\u4E0E\u89C4\u8303\u68C0\u67E5"
105
- },
106
- {
107
- id: "torch-compile",
108
- dir: "plugins-official/torch-compile",
109
- displayName: "torch.compile \u56FE\u6A21\u5F0F",
110
- script: "init.sh",
111
- aliases: ["torch", "compile", "graph"],
112
- skills: 6,
113
- agents: 1,
114
- description: "PyTorch torch.compile \u56FE\u6A21\u5F0F\u9002\u914D"
15
+ import { readdirSync, readFileSync } from "fs";
16
+ import { join, dirname } from "path";
17
+ import { fileURLToPath } from "url";
18
+ import { parse as parseYaml } from "yaml";
19
+ var __filename = fileURLToPath(import.meta.url);
20
+ var __dirname = dirname(__filename);
21
+ function loadPluginsFromYaml() {
22
+ const pluginsDir = join(__dirname, "plugins.d");
23
+ const defaultsPath = join(pluginsDir, "_defaults.yml");
24
+ let defaults = {};
25
+ try {
26
+ defaults = parseYaml(readFileSync(defaultsPath, "utf-8")) || {};
27
+ } catch {
115
28
  }
116
- ];
29
+ const plugins = [];
30
+ try {
31
+ for (const file of readdirSync(pluginsDir)) {
32
+ if (file.startsWith("_") || !file.endsWith(".yml")) continue;
33
+ try {
34
+ const content = parseYaml(readFileSync(join(pluginsDir, file), "utf-8"));
35
+ if (!content || !content.id) continue;
36
+ plugins.push({
37
+ id: content.id,
38
+ dir: content.dir,
39
+ displayName: content.displayName || content.id,
40
+ script: content.script || defaults.script || "init.sh",
41
+ aliases: content.aliases || [],
42
+ skills: content.skills ?? defaults.skills ?? 0,
43
+ agents: content.agents ?? defaults.agents ?? 0,
44
+ description: content.description || ""
45
+ });
46
+ } catch {
47
+ }
48
+ }
49
+ } catch {
50
+ }
51
+ return plugins;
52
+ }
53
+ var PLUGIN_REGISTRY = loadPluginsFromYaml();
117
54
  function findPlugin(query) {
118
55
  const normalized = query.toLowerCase().trim();
119
56
  const exactMatch = PLUGIN_REGISTRY.find(
@@ -139,11 +76,11 @@ function getPluginById(id) {
139
76
  import { execa } from "execa";
140
77
  import { existsSync as existsSync2 } from "fs";
141
78
  import { homedir as homedir2 } from "os";
142
- import { join as join2 } from "path";
79
+ import { join as join3 } from "path";
143
80
 
144
81
  // src/utils/paths.ts
145
82
  import { homedir } from "os";
146
- import { join } from "path";
83
+ import { join as join2 } from "path";
147
84
  import { existsSync } from "fs";
148
85
 
149
86
  // src/utils/i18n.ts
@@ -282,7 +219,38 @@ var zh_CN = {
282
219
  wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
283
220
  install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
284
221
  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"
222
+ wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
223
+ skill_count_format: "{count} \u4E2A Skills",
224
+ result_success_format: "{count} \u6210\u529F",
225
+ result_failed_format: "{count} \u5931\u8D25",
226
+ uninstall_remove_label: "\u79FB\u9664",
227
+ uninstall_remove_file_format: "\u79FB\u9664 {count} \u4E2A\u6587\u4EF6",
228
+ uninstall_remove_dir_format: "{count} \u4E2A\u76EE\u5F55",
229
+ uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
230
+ uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
231
+ available_plugins: "\u53EF\u7528\u63D2\u4EF6",
232
+ available_skills: "\u53EF\u7528 Skills",
233
+ skill_total_count: "\u5171 {count} \u4E2A Skills",
234
+ wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
235
+ wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
236
+ update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
237
+ hint_select_move: "\u79FB\u52A8",
238
+ hint_select_space: "\u7A7A\u683C",
239
+ hint_select_check: "\u52FE\u9009",
240
+ hint_select_confirm: "\u786E\u8BA4",
241
+ hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
242
+ doctor_not_installed: "\u672A\u5B89\u88C5",
243
+ doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
244
+ doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
245
+ doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
246
+ doctor_not_exist: "\u4E0D\u5B58\u5728",
247
+ doctor_created: "\u5DF2\u521B\u5EFA {path}",
248
+ doctor_config_exists: "\u5B58\u5728",
249
+ doctor_config_not_exist: "\u4E0D\u5B58\u5728",
250
+ doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF, {fixes} \u9879\u5DF2\u4FEE\u590D",
251
+ doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF",
252
+ count_suffix: "\u4E2A",
253
+ try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
286
254
  };
287
255
  var en_US = {
288
256
  wizard_title: "CANNBot Install Helper",
@@ -419,7 +387,38 @@ var en_US = {
419
387
  wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
420
388
  install_level_project: "Install to current directory (recommended, current project only)",
421
389
  install_level_global: "Install globally (all projects)",
422
- wizard_select_mode_title: "Select installation type"
390
+ wizard_select_mode_title: "Select installation type",
391
+ skill_count_format: "{count} Skills",
392
+ result_success_format: "{count} succeeded",
393
+ result_failed_format: "{count} failed",
394
+ uninstall_remove_label: "Removed",
395
+ uninstall_remove_file_format: "Removed {count} files",
396
+ uninstall_remove_dir_format: "{count} directories",
397
+ uninstall_clean_empty_dir: "Cleaned empty directory",
398
+ uninstall_skill_not_installed: "Not installed",
399
+ available_plugins: "Available plugins",
400
+ available_skills: "Available Skills",
401
+ skill_total_count: "Total {count} Skills",
402
+ wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
403
+ wizard_install_path: "Install path",
404
+ update_install_hint: "Use {cmd} to install plugins",
405
+ hint_select_move: "Navigate",
406
+ hint_select_space: "Space",
407
+ hint_select_check: "Toggle",
408
+ hint_select_confirm: "Confirm",
409
+ hint_checkbox_suffix: "Press enter without selection to go back",
410
+ doctor_not_installed: "Not installed",
411
+ doctor_links_valid: "{count} symlinks all valid",
412
+ doctor_broken_links: "{count} broken links",
413
+ doctor_fixed: "Cleaned {count} broken links",
414
+ doctor_not_exist: "Does not exist",
415
+ doctor_created: "Created {path}",
416
+ doctor_config_exists: "Exists",
417
+ doctor_config_not_exist: "Does not exist",
418
+ doctor_result_with_fix: "{warnings} warnings, {errors} errors, {fixes} fixed",
419
+ doctor_result_without_fix: "{warnings} warnings, {errors} errors",
420
+ count_suffix: "",
421
+ try_prompt: "help me develop an Abs operator"
423
422
  };
424
423
  var currentLang = "zh_CN";
425
424
  function setLanguage(lang) {
@@ -450,9 +449,9 @@ function validateLevel(level) {
450
449
  }
451
450
  function detectTraeVariant() {
452
451
  const home = homedir();
453
- if (existsSync(join(home, ".trae-cn"))) return "ide";
454
- if (existsSync(join(home, ".marscode"))) return "plugin";
455
- if (existsSync(join(home, ".traecli"))) return "cli";
452
+ if (existsSync(join2(home, ".trae-cn"))) return "ide";
453
+ if (existsSync(join2(home, ".marscode"))) return "plugin";
454
+ if (existsSync(join2(home, ".traecli"))) return "cli";
456
455
  return "unknown";
457
456
  }
458
457
  function getConfigRoot(tool, level, base) {
@@ -460,24 +459,24 @@ function getConfigRoot(tool, level, base) {
460
459
  if (level === "global") {
461
460
  switch (tool) {
462
461
  case "opencode":
463
- return join(home, ".config", "opencode");
462
+ return join2(home, ".config", "opencode");
464
463
  case "claude":
465
- return join(home, ".claude");
464
+ return join2(home, ".claude");
466
465
  case "trae": {
467
466
  const variant = detectTraeVariant();
468
467
  switch (variant) {
469
468
  case "plugin":
470
- return join(home, ".marscode");
469
+ return join2(home, ".marscode");
471
470
  case "cli":
472
- return join(home, ".traecli");
471
+ return join2(home, ".traecli");
473
472
  default:
474
- return join(home, ".trae-cn");
473
+ return join2(home, ".trae-cn");
475
474
  }
476
475
  }
477
476
  case "cursor":
478
- return join(home, ".cursor");
477
+ return join2(home, ".cursor");
479
478
  case "copilot":
480
- return join(home, ".copilot");
479
+ return join2(home, ".copilot");
481
480
  default:
482
481
  throw new Error(
483
482
  t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
@@ -487,24 +486,24 @@ function getConfigRoot(tool, level, base) {
487
486
  const baseDir = base || process.cwd();
488
487
  switch (tool) {
489
488
  case "opencode":
490
- return join(baseDir, ".opencode");
489
+ return join2(baseDir, ".opencode");
491
490
  case "claude":
492
- return join(baseDir, ".claude");
491
+ return join2(baseDir, ".claude");
493
492
  case "trae": {
494
493
  const variant = detectTraeVariant();
495
494
  switch (variant) {
496
495
  case "plugin":
497
- return join(baseDir, ".marscode");
496
+ return join2(baseDir, ".marscode");
498
497
  case "cli":
499
- return join(baseDir, ".traecli");
498
+ return join2(baseDir, ".traecli");
500
499
  default:
501
- return join(baseDir, ".trae");
500
+ return join2(baseDir, ".trae");
502
501
  }
503
502
  }
504
503
  case "cursor":
505
- return join(baseDir, ".cursor");
504
+ return join2(baseDir, ".cursor");
506
505
  case "copilot":
507
- return join(baseDir, ".github");
506
+ return join2(baseDir, ".github");
508
507
  default:
509
508
  throw new Error(
510
509
  t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
@@ -515,19 +514,19 @@ function getConfigFileName(tool) {
515
514
  return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
516
515
  }
517
516
  function getSkillsDir(configRoot) {
518
- return join(configRoot, "skills");
517
+ return join2(configRoot, "skills");
519
518
  }
520
519
  function getAgentsDir(configRoot) {
521
- return join(configRoot, "agents");
520
+ return join2(configRoot, "agents");
522
521
  }
523
522
  function getManifestPath(configRoot) {
524
- return join(configRoot, "cannbot-manifest.json");
523
+ return join2(configRoot, "cannbot-manifest.json");
525
524
  }
526
525
  function getCannbotConfigDir() {
527
- return join(homedir(), ".cannbot");
526
+ return join2(homedir(), ".cannbot");
528
527
  }
529
528
  function getCannbotRepoPath() {
530
- return join(getCannbotConfigDir(), "repo");
529
+ return join2(getCannbotConfigDir(), "repo");
531
530
  }
532
531
 
533
532
  // src/core/detector.ts
@@ -565,12 +564,12 @@ async function detectTrae() {
565
564
  const home = homedir2();
566
565
  const variant = detectTraeVariant();
567
566
  let path;
568
- if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
569
- path = join2(home, ".trae-cn");
570
- } else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
571
- path = join2(home, ".marscode");
572
- } else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
573
- path = join2(home, ".traecli");
567
+ if (variant === "ide" && existsSync2(join3(home, ".trae-cn"))) {
568
+ path = join3(home, ".trae-cn");
569
+ } else if (variant === "plugin" && existsSync2(join3(home, ".marscode"))) {
570
+ path = join3(home, ".marscode");
571
+ } else if (variant === "cli" && existsSync2(join3(home, ".traecli"))) {
572
+ path = join3(home, ".traecli");
574
573
  } else if (variant === "unknown") {
575
574
  const cmdPath = await getCommandPath("trae");
576
575
  if (cmdPath) path = cmdPath;
@@ -634,13 +633,13 @@ function getAllTools() {
634
633
  }
635
634
 
636
635
  // src/core/manifest.ts
637
- import { existsSync as existsSync3, readFileSync } from "fs";
638
- import { join as join3 } from "path";
636
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
637
+ import { join as join4 } from "path";
639
638
  function readManifest(configRoot) {
640
639
  const manifestPath = getManifestPath(configRoot);
641
640
  if (existsSync3(manifestPath)) {
642
641
  try {
643
- const content = readFileSync(manifestPath, "utf-8");
642
+ const content = readFileSync2(manifestPath, "utf-8");
644
643
  return JSON.parse(content);
645
644
  } catch {
646
645
  }
@@ -653,16 +652,16 @@ function readAllManifests(configRoot) {
653
652
  const standardPath = getManifestPath(configRoot);
654
653
  if (existsSync3(standardPath)) {
655
654
  try {
656
- const content = readFileSync(standardPath, "utf-8");
655
+ const content = readFileSync2(standardPath, "utf-8");
657
656
  manifests.push(JSON.parse(content));
658
657
  } catch {
659
658
  }
660
659
  }
661
660
  for (const plugin of plugins) {
662
- const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
661
+ const pluginManifestPath = join4(configRoot, `${plugin.id}-manifest.json`);
663
662
  if (existsSync3(pluginManifestPath)) {
664
663
  try {
665
- const content = readFileSync(pluginManifestPath, "utf-8");
664
+ const content = readFileSync2(pluginManifestPath, "utf-8");
666
665
  manifests.push(JSON.parse(content));
667
666
  } catch {
668
667
  }
@@ -705,27 +704,27 @@ function scanInstalled() {
705
704
  return installed;
706
705
  }
707
706
  function verifyPluginFilesExist(configRoot, manifest, tool, level) {
708
- const skillsDir = join3(configRoot, "skills");
709
- const agentsDir = join3(configRoot, "agents");
707
+ const skillsDir = join4(configRoot, "skills");
708
+ const agentsDir = join4(configRoot, "agents");
710
709
  if (manifest.installed_skills && manifest.installed_skills.length > 0) {
711
710
  const hasAnySkill = manifest.installed_skills.some((skillId) => {
712
- const skillPath = join3(skillsDir, skillId);
711
+ const skillPath = join4(skillsDir, skillId);
713
712
  return existsSync3(skillPath);
714
713
  });
715
714
  if (!hasAnySkill) return false;
716
715
  }
717
716
  if (manifest.installed_agents && manifest.installed_agents.length > 0) {
718
717
  const hasAnyAgent = manifest.installed_agents.some((agentId) => {
719
- const agentPath = join3(agentsDir, agentId);
718
+ const agentPath = join4(agentsDir, agentId);
720
719
  return existsSync3(agentPath);
721
720
  });
722
721
  if (!hasAnyAgent) return false;
723
722
  }
724
723
  let agentsMdPath;
725
724
  if (tool === "opencode" && level === "project") {
726
- agentsMdPath = join3(configRoot, "..", "AGENTS.md");
725
+ agentsMdPath = join4(configRoot, "..", "AGENTS.md");
727
726
  } else {
728
- agentsMdPath = join3(configRoot, "AGENTS.md");
727
+ agentsMdPath = join4(configRoot, "AGENTS.md");
729
728
  }
730
729
  if (!existsSync3(agentsMdPath)) return false;
731
730
  return true;
@@ -785,24 +784,24 @@ function printBoxTitle(title, width = 65) {
785
784
  }
786
785
  function showOperationHints(isCheckbox = false) {
787
786
  if (isCheckbox) {
788
- 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
787
+ 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
789
788
  `);
790
789
  } else {
791
- console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4
790
+ console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
792
791
  `);
793
792
  }
794
793
  }
795
794
 
796
795
  // src/utils/config.ts
797
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
798
- import { join as join4 } from "path";
796
+ import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
797
+ import { join as join5 } from "path";
799
798
  import { homedir as homedir3 } from "os";
800
799
  import { parse, stringify } from "yaml";
801
800
  function getConfigDir() {
802
- return join4(homedir3(), ".cannbot");
801
+ return join5(homedir3(), ".cannbot");
803
802
  }
804
803
  function getConfigPath() {
805
- return join4(getConfigDir(), "config.yaml");
804
+ return join5(getConfigDir(), "config.yaml");
806
805
  }
807
806
  function readConfig() {
808
807
  const configPath = getConfigPath();
@@ -813,7 +812,7 @@ function readConfig() {
813
812
  };
814
813
  }
815
814
  try {
816
- const content = readFileSync2(configPath, "utf-8");
815
+ const content = readFileSync3(configPath, "utf-8");
817
816
  const parsed = parse(content);
818
817
  return {
819
818
  language: parsed.language || "zh_CN",
@@ -1097,8 +1096,8 @@ async function stepConfirm(tool, level, plugins) {
1097
1096
  const displayPath = getConfigRoot(tool, level);
1098
1097
  const toolName = getToolDisplayName(tool);
1099
1098
  const levelText = level === "project" ? "project" : "global";
1100
- 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`);
1101
- logger.info(`\u5B89\u88C5\u8DEF\u5F84: ${chalk2.cyan(displayPath)}`);
1099
+ logger.info(t("wizard_confirm_install_format").replace("{count}", chalk2.bold(String(plugins.length))).replace("{tool}", chalk2.green(toolName)).replace("{level}", chalk2.cyan(levelText)));
1100
+ logger.info(`${t("wizard_install_path")}: ${chalk2.cyan(displayPath)}`);
1102
1101
  for (const plugin of selectedPlugins) {
1103
1102
  if (plugin) {
1104
1103
  logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
@@ -1119,14 +1118,19 @@ async function stepConfirm(tool, level, plugins) {
1119
1118
  }
1120
1119
 
1121
1120
  // src/core/repository.ts
1122
- import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
1123
- import { join as join6 } from "path";
1121
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
1122
+ import { join as join7, dirname as dirname3 } from "path";
1123
+ import { fileURLToPath as fileURLToPath3 } from "url";
1124
1124
  import { execa as execa2 } from "execa";
1125
+ import { parse as parseYaml3 } from "yaml";
1125
1126
 
1126
1127
  // src/core/scanner.ts
1127
- import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1128
- import { join as join5, dirname } from "path";
1129
- import { parse as parseYaml } from "yaml";
1128
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1129
+ import { join as join6, dirname as dirname2 } from "path";
1130
+ import { fileURLToPath as fileURLToPath2 } from "url";
1131
+ import { parse as parseYaml2 } from "yaml";
1132
+ var __filename2 = fileURLToPath2(import.meta.url);
1133
+ var __dirname2 = dirname2(__filename2);
1130
1134
  var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
1131
1135
  "node_modules",
1132
1136
  ".git",
@@ -1147,36 +1151,43 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
1147
1151
  "docs",
1148
1152
  "scripts"
1149
1153
  ]);
1150
- var SKILL_SCAN_DIRS = [
1151
- "ops",
1152
- "model",
1153
- "graph",
1154
- "infra",
1155
- "ops-lab"
1156
- ];
1157
- var PLUGIN_SCAN_DIRS = [
1158
- "plugins-official",
1159
- "plugins-community"
1160
- ];
1154
+ function loadScanConfig() {
1155
+ const configPath = join6(__dirname2, "config", "defaults.yaml");
1156
+ try {
1157
+ const config = parseYaml2(readFileSync4(configPath, "utf-8"));
1158
+ return {
1159
+ skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
1160
+ pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
1161
+ };
1162
+ } catch {
1163
+ return {
1164
+ skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
1165
+ pluginDirs: ["plugins-official", "plugins-community"]
1166
+ };
1167
+ }
1168
+ }
1169
+ var scanConfig = loadScanConfig();
1170
+ var SKILL_SCAN_DIRS = scanConfig.skillDirs;
1171
+ var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
1161
1172
  function scanSkills(repoPath) {
1162
1173
  const skills = [];
1163
1174
  const seen = /* @__PURE__ */ new Set();
1164
1175
  for (const dir of SKILL_SCAN_DIRS) {
1165
- const fullPath = join5(repoPath, dir);
1176
+ const fullPath = join6(repoPath, dir);
1166
1177
  if (!existsSync5(fullPath)) continue;
1167
1178
  scanDirectory(fullPath, repoPath, dir, skills, seen);
1168
1179
  }
1169
1180
  for (const pluginDir of PLUGIN_SCAN_DIRS) {
1170
- const pluginParent = join5(repoPath, pluginDir);
1181
+ const pluginParent = join6(repoPath, pluginDir);
1171
1182
  if (!existsSync5(pluginParent)) continue;
1172
1183
  for (const plugin of safeReaddir(pluginParent)) {
1173
- const pluginPath = join5(pluginParent, plugin);
1184
+ const pluginPath = join6(pluginParent, plugin);
1174
1185
  if (!isDirectory(pluginPath)) continue;
1175
- const skillsDir = join5(pluginPath, "skills");
1186
+ const skillsDir = join6(pluginPath, "skills");
1176
1187
  if (existsSync5(skillsDir)) {
1177
1188
  scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
1178
1189
  }
1179
- const skillDir = join5(pluginPath, "skill");
1190
+ const skillDir = join6(pluginPath, "skill");
1180
1191
  if (existsSync5(skillDir)) {
1181
1192
  scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
1182
1193
  }
@@ -1186,7 +1197,7 @@ function scanSkills(repoPath) {
1186
1197
  }
1187
1198
  function parseFrontmatter(filePath) {
1188
1199
  try {
1189
- const content = readFileSync3(filePath, "utf-8");
1200
+ const content = readFileSync4(filePath, "utf-8");
1190
1201
  const lines = content.split("\n");
1191
1202
  if (lines.length < 3) return null;
1192
1203
  if (lines[0].trim() !== "---") return null;
@@ -1199,7 +1210,7 @@ function parseFrontmatter(filePath) {
1199
1210
  }
1200
1211
  if (endIndex === -1) return null;
1201
1212
  const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
1202
- const parsed = parseYaml(yamlBlock);
1213
+ const parsed = parseYaml2(yamlBlock);
1203
1214
  if (!parsed || typeof parsed !== "object") return null;
1204
1215
  if (!parsed.name || typeof parsed.name !== "string") return null;
1205
1216
  return {
@@ -1212,17 +1223,17 @@ function parseFrontmatter(filePath) {
1212
1223
  }
1213
1224
  function getCurrentCommit(repoPath) {
1214
1225
  try {
1215
- const headFile = join5(repoPath, ".git", "HEAD");
1226
+ const headFile = join6(repoPath, ".git", "HEAD");
1216
1227
  if (!existsSync5(headFile)) return "unknown";
1217
- const headContent = readFileSync3(headFile, "utf-8").trim();
1228
+ const headContent = readFileSync4(headFile, "utf-8").trim();
1218
1229
  if (headContent.startsWith("ref: ")) {
1219
- const refPath = join5(repoPath, ".git", headContent.slice(5));
1230
+ const refPath = join6(repoPath, ".git", headContent.slice(5));
1220
1231
  if (existsSync5(refPath)) {
1221
- return readFileSync3(refPath, "utf-8").trim();
1232
+ return readFileSync4(refPath, "utf-8").trim();
1222
1233
  }
1223
- const packedRefsPath = join5(repoPath, ".git", "packed-refs");
1234
+ const packedRefsPath = join6(repoPath, ".git", "packed-refs");
1224
1235
  if (existsSync5(packedRefsPath)) {
1225
- const packedRefs = readFileSync3(packedRefsPath, "utf-8");
1236
+ const packedRefs = readFileSync4(packedRefsPath, "utf-8");
1226
1237
  const refName = headContent.slice(5);
1227
1238
  const lines = packedRefs.split("\n");
1228
1239
  for (const line of lines) {
@@ -1241,7 +1252,7 @@ function readScanCache() {
1241
1252
  try {
1242
1253
  const cachePath = getCachePath();
1243
1254
  if (!existsSync5(cachePath)) return null;
1244
- const content = readFileSync3(cachePath, "utf-8");
1255
+ const content = readFileSync4(cachePath, "utf-8");
1245
1256
  const cache = JSON.parse(content);
1246
1257
  if (!cache.skills || !cache.timestamp) return null;
1247
1258
  const age = Date.now() - cache.timestamp;
@@ -1254,7 +1265,7 @@ function readScanCache() {
1254
1265
  function writeScanCache(cache) {
1255
1266
  try {
1256
1267
  const cachePath = getCachePath();
1257
- const cacheDir = dirname(cachePath);
1268
+ const cacheDir = dirname2(cachePath);
1258
1269
  if (!existsSync5(cacheDir)) {
1259
1270
  mkdirSync2(cacheDir, { recursive: true });
1260
1271
  }
@@ -1263,10 +1274,10 @@ function writeScanCache(cache) {
1263
1274
  }
1264
1275
  }
1265
1276
  function getCachePath() {
1266
- return join5(getCannbotConfigDir(), "scan-cache.json");
1277
+ return join6(getCannbotConfigDir(), "scan-cache.json");
1267
1278
  }
1268
1279
  function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1269
- const selfSkillMd = join5(dirPath, "SKILL.md");
1280
+ const selfSkillMd = join6(dirPath, "SKILL.md");
1270
1281
  if (existsSync5(selfSkillMd)) {
1271
1282
  const frontmatter = parseFrontmatter(selfSkillMd);
1272
1283
  if (frontmatter && !seen.has(frontmatter.name)) {
@@ -1281,10 +1292,10 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1281
1292
  return;
1282
1293
  }
1283
1294
  for (const entry of safeReaddir(dirPath)) {
1284
- const fullPath = join5(dirPath, entry);
1295
+ const fullPath = join6(dirPath, entry);
1285
1296
  if (EXCLUDE_DIRS.has(entry)) continue;
1286
1297
  if (!isDirectory(fullPath)) continue;
1287
- const skillMd = join5(fullPath, "SKILL.md");
1298
+ const skillMd = join6(fullPath, "SKILL.md");
1288
1299
  if (existsSync5(skillMd)) {
1289
1300
  const frontmatter = parseFrontmatter(skillMd);
1290
1301
  if (frontmatter && !seen.has(frontmatter.name)) {
@@ -1303,7 +1314,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1303
1314
  }
1304
1315
  function safeReaddir(dir) {
1305
1316
  try {
1306
- return readdirSync(dir);
1317
+ return readdirSync2(dir);
1307
1318
  } catch {
1308
1319
  return [];
1309
1320
  }
@@ -1543,7 +1554,21 @@ function getAllCategories() {
1543
1554
  }
1544
1555
 
1545
1556
  // src/core/repository.ts
1546
- var REPO_URL = "https://gitcode.com/cann/cannbot-skills.git";
1557
+ var __filename3 = fileURLToPath3(import.meta.url);
1558
+ var __dirname3 = dirname3(__filename3);
1559
+ function loadRepoUrl() {
1560
+ if (process.env.CANNBOT_REPO_URL) {
1561
+ return process.env.CANNBOT_REPO_URL;
1562
+ }
1563
+ const configPath = join7(__dirname3, "config", "defaults.yaml");
1564
+ try {
1565
+ const config = parseYaml3(readFileSync5(configPath, "utf-8"));
1566
+ return config.repository?.url || "https://gitcode.com/cann/cannbot-skills.git";
1567
+ } catch {
1568
+ return "https://gitcode.com/cann/cannbot-skills.git";
1569
+ }
1570
+ }
1571
+ var REPO_URL = loadRepoUrl();
1547
1572
  var RepositoryManager = class {
1548
1573
  repoPath;
1549
1574
  constructor(customPath) {
@@ -1601,8 +1626,8 @@ var RepositoryManager = class {
1601
1626
  }
1602
1627
  isValidRepo(path) {
1603
1628
  if (!existsSync6(path)) return false;
1604
- const gitDir = join6(path, ".git");
1605
- const pluginsDir = join6(path, "plugins-official");
1629
+ const gitDir = join7(path, ".git");
1630
+ const pluginsDir = join7(path, "plugins-official");
1606
1631
  return existsSync6(gitDir) && existsSync6(pluginsDir);
1607
1632
  }
1608
1633
  async cloneRepo() {
@@ -1638,17 +1663,17 @@ function createRepositoryManager(customPath) {
1638
1663
 
1639
1664
  // src/core/installer.ts
1640
1665
  import { existsSync as existsSync9 } from "fs";
1641
- import { join as join9 } from "path";
1666
+ import { join as join10 } from "path";
1642
1667
  import { execa as execa3 } from "execa";
1643
1668
 
1644
1669
  // src/core/record.ts
1645
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
1646
- import { join as join7 } from "path";
1670
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
1671
+ import { join as join8 } from "path";
1647
1672
  function getInstallsDir() {
1648
- return join7(getCannbotConfigDir(), "installs");
1673
+ return join8(getCannbotConfigDir(), "installs");
1649
1674
  }
1650
1675
  function getRecordPath(pluginId) {
1651
- return join7(getInstallsDir(), `${pluginId}.json`);
1676
+ return join8(getInstallsDir(), `${pluginId}.json`);
1652
1677
  }
1653
1678
  function readRecord(pluginId) {
1654
1679
  const recordPath = getRecordPath(pluginId);
@@ -1656,7 +1681,7 @@ function readRecord(pluginId) {
1656
1681
  return null;
1657
1682
  }
1658
1683
  try {
1659
- const content = readFileSync4(recordPath, "utf-8");
1684
+ const content = readFileSync6(recordPath, "utf-8");
1660
1685
  return JSON.parse(content);
1661
1686
  } catch {
1662
1687
  return null;
@@ -1680,22 +1705,22 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1680
1705
  const files = [];
1681
1706
  const directories = [];
1682
1707
  if (manifest) {
1683
- const skillsDir = join7(configRoot, "skills");
1708
+ const skillsDir = join8(configRoot, "skills");
1684
1709
  if (existsSync7(skillsDir)) {
1685
1710
  directories.push(skillsDir);
1686
1711
  for (const skillName of manifest.installed_skills || []) {
1687
- const skillPath = join7(skillsDir, skillName);
1712
+ const skillPath = join8(skillsDir, skillName);
1688
1713
  if (existsSync7(skillPath) || isSymlink(skillPath)) {
1689
1714
  files.push(skillPath);
1690
1715
  }
1691
1716
  }
1692
1717
  }
1693
- const agentsDir = join7(configRoot, "agents");
1718
+ const agentsDir = join8(configRoot, "agents");
1694
1719
  if (existsSync7(agentsDir)) {
1695
1720
  directories.push(agentsDir);
1696
1721
  for (const agentName of manifest.installed_agents || []) {
1697
- const agentPath = join7(agentsDir, agentName);
1698
- const agentPathMd = join7(agentsDir, agentName + ".md");
1722
+ const agentPath = join8(agentsDir, agentName);
1723
+ const agentPathMd = join8(agentsDir, agentName + ".md");
1699
1724
  if (existsSync7(agentPath) || isSymlink(agentPath)) {
1700
1725
  files.push(agentPath);
1701
1726
  } else if (existsSync7(agentPathMd) || isSymlink(agentPathMd)) {
@@ -1704,30 +1729,30 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1704
1729
  }
1705
1730
  }
1706
1731
  }
1707
- const workflowsLink = join7(configRoot, "workflows");
1732
+ const workflowsLink = join8(configRoot, "workflows");
1708
1733
  if (isSymlink(workflowsLink)) {
1709
1734
  files.push(workflowsLink);
1710
1735
  }
1711
- const manifestPath = join7(configRoot, "cannbot-manifest.json");
1736
+ const manifestPath = join8(configRoot, "cannbot-manifest.json");
1712
1737
  if (existsSync7(manifestPath)) {
1713
1738
  files.push(manifestPath);
1714
1739
  }
1715
- const pluginManifestPath = join7(configRoot, `${pluginId}-manifest.json`);
1740
+ const pluginManifestPath = join8(configRoot, `${pluginId}-manifest.json`);
1716
1741
  if (existsSync7(pluginManifestPath)) {
1717
1742
  files.push(pluginManifestPath);
1718
1743
  }
1719
1744
  const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1720
- const configFilePath = level === "project" ? join7(installPath, configFileName) : join7(configRoot, configFileName);
1745
+ const configFilePath = level === "project" ? join8(installPath, configFileName) : join8(configRoot, configFileName);
1721
1746
  if (existsSync7(configFilePath)) {
1722
1747
  files.push(configFilePath);
1723
1748
  }
1724
1749
  const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
1725
1750
  for (const repoName of repoLinks) {
1726
- const repoLinkPath = join7(installPath, repoName);
1751
+ const repoLinkPath = join8(installPath, repoName);
1727
1752
  if (isSymlink(repoLinkPath)) {
1728
1753
  files.push(repoLinkPath);
1729
1754
  }
1730
- const repoLinkInConfig = join7(configRoot, repoName);
1755
+ const repoLinkInConfig = join8(configRoot, repoName);
1731
1756
  if (isSymlink(repoLinkInConfig)) {
1732
1757
  files.push(repoLinkInConfig);
1733
1758
  }
@@ -1752,7 +1777,7 @@ function isSymlink(path) {
1752
1777
  }
1753
1778
  }
1754
1779
  function getSkillRecordPath() {
1755
- return join7(getInstallsDir(), "skills.json");
1780
+ return join8(getInstallsDir(), "skills.json");
1756
1781
  }
1757
1782
  function readSkillRecord() {
1758
1783
  const recordPath = getSkillRecordPath();
@@ -1760,7 +1785,7 @@ function readSkillRecord() {
1760
1785
  return {};
1761
1786
  }
1762
1787
  try {
1763
- const content = readFileSync4(recordPath, "utf-8");
1788
+ const content = readFileSync6(recordPath, "utf-8");
1764
1789
  return JSON.parse(content);
1765
1790
  } catch {
1766
1791
  return {};
@@ -1810,16 +1835,16 @@ function getInstalledSkills(tool, level, installPath) {
1810
1835
  const record = readSkillRecord();
1811
1836
  const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
1812
1837
  const configRoot = getConfigRoot(tool, level);
1813
- const skillsDir = join7(configRoot, "skills");
1838
+ const skillsDir = join8(configRoot, "skills");
1814
1839
  return recordedSkills.filter((skillId) => {
1815
- const skillPath = join7(skillsDir, skillId);
1840
+ const skillPath = join8(skillsDir, skillId);
1816
1841
  return existsSync7(skillPath) || isSymlink(skillPath);
1817
1842
  });
1818
1843
  }
1819
1844
 
1820
1845
  // src/core/backup.ts
1821
- import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
1822
- import { join as join8 } from "path";
1846
+ import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
1847
+ import { join as join9 } from "path";
1823
1848
  function getAgentsFileName(tool) {
1824
1849
  return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1825
1850
  }
@@ -1834,7 +1859,7 @@ function detectCurrentPlugin(configRoot, tool) {
1834
1859
  };
1835
1860
  }
1836
1861
  }
1837
- const agentsFile = join8(configRoot, getAgentsFileName(tool));
1862
+ const agentsFile = join9(configRoot, getAgentsFileName(tool));
1838
1863
  if (!existsSync8(agentsFile)) {
1839
1864
  return null;
1840
1865
  }
@@ -1853,13 +1878,13 @@ function detectCurrentPlugin(configRoot, tool) {
1853
1878
  return null;
1854
1879
  }
1855
1880
  function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
1856
- const agentsFile = join8(configRoot, getAgentsFileName(tool));
1881
+ const agentsFile = join9(configRoot, getAgentsFileName(tool));
1857
1882
  if (!existsSync8(agentsFile)) {
1858
1883
  return null;
1859
1884
  }
1860
1885
  const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
1861
1886
  const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
1862
- const backupPath = join8(configRoot, backupFileName);
1887
+ const backupPath = join9(configRoot, backupFileName);
1863
1888
  try {
1864
1889
  copyFileSync(agentsFile, backupPath);
1865
1890
  return {
@@ -1876,7 +1901,7 @@ function findBackups(configRoot) {
1876
1901
  if (!existsSync8(configRoot)) {
1877
1902
  return [];
1878
1903
  }
1879
- const files = readdirSync2(configRoot);
1904
+ const files = readdirSync3(configRoot);
1880
1905
  const backups = [];
1881
1906
  for (const file of files) {
1882
1907
  if (file.includes(".cannbot-backup.")) {
@@ -1889,7 +1914,7 @@ function findBackups(configRoot) {
1889
1914
  const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
1890
1915
  const plugin = findPlugin(pluginId);
1891
1916
  backups.push({
1892
- filePath: join8(configRoot, file),
1917
+ filePath: join9(configRoot, file),
1893
1918
  pluginId,
1894
1919
  pluginName: plugin?.displayName || pluginId,
1895
1920
  backupTime: timestamp
@@ -1902,7 +1927,7 @@ function findBackups(configRoot) {
1902
1927
  }
1903
1928
  function restoreBackup(backupPath, configRoot, tool) {
1904
1929
  try {
1905
- const agentsFile = join8(configRoot, getAgentsFileName(tool));
1930
+ const agentsFile = join9(configRoot, getAgentsFileName(tool));
1906
1931
  copyFileSync(backupPath, agentsFile);
1907
1932
  return true;
1908
1933
  } catch {
@@ -2003,7 +2028,7 @@ async function installPlugin(opts) {
2003
2028
  warnings: []
2004
2029
  };
2005
2030
  }
2006
- const scriptPath = join9(opts.repoPath, plugin.dir, plugin.script);
2031
+ const scriptPath = join10(opts.repoPath, plugin.dir, plugin.script);
2007
2032
  if (!existsSync9(scriptPath)) {
2008
2033
  return {
2009
2034
  success: false,
@@ -2020,7 +2045,7 @@ async function installPlugin(opts) {
2020
2045
  }
2021
2046
  const cwd = opts.installPath || process.cwd();
2022
2047
  const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
2023
- const agentsFile = join9(configRoot, getAgentsFileName(opts.tool));
2048
+ const agentsFile = join10(configRoot, getAgentsFileName(opts.tool));
2024
2049
  let backupInfo = null;
2025
2050
  if (existsSync9(agentsFile)) {
2026
2051
  const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
@@ -2115,14 +2140,14 @@ async function installPlugin(opts) {
2115
2140
  }
2116
2141
 
2117
2142
  // src/core/skill-installer.ts
2118
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
2119
- import { join as join10 } from "path";
2143
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
2144
+ import { join as join11 } from "path";
2120
2145
  import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
2121
2146
  import chalk4 from "chalk";
2122
2147
  import Table from "cli-table3";
2123
2148
  async function installSkills(skillIds, tool, level, repoPath) {
2124
2149
  const configRoot = getConfigRoot(tool, level);
2125
- const skillsDir = join10(configRoot, "skills");
2150
+ const skillsDir = join11(configRoot, "skills");
2126
2151
  const installPath = level === "project" ? process.cwd() : configRoot;
2127
2152
  if (!existsSync10(skillsDir)) {
2128
2153
  mkdirSync5(skillsDir, { recursive: true });
@@ -2135,8 +2160,8 @@ async function installSkills(skillIds, tool, level, repoPath) {
2135
2160
  results.push({ skillId, success: false, error: "Skill not found in registry" });
2136
2161
  continue;
2137
2162
  }
2138
- const sourcePath = join10(repoPath, skill.source, skill.id);
2139
- const targetPath = join10(skillsDir, skillId);
2163
+ const sourcePath = join11(repoPath, skill.source, skill.id);
2164
+ const targetPath = join11(skillsDir, skillId);
2140
2165
  if (!existsSync10(sourcePath)) {
2141
2166
  results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
2142
2167
  continue;
@@ -2159,21 +2184,21 @@ async function installSkills(skillIds, tool, level, repoPath) {
2159
2184
  }
2160
2185
  async function uninstallSkills(skillIds, tool, level) {
2161
2186
  const configRoot = getConfigRoot(tool, level);
2162
- const skillsDir = join10(configRoot, "skills");
2187
+ const skillsDir = join11(configRoot, "skills");
2163
2188
  const installPath = level === "project" ? process.cwd() : configRoot;
2164
2189
  const results = [];
2165
2190
  const removedIds = [];
2166
2191
  for (const skillId of skillIds) {
2167
- const targetPath = join10(skillsDir, skillId);
2192
+ const targetPath = join11(skillsDir, skillId);
2168
2193
  try {
2169
2194
  if (existsSync10(targetPath) || isSymlink2(targetPath)) {
2170
2195
  unlinkSync3(targetPath);
2171
2196
  results.push({ skillId, success: true });
2172
2197
  removedIds.push(skillId);
2173
- logger.step(` \u79FB\u9664: ${skillId}`);
2198
+ logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
2174
2199
  } else {
2175
2200
  results.push({ skillId, success: false, error: "Not installed" });
2176
- logger.warn(`${skillId} \u672A\u5B89\u88C5`);
2201
+ logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
2177
2202
  }
2178
2203
  } catch (error) {
2179
2204
  results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
@@ -2184,10 +2209,10 @@ async function uninstallSkills(skillIds, tool, level) {
2184
2209
  }
2185
2210
  if (existsSync10(skillsDir)) {
2186
2211
  try {
2187
- const entries = readdirSync3(skillsDir);
2212
+ const entries = readdirSync4(skillsDir);
2188
2213
  if (entries.length === 0) {
2189
2214
  rmdirSync(skillsDir);
2190
- logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: skills/`);
2215
+ logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
2191
2216
  }
2192
2217
  } catch {
2193
2218
  }
@@ -2296,7 +2321,7 @@ function listAllSkills() {
2296
2321
  console.log(table.toString());
2297
2322
  }
2298
2323
  console.log();
2299
- console.log(chalk4.dim(` \u5171 ${getAllSkills().length} \u4E2A Skills`));
2324
+ console.log(chalk4.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
2300
2325
  console.log();
2301
2326
  }
2302
2327
  function isSymlink2(path) {
@@ -2333,7 +2358,8 @@ function printPluginList(plugins, installed) {
2333
2358
  ]);
2334
2359
  });
2335
2360
  console.log();
2336
- console.log(chalk5.bold(` ${t("list_title")} (${plugins.length} \u4E2A)`));
2361
+ const countSuffix = t("count_suffix");
2362
+ console.log(chalk5.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
2337
2363
  console.log(table.toString());
2338
2364
  console.log();
2339
2365
  }
@@ -2366,22 +2392,25 @@ function printEnhancedSummary(results, tool, configRoot) {
2366
2392
  return;
2367
2393
  }
2368
2394
  const toolName = getToolDisplayName(tool);
2395
+ const countSuffix = t("count_suffix");
2369
2396
  const lines = [
2370
2397
  chalk5.bold(` ${t("install_done")}!`),
2371
2398
  "",
2372
2399
  ` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
2373
- ` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills} \u4E2A ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents} \u4E2A`,
2400
+ ` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
2374
2401
  "",
2375
2402
  chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
2376
2403
  ` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
2377
- ` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50")}`,
2404
+ ` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green(t("try_prompt"))}`,
2378
2405
  ` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
2379
2406
  ` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
2380
2407
  ""
2381
2408
  ];
2382
2409
  if (successResults.length === 1) {
2410
+ const plugin = findPlugin(successResults[0].pluginId);
2411
+ const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
2383
2412
  lines.push(
2384
- ` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(`plugins-official/${successResults[0].pluginId}/quickstart.md`)}`
2413
+ ` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(docsPath)}`
2385
2414
  );
2386
2415
  }
2387
2416
  console.log();
@@ -2392,15 +2421,15 @@ function printEnhancedSummary(results, tool, configRoot) {
2392
2421
  }
2393
2422
 
2394
2423
  // src/commands/init.ts
2395
- import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
2396
- import { join as join11 } from "path";
2424
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
2425
+ import { join as join12 } from "path";
2397
2426
  var BACK2 = "__back__";
2398
2427
  var CANCEL2 = "__cancel__";
2399
2428
  async function initCommand() {
2400
2429
  const cwd = process.cwd();
2401
- if (existsSync11(join11(cwd, "package.json")) && (() => {
2430
+ if (existsSync11(join12(cwd, "package.json")) && (() => {
2402
2431
  try {
2403
- const pkg = JSON.parse(readFileSync5(join11(cwd, "package.json"), "utf-8"));
2432
+ const pkg = JSON.parse(readFileSync7(join12(cwd, "package.json"), "utf-8"));
2404
2433
  return pkg.name === "@cannbot-ai/install-helper";
2405
2434
  } catch {
2406
2435
  return false;
@@ -2582,7 +2611,7 @@ async function skillInstallFlow() {
2582
2611
  spinner.start();
2583
2612
  const repoPath = await repoManager.ensureRepo();
2584
2613
  spinner.succeed(t("loading_plugin_data_complete"));
2585
- logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)} \u4E2A Skills...`);
2614
+ logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk6.bold(String(selectedSkills.length)))}...`);
2586
2615
  const results = await installSkills(selectedSkills, tool, level, repoPath);
2587
2616
  let successCount = 0;
2588
2617
  let failCount = 0;
@@ -2600,7 +2629,7 @@ async function skillInstallFlow() {
2600
2629
  }
2601
2630
  const configRoot = getConfigRoot(tool, level);
2602
2631
  logger.blank();
2603
- logger.success(`${t("skill_install_done")}: ${chalk6.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk6.red(failCount + " \u5931\u8D25") : chalk6.dim(failCount + " \u5931\u8D25")}`);
2632
+ 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)))}`);
2604
2633
  logger.blank();
2605
2634
  logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
2606
2635
  logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
@@ -2626,8 +2655,8 @@ async function listCommand() {
2626
2655
 
2627
2656
  // src/commands/doctor.ts
2628
2657
  import chalk7 from "chalk";
2629
- import { existsSync as existsSync12, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2630
- import { join as join12 } from "path";
2658
+ import { existsSync as existsSync12, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2659
+ import { join as join13 } from "path";
2631
2660
  async function doctorCommand(options = {}) {
2632
2661
  console.log();
2633
2662
  console.log(chalk7.bold(` ${t("doctor_title")}`));
@@ -2646,7 +2675,7 @@ async function doctorCommand(options = {}) {
2646
2675
  chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
2647
2676
  );
2648
2677
  } else {
2649
- console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 \u672A\u5B89\u88C5`);
2678
+ console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
2650
2679
  }
2651
2680
  }
2652
2681
  console.log();
@@ -2661,7 +2690,7 @@ async function doctorCommand(options = {}) {
2661
2690
  chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
2662
2691
  );
2663
2692
  } else {
2664
- console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 \u672A\u5B89\u88C5`);
2693
+ console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
2665
2694
  }
2666
2695
  }
2667
2696
  console.log();
@@ -2673,65 +2702,65 @@ async function doctorCommand(options = {}) {
2673
2702
  if (existsSync12(skillsDir)) {
2674
2703
  const brokenLinks = checkBrokenLinks(skillsDir);
2675
2704
  if (brokenLinks === 0) {
2676
- const count = readdirSync4(skillsDir).length;
2677
- console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2705
+ const count = readdirSync5(skillsDir).length;
2706
+ console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
2678
2707
  } else {
2679
- console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2708
+ console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
2680
2709
  warnings++;
2681
2710
  if (options.fix) {
2682
2711
  const fixed = fixBrokenLinks(skillsDir);
2683
2712
  fixes += fixed;
2684
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2713
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
2685
2714
  }
2686
2715
  }
2687
2716
  } else {
2688
- console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 \u4E0D\u5B58\u5728`);
2717
+ console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
2689
2718
  if (options.fix) {
2690
2719
  mkdirSync6(skillsDir, { recursive: true });
2691
2720
  fixes++;
2692
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${skillsDir}`);
2721
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
2693
2722
  }
2694
2723
  }
2695
2724
  if (existsSync12(agentsDir)) {
2696
2725
  const brokenLinks = checkBrokenLinks(agentsDir);
2697
2726
  if (brokenLinks === 0) {
2698
- const count = readdirSync4(agentsDir).length;
2699
- console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2727
+ const count = readdirSync5(agentsDir).length;
2728
+ console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
2700
2729
  } else {
2701
- console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2730
+ console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
2702
2731
  warnings++;
2703
2732
  if (options.fix) {
2704
2733
  const fixed = fixBrokenLinks(agentsDir);
2705
2734
  fixes += fixed;
2706
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2735
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
2707
2736
  }
2708
2737
  }
2709
2738
  } else {
2710
- console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 \u4E0D\u5B58\u5728`);
2739
+ console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
2711
2740
  if (options.fix) {
2712
2741
  mkdirSync6(agentsDir, { recursive: true });
2713
2742
  fixes++;
2714
- console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${agentsDir}`);
2743
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
2715
2744
  }
2716
2745
  }
2717
2746
  console.log();
2718
2747
  console.log(chalk7.bold(` ${t("doctor_config")}`));
2719
2748
  const configFile = getConfigFileName(primaryTool);
2720
- const configPath = join12(process.cwd(), configFile);
2749
+ const configPath = join13(process.cwd(), configFile);
2721
2750
  if (existsSync12(configPath)) {
2722
- console.log(chalk7.green(" \u2713") + ` ${configFile} \u5B58\u5728`);
2751
+ console.log(chalk7.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
2723
2752
  } else {
2724
- console.log(chalk7.dim(" \u2014") + ` ${configFile} \u4E0D\u5B58\u5728`);
2753
+ console.log(chalk7.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
2725
2754
  }
2726
2755
  console.log();
2727
2756
  console.log(chalk7.dim(" " + "\u2500".repeat(46)));
2728
2757
  if (options.fix && fixes > 0) {
2729
2758
  console.log(
2730
- ` ${t("doctor_result")}: ${warnings} \u4E2A\u8B66\u544A, ${errors} \u4E2A\u9519\u8BEF, ${fixes} \u9879\u5DF2\u4FEE\u590D`
2759
+ ` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
2731
2760
  );
2732
2761
  } else {
2733
2762
  console.log(
2734
- ` ${t("doctor_result")}: ${warnings} \u4E2A\u8B66\u544A, ${errors} \u4E2A\u9519\u8BEF`
2763
+ ` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
2735
2764
  );
2736
2765
  }
2737
2766
  console.log();
@@ -2739,9 +2768,9 @@ async function doctorCommand(options = {}) {
2739
2768
  function checkBrokenLinks(dir) {
2740
2769
  let broken = 0;
2741
2770
  try {
2742
- const entries = readdirSync4(dir);
2771
+ const entries = readdirSync5(dir);
2743
2772
  for (const entry of entries) {
2744
- const fullPath = join12(dir, entry);
2773
+ const fullPath = join13(dir, entry);
2745
2774
  try {
2746
2775
  const stats = lstatSync3(fullPath);
2747
2776
  if (stats.isSymbolicLink()) {
@@ -2760,9 +2789,9 @@ function checkBrokenLinks(dir) {
2760
2789
  function fixBrokenLinks(dir) {
2761
2790
  let fixed = 0;
2762
2791
  try {
2763
- const entries = readdirSync4(dir);
2792
+ const entries = readdirSync5(dir);
2764
2793
  for (const entry of entries) {
2765
- const fullPath = join12(dir, entry);
2794
+ const fullPath = join13(dir, entry);
2766
2795
  try {
2767
2796
  const stats = lstatSync3(fullPath);
2768
2797
  if (stats.isSymbolicLink()) {
@@ -2826,9 +2855,9 @@ async function installCommand(names, options) {
2826
2855
  try {
2827
2856
  const scanRepoManager = createRepositoryManager();
2828
2857
  await scanRepoManager.ensureRepoAndScan();
2829
- scanSpinner2.succeed("Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210");
2858
+ scanSpinner2.succeed(t("loading_skills_list_complete"));
2830
2859
  } catch {
2831
- scanSpinner2.warn("Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E");
2860
+ scanSpinner2.warn(t("loading_skills_list_failed"));
2832
2861
  }
2833
2862
  let tool2;
2834
2863
  if (options.tool) {
@@ -2860,8 +2889,8 @@ async function installCommand(names, options) {
2860
2889
  const spinner = createSpinner(t("loading_plugin_data"));
2861
2890
  spinner.start();
2862
2891
  const repoPath = await repoManager.ensureRepo();
2863
- spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
2864
- logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)} \u4E2A Skills...`);
2892
+ spinner.succeed(t("loading_plugin_data_complete"));
2893
+ logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(names.length)))}...`);
2865
2894
  const results = await installSkills(names, tool2, level2, repoPath);
2866
2895
  let successCount = 0;
2867
2896
  let failCount = 0;
@@ -2879,7 +2908,7 @@ async function installCommand(names, options) {
2879
2908
  }
2880
2909
  const configRoot = getConfigRoot(tool2, level2);
2881
2910
  logger.blank();
2882
- logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
2911
+ 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)))}`);
2883
2912
  logger.blank();
2884
2913
  logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
2885
2914
  logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
@@ -2894,9 +2923,9 @@ async function installCommand(names, options) {
2894
2923
  try {
2895
2924
  const scanRepoManager = createRepositoryManager();
2896
2925
  await scanRepoManager.ensureRepoAndScan();
2897
- scanSpinner.succeed("Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210");
2926
+ scanSpinner.succeed(t("loading_skills_list_complete"));
2898
2927
  } catch {
2899
- scanSpinner.warn("Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E");
2928
+ scanSpinner.warn(t("loading_skills_list_failed"));
2900
2929
  }
2901
2930
  const plugins = [];
2902
2931
  const skills = [];
@@ -2913,11 +2942,11 @@ async function installCommand(names, options) {
2913
2942
  }
2914
2943
  logger.error(`${t("error_plugin_not_found")}: ${name}`);
2915
2944
  const allPlugins = getAllPlugins();
2916
- logger.info("\u53EF\u7528\u63D2\u4EF6:");
2945
+ logger.info(t("available_plugins") + ":");
2917
2946
  for (const p of allPlugins) {
2918
2947
  logger.step(` ${p.id} (${p.aliases.join(", ")})`);
2919
2948
  }
2920
- logger.info(`\u53EF\u7528 Skills: ${chalk9.cyan("install-helper install --list")}`);
2949
+ logger.info(`${t("available_skills")}: ${chalk9.cyan("install-helper install --list")}`);
2921
2950
  return;
2922
2951
  }
2923
2952
  let tool;
@@ -2989,7 +3018,7 @@ async function installCommand(names, options) {
2989
3018
  spinner.start();
2990
3019
  const repoPath = await repoManager.ensureRepo();
2991
3020
  spinner.succeed(t("loading_plugin_data_complete"));
2992
- logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)} \u4E2A Skills...`);
3021
+ logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(skills.length)))}...`);
2993
3022
  const results = await installSkills(skills, tool, level, repoPath);
2994
3023
  let successCount = 0;
2995
3024
  let failCount = 0;
@@ -3006,7 +3035,7 @@ async function installCommand(names, options) {
3006
3035
  }
3007
3036
  }
3008
3037
  logger.blank();
3009
- logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
3038
+ 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)))}`);
3010
3039
  logger.blank();
3011
3040
  logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
3012
3041
  logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
@@ -3041,7 +3070,7 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
3041
3070
  }
3042
3071
 
3043
3072
  // src/commands/uninstall.ts
3044
- import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
3073
+ import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
3045
3074
  import chalk10 from "chalk";
3046
3075
  async function uninstallCommand(names, options) {
3047
3076
  const tool = options.tool ? validateTool(options.tool) : "opencode";
@@ -3078,7 +3107,7 @@ async function uninstallCommand(names, options) {
3078
3107
  if (existsSync13(filePath) || isSymlink3(filePath)) {
3079
3108
  unlinkSync5(filePath);
3080
3109
  const name = filePath.split("/").pop() || filePath;
3081
- logger.step(` \u79FB\u9664: ${name}`);
3110
+ logger.step(` ${t("uninstall_remove_label")}: ${name}`);
3082
3111
  removedFiles++;
3083
3112
  }
3084
3113
  } catch {
@@ -3092,11 +3121,11 @@ async function uninstallCommand(names, options) {
3092
3121
  continue;
3093
3122
  }
3094
3123
  if (existsSync13(dirPath)) {
3095
- const entries = readdirSync5(dirPath);
3124
+ const entries = readdirSync6(dirPath);
3096
3125
  if (entries.length === 0) {
3097
3126
  rmdirSync2(dirPath);
3098
3127
  const name = dirPath.split("/").pop() || dirPath;
3099
- logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${name}/`);
3128
+ logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
3100
3129
  removedDirs++;
3101
3130
  }
3102
3131
  }
@@ -3124,13 +3153,13 @@ async function uninstallCommand(names, options) {
3124
3153
  }
3125
3154
  const totalRemoved = removedFiles + removedDirs;
3126
3155
  if (totalRemoved > 0) {
3127
- logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08\u79FB\u9664 ${removedFiles} \u4E2A\u6587\u4EF6\uFF0C${removedDirs} \u4E2A\u76EE\u5F55\uFF09`);
3156
+ 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`);
3128
3157
  } else {
3129
3158
  logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
3130
3159
  }
3131
3160
  }
3132
3161
  if (skills.length > 0) {
3133
- logger.info(`${t("uninstall_skills_in_progress")} ${skills.length} \u4E2A Skills...`);
3162
+ logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
3134
3163
  const results = await uninstallSkills(skills, tool, level);
3135
3164
  let successCount = 0;
3136
3165
  let failCount = 0;
@@ -3142,7 +3171,7 @@ async function uninstallCommand(names, options) {
3142
3171
  }
3143
3172
  }
3144
3173
  logger.blank();
3145
- logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk10.red(failCount + " \u5931\u8D25") : chalk10.dim(failCount + " \u5931\u8D25")}`);
3174
+ 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)))}`);
3146
3175
  logger.blank();
3147
3176
  }
3148
3177
  }
@@ -3173,7 +3202,7 @@ async function updateCommand(pluginNames, options) {
3173
3202
  pluginsToUpdate = manifests.map((m) => m.team);
3174
3203
  if (pluginsToUpdate.length === 0) {
3175
3204
  logger.info(t("update_no_plugins"));
3176
- logger.info(`\u4F7F\u7528 ${chalk11.cyan("install-helper install <plugin>")} \u5B89\u88C5\u63D2\u4EF6`);
3205
+ logger.info(t("update_install_hint").replace("{cmd}", chalk11.cyan("install-helper install <plugin>")));
3177
3206
  return;
3178
3207
  }
3179
3208
  } else {
@@ -3236,8 +3265,8 @@ async function updateCommand(pluginNames, options) {
3236
3265
 
3237
3266
  // src/commands/info.ts
3238
3267
  import chalk12 from "chalk";
3239
- import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
3240
- import { join as join13 } from "path";
3268
+ import { existsSync as existsSync14, readdirSync as readdirSync7 } from "fs";
3269
+ import { join as join14 } from "path";
3241
3270
  async function infoCommand(pluginName) {
3242
3271
  const plugin = findPlugin(pluginName);
3243
3272
  if (!plugin) {
@@ -3261,13 +3290,13 @@ async function infoCommand(pluginName) {
3261
3290
  try {
3262
3291
  const repoManager = createRepositoryManager();
3263
3292
  const repoPath = await repoManager.ensureRepo();
3264
- const quickstartPath = join13(repoPath, plugin.dir, "quickstart.md");
3293
+ const quickstartPath = join14(repoPath, plugin.dir, "quickstart.md");
3265
3294
  if (existsSync14(quickstartPath)) {
3266
3295
  console.log(` ${chalk12.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
3267
3296
  }
3268
- const agentsDir = join13(repoPath, plugin.dir, "agents");
3297
+ const agentsDir = join14(repoPath, plugin.dir, "agents");
3269
3298
  if (existsSync14(agentsDir)) {
3270
- const agentFiles = readdirSync6(agentsDir).filter((f) => f.endsWith(".md"));
3299
+ const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
3271
3300
  if (agentFiles.length > 0) {
3272
3301
  console.log();
3273
3302
  console.log(` ${chalk12.bold("Agents:")}`);
@@ -3324,7 +3353,7 @@ function createCLI() {
3324
3353
  setLanguage(config.language);
3325
3354
  }
3326
3355
  const program2 = new Command();
3327
- program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.14");
3356
+ program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.16");
3328
3357
  program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
3329
3358
  await initCommand();
3330
3359
  });
@@ -3361,7 +3390,7 @@ function createCLI() {
3361
3390
  // src/index.ts
3362
3391
  process.on("uncaughtException", (error) => {
3363
3392
  if (error.name === "ExitPromptError") {
3364
- console.log("\n\u5DF2\u53D6\u6D88\u5B89\u88C5");
3393
+ console.log("\n" + t("init_cancelled"));
3365
3394
  process.exit(0);
3366
3395
  }
3367
3396
  throw error;