@cannbot-ai/install-helper 0.0.2-beta.34 → 0.0.2-beta.36

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,13 +12,13 @@ import { select, checkbox, Separator } from "@inquirer/prompts";
12
12
  import chalk3 from "chalk";
13
13
 
14
14
  // src/core/registry.ts
15
- import { readdirSync, readFileSync as readFileSync2, existsSync, statSync } from "fs";
15
+ import { readdirSync, readFileSync as readFileSync2, existsSync } from "fs";
16
16
  import { join as join2, dirname as dirname2 } from "path";
17
17
  import { fileURLToPath as fileURLToPath2 } from "url";
18
18
  import { parse as parseYaml } from "yaml";
19
19
 
20
20
  // src/utils/logger.ts
21
- import chalk from "chalk";
21
+ import chalk2 from "chalk";
22
22
  import ora from "ora";
23
23
 
24
24
  // src/utils/i18n.ts
@@ -52,20 +52,95 @@ function t(key) {
52
52
  return messages[key] !== void 0 ? messages[key] : key;
53
53
  }
54
54
 
55
+ // src/ui/theme.ts
56
+ import chalk from "chalk";
57
+ var selectTheme = {
58
+ helpMode: "never"
59
+ };
60
+ function isCJK(code) {
61
+ return code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65519 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983;
62
+ }
63
+ function isEmoji(code) {
64
+ return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
65
+ }
66
+ function getDisplayWidth(str) {
67
+ let width = 0;
68
+ for (const ch of str) {
69
+ const code = ch.codePointAt(0);
70
+ if (isCJK(code) || isEmoji(code)) {
71
+ width += 2;
72
+ } else {
73
+ width += 1;
74
+ }
75
+ }
76
+ return width;
77
+ }
78
+ function wrapDescription(text) {
79
+ const prefix = " \u{1F4A1} ";
80
+ const indent = " ";
81
+ const cols = process.stdout.columns || 120;
82
+ const maxLen = Math.max(20, cols - getDisplayWidth(prefix) - 2);
83
+ const tokens = [];
84
+ let i = 0;
85
+ while (i < text.length) {
86
+ const code = text[i].codePointAt(0);
87
+ if (isCJK(code)) {
88
+ tokens.push({ text: text[i], width: 2 });
89
+ i++;
90
+ } else {
91
+ let j = i;
92
+ while (j < text.length) {
93
+ const c = text[j].codePointAt(0);
94
+ if (isCJK(c)) break;
95
+ j++;
96
+ }
97
+ const word = text.slice(i, j);
98
+ tokens.push({ text: word, width: getDisplayWidth(word) });
99
+ i = j;
100
+ }
101
+ }
102
+ const lines = [];
103
+ let line = "";
104
+ let lineWidth = 0;
105
+ for (const token of tokens) {
106
+ if (lineWidth + token.width > maxLen && line) {
107
+ lines.push(line.trimEnd());
108
+ line = token.text;
109
+ lineWidth = token.width;
110
+ } else {
111
+ line += token.text;
112
+ lineWidth += token.width;
113
+ }
114
+ }
115
+ if (line) lines.push(line.trimEnd());
116
+ return lines.map((l, i2) => chalk.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
117
+ }
118
+ var checkboxTheme = {
119
+ icon: {
120
+ checked: "\x1B[32m\u2611\x1B[22m",
121
+ unchecked: "\u2610",
122
+ cursor: "\u276F"
123
+ },
124
+ helpMode: "never",
125
+ style: {
126
+ description: (text) => wrapDescription(text)
127
+ }
128
+ };
129
+
55
130
  // src/utils/logger.ts
56
131
  var logger = {
57
- success: (msg) => console.log(chalk.green("\u2713") + " " + msg),
58
- error: (msg) => console.log(chalk.red("\u2717") + " " + msg),
59
- warn: (msg) => console.log(chalk.yellow("\u26A0") + " " + msg),
60
- info: (msg) => console.log(chalk.cyan("\u2192") + " " + msg),
61
- step: (msg) => console.log(chalk.dim(msg)),
132
+ success: (msg) => console.log(chalk2.green("\u2713") + " " + msg),
133
+ error: (msg) => console.log(chalk2.red("\u2717") + " " + msg),
134
+ warn: (msg) => console.log(chalk2.yellow("\u26A0") + " " + msg),
135
+ info: (msg) => console.log(chalk2.cyan("\u2192") + " " + msg),
136
+ step: (msg) => console.log(chalk2.dim(msg)),
62
137
  blank: () => console.log()
63
138
  };
64
139
  function createSpinner(text) {
65
140
  return ora({ text, color: "cyan" });
66
141
  }
67
142
  function printBanner(subtitle) {
68
- console.log(chalk.cyan(`
143
+ console.log(chalk2.cyan(`
69
144
  ____ _ _ _ _ _ ____ _
70
145
  / ___| / \\ | \\ | | \\ | | __ ) ___ | |_
71
146
  | | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
@@ -73,22 +148,10 @@ function printBanner(subtitle) {
73
148
  \\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
74
149
  `));
75
150
  if (subtitle) {
76
- console.log(chalk.bold(` ${subtitle}`));
151
+ console.log(chalk2.bold(` ${subtitle}`));
77
152
  console.log();
78
153
  }
79
154
  }
80
- function getDisplayWidth(str) {
81
- let width = 0;
82
- for (const ch of str) {
83
- const code = ch.codePointAt(0);
84
- if (code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65519 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983) {
85
- width += 2;
86
- } else {
87
- width += 1;
88
- }
89
- }
90
- return width;
91
- }
92
155
  function printBoxTitle(title, width = 65) {
93
156
  const border = "\u2550".repeat(width);
94
157
  const displayWidth = getDisplayWidth(title);
@@ -97,9 +160,9 @@ function printBoxTitle(title, width = 65) {
97
160
  const rightPadding = totalPadding - leftPadding;
98
161
  const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
99
162
  console.log();
100
- console.log(chalk.cyan.bold(` \u2554${border}\u2557`));
101
- console.log(chalk.cyan.bold(` \u2551`) + chalk.cyan.bold(paddedTitle) + chalk.cyan.bold(`\u2551`));
102
- console.log(chalk.cyan.bold(` \u255A${border}\u255D`));
163
+ console.log(chalk2.cyan.bold(` \u2554${border}\u2557`));
164
+ console.log(chalk2.cyan.bold(` \u2551`) + chalk2.cyan.bold(paddedTitle) + chalk2.cyan.bold(`\u2551`));
165
+ console.log(chalk2.cyan.bold(` \u255A${border}\u255D`));
103
166
  console.log();
104
167
  }
105
168
  function showOperationHints(isCheckbox = false) {
@@ -112,6 +175,23 @@ function showOperationHints(isCheckbox = false) {
112
175
  }
113
176
  }
114
177
 
178
+ // src/utils/fs-helpers.ts
179
+ import { lstatSync, statSync } from "fs";
180
+ function isSymlink(path) {
181
+ try {
182
+ return lstatSync(path).isSymbolicLink();
183
+ } catch {
184
+ return false;
185
+ }
186
+ }
187
+ function isDirectory(path) {
188
+ try {
189
+ return statSync(path).isDirectory();
190
+ } catch {
191
+ return false;
192
+ }
193
+ }
194
+
115
195
  // src/core/registry.ts
116
196
  var __filename2 = fileURLToPath2(import.meta.url);
117
197
  var __dirname2 = dirname2(__filename2);
@@ -193,13 +273,6 @@ function mergeDynamicPlugins(repoPath) {
193
273
  );
194
274
  }
195
275
  }
196
- function isDirectory(path) {
197
- try {
198
- return statSync(path).isDirectory();
199
- } catch {
200
- return false;
201
- }
202
- }
203
276
  function findPlugin(query) {
204
277
  const normalized = query.toLowerCase().trim();
205
278
  const exactMatch = PLUGIN_REGISTRY.find(
@@ -344,8 +417,9 @@ async function getCommandVersion(cmd, args = ["--version"]) {
344
417
  }
345
418
  async function getCommandPath(cmd) {
346
419
  try {
347
- const result = await execa("which", [cmd], { timeout: 5e3 });
348
- return result.stdout.trim();
420
+ const whichCmd = process.platform === "win32" ? "where" : "which";
421
+ const result = await execa(whichCmd, [cmd], { timeout: 5e3 });
422
+ return result.stdout.trim().split("\n")[0];
349
423
  } catch {
350
424
  return void 0;
351
425
  }
@@ -431,7 +505,7 @@ function getToolDisplayName(tool) {
431
505
  return names[tool];
432
506
  }
433
507
  function getAllTools() {
434
- return ["opencode", "claude", "trae", "cursor", "copilot"];
508
+ return VALID_TOOLS;
435
509
  }
436
510
 
437
511
  // src/core/manifest.ts
@@ -474,7 +548,7 @@ function readAllManifests(configRoot) {
474
548
  function scanInstalled() {
475
549
  const plugins = getAllPlugins();
476
550
  const installed = [];
477
- const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
551
+ const tools = VALID_TOOLS;
478
552
  const levels = ["project", "global"];
479
553
  for (const tool of tools) {
480
554
  for (const level of levels) {
@@ -535,7 +609,6 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
535
609
  // src/utils/config.ts
536
610
  import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync4 } from "fs";
537
611
  import { join as join6 } from "path";
538
- import { homedir as homedir3 } from "os";
539
612
  import { parse, stringify } from "yaml";
540
613
 
541
614
  // src/utils/fs.ts
@@ -559,7 +632,7 @@ function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
559
632
 
560
633
  // src/utils/config.ts
561
634
  function getConfigDir() {
562
- return join6(homedir3(), ".cannbot");
635
+ return getCannbotConfigDir();
563
636
  }
564
637
  function getConfigPath() {
565
638
  return join6(getConfigDir(), "config.yaml");
@@ -620,84 +693,11 @@ function removeInstalledPlugin(pluginId) {
620
693
  writeConfig(config);
621
694
  }
622
695
 
623
- // src/ui/theme.ts
624
- import chalk2 from "chalk";
625
- var selectTheme = {
626
- helpMode: "never"
627
- };
628
- function isCJK(code) {
629
- return code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65519 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983;
630
- }
631
- function isEmoji(code) {
632
- return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
633
- }
634
- function getDisplayWidth2(str) {
635
- let width = 0;
636
- for (const ch of str) {
637
- const code = ch.codePointAt(0);
638
- if (isCJK(code) || isEmoji(code)) {
639
- width += 2;
640
- } else {
641
- width += 1;
642
- }
643
- }
644
- return width;
645
- }
646
- function wrapDescription(text) {
647
- const prefix = " \u{1F4A1} ";
648
- const indent = " ";
649
- const cols = process.stdout.columns || 120;
650
- const maxLen = Math.max(20, cols - getDisplayWidth2(prefix) - 2);
651
- const tokens = [];
652
- let i = 0;
653
- while (i < text.length) {
654
- const code = text[i].codePointAt(0);
655
- if (isCJK(code)) {
656
- tokens.push({ text: text[i], width: 2 });
657
- i++;
658
- } else {
659
- let j = i;
660
- while (j < text.length) {
661
- const c = text[j].codePointAt(0);
662
- if (isCJK(c)) break;
663
- j++;
664
- }
665
- const word = text.slice(i, j);
666
- tokens.push({ text: word, width: getDisplayWidth2(word) });
667
- i = j;
668
- }
669
- }
670
- const lines = [];
671
- let line = "";
672
- let lineWidth = 0;
673
- for (const token of tokens) {
674
- if (lineWidth + token.width > maxLen && line) {
675
- lines.push(line.trimEnd());
676
- line = token.text;
677
- lineWidth = token.width;
678
- } else {
679
- line += token.text;
680
- lineWidth += token.width;
681
- }
682
- }
683
- if (line) lines.push(line.trimEnd());
684
- return lines.map((l, i2) => chalk2.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
685
- }
686
- var checkboxTheme = {
687
- icon: {
688
- checked: "\x1B[32m\u2611\x1B[22m",
689
- unchecked: "\u2610",
690
- cursor: "\u276F"
691
- },
692
- helpMode: "never",
693
- style: {
694
- description: (text) => wrapDescription(text)
695
- }
696
- };
697
-
698
- // src/ui/wizard.ts
696
+ // src/utils/constants.ts
699
697
  var BACK = "__back__";
700
698
  var CANCEL = "__cancel__";
699
+
700
+ // src/ui/wizard.ts
701
701
  async function selectToolWithDetection() {
702
702
  const config = readConfig();
703
703
  logger.blank();
@@ -781,7 +781,7 @@ async function stepTool(detectedTools, lastTool) {
781
781
  if (detectedTools.length === 0) {
782
782
  logger.error(t("error_tool_not_found"));
783
783
  const choices2 = [
784
- ...["opencode", "claude", "trae", "cursor", "copilot"].map((tool) => ({
784
+ ...VALID_TOOLS.map((tool) => ({
785
785
  name: `> ${getToolDisplayName(tool)}`,
786
786
  value: tool
787
787
  })),
@@ -950,7 +950,7 @@ import { execa as execa2 } from "execa";
950
950
  import { parse as parseYaml3 } from "yaml";
951
951
 
952
952
  // src/core/scanner.ts
953
- import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2, mkdirSync as mkdirSync2 } from "fs";
953
+ import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
954
954
  import { join as join7, dirname as dirname4 } from "path";
955
955
  import { fileURLToPath as fileURLToPath3 } from "url";
956
956
  import { parse as parseYaml2 } from "yaml";
@@ -1007,7 +1007,7 @@ function scanSkills(repoPath) {
1007
1007
  if (!existsSync7(pluginParent)) continue;
1008
1008
  for (const plugin of safeReaddir(pluginParent)) {
1009
1009
  const pluginPath = join7(pluginParent, plugin);
1010
- if (!isDirectory2(pluginPath)) continue;
1010
+ if (!isDirectory(pluginPath)) continue;
1011
1011
  const skillsDir = join7(pluginPath, "skills");
1012
1012
  if (existsSync7(skillsDir)) {
1013
1013
  scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
@@ -1123,7 +1123,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1123
1123
  for (const entry of safeReaddir(dirPath)) {
1124
1124
  const fullPath = join7(dirPath, entry);
1125
1125
  if (EXCLUDE_DIRS.has(entry)) continue;
1126
- if (!isDirectory2(fullPath)) continue;
1126
+ if (!isDirectory(fullPath)) continue;
1127
1127
  const skillMd = join7(fullPath, "SKILL.md");
1128
1128
  if (existsSync7(skillMd)) {
1129
1129
  const frontmatter = parseFrontmatter(skillMd);
@@ -1148,29 +1148,22 @@ function safeReaddir(dir) {
1148
1148
  return [];
1149
1149
  }
1150
1150
  }
1151
- function isDirectory2(path) {
1152
- try {
1153
- return statSync2(path).isDirectory();
1154
- } catch {
1155
- return false;
1156
- }
1157
- }
1158
1151
 
1159
1152
  // src/core/skill-registry.ts
1160
1153
  var CATEGORY_DEFS = [
1161
- { id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
1162
- { id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
1163
- { id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
1164
- { id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
1165
- { id: "ascendc", name: "AscendC \u5F00\u53D1" },
1166
- { id: "pypto", name: "PyPTO \u5F00\u53D1" },
1167
- { id: "tilelang", name: "TileLang \u5F00\u53D1" },
1168
- { id: "triton", name: "Triton \u5F00\u53D1" },
1169
- { id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
1170
- { id: "runtime", name: "Runtime \u8FC1\u79FB" },
1171
- { id: "graph", name: "\u56FE\u6A21\u5F0F" },
1172
- { id: "platform", name: "\u5E73\u53F0\u5DE5\u5177" },
1173
- { id: "other", name: "\u5176\u4ED6 Skills" }
1154
+ { id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
1155
+ { id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
1156
+ { id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
1157
+ { id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
1158
+ { id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
1159
+ { id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
1160
+ { id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
1161
+ { id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
1162
+ { id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
1163
+ { id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
1164
+ { id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
1165
+ { id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
1166
+ { id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
1174
1167
  ];
1175
1168
  var dynamicSkills = [];
1176
1169
  var initialized = false;
@@ -1380,9 +1373,10 @@ function getAllSkills() {
1380
1373
  }
1381
1374
  function getAllCategories() {
1382
1375
  const skills = getActiveSkills();
1376
+ const isEn = t("list_name") === "Name";
1383
1377
  return CATEGORY_DEFS.map((def) => ({
1384
1378
  id: def.id,
1385
- name: def.name,
1379
+ name: isEn ? def.nameEn : def.name,
1386
1380
  skills: skills.filter((s) => {
1387
1381
  const catId = CATEGORY_MAP[s.id];
1388
1382
  if (catId) return catId === def.id;
@@ -1510,7 +1504,7 @@ import { join as join11 } from "path";
1510
1504
  import { execa as execa3 } from "execa";
1511
1505
 
1512
1506
  // src/core/record.ts
1513
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, lstatSync, unlinkSync as unlinkSync2 } from "fs";
1507
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
1514
1508
  import { join as join9 } from "path";
1515
1509
  function getInstallsDir() {
1516
1510
  return join9(getCannbotConfigDir(), "installs");
@@ -1613,13 +1607,6 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1613
1607
  directories
1614
1608
  };
1615
1609
  }
1616
- function isSymlink(path) {
1617
- try {
1618
- return lstatSync(path).isSymbolicLink();
1619
- } catch {
1620
- return false;
1621
- }
1622
- }
1623
1610
  function getSkillRecordPath() {
1624
1611
  return join9(getInstallsDir(), "skills.json");
1625
1612
  }
@@ -1690,9 +1677,6 @@ function getInstalledSkills(tool, level, installPath) {
1690
1677
  // src/core/backup.ts
1691
1678
  import { existsSync as existsSync10, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
1692
1679
  import { join as join10 } from "path";
1693
- function getAgentsFileName(tool) {
1694
- return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1695
- }
1696
1680
  function detectCurrentPlugin(configRoot, tool) {
1697
1681
  const manifest = readManifest(configRoot);
1698
1682
  if (manifest?.team) {
@@ -1801,20 +1785,9 @@ function formatTimestamp(date) {
1801
1785
  }
1802
1786
  function readAllRecords() {
1803
1787
  const records = [];
1804
- const plugins = [
1805
- "ops-direct-invoke",
1806
- "ops-direct-invoke-flash",
1807
- "ops-registry-invoke",
1808
- "pypto-op-orchestrator",
1809
- "triton-op-generator",
1810
- "tilelang-op-orchestrator",
1811
- "model-infer-optimize",
1812
- "catlass-op-generator",
1813
- "ops-code-reviewer",
1814
- "torch-compile"
1815
- ];
1816
- for (const pluginId of plugins) {
1817
- const record = readRecord(pluginId);
1788
+ const plugins = getAllPlugins();
1789
+ for (const plugin of plugins) {
1790
+ const record = readRecord(plugin.id);
1818
1791
  if (record) {
1819
1792
  records.push(record);
1820
1793
  }
@@ -1869,7 +1842,7 @@ async function installPlugin(opts) {
1869
1842
  pluginId: opts.pluginId,
1870
1843
  skillsCount: 0,
1871
1844
  agentsCount: 0,
1872
- errors: [`Plugin not found: ${opts.pluginId}`],
1845
+ errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
1873
1846
  warnings: []
1874
1847
  };
1875
1848
  }
@@ -1880,7 +1853,7 @@ async function installPlugin(opts) {
1880
1853
  pluginId: opts.pluginId,
1881
1854
  skillsCount: 0,
1882
1855
  agentsCount: 0,
1883
- errors: [`Script not found: ${scriptPath}`],
1856
+ errors: [t("error_script_not_found").replace("{path}", scriptPath)],
1884
1857
  warnings: []
1885
1858
  };
1886
1859
  }
@@ -1890,7 +1863,7 @@ async function installPlugin(opts) {
1890
1863
  }
1891
1864
  const cwd = opts.installPath || process.cwd();
1892
1865
  const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
1893
- const agentsFile = join11(configRoot, getAgentsFileName(opts.tool));
1866
+ const agentsFile = join11(configRoot, getAgentsFileName2(opts.tool));
1894
1867
  let backupInfo = null;
1895
1868
  if (existsSync11(agentsFile)) {
1896
1869
  const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
@@ -1934,13 +1907,14 @@ async function installPlugin(opts) {
1934
1907
  }
1935
1908
  }
1936
1909
  try {
1910
+ const shell = process.platform === "win32" ? "sh" : "bash";
1937
1911
  const execOptions = {
1938
1912
  cwd,
1939
1913
  timeout: 3e5,
1940
1914
  stdio: "pipe",
1941
1915
  input: opts.yes ? "y\n" : void 0
1942
1916
  };
1943
- await execa3("bash", [scriptPath, ...args], execOptions);
1917
+ await execa3(shell, [scriptPath, ...args], execOptions);
1944
1918
  const manifest = readManifest(configRoot);
1945
1919
  let skillsCount = 0;
1946
1920
  let agentsCount = 0;
@@ -1982,7 +1956,7 @@ async function installPlugin(opts) {
1982
1956
  warnings: []
1983
1957
  };
1984
1958
  } catch (error) {
1985
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1959
+ const errorMessage = error instanceof Error ? error.message : t("error_unknown");
1986
1960
  return {
1987
1961
  success: false,
1988
1962
  pluginId: opts.pluginId,
@@ -1995,7 +1969,7 @@ async function installPlugin(opts) {
1995
1969
  }
1996
1970
 
1997
1971
  // src/core/skill-installer.ts
1998
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
1972
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, readdirSync as readdirSync4, rmdirSync, realpathSync } from "fs";
1999
1973
  import { join as join12, dirname as dirname6 } from "path";
2000
1974
  import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
2001
1975
  import chalk5 from "chalk";
@@ -2012,25 +1986,29 @@ async function installSkills(skillIds, tool, level, repoPath) {
2012
1986
  for (const skillId of skillIds) {
2013
1987
  const skill = findSkill(skillId);
2014
1988
  if (!skill) {
2015
- results.push({ skillId, success: false, error: "Skill not found in registry" });
1989
+ results.push({ skillId, success: false, error: t("error_skill_not_found") });
2016
1990
  continue;
2017
1991
  }
2018
1992
  const sourcePath = resolveSkillSourcePath(skill, repoPath);
2019
1993
  const targetPath = join12(skillsDir, skillId);
2020
1994
  if (!existsSync12(sourcePath)) {
2021
- results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
1995
+ results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
2022
1996
  continue;
2023
1997
  }
2024
1998
  try {
2025
1999
  const resolvedSource = realpathSync(sourcePath);
2026
- if (existsSync12(targetPath) || isSymlink2(targetPath)) {
2000
+ if (existsSync12(targetPath) || isSymlink(targetPath)) {
2027
2001
  unlinkSync4(targetPath);
2028
2002
  }
2029
2003
  symlinkSync(resolvedSource, targetPath);
2030
2004
  results.push({ skillId, success: true });
2031
2005
  installedIds.push(skillId);
2032
2006
  } catch (error) {
2033
- results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
2007
+ let msg = error instanceof Error ? error.message : t("error_unknown");
2008
+ if (process.platform === "win32" && msg.includes("EPERM")) {
2009
+ msg = t("error_symlink_windows");
2010
+ }
2011
+ results.push({ skillId, success: false, error: msg });
2034
2012
  }
2035
2013
  }
2036
2014
  if (installedIds.length > 0) {
@@ -2047,17 +2025,17 @@ async function uninstallSkills(skillIds, tool, level) {
2047
2025
  for (const skillId of skillIds) {
2048
2026
  const targetPath = join12(skillsDir, skillId);
2049
2027
  try {
2050
- if (existsSync12(targetPath) || isSymlink2(targetPath)) {
2028
+ if (existsSync12(targetPath) || isSymlink(targetPath)) {
2051
2029
  unlinkSync4(targetPath);
2052
2030
  results.push({ skillId, success: true });
2053
2031
  removedIds.push(skillId);
2054
2032
  logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
2055
2033
  } else {
2056
- results.push({ skillId, success: false, error: "Not installed" });
2034
+ results.push({ skillId, success: false, error: t("error_not_installed") });
2057
2035
  logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
2058
2036
  }
2059
2037
  } catch (error) {
2060
- results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
2038
+ results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
2061
2039
  }
2062
2040
  }
2063
2041
  if (removedIds.length > 0) {
@@ -2079,8 +2057,6 @@ var SELECT_ALL = "__select_all__";
2079
2057
  var SELECT_ALL_CATEGORY = "__select_all_category__";
2080
2058
  var CONTINUE_SELECT = "__continue_select__";
2081
2059
  async function interactiveSkillSelect(tool, level) {
2082
- const BACK3 = "__back__";
2083
- const CANCEL3 = "__cancel__";
2084
2060
  const categories = getAllCategories();
2085
2061
  const allSkillIds = getAllSkills().map((s) => s.id);
2086
2062
  let accumulated = [];
@@ -2103,8 +2079,8 @@ async function interactiveSkillSelect(tool, level) {
2103
2079
  })),
2104
2080
  new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
2105
2081
  ...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
2106
- { name: "<- " + t("wizard_back"), value: BACK3 },
2107
- { name: "x " + t("wizard_cancel"), value: CANCEL3 }
2082
+ { name: "<- " + t("wizard_back"), value: BACK },
2083
+ { name: "x " + t("wizard_cancel"), value: CANCEL }
2108
2084
  ];
2109
2085
  showOperationHints();
2110
2086
  selectedCategoryId = await select3({
@@ -2114,8 +2090,8 @@ async function interactiveSkillSelect(tool, level) {
2114
2090
  theme: selectTheme,
2115
2091
  pageSize: 15
2116
2092
  });
2117
- if (selectedCategoryId === BACK3) return "back";
2118
- if (selectedCategoryId === CANCEL3) return "cancel";
2093
+ if (selectedCategoryId === BACK) return "back";
2094
+ if (selectedCategoryId === CANCEL) return "cancel";
2119
2095
  if (selectedCategoryId === SELECT_ALL) {
2120
2096
  return allSkillIds;
2121
2097
  }
@@ -2255,13 +2231,6 @@ function resolveSkillSourcePath(skill, repoPath) {
2255
2231
  }
2256
2232
  return join12(repoPath, skill.source, skill.id);
2257
2233
  }
2258
- function isSymlink2(path) {
2259
- try {
2260
- return lstatSync2(path).isSymbolicLink();
2261
- } catch {
2262
- return false;
2263
- }
2264
- }
2265
2234
 
2266
2235
  // src/ui/display.ts
2267
2236
  import chalk6 from "chalk";
@@ -2354,8 +2323,6 @@ function printEnhancedSummary(results, tool, configRoot) {
2354
2323
  // src/commands/init.ts
2355
2324
  import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
2356
2325
  import { join as join13 } from "path";
2357
- var BACK2 = "__back__";
2358
- var CANCEL2 = "__cancel__";
2359
2326
  async function initCommand() {
2360
2327
  const cwd = process.cwd();
2361
2328
  if (existsSync13(join13(cwd, "package.json")) && (() => {
@@ -2414,7 +2381,6 @@ async function initCommand() {
2414
2381
  }
2415
2382
  async function selectMode() {
2416
2383
  printBoxTitle(t("wizard_select_mode_title"));
2417
- const CANCEL3 = "__cancel__";
2418
2384
  const choices = [
2419
2385
  {
2420
2386
  name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
@@ -2425,7 +2391,7 @@ async function selectMode() {
2425
2391
  value: "skill"
2426
2392
  },
2427
2393
  new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
2428
- { name: "x " + t("wizard_cancel"), value: CANCEL3 }
2394
+ { name: "x " + t("wizard_cancel"), value: CANCEL }
2429
2395
  ];
2430
2396
  showOperationHints();
2431
2397
  const result = await select4({
@@ -2434,7 +2400,7 @@ async function selectMode() {
2434
2400
  loop: false,
2435
2401
  theme: selectTheme
2436
2402
  });
2437
- if (result === CANCEL3) return null;
2403
+ if (result === CANCEL) return null;
2438
2404
  return result;
2439
2405
  }
2440
2406
  async function pluginInstallFlow() {
@@ -2511,11 +2477,11 @@ async function skillInstallFlow() {
2511
2477
  }
2512
2478
  case 1: {
2513
2479
  const result = await stepLevel();
2514
- if (result === BACK2) {
2480
+ if (result === BACK) {
2515
2481
  step = 0;
2516
2482
  break;
2517
2483
  }
2518
- if (result === CANCEL2) {
2484
+ if (result === CANCEL) {
2519
2485
  logger.info(t("init_cancelled"));
2520
2486
  return "done";
2521
2487
  }
@@ -2586,13 +2552,12 @@ async function listCommand() {
2586
2552
 
2587
2553
  // src/commands/doctor.ts
2588
2554
  import chalk8 from "chalk";
2589
- import { existsSync as existsSync14, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync5, mkdirSync as mkdirSync6 } from "fs";
2555
+ import { existsSync as existsSync14, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync5, mkdirSync as mkdirSync6 } from "fs";
2590
2556
  import { join as join14 } from "path";
2591
2557
  async function doctorCommand(options = {}) {
2592
2558
  console.log();
2593
2559
  console.log(chalk8.bold(` ${t("doctor_title")}`));
2594
2560
  console.log(chalk8.dim(" " + "\u2500".repeat(46)));
2595
- let errors = 0;
2596
2561
  let warnings = 0;
2597
2562
  let fixes = 0;
2598
2563
  console.log();
@@ -2687,11 +2652,11 @@ async function doctorCommand(options = {}) {
2687
2652
  console.log(chalk8.dim(" " + "\u2500".repeat(46)));
2688
2653
  if (options.fix && fixes > 0) {
2689
2654
  console.log(
2690
- ` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
2655
+ ` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
2691
2656
  );
2692
2657
  } else {
2693
2658
  console.log(
2694
- ` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
2659
+ ` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
2695
2660
  );
2696
2661
  }
2697
2662
  console.log();
@@ -2703,7 +2668,7 @@ function checkBrokenLinks(dir) {
2703
2668
  for (const entry of entries) {
2704
2669
  const fullPath = join14(dir, entry);
2705
2670
  try {
2706
- const stats = lstatSync3(fullPath);
2671
+ const stats = lstatSync2(fullPath);
2707
2672
  if (stats.isSymbolicLink()) {
2708
2673
  if (!existsSync14(fullPath)) {
2709
2674
  broken++;
@@ -2724,7 +2689,7 @@ function fixBrokenLinks(dir) {
2724
2689
  for (const entry of entries) {
2725
2690
  const fullPath = join14(dir, entry);
2726
2691
  try {
2727
- const stats = lstatSync3(fullPath);
2692
+ const stats = lstatSync2(fullPath);
2728
2693
  if (stats.isSymbolicLink()) {
2729
2694
  if (!existsSync14(fullPath)) {
2730
2695
  unlinkSync5(fullPath);
@@ -2742,7 +2707,6 @@ function fixBrokenLinks(dir) {
2742
2707
  // src/commands/status.ts
2743
2708
  import chalk9 from "chalk";
2744
2709
  async function statusCommand() {
2745
- const plugins = getAllPlugins();
2746
2710
  const installed = scanInstalled();
2747
2711
  const installedMap = new Map(installed.map((p) => [p.id, p]));
2748
2712
  console.log();
@@ -3073,8 +3037,8 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
3073
3037
  }
3074
3038
 
3075
3039
  // src/commands/uninstall.ts
3076
- import { existsSync as existsSync15, unlinkSync as unlinkSync6, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
3077
- import { resolve } from "path";
3040
+ import { existsSync as existsSync15, unlinkSync as unlinkSync6, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
3041
+ import { resolve, basename } from "path";
3078
3042
  import chalk11 from "chalk";
3079
3043
  async function uninstallCommand(names, options) {
3080
3044
  const tool = options.tool ? validateTool(options.tool) : "opencode";
@@ -3115,9 +3079,9 @@ async function uninstallCommand(names, options) {
3115
3079
  for (const filePath of record.files) {
3116
3080
  if (!isSafePath(filePath)) continue;
3117
3081
  try {
3118
- if (existsSync15(filePath) || isSymlink3(filePath)) {
3082
+ if (existsSync15(filePath) || isSymlink(filePath)) {
3119
3083
  unlinkSync6(filePath);
3120
- const name = filePath.split("/").pop() || filePath;
3084
+ const name = basename(filePath);
3121
3085
  logger.step(` ${t("uninstall_remove_label")}: ${name}`);
3122
3086
  removedFiles++;
3123
3087
  }
@@ -3134,7 +3098,7 @@ async function uninstallCommand(names, options) {
3134
3098
  const entries = readdirSync6(dirPath);
3135
3099
  if (entries.length === 0) {
3136
3100
  rmdirSync2(dirPath);
3137
- const name = dirPath.split("/").pop() || dirPath;
3101
+ const name = basename(dirPath);
3138
3102
  logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
3139
3103
  removedDirs++;
3140
3104
  }
@@ -3163,7 +3127,7 @@ async function uninstallCommand(names, options) {
3163
3127
  }
3164
3128
  const totalRemoved = removedFiles + removedDirs;
3165
3129
  if (totalRemoved > 0) {
3166
- 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`);
3130
+ logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
3167
3131
  } else {
3168
3132
  logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
3169
3133
  }
@@ -3185,13 +3149,6 @@ async function uninstallCommand(names, options) {
3185
3149
  logger.blank();
3186
3150
  }
3187
3151
  }
3188
- function isSymlink3(path) {
3189
- try {
3190
- return lstatSync4(path).isSymbolicLink();
3191
- } catch {
3192
- return false;
3193
- }
3194
- }
3195
3152
 
3196
3153
  // src/commands/update.ts
3197
3154
  import chalk12 from "chalk";
@@ -3360,13 +3317,27 @@ async function langCommand(action, value) {
3360
3317
  }
3361
3318
 
3362
3319
  // src/cli.ts
3320
+ import { readFileSync as readFileSync9 } from "fs";
3321
+ import { fileURLToPath as fileURLToPath5 } from "url";
3322
+ import { dirname as dirname7, join as join16 } from "path";
3323
+ function readVersion() {
3324
+ try {
3325
+ const __filename5 = fileURLToPath5(import.meta.url);
3326
+ const __dirname5 = dirname7(__filename5);
3327
+ const pkgPath = join16(__dirname5, "..", "package.json");
3328
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
3329
+ return pkg.version || "0.0.0";
3330
+ } catch {
3331
+ return "0.0.0";
3332
+ }
3333
+ }
3363
3334
  function createCLI() {
3364
3335
  const config = readConfig();
3365
3336
  if (config.language) {
3366
3337
  setLanguage(config.language);
3367
3338
  }
3368
3339
  const program2 = new Command();
3369
- program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.34");
3340
+ program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
3370
3341
  program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
3371
3342
  await initCommand();
3372
3343
  });