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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import { select as select3, Separator as Separator3 } from "@inquirer/prompts";
8
- import chalk5 from "chalk";
7
+ import { select as select4, Separator as Separator3 } from "@inquirer/prompts";
8
+ import chalk6 from "chalk";
9
9
 
10
10
  // src/ui/wizard.ts
11
11
  import { select, checkbox, Separator } from "@inquirer/prompts";
@@ -232,7 +232,7 @@ async function getCommandVersion(cmd, args = ["--version"]) {
232
232
  }
233
233
  async function getCommandPath(cmd) {
234
234
  try {
235
- const result = await execa("sh", ["-c", `command -v ${cmd}`], { timeout: 5e3 });
235
+ const result = await execa("which", [cmd], { timeout: 5e3 });
236
236
  return result.stdout.trim();
237
237
  } catch {
238
238
  return void 0;
@@ -475,10 +475,60 @@ var zh_CN = {
475
475
  status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
476
476
  status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
477
477
  status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
478
- status_install_time: "\u5B89\u88C5\u65F6\u95F4"
478
+ status_install_time: "\u5B89\u88C5\u65F6\u95F4",
479
+ install_installed_to: "\u5B89\u88C5\u5230",
480
+ install_skills_count: "Skills",
481
+ install_agents_count: "Agents",
482
+ lang_supported: "\u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US",
483
+ lang_usage: "\u7528\u6CD5: install-helper lang set <language>",
484
+ lang_specify: "\u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN",
485
+ lang_unknown_action: "\u672A\u77E5\u64CD\u4F5C",
486
+ lang_supported_actions: "\u652F\u6301\u7684\u64CD\u4F5C: show, set",
487
+ info_aliases: "\u522B\u540D",
488
+ uninstall_not_found: "\u672A\u627E\u5230",
489
+ uninstall_no_record: "\u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
490
+ uninstall_no_record_hint: "\u5982\u9700\u5F3A\u5236\u6E05\u7406\uFF0C\u8BF7\u624B\u52A8\u5220\u9664 .opencode/ \u6216 .claude/ \u76EE\u5F55\u4E2D\u7684\u76F8\u5173\u6587\u4EF6",
491
+ uninstall_in_progress: "\u6B63\u5728\u5378\u8F7D",
492
+ uninstall_removed: "\u5DF2\u5378\u8F7D",
493
+ uninstall_files_gone: "\u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55",
494
+ uninstall_skills_in_progress: "\u6B63\u5728\u5378\u8F7D",
495
+ init_pkg_dir_warn: "\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55",
496
+ init_pkg_dir_hint: "\u8BF7\u5728\u4F60\u7684\u9879\u76EE\u76EE\u5F55\u4E2D\u8FD0\u884C\u6B64\u547D\u4EE4\uFF0C\u800C\u4E0D\u662F\u5728 install-helper \u5B89\u88C5\u76EE\u5F55\u4E2D",
497
+ init_pkg_dir_example: "\u793A\u4F8B\uFF1Acd ~/my-project && install-helper",
498
+ init_cancelled: "\u5DF2\u53D6\u6D88\u5B89\u88C5",
499
+ backup_detected: "\u68C0\u6D4B\u5230\u5F53\u524D\u76EE\u5F55\u5DF2\u5B89\u88C5\u63D2\u4EF6",
500
+ backup_current: "\u5F53\u524D\u5B89\u88C5",
501
+ backup_installing: "\u5373\u5C06\u5B89\u88C5",
502
+ backup_overwrite_warning: "\u7EE7\u7EED\u5B89\u88C5\u5C06\u8986\u76D6\u73B0\u6709\u7684 AGENTS.md \u914D\u7F6E\u6587\u4EF6\u3002",
503
+ backup_auto_create: "\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u521B\u5EFA\u5907\u4EFD\uFF0C\u4EE5\u4FBF\u540E\u7EED\u6062\u590D\u3002",
504
+ backup_overwrite: "\u8986\u76D6\u5B89\u88C5\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09",
505
+ backup_cancel: "\u53D6\u6D88\u5B89\u88C5",
506
+ backup_restore_prompt: "\u68C0\u6D4B\u5230\u5907\u4EFD\u914D\u7F6E\uFF0C\u662F\u5426\u6062\u590D\uFF1F",
507
+ backup_restore: "\u6062\u590D",
508
+ backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
509
+ backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
510
+ backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
511
+ backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
512
+ loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
513
+ loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
514
+ loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
515
+ loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
516
+ loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
517
+ install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
518
+ install_to: "\u5B89\u88C5\u5230",
519
+ start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
520
+ wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
521
+ wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
522
+ wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
523
+ wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
524
+ wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
525
+ wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
526
+ install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
527
+ install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
528
+ wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B"
479
529
  };
480
530
  var en_US = {
481
- wizard_title: "CANNBot Skills Setup Wizard",
531
+ wizard_title: "CANNBot Install Helper",
482
532
  wizard_detect: "Detecting installed AI coding tools...",
483
533
  wizard_select_tool: "Select AI coding tool",
484
534
  wizard_select_level: "Select install level",
@@ -560,7 +610,57 @@ var en_US = {
560
610
  status_title: "Installed Plugins",
561
611
  status_none: "No installed plugins",
562
612
  status_hint: "Run install-helper init to start",
563
- status_install_time: "Installed at"
613
+ status_install_time: "Installed at",
614
+ install_installed_to: "Installed to",
615
+ install_skills_count: "Skills",
616
+ install_agents_count: "Agents",
617
+ lang_supported: "Supported languages: zh_CN, en_US",
618
+ lang_usage: "Usage: install-helper lang set <language>",
619
+ lang_specify: "Please specify language: install-helper lang set zh_CN",
620
+ lang_unknown_action: "Unknown action",
621
+ lang_supported_actions: "Supported actions: show, set",
622
+ info_aliases: "Aliases",
623
+ uninstall_not_found: "Not found",
624
+ uninstall_no_record: "No install record found, may not be installed or record is corrupted",
625
+ uninstall_no_record_hint: "For manual cleanup, delete related files in .opencode/ or .claude/ directory",
626
+ uninstall_in_progress: "Uninstalling",
627
+ uninstall_removed: "Uninstalled",
628
+ uninstall_files_gone: " files no longer exist, install record cleaned",
629
+ uninstall_skills_in_progress: "Uninstalling",
630
+ init_pkg_dir_warn: "Current directory appears to be the install-helper package directory",
631
+ init_pkg_dir_hint: "Please run this command in your project directory, not in the install-helper package directory",
632
+ init_pkg_dir_example: "Example: cd ~/my-project && install-helper",
633
+ init_cancelled: "Installation cancelled",
634
+ backup_detected: "Plugin already installed in current directory",
635
+ backup_current: "Currently installed",
636
+ backup_installing: "About to install",
637
+ backup_overwrite_warning: "Continuing will overwrite existing AGENTS.md configuration.",
638
+ backup_auto_create: "System will automatically create backup for future restoration.",
639
+ backup_overwrite: "Overwrite (auto backup)",
640
+ backup_cancel: "Cancel installation",
641
+ backup_restore_prompt: "Backup configuration detected, restore?",
642
+ backup_restore: "Restore",
643
+ backup_only_uninstall: "Uninstall only (no restore)",
644
+ backup_select_action: "Please select an action",
645
+ backup_restore_success: "Configuration restored",
646
+ backup_created: "Backup created",
647
+ loading_skills_list: "Loading Skills list...",
648
+ loading_skills_list_complete: "Skills list loaded",
649
+ loading_plugin_data: "Loading plugin data...",
650
+ loading_plugin_data_complete: "Plugin data loaded",
651
+ loading_skills_list_failed: "Failed to load Skills list, using built-in data",
652
+ install_no_skill_selected: "No Skill selected",
653
+ install_to: "Install to",
654
+ start_to_use: "Start {tool} to use",
655
+ wizard_step_1_title: "[1/4] Select AI coding tool",
656
+ wizard_step_2_title: "[2/4] Select installation location",
657
+ wizard_step_3_title: "[3/4] Select plugins to install",
658
+ wizard_step_4_title: "[4/4] Confirm installation",
659
+ wizard_step_3_skill_title: "[3/4] Select Skill category",
660
+ wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
661
+ install_level_project: "Install to current directory (recommended, current project only)",
662
+ install_level_global: "Install globally (all projects)",
663
+ wizard_select_mode_title: "Select installation type"
564
664
  };
565
665
  var currentLang = "zh_CN";
566
666
  function setLanguage(lang) {
@@ -624,7 +724,7 @@ function printBoxTitle(title, width = 65) {
624
724
  }
625
725
  function showOperationHints(isCheckbox = false) {
626
726
  if (isCheckbox) {
627
- console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u7A7A\u683C\x1B[0m \u9009\u62E9 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4
727
+ console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u7A7A\u683C\x1B[0m \u52FE\u9009 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4\uFF08\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE\uFF09
628
728
  `);
629
729
  } else {
630
730
  console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m \u79FB\u52A8 | \x1B[36m\u23CE\x1B[0m \u786E\u8BA4
@@ -793,7 +893,7 @@ async function runWizard() {
793
893
  }
794
894
  }
795
895
  async function stepTool(detectedTools, lastTool) {
796
- printBoxTitle("\u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177");
896
+ printBoxTitle(t("wizard_step_1_title"));
797
897
  if (detectedTools.length === 0) {
798
898
  logger.error(t("error_tool_not_found"));
799
899
  const choices2 = [
@@ -868,10 +968,10 @@ async function stepToolManual(lastTool) {
868
968
  return result;
869
969
  }
870
970
  async function stepLevel(lastLevel) {
871
- printBoxTitle("\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E");
971
+ printBoxTitle(t("wizard_step_2_title"));
872
972
  const choices = [
873
- { name: "> project \u2014 \u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09", value: "project" },
874
- { name: "> global \u2014 \u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09", value: "global" },
973
+ { name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
974
+ { name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
875
975
  new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
876
976
  { name: "<- " + t("wizard_back"), value: BACK },
877
977
  { name: "x " + t("wizard_cancel"), value: CANCEL }
@@ -889,7 +989,7 @@ async function stepLevel(lastLevel) {
889
989
  return result;
890
990
  }
891
991
  async function stepPlugins(tool, level) {
892
- printBoxTitle("\u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6");
992
+ printBoxTitle(t("wizard_step_3_title"));
893
993
  const plugins = getAllPlugins();
894
994
  const configRoot = getConfigRoot(tool, level);
895
995
  const manifests = readAllManifests(configRoot);
@@ -903,20 +1003,16 @@ async function stepPlugins(tool, level) {
903
1003
  checked: false
904
1004
  };
905
1005
  });
906
- choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
907
- choices.push({ name: "<- " + t("wizard_back"), value: BACK, checked: false });
908
- choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL, checked: false });
909
1006
  showOperationHints(true);
910
1007
  const selected = await checkbox({
911
1008
  message: t("wizard_select_plugins"),
912
1009
  choices,
913
1010
  loop: false,
914
1011
  instructions: false,
915
- theme: checkboxTheme
1012
+ theme: checkboxTheme,
1013
+ pageSize: 15
916
1014
  });
917
- if (selected.includes(BACK)) return BACK;
918
- if (selected.includes(CANCEL)) return CANCEL;
919
- const pluginIds = selected.filter((v) => v !== BACK && v !== CANCEL);
1015
+ const pluginIds = selected;
920
1016
  if (pluginIds.length === 0) {
921
1017
  const action = await select({
922
1018
  message: t("wizard_no_selection"),
@@ -934,14 +1030,17 @@ async function stepPlugins(tool, level) {
934
1030
  return pluginIds;
935
1031
  }
936
1032
  async function stepConfirm(tool, level, plugins) {
937
- printBoxTitle("\u786E\u8BA4\u5B89\u88C5");
1033
+ printBoxTitle(t("wizard_step_4_title"));
938
1034
  const allPlugins = getAllPlugins();
939
1035
  const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
940
1036
  const displayPath = getConfigRoot(tool, level);
941
- logger.info(`\u5373\u5C06\u5B89\u88C5 ${chalk2.bold(plugins.length)} \u4E2A\u573A\u666F\u5230 ${chalk2.cyan(displayPath)}\uFF1A`);
1037
+ const toolName = getToolDisplayName(tool);
1038
+ const levelText = level === "project" ? "project" : "global";
1039
+ 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`);
1040
+ logger.info(`\u5B89\u88C5\u8DEF\u5F84: ${chalk2.cyan(displayPath)}`);
942
1041
  for (const plugin of selectedPlugins) {
943
1042
  if (plugin) {
944
- logger.step(` \u2022 ${chalk2.bold(plugin.displayName)} ${chalk2.dim(`(${plugin.skills} skills, ${plugin.agents} agents)`)}`);
1043
+ logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
945
1044
  }
946
1045
  }
947
1046
  logger.blank();
@@ -965,7 +1064,7 @@ import { execa as execa2 } from "execa";
965
1064
 
966
1065
  // src/core/scanner.ts
967
1066
  import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
968
- import { join as join5, dirname } from "path";
1067
+ import { join as join5 } from "path";
969
1068
  import { parse as parseYaml } from "yaml";
970
1069
  var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
971
1070
  "node_modules",
@@ -1103,8 +1202,7 @@ function writeScanCache(cache) {
1103
1202
  }
1104
1203
  }
1105
1204
  function getCachePath() {
1106
- const homeDir = process.env.HOME || process.env.USERPROFILE || "/tmp";
1107
- return join5(homeDir, ".cannbot", "scan-cache.json");
1205
+ return join5(getCannbotConfigDir(), "scan-cache.json");
1108
1206
  }
1109
1207
  function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1110
1208
  const selfSkillMd = join5(dirPath, "SKILL.md");
@@ -1478,8 +1576,8 @@ function createRepositoryManager(customPath) {
1478
1576
  }
1479
1577
 
1480
1578
  // src/core/installer.ts
1481
- import { existsSync as existsSync8 } from "fs";
1482
- import { join as join8 } from "path";
1579
+ import { existsSync as existsSync9 } from "fs";
1580
+ import { join as join9 } from "path";
1483
1581
  import { execa as execa3 } from "execa";
1484
1582
 
1485
1583
  // src/core/record.ts
@@ -1573,7 +1671,6 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1573
1671
  files.push(repoLinkInConfig);
1574
1672
  }
1575
1673
  }
1576
- directories.push(configRoot);
1577
1674
  return {
1578
1675
  pluginId,
1579
1676
  displayName,
@@ -1653,6 +1750,179 @@ function getInstalledSkills(tool, level, installPath) {
1653
1750
  return record[tool]?.[level]?.[installPath]?.skills || [];
1654
1751
  }
1655
1752
 
1753
+ // src/core/backup.ts
1754
+ import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
1755
+ import { join as join8 } from "path";
1756
+ function getAgentsFileName(tool) {
1757
+ return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1758
+ }
1759
+ function detectCurrentPlugin(configRoot, tool) {
1760
+ const manifest = readManifest(configRoot);
1761
+ if (manifest?.team) {
1762
+ const plugin = findPlugin(manifest.team);
1763
+ if (plugin) {
1764
+ return {
1765
+ pluginId: plugin.id,
1766
+ pluginName: plugin.displayName
1767
+ };
1768
+ }
1769
+ }
1770
+ const agentsFile = join8(configRoot, getAgentsFileName(tool));
1771
+ if (!existsSync8(agentsFile)) {
1772
+ return null;
1773
+ }
1774
+ const allRecords = readAllRecords();
1775
+ for (const record of allRecords) {
1776
+ if (record.configRoot === configRoot && record.tool === tool) {
1777
+ const plugin = findPlugin(record.pluginId);
1778
+ if (plugin) {
1779
+ return {
1780
+ pluginId: plugin.id,
1781
+ pluginName: plugin.displayName
1782
+ };
1783
+ }
1784
+ }
1785
+ }
1786
+ return null;
1787
+ }
1788
+ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
1789
+ const agentsFile = join8(configRoot, getAgentsFileName(tool));
1790
+ if (!existsSync8(agentsFile)) {
1791
+ return null;
1792
+ }
1793
+ const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
1794
+ const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
1795
+ const backupPath = join8(configRoot, backupFileName);
1796
+ try {
1797
+ copyFileSync(agentsFile, backupPath);
1798
+ return {
1799
+ filePath: backupPath,
1800
+ pluginId: fromPluginId,
1801
+ pluginName: fromPluginName,
1802
+ backupTime: timestamp
1803
+ };
1804
+ } catch {
1805
+ return null;
1806
+ }
1807
+ }
1808
+ function findBackups(configRoot) {
1809
+ if (!existsSync8(configRoot)) {
1810
+ return [];
1811
+ }
1812
+ const files = readdirSync2(configRoot);
1813
+ const backups = [];
1814
+ for (const file of files) {
1815
+ if (file.includes(".cannbot-backup.")) {
1816
+ const parts = file.split(".cannbot-backup.");
1817
+ if (parts.length === 2) {
1818
+ const pluginIdAndTime = parts[1];
1819
+ const lastDotIndex = pluginIdAndTime.lastIndexOf(".");
1820
+ if (lastDotIndex > 0) {
1821
+ const pluginId = pluginIdAndTime.substring(0, lastDotIndex);
1822
+ const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
1823
+ const plugin = findPlugin(pluginId);
1824
+ backups.push({
1825
+ filePath: join8(configRoot, file),
1826
+ pluginId,
1827
+ pluginName: plugin?.displayName || pluginId,
1828
+ backupTime: timestamp
1829
+ });
1830
+ }
1831
+ }
1832
+ }
1833
+ }
1834
+ return backups.sort((a, b) => b.backupTime.localeCompare(a.backupTime));
1835
+ }
1836
+ function restoreBackup(backupPath, configRoot, tool) {
1837
+ try {
1838
+ const agentsFile = join8(configRoot, getAgentsFileName(tool));
1839
+ copyFileSync(backupPath, agentsFile);
1840
+ return true;
1841
+ } catch {
1842
+ return false;
1843
+ }
1844
+ }
1845
+ function deleteBackup(backupPath) {
1846
+ try {
1847
+ if (existsSync8(backupPath)) {
1848
+ unlinkSync2(backupPath);
1849
+ return true;
1850
+ }
1851
+ return false;
1852
+ } catch {
1853
+ return false;
1854
+ }
1855
+ }
1856
+ function formatTimestamp(date) {
1857
+ const year = date.getFullYear();
1858
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1859
+ const day = String(date.getDate()).padStart(2, "0");
1860
+ const hours = String(date.getHours()).padStart(2, "0");
1861
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1862
+ const seconds = String(date.getSeconds()).padStart(2, "0");
1863
+ return `${year}${month}${day}-${hours}${minutes}${seconds}`;
1864
+ }
1865
+ function readAllRecords() {
1866
+ const records = [];
1867
+ const plugins = [
1868
+ "ops-direct-invoke",
1869
+ "ops-direct-invoke-flash",
1870
+ "ops-registry-invoke",
1871
+ "pypto-op-orchestrator",
1872
+ "triton-op-generator",
1873
+ "tilelang-op-orchestrator",
1874
+ "model-infer-optimize",
1875
+ "catlass-op-generator",
1876
+ "ops-code-reviewer",
1877
+ "torch-compile"
1878
+ ];
1879
+ for (const pluginId of plugins) {
1880
+ const record = readRecord(pluginId);
1881
+ if (record) {
1882
+ records.push(record);
1883
+ }
1884
+ }
1885
+ return records;
1886
+ }
1887
+
1888
+ // src/ui/backup-prompts.ts
1889
+ import { select as select2 } from "@inquirer/prompts";
1890
+ import chalk3 from "chalk";
1891
+ async function showOverwriteWarning(currentPluginName, newPluginName) {
1892
+ console.log();
1893
+ console.log(chalk3.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
1894
+ console.log();
1895
+ console.log(` ${t("backup_current")}: ${chalk3.cyan(currentPluginName)}`);
1896
+ console.log(` ${t("backup_installing")}: ${chalk3.cyan(newPluginName)}`);
1897
+ console.log();
1898
+ console.log(` ${t("backup_overwrite_warning")}`);
1899
+ console.log(` ${t("backup_auto_create")}`);
1900
+ console.log();
1901
+ const choice = await select2({
1902
+ message: t("backup_select_action"),
1903
+ choices: [
1904
+ { name: t("backup_overwrite"), value: "overwrite" },
1905
+ { name: t("backup_cancel"), value: "cancel" }
1906
+ ]
1907
+ });
1908
+ return choice;
1909
+ }
1910
+ async function showRestorePrompt(backups) {
1911
+ console.log();
1912
+ console.log(chalk3.cyan(` ${t("backup_restore_prompt")}`));
1913
+ console.log();
1914
+ const choices = backups.map((b) => ({
1915
+ name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
1916
+ value: b.filePath
1917
+ }));
1918
+ choices.push({ name: t("backup_only_uninstall"), value: "none" });
1919
+ const choice = await select2({
1920
+ message: t("backup_select_action"),
1921
+ choices
1922
+ });
1923
+ return choice;
1924
+ }
1925
+
1656
1926
  // src/core/installer.ts
1657
1927
  async function installPlugin(opts) {
1658
1928
  const plugin = getPluginById(opts.pluginId);
@@ -1666,8 +1936,8 @@ async function installPlugin(opts) {
1666
1936
  warnings: []
1667
1937
  };
1668
1938
  }
1669
- const scriptPath = join8(opts.repoPath, plugin.dir, plugin.script);
1670
- if (!existsSync8(scriptPath)) {
1939
+ const scriptPath = join9(opts.repoPath, plugin.dir, plugin.script);
1940
+ if (!existsSync9(scriptPath)) {
1671
1941
  return {
1672
1942
  success: false,
1673
1943
  pluginId: opts.pluginId,
@@ -1682,13 +1952,49 @@ async function installPlugin(opts) {
1682
1952
  args.push(opts.installPath);
1683
1953
  }
1684
1954
  const cwd = opts.installPath || process.cwd();
1955
+ const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
1956
+ const agentsFile = join9(configRoot, getAgentsFileName(opts.tool));
1957
+ let backupInfo = null;
1958
+ if (existsSync9(agentsFile)) {
1959
+ const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
1960
+ if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
1961
+ let choice = "overwrite";
1962
+ if (!opts.yes) {
1963
+ choice = await showOverwriteWarning(
1964
+ currentPlugin.pluginName,
1965
+ plugin.displayName
1966
+ );
1967
+ }
1968
+ if (choice === "cancel") {
1969
+ logger.info(t("backup_cancel"));
1970
+ return {
1971
+ success: false,
1972
+ pluginId: opts.pluginId,
1973
+ skillsCount: 0,
1974
+ agentsCount: 0,
1975
+ errors: [t("backup_cancel")],
1976
+ warnings: []
1977
+ };
1978
+ }
1979
+ backupInfo = createBackup(
1980
+ configRoot,
1981
+ opts.tool,
1982
+ currentPlugin.pluginId,
1983
+ currentPlugin.pluginName
1984
+ );
1985
+ if (backupInfo) {
1986
+ logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
1987
+ }
1988
+ }
1989
+ }
1685
1990
  try {
1686
- await execa3("bash", [scriptPath, ...args], {
1991
+ const execOptions = {
1687
1992
  cwd,
1688
1993
  timeout: 3e5,
1689
- stdio: "pipe"
1690
- });
1691
- const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
1994
+ stdio: "pipe",
1995
+ input: opts.yes ? "y\n" : void 0
1996
+ };
1997
+ await execa3("bash", [scriptPath, ...args], execOptions);
1692
1998
  const manifest = readManifest(configRoot);
1693
1999
  let skillsCount = 0;
1694
2000
  let agentsCount = 0;
@@ -1709,6 +2015,14 @@ async function installPlugin(opts) {
1709
2015
  configRoot,
1710
2016
  manifest
1711
2017
  );
2018
+ if (backupInfo) {
2019
+ record.backup = {
2020
+ filePath: backupInfo.filePath,
2021
+ fromPluginId: backupInfo.pluginId,
2022
+ fromPluginName: backupInfo.pluginName,
2023
+ backupTime: backupInfo.backupTime
2024
+ };
2025
+ }
1712
2026
  writeRecord(record);
1713
2027
  } catch {
1714
2028
  }
@@ -1734,16 +2048,16 @@ async function installPlugin(opts) {
1734
2048
  }
1735
2049
 
1736
2050
  // src/core/skill-installer.ts
1737
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync2, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
1738
- import { join as join9 } from "path";
1739
- import { select as select2, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
1740
- import chalk3 from "chalk";
2051
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
2052
+ import { join as join10 } from "path";
2053
+ import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
2054
+ import chalk4 from "chalk";
1741
2055
  import Table from "cli-table3";
1742
2056
  async function installSkills(skillIds, tool, level, repoPath) {
1743
2057
  const configRoot = getConfigRoot(tool, level);
1744
- const skillsDir = join9(configRoot, "skills");
2058
+ const skillsDir = join10(configRoot, "skills");
1745
2059
  const installPath = level === "project" ? process.cwd() : configRoot;
1746
- if (!existsSync9(skillsDir)) {
2060
+ if (!existsSync10(skillsDir)) {
1747
2061
  mkdirSync5(skillsDir, { recursive: true });
1748
2062
  }
1749
2063
  const results = [];
@@ -1754,15 +2068,15 @@ async function installSkills(skillIds, tool, level, repoPath) {
1754
2068
  results.push({ skillId, success: false, error: "Skill not found in registry" });
1755
2069
  continue;
1756
2070
  }
1757
- const sourcePath = join9(repoPath, skill.source, skill.id);
1758
- const targetPath = join9(skillsDir, skillId);
1759
- if (!existsSync9(sourcePath)) {
2071
+ const sourcePath = join10(repoPath, skill.source, skill.id);
2072
+ const targetPath = join10(skillsDir, skillId);
2073
+ if (!existsSync10(sourcePath)) {
1760
2074
  results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
1761
2075
  continue;
1762
2076
  }
1763
2077
  try {
1764
- if (existsSync9(targetPath) || isSymlink2(targetPath)) {
1765
- unlinkSync2(targetPath);
2078
+ if (existsSync10(targetPath) || isSymlink2(targetPath)) {
2079
+ unlinkSync3(targetPath);
1766
2080
  }
1767
2081
  symlinkSync(realpathSync(sourcePath), targetPath);
1768
2082
  results.push({ skillId, success: true });
@@ -1778,15 +2092,15 @@ async function installSkills(skillIds, tool, level, repoPath) {
1778
2092
  }
1779
2093
  async function uninstallSkills(skillIds, tool, level) {
1780
2094
  const configRoot = getConfigRoot(tool, level);
1781
- const skillsDir = join9(configRoot, "skills");
2095
+ const skillsDir = join10(configRoot, "skills");
1782
2096
  const installPath = level === "project" ? process.cwd() : configRoot;
1783
2097
  const results = [];
1784
2098
  const removedIds = [];
1785
2099
  for (const skillId of skillIds) {
1786
- const targetPath = join9(skillsDir, skillId);
2100
+ const targetPath = join10(skillsDir, skillId);
1787
2101
  try {
1788
- if (existsSync9(targetPath) || isSymlink2(targetPath)) {
1789
- unlinkSync2(targetPath);
2102
+ if (existsSync10(targetPath) || isSymlink2(targetPath)) {
2103
+ unlinkSync3(targetPath);
1790
2104
  results.push({ skillId, success: true });
1791
2105
  removedIds.push(skillId);
1792
2106
  logger.step(` \u79FB\u9664: ${skillId}`);
@@ -1801,7 +2115,7 @@ async function uninstallSkills(skillIds, tool, level) {
1801
2115
  if (removedIds.length > 0) {
1802
2116
  removeSkillsFromRecord(removedIds, tool, level, installPath);
1803
2117
  }
1804
- if (existsSync9(skillsDir)) {
2118
+ if (existsSync10(skillsDir)) {
1805
2119
  try {
1806
2120
  const entries = readdirSync3(skillsDir);
1807
2121
  if (entries.length === 0) {
@@ -1811,16 +2125,6 @@ async function uninstallSkills(skillIds, tool, level) {
1811
2125
  } catch {
1812
2126
  }
1813
2127
  }
1814
- if (existsSync9(configRoot)) {
1815
- try {
1816
- const entries = readdirSync3(configRoot);
1817
- if (entries.length === 0) {
1818
- rmdirSync(configRoot);
1819
- logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
1820
- }
1821
- } catch {
1822
- }
1823
- }
1824
2128
  return results;
1825
2129
  }
1826
2130
  async function interactiveSkillSelect() {
@@ -1832,7 +2136,7 @@ async function interactiveSkillSelect() {
1832
2136
  while (true) {
1833
2137
  switch (step) {
1834
2138
  case 0: {
1835
- printBoxTitle("\u9009\u62E9 Skill \u7C7B\u522B");
2139
+ printBoxTitle(t("wizard_step_3_skill_title"));
1836
2140
  const categoryChoices = [
1837
2141
  ...categories.map((cat) => ({
1838
2142
  name: `> ${cat.name} (${cat.skills.length} skills)`,
@@ -1843,11 +2147,12 @@ async function interactiveSkillSelect() {
1843
2147
  { name: "x " + t("wizard_cancel"), value: CANCEL3 }
1844
2148
  ];
1845
2149
  showOperationHints();
1846
- selectedCategoryId = await select2({
2150
+ selectedCategoryId = await select3({
1847
2151
  message: t("skill_select_category"),
1848
2152
  choices: categoryChoices,
1849
2153
  loop: false,
1850
- theme: selectTheme
2154
+ theme: selectTheme,
2155
+ pageSize: 15
1851
2156
  });
1852
2157
  if (selectedCategoryId === BACK3) return "back";
1853
2158
  if (selectedCategoryId === CANCEL3) return "cancel";
@@ -1857,7 +2162,7 @@ async function interactiveSkillSelect() {
1857
2162
  case 1: {
1858
2163
  const category = categories.find((c) => c.id === selectedCategoryId);
1859
2164
  if (!category) return [];
1860
- printBoxTitle(`\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 ${category.name}`);
2165
+ printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
1861
2166
  const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
1862
2167
  const levels = ["project", "global"];
1863
2168
  const installedSkills = [];
@@ -1877,25 +2182,18 @@ async function interactiveSkillSelect() {
1877
2182
  checked: false
1878
2183
  };
1879
2184
  });
1880
- skillChoices.push(new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1881
- skillChoices.push({ name: "<- " + t("wizard_back"), value: BACK3, checked: false });
1882
- skillChoices.push({ name: "x " + t("wizard_cancel"), value: CANCEL3, checked: false });
1883
2185
  showOperationHints(true);
1884
2186
  const selectedSkills = await checkbox2({
1885
2187
  message: t("skill_select_items"),
1886
2188
  choices: skillChoices,
1887
2189
  loop: false,
1888
2190
  instructions: false,
1889
- theme: checkboxTheme
2191
+ theme: checkboxTheme,
2192
+ pageSize: 15
1890
2193
  });
1891
- if (selectedSkills.includes(BACK3)) {
1892
- step = 0;
1893
- break;
1894
- }
1895
- if (selectedSkills.includes(CANCEL3)) return "cancel";
1896
- const skillIds = selectedSkills.filter((v) => v !== BACK3 && v !== CANCEL3);
2194
+ const skillIds = selectedSkills;
1897
2195
  if (skillIds.length === 0) {
1898
- const action = await select2({
2196
+ const action = await select3({
1899
2197
  message: t("wizard_no_selection"),
1900
2198
  choices: [
1901
2199
  new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
@@ -1919,11 +2217,11 @@ async function interactiveSkillSelect() {
1919
2217
  function listAllSkills() {
1920
2218
  const categories = getAllCategories();
1921
2219
  console.log();
1922
- console.log(chalk3.bold(` ${t("skill_list_title")}`));
1923
- console.log(chalk3.dim(" " + "\u2500".repeat(60)));
2220
+ console.log(chalk4.bold(` ${t("skill_list_title")}`));
2221
+ console.log(chalk4.dim(" " + "\u2500".repeat(60)));
1924
2222
  for (const category of categories) {
1925
2223
  console.log();
1926
- console.log(chalk3.bold(` ${category.name}`) + chalk3.dim(` (${category.skills.length} skills)`));
2224
+ console.log(chalk4.bold(` ${category.name}`) + chalk4.dim(` (${category.skills.length} skills)`));
1927
2225
  const table = new Table({
1928
2226
  style: { head: [], border: [] },
1929
2227
  colWidths: [35, 45],
@@ -1931,14 +2229,14 @@ function listAllSkills() {
1931
2229
  });
1932
2230
  for (const skill of category.skills) {
1933
2231
  table.push([
1934
- chalk3.cyan(skill.id),
2232
+ chalk4.cyan(skill.id),
1935
2233
  skill.description
1936
2234
  ]);
1937
2235
  }
1938
2236
  console.log(table.toString());
1939
2237
  }
1940
2238
  console.log();
1941
- console.log(chalk3.dim(` \u5171 ${getAllSkills().length} \u4E2A Skills`));
2239
+ console.log(chalk4.dim(` \u5171 ${getAllSkills().length} \u4E2A Skills`));
1942
2240
  console.log();
1943
2241
  }
1944
2242
  function isSymlink2(path) {
@@ -1950,22 +2248,22 @@ function isSymlink2(path) {
1950
2248
  }
1951
2249
 
1952
2250
  // src/ui/display.ts
1953
- import chalk4 from "chalk";
2251
+ import chalk5 from "chalk";
1954
2252
  import Table2 from "cli-table3";
1955
2253
  function printPluginList(plugins, installed) {
1956
2254
  const table = new Table2({
1957
2255
  head: [
1958
- chalk4.cyan(t("list_id")),
1959
- chalk4.cyan(t("list_name")),
1960
- chalk4.cyan(t("list_status")),
1961
- chalk4.cyan(t("list_skills")),
1962
- chalk4.cyan(t("list_agents"))
2256
+ chalk5.cyan(t("list_id")),
2257
+ chalk5.cyan(t("list_name")),
2258
+ chalk5.cyan(t("list_status")),
2259
+ chalk5.cyan(t("list_skills")),
2260
+ chalk5.cyan(t("list_agents"))
1963
2261
  ],
1964
2262
  style: { head: [], border: [] }
1965
2263
  });
1966
2264
  plugins.forEach((plugin, index) => {
1967
2265
  const isInstalled = installed.has(plugin.id);
1968
- const status = isInstalled ? chalk4.green(`\u2713 ${t("status_installed")}`) : chalk4.dim(`\u2014`);
2266
+ const status = isInstalled ? chalk5.green(`\u2713 ${t("status_installed")}`) : chalk5.dim(`\u2014`);
1969
2267
  table.push([
1970
2268
  String(index + 1),
1971
2269
  plugin.displayName,
@@ -1975,7 +2273,7 @@ function printPluginList(plugins, installed) {
1975
2273
  ]);
1976
2274
  });
1977
2275
  console.log();
1978
- console.log(chalk4.bold(` ${t("list_title")} (${plugins.length} \u4E2A)`));
2276
+ console.log(chalk5.bold(` ${t("list_title")} (${plugins.length} \u4E2A)`));
1979
2277
  console.log(table.toString());
1980
2278
  console.log();
1981
2279
  }
@@ -1984,7 +2282,7 @@ function printInstallSummary(results) {
1984
2282
  const totalCount = results.length;
1985
2283
  console.log();
1986
2284
  console.log(
1987
- chalk4.bold(
2285
+ chalk5.bold(
1988
2286
  ` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
1989
2287
  )
1990
2288
  );
@@ -1992,10 +2290,10 @@ function printInstallSummary(results) {
1992
2290
  for (const result of results) {
1993
2291
  if (result.success) {
1994
2292
  console.log(
1995
- chalk4.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
2293
+ chalk5.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
1996
2294
  );
1997
2295
  } else {
1998
- console.log(chalk4.red(" \u2717") + ` ${result.displayName}`);
2296
+ console.log(chalk5.red(" \u2717") + ` ${result.displayName}`);
1999
2297
  }
2000
2298
  }
2001
2299
  console.log();
@@ -2009,21 +2307,21 @@ function printEnhancedSummary(results, tool, configRoot) {
2009
2307
  }
2010
2308
  const toolName = getToolDisplayName(tool);
2011
2309
  const lines = [
2012
- chalk4.bold(` ${t("install_done")}!`),
2310
+ chalk5.bold(` ${t("install_done")}!`),
2013
2311
  "",
2014
- ` ${chalk4.dim("\u5B89\u88C5\u5230:")} ${configRoot}`,
2015
- ` ${chalk4.dim("Skills:")} ${totalSkills} \u4E2A ${chalk4.dim("|")} ${chalk4.dim("Agents:")} ${totalAgents} \u4E2A`,
2312
+ ` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
2313
+ ` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills} \u4E2A ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents} \u4E2A`,
2016
2314
  "",
2017
- chalk4.bold(` ${t("install_enhanced_next_steps")}:`),
2018
- ` ${chalk4.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk4.green(toolName.toLowerCase())}`,
2019
- ` ${chalk4.cyan("2.")} ${t("install_enhanced_try")}: ${chalk4.green("\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50")}`,
2020
- ` ${chalk4.cyan("3.")} ${t("install_enhanced_more")}: ${chalk4.green("install-helper list")}`,
2021
- ` ${chalk4.cyan("4.")} ${t("install_enhanced_check")}: ${chalk4.green("install-helper doctor")}`,
2315
+ chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
2316
+ ` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
2317
+ ` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50")}`,
2318
+ ` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
2319
+ ` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
2022
2320
  ""
2023
2321
  ];
2024
2322
  if (successResults.length === 1) {
2025
2323
  lines.push(
2026
- ` ${chalk4.dim(t("install_enhanced_docs"))}: ${chalk4.dim(`plugins-official/${successResults[0].pluginId}/quickstart.md`)}`
2324
+ ` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(`plugins-official/${successResults[0].pluginId}/quickstart.md`)}`
2027
2325
  );
2028
2326
  }
2029
2327
  console.log();
@@ -2034,34 +2332,34 @@ function printEnhancedSummary(results, tool, configRoot) {
2034
2332
  }
2035
2333
 
2036
2334
  // src/commands/init.ts
2037
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
2038
- import { join as join10 } from "path";
2335
+ import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
2336
+ import { join as join11 } from "path";
2039
2337
  var BACK2 = "__back__";
2040
2338
  var CANCEL2 = "__cancel__";
2041
2339
  async function initCommand() {
2042
2340
  const cwd = process.cwd();
2043
- if (existsSync10(join10(cwd, "package.json")) && (() => {
2341
+ if (existsSync11(join11(cwd, "package.json")) && (() => {
2044
2342
  try {
2045
- const pkg = JSON.parse(readFileSync5(join10(cwd, "package.json"), "utf-8"));
2343
+ const pkg = JSON.parse(readFileSync5(join11(cwd, "package.json"), "utf-8"));
2046
2344
  return pkg.name === "@cannbot-ai/install-helper";
2047
2345
  } catch {
2048
2346
  return false;
2049
2347
  }
2050
2348
  })()) {
2051
- logger.warn("\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55");
2052
- logger.info("\u8BF7\u5728\u4F60\u7684\u9879\u76EE\u76EE\u5F55\u4E2D\u8FD0\u884C\u6B64\u547D\u4EE4\uFF0C\u800C\u4E0D\u662F\u5728 install-helper \u5B89\u88C5\u76EE\u5F55\u4E2D");
2053
- logger.info("\u793A\u4F8B\uFF1Acd ~/my-project && install-helper");
2349
+ logger.warn(t("init_pkg_dir_warn"));
2350
+ logger.info(t("init_pkg_dir_hint"));
2351
+ logger.info(t("init_pkg_dir_example"));
2054
2352
  return;
2055
2353
  }
2056
2354
  printBanner(t("wizard_title"));
2057
- const spinner = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2355
+ const spinner = createSpinner(t("loading_skills_list"));
2058
2356
  spinner.start();
2059
2357
  try {
2060
2358
  const repoManager = createRepositoryManager();
2061
2359
  await repoManager.ensureRepoAndScan();
2062
- spinner.succeed("Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210");
2360
+ spinner.succeed(t("loading_skills_list_complete"));
2063
2361
  } catch {
2064
- spinner.warn("Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E");
2362
+ spinner.warn(t("loading_skills_list_failed"));
2065
2363
  }
2066
2364
  let step = 0;
2067
2365
  let mode = "plugin";
@@ -2095,7 +2393,7 @@ async function initCommand() {
2095
2393
  }
2096
2394
  }
2097
2395
  async function selectMode() {
2098
- printBoxTitle("\u9009\u62E9\u5B89\u88C5\u7C7B\u578B");
2396
+ printBoxTitle(t("wizard_select_mode_title"));
2099
2397
  const CANCEL3 = "__cancel__";
2100
2398
  const choices = [
2101
2399
  {
@@ -2110,7 +2408,7 @@ async function selectMode() {
2110
2408
  { name: "x " + t("wizard_cancel"), value: CANCEL3 }
2111
2409
  ];
2112
2410
  showOperationHints();
2113
- const result = await select3({
2411
+ const result = await select4({
2114
2412
  message: t("wizard_select_mode"),
2115
2413
  choices,
2116
2414
  loop: false,
@@ -2123,14 +2421,14 @@ async function pluginInstallFlow() {
2123
2421
  const answers = await runWizard();
2124
2422
  if (!answers.confirmed) {
2125
2423
  if (answers.back) return "back";
2126
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2424
+ logger.info(t("init_cancelled"));
2127
2425
  return "done";
2128
2426
  }
2129
2427
  const repoManager = createRepositoryManager();
2130
- const spinner = createSpinner(t("install_cloning"));
2428
+ const spinner = createSpinner(t("loading_plugin_data"));
2131
2429
  spinner.start();
2132
2430
  const repoPath = await repoManager.ensureRepo();
2133
- spinner.succeed(t("install_repo_ready"));
2431
+ spinner.succeed(t("loading_plugin_data_complete"));
2134
2432
  const allPlugins = getAllPlugins();
2135
2433
  const selectedPlugins = answers.plugins.map(
2136
2434
  (id) => allPlugins.find((p) => p.id === id)
@@ -2184,7 +2482,7 @@ async function skillInstallFlow() {
2184
2482
  const result = await selectToolWithDetection();
2185
2483
  if (result === "back") return "back";
2186
2484
  if (result === "cancel") {
2187
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2485
+ logger.info(t("init_cancelled"));
2188
2486
  return "done";
2189
2487
  }
2190
2488
  tool = result;
@@ -2198,7 +2496,7 @@ async function skillInstallFlow() {
2198
2496
  break;
2199
2497
  }
2200
2498
  if (result === CANCEL2) {
2201
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2499
+ logger.info(t("init_cancelled"));
2202
2500
  return "done";
2203
2501
  }
2204
2502
  level = result;
@@ -2212,37 +2510,40 @@ async function skillInstallFlow() {
2212
2510
  break;
2213
2511
  }
2214
2512
  if (selectedSkills === "cancel") {
2215
- logger.info("\u5DF2\u53D6\u6D88\u5B89\u88C5");
2513
+ logger.info(t("init_cancelled"));
2216
2514
  return "done";
2217
2515
  }
2218
2516
  if (selectedSkills.length === 0) {
2219
- logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Skill");
2517
+ logger.info(t("install_no_skill_selected"));
2220
2518
  return "done";
2221
2519
  }
2222
2520
  const repoManager = createRepositoryManager();
2223
- const spinner = createSpinner(t("install_cloning"));
2521
+ const spinner = createSpinner(t("loading_plugin_data"));
2224
2522
  spinner.start();
2225
2523
  const repoPath = await repoManager.ensureRepo();
2226
- spinner.succeed(t("install_repo_ready"));
2227
- logger.info(`${t("skill_install_progress")} ${chalk5.bold(selectedSkills.length)} \u4E2A Skills...`);
2524
+ spinner.succeed(t("loading_plugin_data_complete"));
2525
+ logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)} \u4E2A Skills...`);
2228
2526
  const results = await installSkills(selectedSkills, tool, level, repoPath);
2229
2527
  let successCount = 0;
2230
2528
  let failCount = 0;
2231
- for (const result of results) {
2529
+ const total = results.length;
2530
+ for (let i = 0; i < results.length; i++) {
2531
+ const result = results[i];
2532
+ const progress = `[${i + 1}/${total}]`;
2232
2533
  if (result.success) {
2233
- logger.success(`${result.skillId}`);
2534
+ logger.success(`${progress} ${result.skillId}`);
2234
2535
  successCount++;
2235
2536
  } else {
2236
- logger.error(`${result.skillId}: ${result.error}`);
2537
+ logger.error(`${progress} ${result.skillId}: ${result.error}`);
2237
2538
  failCount++;
2238
2539
  }
2239
2540
  }
2240
2541
  const configRoot = getConfigRoot(tool, level);
2241
2542
  logger.blank();
2242
- logger.success(`${t("skill_install_done")}: ${chalk5.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk5.red(failCount + " \u5931\u8D25") : chalk5.dim(failCount + " \u5931\u8D25")}`);
2543
+ logger.success(`${t("skill_install_done")}: ${chalk6.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk6.red(failCount + " \u5931\u8D25") : chalk6.dim(failCount + " \u5931\u8D25")}`);
2243
2544
  logger.blank();
2244
- logger.info(`\u5B89\u88C5\u5230: ${chalk5.cyan(configRoot + "/skills/")}`);
2245
- logger.info(`\u542F\u52A8 ${chalk5.green(tool)} \u5373\u53EF\u4F7F\u7528`);
2545
+ logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
2546
+ logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
2246
2547
  logger.blank();
2247
2548
  return "done";
2248
2549
  }
@@ -2264,32 +2565,32 @@ async function listCommand() {
2264
2565
  }
2265
2566
 
2266
2567
  // src/commands/doctor.ts
2267
- import chalk6 from "chalk";
2268
- import { existsSync as existsSync11, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync3, mkdirSync as mkdirSync6 } from "fs";
2269
- import { join as join11 } from "path";
2568
+ import chalk7 from "chalk";
2569
+ import { existsSync as existsSync12, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2570
+ import { join as join12 } from "path";
2270
2571
  async function doctorCommand(options = {}) {
2271
2572
  console.log();
2272
- console.log(chalk6.bold(` ${t("doctor_title")}`));
2273
- console.log(chalk6.dim(" " + "\u2500".repeat(46)));
2573
+ console.log(chalk7.bold(` ${t("doctor_title")}`));
2574
+ console.log(chalk7.dim(" " + "\u2500".repeat(46)));
2274
2575
  let errors = 0;
2275
2576
  let warnings = 0;
2276
2577
  let fixes = 0;
2277
2578
  console.log();
2278
- console.log(chalk6.bold(` ${t("doctor_tools")}`));
2579
+ console.log(chalk7.bold(` ${t("doctor_tools")}`));
2279
2580
  const detectedTools = await detectTools();
2280
2581
  const allTools = getAllTools();
2281
2582
  for (const tool of allTools) {
2282
2583
  const detected = detectedTools.find((d) => d.name === tool);
2283
2584
  if (detected) {
2284
2585
  console.log(
2285
- chalk6.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
2586
+ chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
2286
2587
  );
2287
2588
  } else {
2288
- console.log(chalk6.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 \u672A\u5B89\u88C5`);
2589
+ console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 \u672A\u5B89\u88C5`);
2289
2590
  }
2290
2591
  }
2291
2592
  console.log();
2292
- console.log(chalk6.bold(` ${t("doctor_plugins")}`));
2593
+ console.log(chalk7.bold(` ${t("doctor_plugins")}`));
2293
2594
  const plugins = getAllPlugins();
2294
2595
  const installed = scanInstalled();
2295
2596
  const installedMap = new Map(installed.map((p) => [p.id, p]));
@@ -2297,73 +2598,73 @@ async function doctorCommand(options = {}) {
2297
2598
  const inst = installedMap.get(plugin.id);
2298
2599
  if (inst) {
2299
2600
  console.log(
2300
- chalk6.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
2601
+ chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
2301
2602
  );
2302
2603
  } else {
2303
- console.log(chalk6.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 \u672A\u5B89\u88C5`);
2604
+ console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 \u672A\u5B89\u88C5`);
2304
2605
  }
2305
2606
  }
2306
2607
  console.log();
2307
- console.log(chalk6.bold(` ${t("doctor_links")}`));
2608
+ console.log(chalk7.bold(` ${t("doctor_links")}`));
2308
2609
  const primaryTool = detectedTools[0]?.name || "opencode";
2309
2610
  const configRoot = getConfigRoot(primaryTool, "project");
2310
2611
  const skillsDir = getSkillsDir(configRoot);
2311
2612
  const agentsDir = getAgentsDir(configRoot);
2312
- if (existsSync11(skillsDir)) {
2613
+ if (existsSync12(skillsDir)) {
2313
2614
  const brokenLinks = checkBrokenLinks(skillsDir);
2314
2615
  if (brokenLinks === 0) {
2315
2616
  const count = readdirSync4(skillsDir).length;
2316
- console.log(chalk6.green(" \u2713") + ` ${skillsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2617
+ console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2317
2618
  } else {
2318
- console.log(chalk6.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2619
+ console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2319
2620
  warnings++;
2320
2621
  if (options.fix) {
2321
2622
  const fixed = fixBrokenLinks(skillsDir);
2322
2623
  fixes += fixed;
2323
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2624
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2324
2625
  }
2325
2626
  }
2326
2627
  } else {
2327
- console.log(chalk6.dim(" \u2014") + ` ${skillsDir} \u2014 \u4E0D\u5B58\u5728`);
2628
+ console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 \u4E0D\u5B58\u5728`);
2328
2629
  if (options.fix) {
2329
2630
  mkdirSync6(skillsDir, { recursive: true });
2330
2631
  fixes++;
2331
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${skillsDir}`);
2632
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${skillsDir}`);
2332
2633
  }
2333
2634
  }
2334
- if (existsSync11(agentsDir)) {
2635
+ if (existsSync12(agentsDir)) {
2335
2636
  const brokenLinks = checkBrokenLinks(agentsDir);
2336
2637
  if (brokenLinks === 0) {
2337
2638
  const count = readdirSync4(agentsDir).length;
2338
- console.log(chalk6.green(" \u2713") + ` ${agentsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2639
+ console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548`);
2339
2640
  } else {
2340
- console.log(chalk6.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2641
+ console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks} \u4E2A\u5931\u6548\u94FE\u63A5`);
2341
2642
  warnings++;
2342
2643
  if (options.fix) {
2343
2644
  const fixed = fixBrokenLinks(agentsDir);
2344
2645
  fixes += fixed;
2345
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2646
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: \u5DF2\u6E05\u7406 ${fixed} \u4E2A\u5931\u6548\u94FE\u63A5`);
2346
2647
  }
2347
2648
  }
2348
2649
  } else {
2349
- console.log(chalk6.dim(" \u2014") + ` ${agentsDir} \u2014 \u4E0D\u5B58\u5728`);
2650
+ console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 \u4E0D\u5B58\u5728`);
2350
2651
  if (options.fix) {
2351
2652
  mkdirSync6(agentsDir, { recursive: true });
2352
2653
  fixes++;
2353
- console.log(chalk6.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${agentsDir}`);
2654
+ console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: \u5DF2\u521B\u5EFA ${agentsDir}`);
2354
2655
  }
2355
2656
  }
2356
2657
  console.log();
2357
- console.log(chalk6.bold(` ${t("doctor_config")}`));
2658
+ console.log(chalk7.bold(` ${t("doctor_config")}`));
2358
2659
  const configFile = getConfigFileName(primaryTool);
2359
- const configPath = join11(process.cwd(), configFile);
2360
- if (existsSync11(configPath)) {
2361
- console.log(chalk6.green(" \u2713") + ` ${configFile} \u5B58\u5728`);
2660
+ const configPath = join12(process.cwd(), configFile);
2661
+ if (existsSync12(configPath)) {
2662
+ console.log(chalk7.green(" \u2713") + ` ${configFile} \u5B58\u5728`);
2362
2663
  } else {
2363
- console.log(chalk6.dim(" \u2014") + ` ${configFile} \u4E0D\u5B58\u5728`);
2664
+ console.log(chalk7.dim(" \u2014") + ` ${configFile} \u4E0D\u5B58\u5728`);
2364
2665
  }
2365
2666
  console.log();
2366
- console.log(chalk6.dim(" " + "\u2500".repeat(46)));
2667
+ console.log(chalk7.dim(" " + "\u2500".repeat(46)));
2367
2668
  if (options.fix && fixes > 0) {
2368
2669
  console.log(
2369
2670
  ` ${t("doctor_result")}: ${warnings} \u4E2A\u8B66\u544A, ${errors} \u4E2A\u9519\u8BEF, ${fixes} \u9879\u5DF2\u4FEE\u590D`
@@ -2380,11 +2681,11 @@ function checkBrokenLinks(dir) {
2380
2681
  try {
2381
2682
  const entries = readdirSync4(dir);
2382
2683
  for (const entry of entries) {
2383
- const fullPath = join11(dir, entry);
2684
+ const fullPath = join12(dir, entry);
2384
2685
  try {
2385
2686
  const stats = lstatSync3(fullPath);
2386
2687
  if (stats.isSymbolicLink()) {
2387
- if (!existsSync11(fullPath)) {
2688
+ if (!existsSync12(fullPath)) {
2388
2689
  broken++;
2389
2690
  }
2390
2691
  }
@@ -2401,12 +2702,12 @@ function fixBrokenLinks(dir) {
2401
2702
  try {
2402
2703
  const entries = readdirSync4(dir);
2403
2704
  for (const entry of entries) {
2404
- const fullPath = join11(dir, entry);
2705
+ const fullPath = join12(dir, entry);
2405
2706
  try {
2406
2707
  const stats = lstatSync3(fullPath);
2407
2708
  if (stats.isSymbolicLink()) {
2408
- if (!existsSync11(fullPath)) {
2409
- unlinkSync3(fullPath);
2709
+ if (!existsSync12(fullPath)) {
2710
+ unlinkSync4(fullPath);
2410
2711
  fixed++;
2411
2712
  }
2412
2713
  }
@@ -2419,36 +2720,36 @@ function fixBrokenLinks(dir) {
2419
2720
  }
2420
2721
 
2421
2722
  // src/commands/status.ts
2422
- import chalk7 from "chalk";
2723
+ import chalk8 from "chalk";
2423
2724
  async function statusCommand() {
2424
2725
  const plugins = getAllPlugins();
2425
2726
  const installed = scanInstalled();
2426
2727
  const installedMap = new Map(installed.map((p) => [p.id, p]));
2427
2728
  console.log();
2428
- console.log(chalk7.bold(` ${t("status_title")}`));
2729
+ console.log(chalk8.bold(` ${t("status_title")}`));
2429
2730
  console.log();
2430
2731
  if (installed.length === 0) {
2431
- console.log(chalk7.dim(` ${t("status_none")}`));
2432
- console.log(chalk7.dim(` ${t("status_hint")}`));
2732
+ console.log(chalk8.dim(` ${t("status_none")}`));
2733
+ console.log(chalk8.dim(` ${t("status_hint")}`));
2433
2734
  console.log();
2434
2735
  return;
2435
2736
  }
2436
2737
  for (const inst of installed) {
2437
2738
  console.log(
2438
- chalk7.green(" \u2713") + ` ${inst.displayName}` + chalk7.dim(` (${inst.tool}, ${inst.level})`)
2739
+ chalk8.green(" \u2713") + ` ${inst.displayName}` + chalk8.dim(` (${inst.tool}, ${inst.level})`)
2439
2740
  );
2440
2741
  console.log(
2441
- chalk7.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
2742
+ chalk8.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
2442
2743
  );
2443
- console.log(chalk7.dim(` ${inst.configRoot}`));
2444
- console.log(chalk7.dim(` ${t("status_install_time")}: ${inst.installTime}`));
2744
+ console.log(chalk8.dim(` ${inst.configRoot}`));
2745
+ console.log(chalk8.dim(` ${t("status_install_time")}: ${inst.installTime}`));
2445
2746
  console.log();
2446
2747
  }
2447
2748
  }
2448
2749
 
2449
2750
  // src/commands/install.ts
2450
2751
  import { confirm } from "@inquirer/prompts";
2451
- import chalk8 from "chalk";
2752
+ import chalk9 from "chalk";
2452
2753
  async function installCommand(names, options) {
2453
2754
  if (options.list) {
2454
2755
  try {
@@ -2460,7 +2761,7 @@ async function installCommand(names, options) {
2460
2761
  return;
2461
2762
  }
2462
2763
  if (names.length === 0) {
2463
- const scanSpinner2 = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2764
+ const scanSpinner2 = createSpinner(t("loading_skills_list"));
2464
2765
  scanSpinner2.start();
2465
2766
  try {
2466
2767
  const scanRepoManager = createRepositoryManager();
@@ -2489,42 +2790,45 @@ async function installCommand(names, options) {
2489
2790
  break;
2490
2791
  }
2491
2792
  if (selectedSkills.length === 0) {
2492
- logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Skill");
2793
+ logger.info(t("install_no_skill_selected"));
2493
2794
  return;
2494
2795
  }
2495
2796
  names = selectedSkills;
2496
2797
  const repoManager = createRepositoryManager();
2497
- const spinner = createSpinner(t("install_cloning"));
2798
+ const spinner = createSpinner(t("loading_plugin_data"));
2498
2799
  spinner.start();
2499
2800
  const repoPath = await repoManager.ensureRepo();
2500
- spinner.succeed(t("install_repo_ready"));
2501
- logger.info(`${t("skill_install_progress")} ${chalk8.bold(names.length)} \u4E2A Skills...`);
2801
+ spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
2802
+ logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)} \u4E2A Skills...`);
2502
2803
  const level2 = options.level || "project";
2503
2804
  const results = await installSkills(names, tool2, level2, repoPath);
2504
2805
  let successCount = 0;
2505
2806
  let failCount = 0;
2506
- for (const result of results) {
2807
+ const total = results.length;
2808
+ for (let i = 0; i < results.length; i++) {
2809
+ const result = results[i];
2810
+ const progress = `[${i + 1}/${total}]`;
2507
2811
  if (result.success) {
2508
- logger.success(`${result.skillId}`);
2812
+ logger.success(`${progress} ${result.skillId}`);
2509
2813
  successCount++;
2510
2814
  } else {
2511
- logger.error(`${result.skillId}: ${result.error}`);
2815
+ logger.error(`${progress} ${result.skillId}: ${result.error}`);
2512
2816
  failCount++;
2513
2817
  }
2514
2818
  }
2515
2819
  const configRoot = getConfigRoot(tool2, level2);
2516
2820
  logger.blank();
2517
- logger.success(`${t("skill_install_done")}: ${chalk8.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk8.red(failCount + " \u5931\u8D25") : chalk8.dim(failCount + " \u5931\u8D25")}`);
2821
+ logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
2518
2822
  logger.blank();
2519
- logger.info(`\u5B89\u88C5\u5230: ${chalk8.cyan(configRoot + "/skills/")}`);
2520
- logger.info(`\u542F\u52A8 ${chalk8.green(tool2)} \u5373\u53EF\u4F7F\u7528`);
2823
+ logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
2824
+ logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
2521
2825
  logger.blank();
2522
2826
  return;
2523
2827
  }
2524
2828
  }
2525
2829
  }
2526
2830
  }
2527
- const scanSpinner = createSpinner("\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...");
2831
+ const scanSpinner = createSpinner(t("loading_skills_list"));
2528
2832
  scanSpinner.start();
2529
2833
  try {
2530
2834
  const scanRepoManager = createRepositoryManager();
@@ -2552,7 +2856,7 @@ async function installCommand(names, options) {
2552
2856
  for (const p of allPlugins) {
2553
2857
  logger.step(` ${p.id} (${p.aliases.join(", ")})`);
2554
2858
  }
2555
- logger.info(`\u53EF\u7528 Skills: ${chalk8.cyan("install-helper install --list")}`);
2859
+ logger.info(`\u53EF\u7528 Skills: ${chalk9.cyan("install-helper install --list")}`);
2556
2860
  return;
2557
2861
  }
2558
2862
  let tool;
@@ -2591,16 +2895,18 @@ async function installCommand(names, options) {
2591
2895
  }
2592
2896
  if (pluginsToInstall.length > 0) {
2593
2897
  const repoManager = createRepositoryManager();
2594
- const spinner = createSpinner(t("install_cloning"));
2898
+ const spinner = createSpinner(t("loading_plugin_data"));
2595
2899
  spinner.start();
2596
2900
  const repoPath = await repoManager.ensureRepo();
2597
- spinner.succeed(t("install_repo_ready"));
2901
+ spinner.succeed(t("loading_plugin_data_complete"));
2598
2902
  const allPlugins = getAllPlugins();
2599
2903
  const results = await installPlugins(
2600
2904
  pluginsToInstall,
2601
2905
  tool,
2602
2906
  level,
2603
- repoPath
2907
+ repoPath,
2908
+ void 0,
2909
+ options.yes
2604
2910
  );
2605
2911
  const summary = results.map((result) => {
2606
2912
  const plugin = allPlugins.find((p) => p.id === result.pluginId);
@@ -2618,28 +2924,31 @@ async function installCommand(names, options) {
2618
2924
  }
2619
2925
  if (skills.length > 0) {
2620
2926
  const repoManager = createRepositoryManager();
2621
- const spinner = createSpinner(t("install_cloning"));
2927
+ const spinner = createSpinner(t("loading_plugin_data"));
2622
2928
  spinner.start();
2623
2929
  const repoPath = await repoManager.ensureRepo();
2624
- spinner.succeed(t("install_repo_ready"));
2625
- logger.info(`${t("skill_install_progress")} ${chalk8.bold(skills.length)} \u4E2A Skills...`);
2930
+ spinner.succeed(t("loading_plugin_data_complete"));
2931
+ logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)} \u4E2A Skills...`);
2626
2932
  const results = await installSkills(skills, tool, level, repoPath);
2627
2933
  let successCount = 0;
2628
2934
  let failCount = 0;
2629
- for (const result of results) {
2935
+ const total = results.length;
2936
+ for (let i = 0; i < results.length; i++) {
2937
+ const result = results[i];
2938
+ const progress = `[${i + 1}/${total}]`;
2630
2939
  if (result.success) {
2631
- logger.success(`${result.skillId}`);
2940
+ logger.success(`${progress} ${result.skillId}`);
2632
2941
  successCount++;
2633
2942
  } else {
2634
- logger.error(`${result.skillId}: ${result.error}`);
2943
+ logger.error(`${progress} ${result.skillId}: ${result.error}`);
2635
2944
  failCount++;
2636
2945
  }
2637
2946
  }
2638
2947
  logger.blank();
2639
- logger.success(`${t("skill_install_done")}: ${chalk8.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk8.red(failCount + " \u5931\u8D25") : chalk8.dim(failCount + " \u5931\u8D25")}`);
2948
+ logger.success(`${t("skill_install_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
2640
2949
  logger.blank();
2641
- logger.info(`\u5B89\u88C5\u5230: ${chalk8.cyan(getConfigRoot(tool, level) + "/skills/")}`);
2642
- logger.info(`\u542F\u52A8 ${chalk8.green(tool)} \u5373\u53EF\u4F7F\u7528`);
2950
+ logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
2951
+ logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
2643
2952
  logger.blank();
2644
2953
  }
2645
2954
  }
@@ -2671,8 +2980,8 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
2671
2980
  }
2672
2981
 
2673
2982
  // src/commands/uninstall.ts
2674
- import { existsSync as existsSync12, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
2675
- import chalk9 from "chalk";
2983
+ import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
2984
+ import chalk10 from "chalk";
2676
2985
  async function uninstallCommand(names, options) {
2677
2986
  const tool = options.tool || "opencode";
2678
2987
  const level = options.level || "project";
@@ -2689,24 +2998,24 @@ async function uninstallCommand(names, options) {
2689
2998
  skills.push(skill.id);
2690
2999
  continue;
2691
3000
  }
2692
- logger.error(`\u672A\u627E\u5230: ${name}`);
3001
+ logger.error(`${t("uninstall_not_found")}: ${name}`);
2693
3002
  }
2694
3003
  for (const pluginId of plugins) {
2695
3004
  const plugin = findPlugin(pluginId);
2696
3005
  if (!plugin) continue;
2697
3006
  const record = readRecord(plugin.id);
2698
3007
  if (!record) {
2699
- logger.warn(`${plugin.displayName} \u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F`);
2700
- logger.info(`\u5982\u9700\u5F3A\u5236\u6E05\u7406\uFF0C\u8BF7\u624B\u52A8\u5220\u9664 .opencode/ \u6216 .claude/ \u76EE\u5F55\u4E2D\u7684\u76F8\u5173\u6587\u4EF6`);
3008
+ logger.warn(`${plugin.displayName} ${t("uninstall_no_record")}`);
3009
+ logger.info(`${t("uninstall_no_record_hint")}`);
2701
3010
  continue;
2702
3011
  }
2703
- logger.info(`\u6B63\u5728\u5378\u8F7D ${plugin.displayName}...`);
3012
+ logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
2704
3013
  let removedFiles = 0;
2705
3014
  let removedDirs = 0;
2706
3015
  for (const filePath of record.files) {
2707
3016
  try {
2708
- if (existsSync12(filePath) || isSymlink3(filePath)) {
2709
- unlinkSync4(filePath);
3017
+ if (existsSync13(filePath) || isSymlink3(filePath)) {
3018
+ unlinkSync5(filePath);
2710
3019
  const name = filePath.split("/").pop() || filePath;
2711
3020
  logger.step(` \u79FB\u9664: ${name}`);
2712
3021
  removedFiles++;
@@ -2714,10 +3023,14 @@ async function uninstallCommand(names, options) {
2714
3023
  } catch {
2715
3024
  }
2716
3025
  }
3026
+ const configRoot = getConfigRoot(record.tool, record.level);
2717
3027
  const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
2718
3028
  for (const dirPath of sortedDirs) {
2719
3029
  try {
2720
- if (existsSync12(dirPath)) {
3030
+ if (dirPath === configRoot) {
3031
+ continue;
3032
+ }
3033
+ if (existsSync13(dirPath)) {
2721
3034
  const entries = readdirSync5(dirPath);
2722
3035
  if (entries.length === 0) {
2723
3036
  rmdirSync2(dirPath);
@@ -2731,15 +3044,32 @@ async function uninstallCommand(names, options) {
2731
3044
  }
2732
3045
  deleteRecord(plugin.id);
2733
3046
  removeInstalledPlugin(plugin.id);
3047
+ const backups = findBackups(configRoot);
3048
+ const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
3049
+ if (otherBackups.length > 0) {
3050
+ const choice = await showRestorePrompt(otherBackups);
3051
+ if (choice !== "none") {
3052
+ const restored = restoreBackup(choice, configRoot, record.tool);
3053
+ if (restored) {
3054
+ const backupInfo = otherBackups.find((b) => b.filePath === choice);
3055
+ logger.success(`${t("backup_restore_success")}: ${backupInfo?.pluginName || ""}`);
3056
+ deleteBackup(choice);
3057
+ }
3058
+ }
3059
+ }
3060
+ const currentBackups = backups.filter((b) => b.pluginId === pluginId);
3061
+ for (const backup of currentBackups) {
3062
+ deleteBackup(backup.filePath);
3063
+ }
2734
3064
  const totalRemoved = removedFiles + removedDirs;
2735
3065
  if (totalRemoved > 0) {
2736
- logger.success(`\u5DF2\u5378\u8F7D ${plugin.displayName}\uFF08\u79FB\u9664 ${removedFiles} \u4E2A\u6587\u4EF6\uFF0C${removedDirs} \u4E2A\u76EE\u5F55\uFF09`);
3066
+ logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08\u79FB\u9664 ${removedFiles} \u4E2A\u6587\u4EF6\uFF0C${removedDirs} \u4E2A\u76EE\u5F55\uFF09`);
2737
3067
  } else {
2738
- logger.warn(`${plugin.displayName} \u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55`);
3068
+ logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
2739
3069
  }
2740
3070
  }
2741
3071
  if (skills.length > 0) {
2742
- logger.info(`\u6B63\u5728\u5378\u8F7D ${skills.length} \u4E2A Skills...`);
3072
+ logger.info(`${t("uninstall_skills_in_progress")} ${skills.length} \u4E2A Skills...`);
2743
3073
  const results = await uninstallSkills(skills, tool, level);
2744
3074
  let successCount = 0;
2745
3075
  let failCount = 0;
@@ -2751,7 +3081,7 @@ async function uninstallCommand(names, options) {
2751
3081
  }
2752
3082
  }
2753
3083
  logger.blank();
2754
- logger.success(`${t("skill_uninstall_done")}: ${chalk9.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk9.red(failCount + " \u5931\u8D25") : chalk9.dim(failCount + " \u5931\u8D25")}`);
3084
+ logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(successCount + " \u6210\u529F")}, ${failCount > 0 ? chalk10.red(failCount + " \u5931\u8D25") : chalk10.dim(failCount + " \u5931\u8D25")}`);
2755
3085
  logger.blank();
2756
3086
  }
2757
3087
  }
@@ -2764,7 +3094,7 @@ function isSymlink3(path) {
2764
3094
  }
2765
3095
 
2766
3096
  // src/commands/update.ts
2767
- import chalk10 from "chalk";
3097
+ import chalk11 from "chalk";
2768
3098
  async function updateCommand(pluginNames, options) {
2769
3099
  let tool;
2770
3100
  if (options.tool) {
@@ -2782,7 +3112,7 @@ async function updateCommand(pluginNames, options) {
2782
3112
  pluginsToUpdate = manifests.map((m) => m.team);
2783
3113
  if (pluginsToUpdate.length === 0) {
2784
3114
  logger.info(t("update_no_plugins"));
2785
- logger.info(`\u4F7F\u7528 ${chalk10.cyan("install-helper install <plugin>")} \u5B89\u88C5\u63D2\u4EF6`);
3115
+ logger.info(`\u4F7F\u7528 ${chalk11.cyan("install-helper install <plugin>")} \u5B89\u88C5\u63D2\u4EF6`);
2786
3116
  return;
2787
3117
  }
2788
3118
  } else {
@@ -2844,42 +3174,42 @@ async function updateCommand(pluginNames, options) {
2844
3174
  }
2845
3175
 
2846
3176
  // src/commands/info.ts
2847
- import chalk11 from "chalk";
2848
- import { existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
2849
- import { join as join12 } from "path";
3177
+ import chalk12 from "chalk";
3178
+ import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
3179
+ import { join as join13 } from "path";
2850
3180
  async function infoCommand(pluginName) {
2851
3181
  const plugin = findPlugin(pluginName);
2852
3182
  if (!plugin) {
2853
- console.log(chalk11.red(` ${t("info_not_found")}: ${pluginName}`));
3183
+ console.log(chalk12.red(` ${t("info_not_found")}: ${pluginName}`));
2854
3184
  return;
2855
3185
  }
2856
3186
  const installed = scanInstalled();
2857
3187
  const isInstalled = installed.some((p) => p.id === plugin.id);
2858
- const statusText = isInstalled ? chalk11.green(`\u2713 ${t("status_installed")}`) : chalk11.dim(`\u2014 ${t("status_not_installed")}`);
3188
+ const statusText = isInstalled ? chalk12.green(`\u2713 ${t("status_installed")}`) : chalk12.dim(`\u2014 ${t("status_not_installed")}`);
2859
3189
  console.log();
2860
- console.log(chalk11.bold(` ${plugin.displayName}`));
2861
- console.log(chalk11.dim(" " + "\u2500".repeat(40)));
3190
+ console.log(chalk12.bold(` ${plugin.displayName}`));
3191
+ console.log(chalk12.dim(" " + "\u2500".repeat(40)));
2862
3192
  console.log();
2863
- console.log(` ${chalk11.dim(t("info_description") + ":")} ${plugin.description}`);
2864
- console.log(` ${chalk11.dim("ID:")} ${plugin.id}`);
2865
- console.log(` ${chalk11.dim(t("info_status") + ":")} ${statusText}`);
2866
- console.log(` ${chalk11.dim(t("info_skills") + ":")} ${plugin.skills}`);
2867
- console.log(` ${chalk11.dim(t("info_agents") + ":")} ${plugin.agents}`);
2868
- console.log(` ${chalk11.dim("\u522B\u540D:")} ${plugin.aliases.join(", ")}`);
3193
+ console.log(` ${chalk12.dim(t("info_description") + ":")} ${plugin.description}`);
3194
+ console.log(` ${chalk12.dim("ID:")} ${plugin.id}`);
3195
+ console.log(` ${chalk12.dim(t("info_status") + ":")} ${statusText}`);
3196
+ console.log(` ${chalk12.dim(t("info_skills") + ":")} ${plugin.skills}`);
3197
+ console.log(` ${chalk12.dim(t("info_agents") + ":")} ${plugin.agents}`);
3198
+ console.log(` ${chalk12.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
2869
3199
  console.log();
2870
3200
  try {
2871
3201
  const repoManager = createRepositoryManager();
2872
3202
  const repoPath = await repoManager.ensureRepo();
2873
- const quickstartPath = join12(repoPath, plugin.dir, "quickstart.md");
2874
- if (existsSync13(quickstartPath)) {
2875
- console.log(` ${chalk11.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
3203
+ const quickstartPath = join13(repoPath, plugin.dir, "quickstart.md");
3204
+ if (existsSync14(quickstartPath)) {
3205
+ console.log(` ${chalk12.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
2876
3206
  }
2877
- const agentsDir = join12(repoPath, plugin.dir, "agents");
2878
- if (existsSync13(agentsDir)) {
3207
+ const agentsDir = join13(repoPath, plugin.dir, "agents");
3208
+ if (existsSync14(agentsDir)) {
2879
3209
  const agentFiles = readdirSync6(agentsDir).filter((f) => f.endsWith(".md"));
2880
3210
  if (agentFiles.length > 0) {
2881
3211
  console.log();
2882
- console.log(` ${chalk11.bold("Agents:")}`);
3212
+ console.log(` ${chalk12.bold("Agents:")}`);
2883
3213
  for (const agent of agentFiles) {
2884
3214
  console.log(` \u2022 ${agent.replace(".md", "")}`);
2885
3215
  }
@@ -2891,39 +3221,39 @@ async function infoCommand(pluginName) {
2891
3221
  }
2892
3222
 
2893
3223
  // src/commands/lang.ts
2894
- import chalk12 from "chalk";
3224
+ import chalk13 from "chalk";
2895
3225
  async function langCommand(action, value) {
2896
3226
  const config = readConfig();
2897
3227
  if (!action || action === "show") {
2898
3228
  console.log();
2899
- console.log(chalk12.bold(` ${t("lang_title")}`));
2900
- console.log(chalk12.dim(" " + "\u2500".repeat(40)));
3229
+ console.log(chalk13.bold(` ${t("lang_title")}`));
3230
+ console.log(chalk13.dim(" " + "\u2500".repeat(40)));
2901
3231
  console.log();
2902
- console.log(` ${chalk12.dim(t("lang_current") + ":")} ${config.language}`);
3232
+ console.log(` ${chalk13.dim(t("lang_current") + ":")} ${config.language}`);
2903
3233
  console.log();
2904
- console.log(chalk12.dim(" \u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US"));
2905
- console.log(chalk12.dim(" \u7528\u6CD5: install-helper lang set <language>"));
3234
+ console.log(chalk13.dim(` ${t("lang_supported")}`));
3235
+ console.log(chalk13.dim(` ${t("lang_usage")}`));
2906
3236
  console.log();
2907
3237
  return;
2908
3238
  }
2909
3239
  if (action === "set") {
2910
3240
  if (!value) {
2911
- console.log(chalk12.red(" \u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN"));
3241
+ console.log(chalk13.red(` ${t("lang_specify")}`));
2912
3242
  return;
2913
3243
  }
2914
3244
  if (value !== "zh_CN" && value !== "en_US") {
2915
- console.log(chalk12.red(` ${t("lang_invalid")}`));
3245
+ console.log(chalk13.red(` ${t("lang_invalid")}`));
2916
3246
  return;
2917
3247
  }
2918
3248
  updateConfig({ language: value });
2919
3249
  setLanguage(value);
2920
3250
  console.log();
2921
- console.log(chalk12.green(` \u2713 ${t("lang_set")} ${value}`));
3251
+ console.log(chalk13.green(` \u2713 ${t("lang_set")} ${value}`));
2922
3252
  console.log();
2923
3253
  return;
2924
3254
  }
2925
- console.log(chalk12.red(` \u672A\u77E5\u64CD\u4F5C: ${action}`));
2926
- console.log(chalk12.dim(" \u652F\u6301\u7684\u64CD\u4F5C: show, set"));
3255
+ console.log(chalk13.red(` ${t("lang_unknown_action")}: ${action}`));
3256
+ console.log(chalk13.dim(` ${t("lang_supported_actions")}`));
2927
3257
  }
2928
3258
 
2929
3259
  // src/cli.ts
@@ -2933,7 +3263,7 @@ function createCLI() {
2933
3263
  setLanguage(config.language);
2934
3264
  }
2935
3265
  const program2 = new Command();
2936
- program2.name("install-helper").description("Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.1");
3266
+ program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.10");
2937
3267
  program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
2938
3268
  await initCommand();
2939
3269
  });