@cannbot-ai/install-helper 1.0.8 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,22 +8,1064 @@ import { select as select4, Separator as Separator3 } from "@inquirer/prompts";
8
8
  import chalk7 from "chalk";
9
9
 
10
10
  // src/ui/wizard.ts
11
- import { select, checkbox, Separator } from "@inquirer/prompts";
11
+ import { select, Separator } from "@inquirer/prompts";
12
12
  import chalk3 from "chalk";
13
13
 
14
14
  // src/core/registry.ts
15
- import { readdirSync, readFileSync } from "fs";
15
+ import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
16
+ import { join as join2, dirname as dirname2 } from "path";
17
+ import { fileURLToPath as fileURLToPath2 } from "url";
18
+ import { parse as parseYaml } from "yaml";
19
+
20
+ // src/utils/logger.ts
21
+ import chalk2 from "chalk";
22
+ import ora from "ora";
23
+
24
+ // src/utils/i18n.ts
25
+ import { readFileSync, existsSync } from "fs";
16
26
  import { join, dirname } from "path";
17
27
  import { fileURLToPath } from "url";
18
- import { parse as parseYaml } from "yaml";
28
+
29
+ // src/locales/zh_CN.json
30
+ var zh_CN_default = {
31
+ wizard_title: "CANNBot Install Helper",
32
+ wizard_detect: "\u6B63\u5728\u68C0\u6D4B\u5DF2\u5B89\u88C5\u7684 AI \u7F16\u7A0B\u5DE5\u5177...",
33
+ wizard_select_tool: "\u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
34
+ wizard_select_level: "\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
35
+ wizard_select_plugin: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
36
+ wizard_confirm: "\u786E\u8BA4\u5B89\u88C5",
37
+ wizard_detected_tool: "\u68C0\u6D4B\u5230",
38
+ wizard_confirm_tool: "\u4F7F\u7528",
39
+ wizard_last_used: "\u4E0A\u6B21\u4F7F\u7528",
40
+ wizard_already_installed: "\u5DF2\u5B89\u88C5",
41
+ install_repo_ready: "\u4ED3\u5E93\u5C31\u7EEA",
42
+ install_progress: "\u6B63\u5728\u5B89\u88C5",
43
+ install_done: "\u5B89\u88C5\u5B8C\u6210",
44
+ install_success: "\u6210\u529F",
45
+ install_already_installed: "\u5DF2\u5B89\u88C5",
46
+ install_reinstall_confirm: "\u662F\u5426\u91CD\u65B0\u5B89\u88C5\uFF08\u8986\u76D6\uFF09",
47
+ install_skip: "\u8DF3\u8FC7",
48
+ install_enhanced_next_steps: "\u4E0B\u4E00\u6B65",
49
+ install_enhanced_launch: "\u542F\u52A8",
50
+ install_enhanced_try: "\u8BD5\u8BD5",
51
+ install_enhanced_more: "\u66F4\u591A",
52
+ install_enhanced_check: "\u68C0\u67E5",
53
+ install_enhanced_docs: "\u6587\u6863",
54
+ update_updating: "\u6B63\u5728\u66F4\u65B0",
55
+ update_done: "\u66F4\u65B0\u5B8C\u6210",
56
+ update_no_plugins: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
57
+ doctor_title: "CANNBot Doctor",
58
+ doctor_tools: "AI \u5DE5\u5177",
59
+ doctor_plugins: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
60
+ doctor_links: "\u94FE\u63A5\u5B8C\u6574\u6027",
61
+ doctor_config: "\u914D\u7F6E\u6587\u4EF6",
62
+ doctor_result: "\u7ED3\u679C",
63
+ doctor_fix_cleaning: "\u6E05\u7406\u5931\u6548\u94FE\u63A5",
64
+ doctor_fix_rebuilding: "\u91CD\u5EFA\u76EE\u5F55",
65
+ info_description: "\u63CF\u8FF0",
66
+ info_skills: "Skills",
67
+ info_agents: "Agents",
68
+ info_status: "\u72B6\u6001",
69
+ info_quickstart: "\u5FEB\u901F\u5F00\u59CB",
70
+ info_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
71
+ lang_title: "\u8BED\u8A00\u8BBE\u7F6E",
72
+ lang_current: "\u5F53\u524D\u8BED\u8A00",
73
+ lang_set: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A",
74
+ lang_invalid: "\u65E0\u6548\u7684\u8BED\u8A00\uFF0C\u652F\u6301: zh_CN, en_US",
75
+ list_title: "\u53EF\u7528\u573A\u666F",
76
+ list_id: "\u5E8F\u53F7",
77
+ list_name: "\u573A\u666F\u540D",
78
+ list_status: "\u72B6\u6001",
79
+ list_description: "\u63CF\u8FF0",
80
+ status_installed: "\u5DF2\u88C5",
81
+ status_not_installed: "\u672A\u88C5",
82
+ error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
83
+ error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
84
+ error_invalid_tool: "\u6682\u4E0D\u652F\u6301\u7684 AI \u5DE5\u5177: {tool}\n\u652F\u6301\u7684\u5DE5\u5177: {tools}",
85
+ error_invalid_level: "\u6682\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u7EA7\u522B: {level}\n\u652F\u6301\u7684\u7EA7\u522B: {levels}",
86
+ error_skill_not_found: "\u672A\u5728\u6CE8\u518C\u8868\u4E2D\u627E\u5230 Skill",
87
+ error_source_not_found: "\u672A\u627E\u5230\u6E90\u6587\u4EF6: {path}",
88
+ error_script_not_found: "\u672A\u627E\u5230\u5B89\u88C5\u811A\u672C: {path}",
89
+ error_unknown: "\u672A\u77E5\u9519\u8BEF",
90
+ error_not_installed: "\u672A\u5B89\u88C5",
91
+ error_no_shell: "\u672A\u627E\u5230 bash \u6216 sh\uFF0C\u65E0\u6CD5\u6267\u884C\u5B89\u88C5\u811A\u672C\u3002\u8BF7\u5B89\u88C5 Git Bash \u6216 WSL",
92
+ error_symlink_windows: "\u521B\u5EFA\u7B26\u53F7\u94FE\u63A5\u5931\u8D25\uFF0C\u8BF7\u4EE5\u7BA1\u7406\u5458\u8EAB\u4EFD\u8FD0\u884C\u6216\u5F00\u542F Windows \u5F00\u53D1\u8005\u6A21\u5F0F",
93
+ skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
94
+ skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
95
+ skill_already_installed: "\u5DF2\u5B89\u88C5",
96
+ skill_select_all: "\u5168\u9009\u6240\u6709 Skills\uFF08\u5171 {count} \u4E2A\uFF09",
97
+ skill_select_all_category: "\u5168\u9009\u5F53\u524D\u7C7B\u522B",
98
+ skill_continue_or_install: "\u5DF2\u9009 {count} \u4E2A Skills\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
99
+ skill_continue_select: "\u7EE7\u7EED\u9009\u62E9\u5176\u4ED6\u7C7B\u522B",
100
+ skill_confirm_install_count: "\u786E\u8BA4\u5B89\u88C5\uFF08\u5DF2\u9009 {count} \u4E2A Skills\uFF09",
101
+ skill_all_install_progress: "\u6B63\u5728\u5B89\u88C5\u5168\u90E8 {count} \u4E2A Skills...",
102
+ skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
103
+ skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
104
+ skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
105
+ skill_list_title: "\u53EF\u7528 Skills",
106
+ wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
107
+ wizard_mode_plugin: "\u5B89\u88C5 Plugin",
108
+ wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
109
+ wizard_mode_skill: "\u5B89\u88C5 Skill",
110
+ wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
111
+ wizard_back: "\u8FD4\u56DE",
112
+ wizard_cancel: "\u9000\u51FA",
113
+ wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
114
+ wizard_back_to_reselect: "\u8FD4\u56DE",
115
+ status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
116
+ status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
117
+ status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
118
+ status_install_time: "\u5B89\u88C5\u65F6\u95F4",
119
+ install_installed_to: "\u5B89\u88C5\u5230",
120
+ install_skills_count: "Skills",
121
+ install_agents_count: "Agents",
122
+ lang_supported: "\u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US",
123
+ lang_usage: "\u7528\u6CD5: install-helper lang set <language>",
124
+ lang_specify: "\u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN",
125
+ lang_unknown_action: "\u672A\u77E5\u64CD\u4F5C",
126
+ lang_supported_actions: "\u652F\u6301\u7684\u64CD\u4F5C: show, set",
127
+ info_aliases: "\u522B\u540D",
128
+ uninstall_not_found: "\u672A\u627E\u5230",
129
+ uninstall_no_record: "\u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
130
+ 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",
131
+ uninstall_in_progress: "\u6B63\u5728\u5378\u8F7D",
132
+ uninstall_summary_format: "{name}\uFF08\u79FB\u9664 {files} \u4E2A\u6587\u4EF6\uFF0C{dirs} \u4E2A\u76EE\u5F55\uFF09",
133
+ uninstall_files_gone: "\u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55",
134
+ uninstall_skills_in_progress: "\u6B63\u5728\u5378\u8F7D",
135
+ init_pkg_dir_warn: "\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55",
136
+ 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",
137
+ init_pkg_dir_example: "\u793A\u4F8B\uFF1Acd ~/my-project && install-helper",
138
+ init_cancelled: "\u5DF2\u53D6\u6D88\u5B89\u88C5",
139
+ backup_detected: "\u68C0\u6D4B\u5230\u5F53\u524D\u76EE\u5F55\u5DF2\u5B89\u88C5\u63D2\u4EF6",
140
+ backup_current: "\u5F53\u524D\u5B89\u88C5",
141
+ backup_installing: "\u5373\u5C06\u5B89\u88C5",
142
+ backup_overwrite_warning: "\u7EE7\u7EED\u5B89\u88C5\u5C06\u8986\u76D6\u73B0\u6709\u7684 AGENTS.md \u914D\u7F6E\u6587\u4EF6\u3002",
143
+ backup_auto_create: "\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u521B\u5EFA\u5907\u4EFD\uFF0C\u4EE5\u4FBF\u540E\u7EED\u6062\u590D\u3002",
144
+ backup_overwrite: "\u8986\u76D6\u5B89\u88C5\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09",
145
+ backup_cancel: "\u53D6\u6D88\u5B89\u88C5",
146
+ backup_restore_prompt: "\u68C0\u6D4B\u5230\u5907\u4EFD\u914D\u7F6E\uFF0C\u662F\u5426\u6062\u590D\uFF1F",
147
+ backup_restore: "\u6062\u590D",
148
+ backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
149
+ backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
150
+ backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
151
+ backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
152
+ backup_failed: "\u5907\u4EFD\u521B\u5EFA\u5931\u8D25\uFF0C\u5B89\u88C5\u5DF2\u4E2D\u6B62\u4EE5\u4FDD\u62A4\u73B0\u6709\u914D\u7F6E",
153
+ loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
154
+ loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
155
+ loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
156
+ loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
157
+ loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
158
+ repo_pull_failed: "\u65E0\u6CD5\u62C9\u53D6\u6700\u65B0\u5185\u5BB9\uFF0C\u4F7F\u7528\u7F13\u5B58\u6570\u636E",
159
+ repo_cloning: "\u9996\u6B21\u4F7F\u7528\uFF0C\u6B63\u5728\u514B\u9686 cannbot-skills \u4ED3\u5E93...",
160
+ config_corrupted: "\u914D\u7F6E\u6587\u4EF6\u5DF2\u635F\u574F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E",
161
+ record_corrupted: "\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F\uFF1A{file}",
162
+ skill_record_corrupted: "Skill \u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
163
+ plugin_yaml_error: "\u63D2\u4EF6\u914D\u7F6E\u89E3\u6790\u5931\u8D25\uFF1A{file}",
164
+ record_write_failed: "\u5B89\u88C5\u8BB0\u5F55\u5199\u5165\u5931\u8D25\uFF0C\u5378\u8F7D\u65F6\u53EF\u80FD\u65E0\u6CD5\u81EA\u52A8\u6E05\u7406",
165
+ install_config_failed: "\u914D\u7F6E\u6587\u4EF6\u5B89\u88C5\u5931\u8D25: {error}",
166
+ install_clone_failed: "git clone \u5931\u8D25: {url}\uFF0C\u8BF7\u624B\u52A8\u514B\u9686: git clone {url} {dir}",
167
+ install_link_config_failed: "\u94FE\u63A5 {name} \u5230\u914D\u7F6E\u76EE\u5F55\u5931\u8D25",
168
+ install_link_project_failed: "\u94FE\u63A5 {name} \u5230\u9879\u76EE\u76EE\u5F55\u5931\u8D25",
169
+ install_link_workflows_failed: "\u94FE\u63A5 workflows \u5230\u914D\u7F6E\u76EE\u5F55\u5931\u8D25",
170
+ install_skill_source_not_found: "Skill \u6E90\u76EE\u5F55\u672A\u627E\u5230: {dir}",
171
+ install_skill_not_found: "Skill \u672A\u627E\u5230: {name} (\u5728 {dir})",
172
+ install_skill_failed: "Skill \u5B89\u88C5\u5931\u8D25: {name}",
173
+ install_agent_not_found: "Agent \u672A\u627E\u5230: {name}",
174
+ install_agent_failed: "Agent \u5B89\u88C5\u5931\u8D25: {name}",
175
+ install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
176
+ install_to: "\u5B89\u88C5\u5230",
177
+ start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
178
+ wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
179
+ wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
180
+ wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
181
+ wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
182
+ wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
183
+ wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
184
+ install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
185
+ install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
186
+ wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
187
+ skill_count_format: "{count} \u4E2A Skills",
188
+ result_success_format: "{count} \u6210\u529F",
189
+ result_failed_format: "{count} \u5931\u8D25",
190
+ uninstall_remove_label: "\u79FB\u9664",
191
+ uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
192
+ uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
193
+ available_plugins: "\u53EF\u7528\u63D2\u4EF6",
194
+ available_skills: "\u53EF\u7528 Skills",
195
+ skill_total_count: "\u5171 {count} \u4E2A Skills",
196
+ wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
197
+ wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
198
+ update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
199
+ hint_select_move: "\u79FB\u52A8",
200
+ hint_select_space: "\u7A7A\u683C",
201
+ hint_select_check: "\u52FE\u9009",
202
+ hint_select_confirm: "\u786E\u8BA4",
203
+ hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
204
+ doctor_not_installed: "\u672A\u5B89\u88C5",
205
+ doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
206
+ doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
207
+ doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
208
+ doctor_not_exist: "\u4E0D\u5B58\u5728",
209
+ doctor_created: "\u5DF2\u521B\u5EFA {path}",
210
+ doctor_config_exists: "\u5B58\u5728",
211
+ doctor_config_not_exist: "\u4E0D\u5B58\u5728",
212
+ doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {fixes} \u9879\u5DF2\u4FEE\u590D",
213
+ doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A",
214
+ count_suffix: "\u4E2A",
215
+ try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
216
+ };
217
+
218
+ // src/locales/en_US.json
219
+ var en_US_default = {
220
+ wizard_title: "CANNBot Install Helper",
221
+ wizard_detect: "Detecting installed AI coding tools...",
222
+ wizard_select_tool: "Select AI coding tool",
223
+ wizard_select_level: "Select install level",
224
+ wizard_select_plugin: "Select a scenario to install",
225
+ wizard_confirm: "Confirm installation",
226
+ wizard_detected_tool: "Detected",
227
+ wizard_confirm_tool: "Confirm using",
228
+ wizard_last_used: "last used",
229
+ wizard_already_installed: "installed",
230
+ install_repo_ready: "Repository ready",
231
+ install_progress: "Installing",
232
+ install_done: "Installation complete",
233
+ install_success: "success",
234
+ install_already_installed: "already installed",
235
+ install_reinstall_confirm: "Reinstall (overwrite)",
236
+ install_skip: "skipped",
237
+ install_enhanced_next_steps: "Next steps",
238
+ install_enhanced_launch: "Launch",
239
+ install_enhanced_try: "Try",
240
+ install_enhanced_more: "More",
241
+ install_enhanced_check: "Check",
242
+ install_enhanced_docs: "Docs",
243
+ update_updating: "Updating",
244
+ update_done: "Update complete",
245
+ update_no_plugins: "No installed plugins",
246
+ doctor_title: "CANNBot Doctor",
247
+ doctor_tools: "AI Tools",
248
+ doctor_plugins: "Installed Plugins",
249
+ doctor_links: "Link Integrity",
250
+ doctor_config: "Configuration",
251
+ doctor_result: "Result",
252
+ doctor_fix_cleaning: "Cleaning broken links",
253
+ doctor_fix_rebuilding: "Rebuilding directories",
254
+ info_description: "Description",
255
+ info_skills: "Skills",
256
+ info_agents: "Agents",
257
+ info_status: "Status",
258
+ info_quickstart: "Quick Start",
259
+ info_not_found: "Plugin not found",
260
+ lang_title: "Language Settings",
261
+ lang_current: "Current language",
262
+ lang_set: "Language set to",
263
+ lang_invalid: "Invalid language, supported: zh_CN, en_US",
264
+ list_title: "Available Scenarios",
265
+ list_id: "#",
266
+ list_name: "Name",
267
+ list_status: "Status",
268
+ list_description: "Description",
269
+ status_installed: "installed",
270
+ status_not_installed: "not installed",
271
+ error_tool_not_found: "No AI coding tool detected",
272
+ error_plugin_not_found: "Plugin not found",
273
+ error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
274
+ error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
275
+ error_skill_not_found: "Skill not found in registry",
276
+ error_source_not_found: "Source not found: {path}",
277
+ error_script_not_found: "Script not found: {path}",
278
+ error_unknown: "Unknown error",
279
+ error_not_installed: "Not installed",
280
+ error_no_shell: "bash or sh not found, cannot execute install script. Please install Git Bash or WSL",
281
+ error_symlink_windows: "Failed to create symlink, please run as administrator or enable Windows Developer Mode",
282
+ skill_select_category: "Select Skill category",
283
+ skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
284
+ skill_already_installed: "installed",
285
+ skill_select_all: "Select ALL Skills ({count} total)",
286
+ skill_select_all_category: "Select all in this category",
287
+ skill_continue_or_install: "{count} Skills selected, choose an action",
288
+ skill_continue_select: "Continue selecting from other categories",
289
+ skill_confirm_install_count: "Install now ({count} Skills selected)",
290
+ skill_all_install_progress: "Installing all {count} Skills...",
291
+ skill_install_progress: "Installing",
292
+ skill_install_done: "Skill installation complete",
293
+ skill_uninstall_done: "Skill uninstallation complete",
294
+ skill_list_title: "Available Skills",
295
+ wizard_select_mode: "Select installation mode",
296
+ wizard_mode_plugin: "Install Plugin",
297
+ wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
298
+ wizard_mode_skill: "Install Skill",
299
+ wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
300
+ wizard_back: "Go back",
301
+ wizard_cancel: "Cancel installation",
302
+ wizard_no_selection: "No items selected, please choose an action",
303
+ wizard_back_to_reselect: "Go back",
304
+ status_title: "Installed Plugins",
305
+ status_none: "No installed plugins",
306
+ status_hint: "Run install-helper init to start",
307
+ status_install_time: "Installed at",
308
+ install_installed_to: "Installed to",
309
+ install_skills_count: "Skills",
310
+ install_agents_count: "Agents",
311
+ lang_supported: "Supported languages: zh_CN, en_US",
312
+ lang_usage: "Usage: install-helper lang set <language>",
313
+ lang_specify: "Please specify language: install-helper lang set zh_CN",
314
+ lang_unknown_action: "Unknown action",
315
+ lang_supported_actions: "Supported actions: show, set",
316
+ info_aliases: "Aliases",
317
+ uninstall_not_found: "Not found",
318
+ uninstall_no_record: "No install record found, may not be installed or record is corrupted",
319
+ uninstall_no_record_hint: "For manual cleanup, delete related files in .opencode/ or .claude/ directory",
320
+ uninstall_in_progress: "Uninstalling",
321
+ uninstall_summary_format: "{name} (removed {files} files, {dirs} directories)",
322
+ uninstall_files_gone: " files no longer exist, install record cleaned",
323
+ uninstall_skills_in_progress: "Uninstalling",
324
+ init_pkg_dir_warn: "Current directory appears to be the install-helper package directory",
325
+ init_pkg_dir_hint: "Please run this command in your project directory, not in the install-helper package directory",
326
+ init_pkg_dir_example: "Example: cd ~/my-project && install-helper",
327
+ init_cancelled: "Installation cancelled",
328
+ backup_detected: "Plugin already installed in current directory",
329
+ backup_current: "Currently installed",
330
+ backup_installing: "About to install",
331
+ backup_overwrite_warning: "Continuing will overwrite existing AGENTS.md configuration.",
332
+ backup_auto_create: "System will automatically create backup for future restoration.",
333
+ backup_overwrite: "Overwrite (auto backup)",
334
+ backup_cancel: "Cancel installation",
335
+ backup_restore_prompt: "Backup configuration detected, restore?",
336
+ backup_restore: "Restore",
337
+ backup_only_uninstall: "Uninstall only (no restore)",
338
+ backup_select_action: "Please select an action",
339
+ backup_restore_success: "Configuration restored",
340
+ backup_created: "Backup created",
341
+ backup_failed: "Backup creation failed, installation aborted to protect existing config",
342
+ loading_skills_list: "Loading Skills list...",
343
+ loading_skills_list_complete: "Skills list loaded",
344
+ loading_plugin_data: "Loading plugin data...",
345
+ loading_plugin_data_complete: "Plugin data loaded",
346
+ loading_skills_list_failed: "Failed to load Skills list, using built-in data",
347
+ repo_pull_failed: "Failed to pull latest updates, using cached data",
348
+ repo_cloning: "First-time setup: cloning cannbot-skills repository...",
349
+ config_corrupted: "Config file corrupted, using defaults",
350
+ record_corrupted: "Install record corrupted: {file}",
351
+ skill_record_corrupted: "Skill install record corrupted",
352
+ plugin_yaml_error: "Plugin config parse error: {file}",
353
+ record_write_failed: "Install record write failed, uninstall may not work properly",
354
+ install_config_failed: "Failed to install config file: {error}",
355
+ install_clone_failed: "git clone failed: {url}, clone manually: git clone {url} {dir}",
356
+ install_link_config_failed: "Failed to link {name} to config directory",
357
+ install_link_project_failed: "Failed to link {name} to project directory",
358
+ install_link_workflows_failed: "Failed to link workflows to config directory",
359
+ install_skill_source_not_found: "Skill source not found: {dir}",
360
+ install_skill_not_found: "Skill not found: {name} in {dir}",
361
+ install_skill_failed: "Failed to install skill: {name}",
362
+ install_agent_not_found: "Agent not found: {name}",
363
+ install_agent_failed: "Failed to install agent: {name}",
364
+ install_no_skill_selected: "No Skill selected",
365
+ install_to: "Install to",
366
+ start_to_use: "Start {tool} to use",
367
+ wizard_step_1_title: "[1/4] Select AI coding tool",
368
+ wizard_step_2_title: "[2/4] Select installation location",
369
+ wizard_step_3_title: "[3/4] Select plugins to install",
370
+ wizard_step_4_title: "[4/4] Confirm installation",
371
+ wizard_step_3_skill_title: "[3/4] Select Skill category",
372
+ wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
373
+ install_level_project: "Install to current directory (recommended, current project only)",
374
+ install_level_global: "Install globally (all projects)",
375
+ wizard_select_mode_title: "Select installation type",
376
+ skill_count_format: "{count} Skills",
377
+ result_success_format: "{count} succeeded",
378
+ result_failed_format: "{count} failed",
379
+ uninstall_remove_label: "Removed",
380
+ uninstall_clean_empty_dir: "Cleaned empty directory",
381
+ uninstall_skill_not_installed: "Not installed",
382
+ available_plugins: "Available plugins",
383
+ available_skills: "Available Skills",
384
+ skill_total_count: "Total {count} Skills",
385
+ wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
386
+ wizard_install_path: "Install path",
387
+ update_install_hint: "Use {cmd} to install plugins",
388
+ hint_select_move: "Navigate",
389
+ hint_select_space: "Space",
390
+ hint_select_check: "Toggle",
391
+ hint_select_confirm: "Confirm",
392
+ hint_checkbox_suffix: "Press enter without selection to go back",
393
+ doctor_not_installed: "Not installed",
394
+ doctor_links_valid: "{count} symlinks all valid",
395
+ doctor_broken_links: "{count} broken links",
396
+ doctor_fixed: "Cleaned {count} broken links",
397
+ doctor_not_exist: "Does not exist",
398
+ doctor_created: "Created {path}",
399
+ doctor_config_exists: "Exists",
400
+ doctor_config_not_exist: "Does not exist",
401
+ doctor_result_with_fix: "{warnings} warnings, {fixes} fixed",
402
+ doctor_result_without_fix: "{warnings} warnings",
403
+ count_suffix: "",
404
+ try_prompt: "help me develop an Abs operator"
405
+ };
406
+
407
+ // src/utils/i18n.ts
19
408
  var __filename = fileURLToPath(import.meta.url);
20
409
  var __dirname = dirname(__filename);
410
+ var EMBEDDED_LOCALES = {
411
+ zh_CN: zh_CN_default,
412
+ en_US: en_US_default
413
+ };
414
+ function loadLocale(locale) {
415
+ const embedded = EMBEDDED_LOCALES[locale];
416
+ if (embedded) return embedded;
417
+ const localePath = join(__dirname, "locales", `${locale}.json`);
418
+ if (existsSync(localePath)) {
419
+ try {
420
+ const content = readFileSync(localePath, "utf-8");
421
+ return JSON.parse(content);
422
+ } catch {
423
+ }
424
+ }
425
+ const fallbackPath = join(__dirname, "locales", "zh_CN.json");
426
+ if (existsSync(fallbackPath)) {
427
+ try {
428
+ const content = readFileSync(fallbackPath, "utf-8");
429
+ return JSON.parse(content);
430
+ } catch {
431
+ }
432
+ }
433
+ return EMBEDDED_LOCALES.zh_CN || {};
434
+ }
435
+ var currentLang = "zh_CN";
436
+ var messages = loadLocale("zh_CN");
437
+ function setLanguage(lang) {
438
+ currentLang = lang;
439
+ messages = loadLocale(lang);
440
+ }
441
+ function t(key) {
442
+ return messages[key] !== void 0 ? messages[key] : key;
443
+ }
444
+
445
+ // src/ui/theme.ts
446
+ import chalk from "chalk";
447
+ var selectTheme = {
448
+ helpMode: "never"
449
+ };
450
+ function isCJK(code) {
451
+ 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;
452
+ }
453
+ function isEmoji(code) {
454
+ return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
455
+ }
456
+ function getDisplayWidth(str) {
457
+ let width = 0;
458
+ for (const ch of str) {
459
+ const code = ch.codePointAt(0);
460
+ if (isCJK(code) || isEmoji(code)) {
461
+ width += 2;
462
+ } else {
463
+ width += 1;
464
+ }
465
+ }
466
+ return width;
467
+ }
468
+ function wrapDescription(text) {
469
+ const prefix = " \u{1F4A1} ";
470
+ const indent = " ";
471
+ const cols = process.stdout.columns || 120;
472
+ const maxLen = Math.max(20, cols - getDisplayWidth(prefix) - 2);
473
+ const tokens = [];
474
+ let i = 0;
475
+ while (i < text.length) {
476
+ const code = text[i].codePointAt(0);
477
+ if (isCJK(code)) {
478
+ tokens.push({ text: text[i], width: 2 });
479
+ i++;
480
+ } else {
481
+ let j = i;
482
+ while (j < text.length) {
483
+ const c = text[j].codePointAt(0);
484
+ if (isCJK(c)) break;
485
+ j++;
486
+ }
487
+ const word = text.slice(i, j);
488
+ tokens.push({ text: word, width: getDisplayWidth(word) });
489
+ i = j;
490
+ }
491
+ }
492
+ const lines = [];
493
+ let line = "";
494
+ let lineWidth = 0;
495
+ for (const token of tokens) {
496
+ if (lineWidth + token.width > maxLen && line) {
497
+ lines.push(line.trimEnd());
498
+ line = token.text;
499
+ lineWidth = token.width;
500
+ } else {
501
+ line += token.text;
502
+ lineWidth += token.width;
503
+ }
504
+ }
505
+ if (line) lines.push(line.trimEnd());
506
+ return lines.map((l, i2) => chalk.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
507
+ }
508
+ var checkboxTheme = {
509
+ icon: {
510
+ checked: "\x1B[32m\u2611\x1B[22m",
511
+ unchecked: "\u2610",
512
+ cursor: "\u276F"
513
+ },
514
+ helpMode: "never",
515
+ style: {
516
+ description: (text) => wrapDescription(text)
517
+ }
518
+ };
519
+
520
+ // src/utils/logger.ts
521
+ var logger = {
522
+ success: (msg) => console.log(chalk2.green("\u2713") + " " + msg),
523
+ error: (msg) => console.log(chalk2.red("\u2717") + " " + msg),
524
+ warn: (msg) => console.log(chalk2.yellow("\u26A0") + " " + msg),
525
+ info: (msg) => console.log(chalk2.cyan("\u2192") + " " + msg),
526
+ step: (msg) => console.log(chalk2.dim(msg)),
527
+ blank: () => console.log()
528
+ };
529
+ function createSpinner(text) {
530
+ return ora({ text, color: "cyan" });
531
+ }
532
+ function printBanner(subtitle) {
533
+ console.log(chalk2.cyan(`
534
+ ____ _ _ _ _ _ ____ _
535
+ / ___| / \\ | \\ | | \\ | | __ ) ___ | |_
536
+ | | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
537
+ | |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
538
+ \\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
539
+ `));
540
+ if (subtitle) {
541
+ console.log(chalk2.bold(` ${subtitle}`));
542
+ console.log();
543
+ }
544
+ }
545
+ function printBoxTitle(title, width = 65) {
546
+ const border = "\u2550".repeat(width);
547
+ const displayWidth = getDisplayWidth(title);
548
+ const totalPadding = width - displayWidth;
549
+ const leftPadding = Math.floor(totalPadding / 2);
550
+ const rightPadding = totalPadding - leftPadding;
551
+ const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
552
+ console.log();
553
+ console.log(chalk2.cyan.bold(` \u2554${border}\u2557`));
554
+ console.log(chalk2.cyan.bold(` \u2551`) + chalk2.cyan.bold(paddedTitle) + chalk2.cyan.bold(`\u2551`));
555
+ console.log(chalk2.cyan.bold(` \u255A${border}\u255D`));
556
+ console.log();
557
+ }
558
+ function showOperationHints(isCheckbox = false) {
559
+ if (isCheckbox) {
560
+ console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m${t("hint_select_space")}\x1B[0m ${t("hint_select_check")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}\uFF08${t("hint_checkbox_suffix")}\uFF09
561
+ `);
562
+ } else {
563
+ console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
564
+ `);
565
+ }
566
+ }
567
+
568
+ // src/utils/fs-helpers.ts
569
+ import { lstatSync, statSync } from "fs";
570
+ function isSymlink(path) {
571
+ try {
572
+ return lstatSync(path).isSymbolicLink();
573
+ } catch {
574
+ return false;
575
+ }
576
+ }
577
+ function isDirectory(path) {
578
+ try {
579
+ return statSync(path).isDirectory();
580
+ } catch {
581
+ return false;
582
+ }
583
+ }
584
+
585
+ // src/embedded-plugins.json
586
+ var embedded_plugins_default = [
587
+ {
588
+ id: "ops-direct-invoke",
589
+ dir: "plugins-official/ops-direct-invoke",
590
+ displayName: "AscendC Kernel \u76F4\u8C03",
591
+ script: "init.sh",
592
+ aliases: [
593
+ "ops-direct",
594
+ "ascendc-direct",
595
+ "direct",
596
+ "kernel"
597
+ ],
598
+ skills: 16,
599
+ agents: 3,
600
+ description: "Ascend C \u76F4\u8C03\u5168\u6D41\u7A0B\uFF1A\u73AF\u5883\u2192\u8BBE\u8BA1\u2192\u5F00\u53D1\u2192\u5BA1\u67E5\u2192\u6027\u80FD\u9A8C\u6536",
601
+ version: "1.2.2",
602
+ configFile: "AGENTS.md",
603
+ configRootConfigLink: false,
604
+ installSkills: [
605
+ {
606
+ dir: "ops",
607
+ skills: [
608
+ "ascendc-tiling-design",
609
+ "npu-arch",
610
+ "ascendc-api-best-practices",
611
+ "ascendc-regbase-best-practice",
612
+ "ascendc-blaze-best-practice",
613
+ "ops-precision-standard",
614
+ "ascendc-docs-search",
615
+ "ascendc-env-check",
616
+ "ascendc-precision-debug",
617
+ "ops-profiling",
618
+ "ascendc-direct-invoke-template",
619
+ "torch-ascendc-op-extension",
620
+ "ascendc-runtime-debug",
621
+ "ascendc-crash-debug",
622
+ "ascendc-code-review"
623
+ ]
624
+ },
625
+ {
626
+ dir: "infra",
627
+ skills: [
628
+ "gitcode-toolkit",
629
+ "gitcode-pr-handler",
630
+ "gitcode-issue-gen",
631
+ "gitcode-issue-handler"
632
+ ]
633
+ }
634
+ ],
635
+ installAgents: [
636
+ "ascendc-kernel-architect",
637
+ "ascendc-kernel-design-reviewer",
638
+ "ascendc-kernel-developer",
639
+ "ascendc-kernel-reviewer"
640
+ ],
641
+ externalRepos: [
642
+ {
643
+ url: "https://gitcode.com/cann/asc-devkit.git",
644
+ dir: "plugins-official/ops-direct-invoke/asc-devkit",
645
+ configRootLink: false
646
+ }
647
+ ]
648
+ },
649
+ {
650
+ id: "ops-direct-invoke-flash",
651
+ dir: "plugins-official/ops-direct-invoke-flash",
652
+ displayName: "AscendC Kernel \u76F4\u8C03\uFF08\u5FEB\u901F\u7248\uFF09",
653
+ script: "init.sh",
654
+ aliases: [
655
+ "flash",
656
+ "kernel-flash"
657
+ ],
658
+ skills: 1,
659
+ agents: 1,
660
+ description: "\u4ECE\u6570\u5B66\u516C\u5F0F\u6216\u4EE3\u7801\u7247\u6BB5\u5FEB\u901F\u6784\u5EFA\u5E76\u9A8C\u8BC1 Ascend NPU \u6838\u51FD\u6570",
661
+ version: "1.2.2",
662
+ configFile: "AGENTS.md",
663
+ configRootConfigLink: true,
664
+ installSkills: [
665
+ {
666
+ dir: "plugins-official/ops-direct-invoke-flash/skills",
667
+ skills: [
668
+ "ops-direct-invoke-flash"
669
+ ]
670
+ },
671
+ {
672
+ dir: "ops",
673
+ skills: [
674
+ "ascendc-st-design",
675
+ "ascendc-whitebox-design"
676
+ ]
677
+ },
678
+ {
679
+ dir: "infra",
680
+ skills: [
681
+ "gitcode-toolkit",
682
+ "gitcode-pr-handler",
683
+ "gitcode-issue-gen",
684
+ "gitcode-issue-handler"
685
+ ]
686
+ }
687
+ ],
688
+ installAgents: [
689
+ "ops-direct-invoke-flash-reviewer"
690
+ ]
691
+ },
692
+ {
693
+ id: "ops-registry-invoke",
694
+ dir: "plugins-official/ops-registry-invoke",
695
+ displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
696
+ script: "init.sh",
697
+ aliases: [
698
+ "ops-registry",
699
+ "ascendc-registry",
700
+ "registry"
701
+ ],
702
+ skills: 12,
703
+ agents: 4,
704
+ description: "\u751F\u4EA7\u7EA7 ACLNN \u6CE8\u518C\u7B97\u5B50\u5168\u6D41\u7A0B\uFF1A\u4EE3\u7801\u2192UT/ST\u2192\u4E0A\u5E93",
705
+ version: "1.2.2",
706
+ configFile: "AGENTS.md",
707
+ configRootConfigLink: false,
708
+ installSkills: [
709
+ {
710
+ dir: "ops",
711
+ skills: [
712
+ "ascendc-blaze-best-practice",
713
+ "ascendc-api-best-practices",
714
+ "ascendc-code-review",
715
+ "ascendc-crash-debug",
716
+ "ascendc-direct-invoke-template",
717
+ "ascendc-docs-gen",
718
+ "ascendc-docs-search",
719
+ "ascendc-env-check",
720
+ "npu-arch",
721
+ "ascendc-performance-best-practices",
722
+ "ascendc-precision-debug",
723
+ "ascendc-regbase-best-practice",
724
+ "ascendc-registry-invoke-template",
725
+ "ascendc-runtime-debug",
726
+ "ascendc-st-design",
727
+ "ascendc-tiling-design",
728
+ "ascendc-ut-develop",
729
+ "ascendc-whitebox-design",
730
+ "ops-precision-standard",
731
+ "ops-profiling",
732
+ "ops-spec-gen"
733
+ ]
734
+ },
735
+ {
736
+ dir: "infra",
737
+ skills: [
738
+ "gitcode-toolkit",
739
+ "gitcode-pr-handler",
740
+ "gitcode-issue-gen",
741
+ "gitcode-issue-handler"
742
+ ]
743
+ },
744
+ {
745
+ dir: "plugins-official/ops-registry-invoke",
746
+ skills: [
747
+ {
748
+ name: "workflow",
749
+ as: "ops-registry-invoke-workflow"
750
+ }
751
+ ]
752
+ }
753
+ ],
754
+ installAgents: [
755
+ "ascendc-ops-architect",
756
+ "ascendc-ops-developer",
757
+ "ascendc-ops-tester"
758
+ ],
759
+ externalRepos: [
760
+ {
761
+ url: "https://gitcode.com/cann/asc-devkit.git",
762
+ dir: "plugins-official/ops-registry-invoke/asc-devkit",
763
+ configRootLink: false
764
+ }
765
+ ]
766
+ },
767
+ {
768
+ id: "catlass-op-generator",
769
+ dir: "plugins-official/catlass-op-generator",
770
+ displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
771
+ script: "init.sh",
772
+ aliases: [
773
+ "catlass"
774
+ ],
775
+ skills: 10,
776
+ agents: 3,
777
+ description: "Catlass \u6A21\u677F\u62FC\u88C5 GEMM/Matmul/Epilogue \u7B97\u5B50\u5F00\u53D1",
778
+ version: "1.2.2",
779
+ configFile: "AGENTS.md",
780
+ configRootConfigLink: true,
781
+ installSkills: [
782
+ {
783
+ dir: "ops",
784
+ skills: [
785
+ "catlass-op-design",
786
+ "catlass-op-develop",
787
+ "catlass-op-perf-tune",
788
+ "ascendc-tiling-design",
789
+ "npu-arch",
790
+ "ascendc-api-best-practices",
791
+ "ops-precision-standard",
792
+ "ascendc-docs-search",
793
+ "ascendc-env-check",
794
+ "ascendc-precision-debug",
795
+ "ops-profiling",
796
+ "ascendc-runtime-debug",
797
+ "ascendc-code-review",
798
+ "torch-ascendc-op-extension"
799
+ ]
800
+ }
801
+ ],
802
+ installAgents: [
803
+ "catlass-op-architect",
804
+ "catlass-op-generator",
805
+ "catlass-op-reviewer"
806
+ ],
807
+ externalRepos: [
808
+ {
809
+ url: "https://gitcode.com/cann/asc-devkit.git",
810
+ dir: "plugins-official/catlass-op-generator/asc-devkit",
811
+ configRootLink: false
812
+ }
813
+ ]
814
+ },
815
+ {
816
+ id: "model-infer-optimize",
817
+ dir: "plugins-official/model-infer-optimize",
818
+ displayName: "NPU \u63A8\u7406\u4F18\u5316",
819
+ script: "init.sh",
820
+ aliases: [
821
+ "model-infer",
822
+ "infer",
823
+ "inference"
824
+ ],
825
+ skills: 11,
826
+ agents: 3,
827
+ description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316\uFF08\u5E76\u884C/KVCache/\u878D\u5408/\u91CF\u5316/\u56FE\u6A21\u5F0F/\u591A\u6D41/SuperKernel\uFF09",
828
+ version: "1.2.2",
829
+ configFile: "AGENTS.md",
830
+ configRootConfigLink: true,
831
+ installSkills: [
832
+ {
833
+ dir: "model",
834
+ skills: [
835
+ "model-infer-migrator",
836
+ "model-infer-parallel-analysis",
837
+ "model-infer-parallel-impl",
838
+ "model-infer-kvcache",
839
+ "model-infer-fusion",
840
+ "model-infer-quantization",
841
+ "model-infer-graph-mode",
842
+ "model-infer-precision-debug",
843
+ "model-infer-runtime-debug",
844
+ "model-infer-multi-stream",
845
+ "model-infer-prefetch",
846
+ "model-infer-superkernel"
847
+ ]
848
+ }
849
+ ],
850
+ installAgents: [
851
+ "model-infer-analyzer",
852
+ "model-infer-implementer",
853
+ "model-infer-reviewer"
854
+ ],
855
+ externalRepos: [
856
+ {
857
+ url: "https://gitcode.com/cann/cann-recipes-infer.git",
858
+ dir: "plugins-official/model-infer-optimize/cann-recipes-infer",
859
+ depth: 1,
860
+ configRootLink: true
861
+ }
862
+ ]
863
+ },
864
+ {
865
+ id: "pypto-op-orchestrator",
866
+ dir: "plugins-official/pypto-op-orchestrator",
867
+ displayName: "PyPTO \u7B97\u5B50\u5F00\u53D1",
868
+ script: "init.sh",
869
+ aliases: [
870
+ "pypto",
871
+ "pytorch"
872
+ ],
873
+ skills: 8,
874
+ agents: 3,
875
+ description: "PyPTO Python API \u7B97\u5B50\u5168\u6D41\u7A0B\u5F00\u53D1\uFF0C\u8986\u76D6\u9700\u6C42\u7406\u89E3\u3001API \u63A2\u7D22\u3001\u5B9E\u73B0\u4E0E\u8C03\u4F18",
876
+ version: "1.2.2",
877
+ configFile: "AGENTS.md",
878
+ configRootConfigLink: false,
879
+ installSkills: [
880
+ {
881
+ dir: "ops",
882
+ skills: [
883
+ "pypto-intent-understand",
884
+ "pypto-api-explore",
885
+ "pypto-op-design",
886
+ "pypto-op-develop",
887
+ "pypto-precision-debug",
888
+ "pypto-precision-compare",
889
+ "pypto-op-perf-tune",
890
+ "pypto-golden-generate"
891
+ ]
892
+ }
893
+ ],
894
+ installAgents: [
895
+ "pypto-op-analyst",
896
+ "pypto-op-developer",
897
+ "pypto-op-perf-tuner"
898
+ ],
899
+ externalRepos: [
900
+ {
901
+ url: "https://gitcode.com/cann/pypto.git",
902
+ dir: "plugins-official/pypto-op-orchestrator/pypto",
903
+ configRootLink: false
904
+ }
905
+ ]
906
+ },
907
+ {
908
+ id: "tilelang-op-orchestrator",
909
+ dir: "plugins-official/tilelang-op-orchestrator",
910
+ displayName: "TileLang \u7B97\u5B50\u5F00\u53D1",
911
+ script: "init.sh",
912
+ aliases: [
913
+ "tilelang"
914
+ ],
915
+ skills: 9,
916
+ agents: 3,
917
+ description: "TileLang Python DSL + @tilelang.jit \u7B97\u5B50\u5F00\u53D1\uFF08\u652F\u6301 Developer/Expert \u53CC\u6A21\u5F0F\uFF09",
918
+ version: "1.2.2",
919
+ configFile: "AGENTS.md",
920
+ configRootConfigLink: false,
921
+ installSkills: [
922
+ {
923
+ dir: "ops",
924
+ skills: [
925
+ "tilelang-api-best-practices",
926
+ "tilelang-env-check",
927
+ "tilelang-op-design",
928
+ "tilelang-op-develop",
929
+ "tilelang-op-test-design",
930
+ "tilelang-perf-optimization",
931
+ "tilelang-programming-model-guide",
932
+ "tilelang-review",
933
+ "tilelang-submodule-pull"
934
+ ]
935
+ }
936
+ ],
937
+ installAgents: [
938
+ "tilelang-op-analyst",
939
+ "tilelang-op-developer",
940
+ "tilelang-op-perf-tuner"
941
+ ],
942
+ externalRepos: [
943
+ {
944
+ url: "https://github.com/tile-ai/tilelang-ascend.git",
945
+ dir: "plugins-official/tilelang-op-orchestrator/tilelang-ascend",
946
+ depth: 1,
947
+ recursive: true,
948
+ configRootLink: false
949
+ }
950
+ ]
951
+ },
952
+ {
953
+ id: "triton-op-generator",
954
+ dir: "plugins-official/triton-op-generator",
955
+ displayName: "Triton \u7B97\u5B50\u5F00\u53D1",
956
+ script: "init.sh",
957
+ aliases: [
958
+ "triton"
959
+ ],
960
+ skills: 6,
961
+ agents: 1,
962
+ description: "Triton-Ascend \u7B97\u5B50 6 \u9636\u6BB5\u81EA\u52A8\u751F\u6210\u4E0E\u4F18\u5316\uFF08\u63CF\u8FF0\u2192\u4EE3\u7801\u2192\u9A8C\u8BC1\u2192\u4F18\u5316\uFF09",
963
+ version: "1.2.2",
964
+ configFile: "AGENTS.md",
965
+ configRootConfigLink: false,
966
+ installSkills: [
967
+ {
968
+ dir: "ops",
969
+ skills: [
970
+ "triton-task-extractor",
971
+ "triton-op-designer",
972
+ "triton-op-coding",
973
+ "triton-op-verifier",
974
+ "triton-latency-optimizer",
975
+ "npu-arch"
976
+ ]
977
+ }
978
+ ],
979
+ installAgents: []
980
+ },
981
+ {
982
+ id: "torch-compile",
983
+ dir: "plugins-official/torch-compile",
984
+ displayName: "torch.compile \u56FE\u6A21\u5F0F",
985
+ script: "init.sh",
986
+ aliases: [
987
+ "torch",
988
+ "compile",
989
+ "graph"
990
+ ],
991
+ skills: 6,
992
+ agents: 1,
993
+ description: "NPU \u56FE\u6A21\u5F0F npugraph_ex \u914D\u7F6E\u3001\u811A\u672C\u8FC1\u79FB\u3001\u7F16\u8BD1\u8BCA\u65AD\u4E0E\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE",
994
+ version: "1.2.2",
995
+ configFile: "AGENTS.md",
996
+ configRootConfigLink: true,
997
+ installSkills: [
998
+ {
999
+ dir: "graph",
1000
+ skills: [
1001
+ "torch-npugraph-ex-knowledge",
1002
+ "torch-npugraph-ex-template",
1003
+ "torch-npugraph-ex-dfx-triage",
1004
+ "torch-npugraph-ex-compile-error-diagnosis",
1005
+ "torch-npugraph-ex-runtime-error-diagnosis",
1006
+ "torch-custom-ops-guide"
1007
+ ]
1008
+ }
1009
+ ],
1010
+ installAgents: [
1011
+ "torch-npugraph-ex"
1012
+ ]
1013
+ },
1014
+ {
1015
+ id: "ops-code-reviewer",
1016
+ dir: "plugins-official/ops-code-reviewer",
1017
+ displayName: "\u4EE3\u7801\u68C0\u89C6",
1018
+ script: "init.sh",
1019
+ aliases: [
1020
+ "code-review",
1021
+ "reviewer",
1022
+ "review"
1023
+ ],
1024
+ skills: 5,
1025
+ agents: 1,
1026
+ description: "Ascend C \u7B97\u5B50\u4EE3\u7801 4 \u9636\u6BB5\u5168\u91CF\u68C0\u89C6\uFF08\u4E3B Agent \u7F16\u6392 + \u5B50 Agent \u5E76\u884C\u68C0\u89C6\uFF09",
1027
+ version: "1.2.2",
1028
+ configFile: "AGENTS.md",
1029
+ configRootConfigLink: false,
1030
+ installSkills: [
1031
+ {
1032
+ dir: "ops",
1033
+ skills: [
1034
+ "ascendc-code-review",
1035
+ "ascendc-docs-search",
1036
+ "ascendc-task-focus",
1037
+ "ascendc-api-best-practices",
1038
+ "npu-arch"
1039
+ ]
1040
+ }
1041
+ ],
1042
+ installAgents: [
1043
+ "ascendc-code-summarizer",
1044
+ "ascendc-ops-reviewer"
1045
+ ]
1046
+ }
1047
+ ];
1048
+
1049
+ // src/core/registry.ts
1050
+ var __filename2 = fileURLToPath2(import.meta.url);
1051
+ var __dirname2 = dirname2(__filename2);
1052
+ function findPluginsDir() {
1053
+ const candidates = [
1054
+ join2(__dirname2, "plugins.d"),
1055
+ join2(__dirname2, "..", "plugins.d"),
1056
+ join2(__dirname2, "..", "..", "plugins.d")
1057
+ ];
1058
+ for (const dir of candidates) {
1059
+ if (existsSync2(dir)) return dir;
1060
+ }
1061
+ return candidates[0];
1062
+ }
21
1063
  function loadPluginsFromYaml() {
22
- const pluginsDir = join(__dirname, "plugins.d");
23
- const defaultsPath = join(pluginsDir, "_defaults.yml");
1064
+ const pluginsDir = findPluginsDir();
1065
+ const defaultsPath = join2(pluginsDir, "_defaults.yml");
24
1066
  let defaults = {};
25
1067
  try {
26
- defaults = parseYaml(readFileSync(defaultsPath, "utf-8")) || {};
1068
+ defaults = parseYaml(readFileSync2(defaultsPath, "utf-8")) || {};
27
1069
  } catch {
28
1070
  }
29
1071
  const plugins = [];
@@ -31,7 +1073,7 @@ function loadPluginsFromYaml() {
31
1073
  for (const file of readdirSync(pluginsDir)) {
32
1074
  if (file.startsWith("_") || !file.endsWith(".yml")) continue;
33
1075
  try {
34
- const content = parseYaml(readFileSync(join(pluginsDir, file), "utf-8"));
1076
+ const content = parseYaml(readFileSync2(join2(pluginsDir, file), "utf-8"));
35
1077
  if (!content || !content.id) continue;
36
1078
  plugins.push({
37
1079
  id: content.id,
@@ -41,19 +1083,70 @@ function loadPluginsFromYaml() {
41
1083
  aliases: content.aliases || [],
42
1084
  skills: content.skills ?? defaults.skills ?? 0,
43
1085
  agents: content.agents ?? defaults.agents ?? 0,
44
- description: content.description || ""
1086
+ description: content.description || "",
1087
+ version: content.version,
1088
+ configFile: content.configFile,
1089
+ configRootConfigLink: content.configRootConfigLink,
1090
+ installSkills: content.installSkills,
1091
+ installAgents: content.installAgents,
1092
+ externalRepos: content.externalRepos
45
1093
  });
46
1094
  } catch {
1095
+ logger.warn(t("plugin_yaml_error").replace("{file}", file));
47
1096
  }
48
1097
  }
49
1098
  } catch {
50
1099
  }
1100
+ if (plugins.length === 0) {
1101
+ return [...embedded_plugins_default];
1102
+ }
51
1103
  plugins.sort(
52
1104
  (a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
53
1105
  );
54
1106
  return plugins;
55
1107
  }
56
1108
  var PLUGIN_REGISTRY = loadPluginsFromYaml();
1109
+ var PLUGIN_DIRS = ["plugins-official", "plugins-community"];
1110
+ function mergeDynamicPlugins(repoPath) {
1111
+ if (!existsSync2(repoPath)) return;
1112
+ const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
1113
+ const dynamic = [];
1114
+ for (const pluginDir of PLUGIN_DIRS) {
1115
+ const parent = join2(repoPath, pluginDir);
1116
+ if (!existsSync2(parent)) continue;
1117
+ let entries = [];
1118
+ try {
1119
+ entries = readdirSync(parent);
1120
+ } catch {
1121
+ continue;
1122
+ }
1123
+ for (const entry of entries) {
1124
+ const pluginPath = join2(parent, entry);
1125
+ if (!isDirectory(pluginPath)) continue;
1126
+ if (entry === "install-helper") continue;
1127
+ const initSh = join2(pluginPath, "init.sh");
1128
+ if (!existsSync2(initSh)) continue;
1129
+ const pluginId = entry;
1130
+ if (existingIds.has(pluginId)) continue;
1131
+ dynamic.push({
1132
+ id: pluginId,
1133
+ dir: `${pluginDir}/${entry}`,
1134
+ displayName: entry,
1135
+ script: "init.sh",
1136
+ aliases: [],
1137
+ skills: 0,
1138
+ agents: 0,
1139
+ description: ""
1140
+ });
1141
+ existingIds.add(pluginId);
1142
+ }
1143
+ }
1144
+ if (dynamic.length > 0) {
1145
+ PLUGIN_REGISTRY = [...PLUGIN_REGISTRY, ...dynamic].sort(
1146
+ (a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
1147
+ );
1148
+ }
1149
+ }
57
1150
  function findPlugin(query) {
58
1151
  const normalized = query.toLowerCase().trim();
59
1152
  const exactMatch = PLUGIN_REGISTRY.find(
@@ -77,47 +1170,14 @@ function getPluginById(id) {
77
1170
 
78
1171
  // src/core/detector.ts
79
1172
  import { execa } from "execa";
80
- import { existsSync as existsSync2 } from "fs";
1173
+ import { existsSync as existsSync4 } from "fs";
81
1174
  import { homedir as homedir2 } from "os";
82
1175
  import { join as join4 } from "path";
83
1176
 
84
1177
  // src/utils/paths.ts
85
1178
  import { homedir } from "os";
86
1179
  import { join as join3 } from "path";
87
- import { existsSync } from "fs";
88
-
89
- // src/utils/i18n.ts
90
- import { readFileSync as readFileSync2 } from "fs";
91
- import { join as join2, dirname as dirname2 } from "path";
92
- import { fileURLToPath as fileURLToPath2 } from "url";
93
- var __filename2 = fileURLToPath2(import.meta.url);
94
- var __dirname2 = dirname2(__filename2);
95
- function loadLocale(locale) {
96
- const localePath = join2(__dirname2, "locales", `${locale}.json`);
97
- try {
98
- const content = readFileSync2(localePath, "utf-8");
99
- return JSON.parse(content);
100
- } catch {
101
- const fallbackPath = join2(__dirname2, "locales", "zh_CN.json");
102
- try {
103
- const content = readFileSync2(fallbackPath, "utf-8");
104
- return JSON.parse(content);
105
- } catch {
106
- return {};
107
- }
108
- }
109
- }
110
- var currentLang = "zh_CN";
111
- var messages = loadLocale("zh_CN");
112
- function setLanguage(lang) {
113
- currentLang = lang;
114
- messages = loadLocale(lang);
115
- }
116
- function t(key) {
117
- return messages[key] !== void 0 ? messages[key] : key;
118
- }
119
-
120
- // src/utils/paths.ts
1180
+ import { existsSync as existsSync3 } from "fs";
121
1181
  var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
122
1182
  var VALID_LEVELS = ["project", "global"];
123
1183
  function validateTool(tool) {
@@ -138,9 +1198,9 @@ function validateLevel(level) {
138
1198
  }
139
1199
  function detectTraeVariant() {
140
1200
  const home = homedir();
141
- if (existsSync(join3(home, ".trae-cn"))) return "ide";
142
- if (existsSync(join3(home, ".marscode"))) return "plugin";
143
- if (existsSync(join3(home, ".traecli"))) return "cli";
1201
+ if (existsSync3(join3(home, ".trae-cn"))) return "ide";
1202
+ if (existsSync3(join3(home, ".marscode"))) return "plugin";
1203
+ if (existsSync3(join3(home, ".traecli"))) return "cli";
144
1204
  return "unknown";
145
1205
  }
146
1206
  function getConfigRoot(tool, level, base) {
@@ -202,6 +1262,9 @@ function getConfigRoot(tool, level, base) {
202
1262
  function getConfigFileName(tool) {
203
1263
  return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
204
1264
  }
1265
+ function getAgentsFileName(tool) {
1266
+ return getConfigFileName(tool);
1267
+ }
205
1268
  function getSkillsDir(configRoot) {
206
1269
  return join3(configRoot, "skills");
207
1270
  }
@@ -231,8 +1294,9 @@ async function getCommandVersion(cmd, args = ["--version"]) {
231
1294
  }
232
1295
  async function getCommandPath(cmd) {
233
1296
  try {
234
- const result = await execa("which", [cmd], { timeout: 5e3 });
235
- return result.stdout.trim();
1297
+ const whichCmd = process.platform === "win32" ? "where" : "which";
1298
+ const result = await execa(whichCmd, [cmd], { timeout: 5e3 });
1299
+ return result.stdout.trim().split("\n")[0];
236
1300
  } catch {
237
1301
  return void 0;
238
1302
  }
@@ -253,11 +1317,11 @@ async function detectTrae() {
253
1317
  const home = homedir2();
254
1318
  const variant = detectTraeVariant();
255
1319
  let path;
256
- if (variant === "ide" && existsSync2(join4(home, ".trae-cn"))) {
1320
+ if (variant === "ide" && existsSync4(join4(home, ".trae-cn"))) {
257
1321
  path = join4(home, ".trae-cn");
258
- } else if (variant === "plugin" && existsSync2(join4(home, ".marscode"))) {
1322
+ } else if (variant === "plugin" && existsSync4(join4(home, ".marscode"))) {
259
1323
  path = join4(home, ".marscode");
260
- } else if (variant === "cli" && existsSync2(join4(home, ".traecli"))) {
1324
+ } else if (variant === "cli" && existsSync4(join4(home, ".traecli"))) {
261
1325
  path = join4(home, ".traecli");
262
1326
  } else if (variant === "unknown") {
263
1327
  const cmdPath = await getCommandPath("trae");
@@ -274,7 +1338,7 @@ async function detectCursor() {
274
1338
  }
275
1339
  if (process.platform === "darwin") {
276
1340
  const appPath = "/Applications/Cursor.app";
277
- if (existsSync2(appPath)) {
1341
+ if (existsSync4(appPath)) {
278
1342
  return { name: "cursor", path: appPath };
279
1343
  }
280
1344
  }
@@ -318,15 +1382,15 @@ function getToolDisplayName(tool) {
318
1382
  return names[tool];
319
1383
  }
320
1384
  function getAllTools() {
321
- return ["opencode", "claude", "trae", "cursor", "copilot"];
1385
+ return VALID_TOOLS;
322
1386
  }
323
1387
 
324
1388
  // src/core/manifest.ts
325
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
1389
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
326
1390
  import { join as join5 } from "path";
327
1391
  function readManifest(configRoot) {
328
1392
  const manifestPath = getManifestPath(configRoot);
329
- if (existsSync3(manifestPath)) {
1393
+ if (existsSync5(manifestPath)) {
330
1394
  try {
331
1395
  const content = readFileSync3(manifestPath, "utf-8");
332
1396
  return JSON.parse(content);
@@ -339,7 +1403,7 @@ function readAllManifests(configRoot) {
339
1403
  const manifests = [];
340
1404
  const plugins = getAllPlugins();
341
1405
  const standardPath = getManifestPath(configRoot);
342
- if (existsSync3(standardPath)) {
1406
+ if (existsSync5(standardPath)) {
343
1407
  try {
344
1408
  const content = readFileSync3(standardPath, "utf-8");
345
1409
  manifests.push(JSON.parse(content));
@@ -348,7 +1412,7 @@ function readAllManifests(configRoot) {
348
1412
  }
349
1413
  for (const plugin of plugins) {
350
1414
  const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
351
- if (existsSync3(pluginManifestPath)) {
1415
+ if (existsSync5(pluginManifestPath)) {
352
1416
  try {
353
1417
  const content = readFileSync3(pluginManifestPath, "utf-8");
354
1418
  manifests.push(JSON.parse(content));
@@ -361,7 +1425,7 @@ function readAllManifests(configRoot) {
361
1425
  function scanInstalled() {
362
1426
  const plugins = getAllPlugins();
363
1427
  const installed = [];
364
- const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
1428
+ const tools = VALID_TOOLS;
365
1429
  const levels = ["project", "global"];
366
1430
  for (const tool of tools) {
367
1431
  for (const level of levels) {
@@ -398,14 +1462,14 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
398
1462
  if (manifest.installed_skills && manifest.installed_skills.length > 0) {
399
1463
  const hasAnySkill = manifest.installed_skills.some((skillId) => {
400
1464
  const skillPath = join5(skillsDir, skillId);
401
- return existsSync3(skillPath);
1465
+ return existsSync5(skillPath);
402
1466
  });
403
1467
  if (!hasAnySkill) return false;
404
1468
  }
405
1469
  if (manifest.installed_agents && manifest.installed_agents.length > 0) {
406
1470
  const hasAnyAgent = manifest.installed_agents.some((agentId) => {
407
1471
  const agentPath = join5(agentsDir, agentId);
408
- return existsSync3(agentPath);
1472
+ return existsSync5(agentPath);
409
1473
  });
410
1474
  if (!hasAnyAgent) return false;
411
1475
  }
@@ -415,86 +1479,44 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
415
1479
  } else {
416
1480
  agentsMdPath = join5(configRoot, "AGENTS.md");
417
1481
  }
418
- if (!existsSync3(agentsMdPath)) return false;
1482
+ if (!existsSync5(agentsMdPath)) return false;
419
1483
  return true;
420
1484
  }
421
1485
 
422
- // src/utils/logger.ts
423
- import chalk from "chalk";
424
- import ora from "ora";
425
- var logger = {
426
- success: (msg) => console.log(chalk.green("\u2713") + " " + msg),
427
- error: (msg) => console.log(chalk.red("\u2717") + " " + msg),
428
- warn: (msg) => console.log(chalk.yellow("\u26A0") + " " + msg),
429
- info: (msg) => console.log(chalk.cyan("\u2192") + " " + msg),
430
- step: (msg) => console.log(chalk.dim(msg)),
431
- blank: () => console.log()
432
- };
433
- function createSpinner(text) {
434
- return ora({ text, color: "cyan" });
435
- }
436
- function printBanner(subtitle) {
437
- console.log(chalk.cyan(`
438
- ____ _ _ _ _ _ ____ _
439
- / ___| / \\ | \\ | | \\ | | __ ) ___ | |_
440
- | | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
441
- | |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
442
- \\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
443
- `));
444
- if (subtitle) {
445
- console.log(chalk.bold(` ${subtitle}`));
446
- console.log();
447
- }
448
- }
449
- function getDisplayWidth(str) {
450
- let width = 0;
451
- for (const ch of str) {
452
- const code = ch.codePointAt(0);
453
- 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) {
454
- width += 2;
455
- } else {
456
- width += 1;
1486
+ // src/utils/config.ts
1487
+ import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync4 } from "fs";
1488
+ import { join as join6 } from "path";
1489
+ import { parse, stringify } from "yaml";
1490
+
1491
+ // src/utils/fs.ts
1492
+ import { writeFileSync, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
1493
+ import { dirname as dirname3 } from "path";
1494
+ function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
1495
+ const dir = dirname3(filePath);
1496
+ if (!existsSync6(dir)) {
1497
+ throw new Error(`Directory does not exist: ${dir}`);
1498
+ }
1499
+ const tmpPath = `${filePath}.tmp`;
1500
+ writeFileSync(tmpPath, data, encoding);
1501
+ if (existsSync6(filePath)) {
1502
+ try {
1503
+ unlinkSync(filePath);
1504
+ } catch {
457
1505
  }
458
1506
  }
459
- return width;
460
- }
461
- function printBoxTitle(title, width = 65) {
462
- const border = "\u2550".repeat(width);
463
- const displayWidth = getDisplayWidth(title);
464
- const totalPadding = width - displayWidth;
465
- const leftPadding = Math.floor(totalPadding / 2);
466
- const rightPadding = totalPadding - leftPadding;
467
- const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
468
- console.log();
469
- console.log(chalk.cyan.bold(` \u2554${border}\u2557`));
470
- console.log(chalk.cyan.bold(` \u2551`) + chalk.cyan.bold(paddedTitle) + chalk.cyan.bold(`\u2551`));
471
- console.log(chalk.cyan.bold(` \u255A${border}\u255D`));
472
- console.log();
473
- }
474
- function showOperationHints(isCheckbox = false) {
475
- if (isCheckbox) {
476
- console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m${t("hint_select_space")}\x1B[0m ${t("hint_select_check")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}\uFF08${t("hint_checkbox_suffix")}\uFF09
477
- `);
478
- } else {
479
- console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
480
- `);
481
- }
1507
+ renameSync(tmpPath, filePath);
482
1508
  }
483
1509
 
484
1510
  // src/utils/config.ts
485
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
486
- import { join as join6 } from "path";
487
- import { homedir as homedir3 } from "os";
488
- import { parse, stringify } from "yaml";
489
1511
  function getConfigDir() {
490
- return join6(homedir3(), ".cannbot");
1512
+ return getCannbotConfigDir();
491
1513
  }
492
1514
  function getConfigPath() {
493
1515
  return join6(getConfigDir(), "config.yaml");
494
1516
  }
495
1517
  function readConfig() {
496
1518
  const configPath = getConfigPath();
497
- if (!existsSync4(configPath)) {
1519
+ if (!existsSync7(configPath)) {
498
1520
  return {
499
1521
  language: "zh_CN",
500
1522
  installedPlugins: []
@@ -511,6 +1533,7 @@ function readConfig() {
511
1533
  installedPlugins: parsed.installedPlugins || []
512
1534
  };
513
1535
  } catch {
1536
+ logger.warn(t("config_corrupted"));
514
1537
  return {
515
1538
  language: "zh_CN",
516
1539
  installedPlugins: []
@@ -519,112 +1542,39 @@ function readConfig() {
519
1542
  }
520
1543
  function writeConfig(config) {
521
1544
  const configDir = getConfigDir();
522
- if (!existsSync4(configDir)) {
1545
+ if (!existsSync7(configDir)) {
523
1546
  mkdirSync(configDir, { recursive: true });
524
1547
  }
525
1548
  const configPath = getConfigPath();
526
1549
  const content = stringify(config);
527
- writeFileSync(configPath, content, "utf-8");
1550
+ atomicWriteFileSync(configPath, content);
528
1551
  }
529
1552
  function updateConfig(updates) {
530
1553
  const config = readConfig();
531
1554
  const updated = { ...config, ...updates };
532
- writeConfig(updated);
533
- return updated;
534
- }
535
- function addInstalledPlugin(pluginId) {
536
- const config = readConfig();
537
- if (!config.installedPlugins.includes(pluginId)) {
538
- config.installedPlugins.push(pluginId);
539
- writeConfig(config);
540
- }
541
- }
542
- function removeInstalledPlugin(pluginId) {
543
- const config = readConfig();
544
- config.installedPlugins = config.installedPlugins.filter(
545
- (id) => id !== pluginId
546
- );
547
- writeConfig(config);
548
- }
549
-
550
- // src/ui/theme.ts
551
- import chalk2 from "chalk";
552
- var selectTheme = {
553
- helpMode: "never"
554
- };
555
- function isCJK(code) {
556
- 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;
557
- }
558
- function isEmoji(code) {
559
- return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
560
- }
561
- function getDisplayWidth2(str) {
562
- let width = 0;
563
- for (const ch of str) {
564
- const code = ch.codePointAt(0);
565
- if (isCJK(code) || isEmoji(code)) {
566
- width += 2;
567
- } else {
568
- width += 1;
569
- }
570
- }
571
- return width;
572
- }
573
- function wrapDescription(text) {
574
- const prefix = " \u{1F4A1} ";
575
- const indent = " ";
576
- const cols = process.stdout.columns || 120;
577
- const maxLen = Math.max(20, cols - getDisplayWidth2(prefix) - 2);
578
- const tokens = [];
579
- let i = 0;
580
- while (i < text.length) {
581
- const code = text[i].codePointAt(0);
582
- if (isCJK(code)) {
583
- tokens.push({ text: text[i], width: 2 });
584
- i++;
585
- } else {
586
- let j = i;
587
- while (j < text.length) {
588
- const c = text[j].codePointAt(0);
589
- if (isCJK(c)) break;
590
- j++;
591
- }
592
- const word = text.slice(i, j);
593
- tokens.push({ text: word, width: getDisplayWidth2(word) });
594
- i = j;
595
- }
596
- }
597
- const lines = [];
598
- let line = "";
599
- let lineWidth = 0;
600
- for (const token of tokens) {
601
- if (lineWidth + token.width > maxLen && line) {
602
- lines.push(line.trimEnd());
603
- line = token.text;
604
- lineWidth = token.width;
605
- } else {
606
- line += token.text;
607
- lineWidth += token.width;
608
- }
609
- }
610
- if (line) lines.push(line.trimEnd());
611
- return lines.map((l, i2) => chalk2.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
1555
+ writeConfig(updated);
1556
+ return updated;
612
1557
  }
613
- var checkboxTheme = {
614
- icon: {
615
- checked: "\x1B[32m\u2611\x1B[22m",
616
- unchecked: "\u2610",
617
- cursor: "\u276F"
618
- },
619
- helpMode: "never",
620
- style: {
621
- description: (text) => wrapDescription(text)
1558
+ function addInstalledPlugin(pluginId) {
1559
+ const config = readConfig();
1560
+ if (!config.installedPlugins.includes(pluginId)) {
1561
+ config.installedPlugins.push(pluginId);
1562
+ writeConfig(config);
622
1563
  }
623
- };
1564
+ }
1565
+ function removeInstalledPlugin(pluginId) {
1566
+ const config = readConfig();
1567
+ config.installedPlugins = config.installedPlugins.filter(
1568
+ (id) => id !== pluginId
1569
+ );
1570
+ writeConfig(config);
1571
+ }
624
1572
 
625
- // src/ui/wizard.ts
1573
+ // src/utils/constants.ts
626
1574
  var BACK = "__back__";
627
1575
  var CANCEL = "__cancel__";
1576
+
1577
+ // src/ui/wizard.ts
628
1578
  async function selectToolWithDetection() {
629
1579
  const config = readConfig();
630
1580
  logger.blank();
@@ -708,7 +1658,7 @@ async function stepTool(detectedTools, lastTool) {
708
1658
  if (detectedTools.length === 0) {
709
1659
  logger.error(t("error_tool_not_found"));
710
1660
  const choices2 = [
711
- ...["opencode", "claude", "trae", "cursor", "copilot"].map((tool) => ({
1661
+ ...VALID_TOOLS.map((tool) => ({
712
1662
  name: `> ${getToolDisplayName(tool)}`,
713
1663
  value: tool
714
1664
  })),
@@ -809,37 +1759,25 @@ async function stepPlugins(tool, level) {
809
1759
  const isInstalled = installedSet.has(p.id);
810
1760
  const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
811
1761
  return {
812
- name: `${chalk3.cyan(p.displayName)}${suffix}`,
1762
+ name: `> ${chalk3.cyan(p.displayName)}${suffix}`,
813
1763
  value: p.id,
814
- checked: false,
815
1764
  description: p.description
816
1765
  };
817
1766
  });
818
- showOperationHints(true);
819
- const selected = await checkbox({
820
- message: t("wizard_select_plugins"),
1767
+ choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1768
+ choices.push({ name: "<- " + t("wizard_back"), value: BACK });
1769
+ choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL });
1770
+ showOperationHints();
1771
+ const selected = await select({
1772
+ message: t("wizard_select_plugin"),
821
1773
  choices,
822
1774
  loop: false,
823
- instructions: false,
824
- theme: checkboxTheme,
1775
+ theme: selectTheme,
825
1776
  pageSize: 15
826
1777
  });
827
- const pluginIds = selected;
828
- if (pluginIds.length === 0) {
829
- const action = await select({
830
- message: t("wizard_no_selection"),
831
- choices: [
832
- new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
833
- { name: "<- " + t("wizard_back_to_reselect"), value: "back" },
834
- { name: "x " + t("wizard_cancel"), value: "cancel" }
835
- ],
836
- loop: false,
837
- theme: selectTheme
838
- });
839
- if (action === "back") return BACK;
840
- return CANCEL;
841
- }
842
- return pluginIds;
1778
+ if (selected === BACK) return BACK;
1779
+ if (selected === CANCEL) return CANCEL;
1780
+ return [selected];
843
1781
  }
844
1782
  async function stepConfirm(tool, level, plugins) {
845
1783
  printBoxTitle(t("wizard_step_4_title"));
@@ -870,19 +1808,19 @@ async function stepConfirm(tool, level, plugins) {
870
1808
  }
871
1809
 
872
1810
  // src/core/repository.ts
873
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
874
- import { join as join8, dirname as dirname4 } from "path";
1811
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
1812
+ import { join as join8, dirname as dirname5 } from "path";
875
1813
  import { fileURLToPath as fileURLToPath4 } from "url";
876
1814
  import { execa as execa2 } from "execa";
877
1815
  import { parse as parseYaml3 } from "yaml";
878
1816
 
879
1817
  // src/core/scanner.ts
880
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
881
- import { join as join7, dirname as dirname3 } from "path";
1818
+ import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
1819
+ import { join as join7, dirname as dirname4 } from "path";
882
1820
  import { fileURLToPath as fileURLToPath3 } from "url";
883
1821
  import { parse as parseYaml2 } from "yaml";
884
1822
  var __filename3 = fileURLToPath3(import.meta.url);
885
- var __dirname3 = dirname3(__filename3);
1823
+ var __dirname3 = dirname4(__filename3);
886
1824
  var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
887
1825
  "node_modules",
888
1826
  ".git",
@@ -907,14 +1845,17 @@ function loadScanConfig() {
907
1845
  const configPath = join7(__dirname3, "config", "repository.yaml");
908
1846
  try {
909
1847
  const config = parseYaml2(readFileSync5(configPath, "utf-8"));
1848
+ const ttlHours = config.scanCacheTtlHours || 24;
910
1849
  return {
911
- skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
912
- pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
1850
+ skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
1851
+ pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"],
1852
+ cacheTtlMs: ttlHours * 60 * 60 * 1e3
913
1853
  };
914
1854
  } catch {
915
1855
  return {
916
- skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
917
- pluginDirs: ["plugins-official", "plugins-community"]
1856
+ skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
1857
+ pluginDirs: ["plugins-official", "plugins-community"],
1858
+ cacheTtlMs: 24 * 60 * 60 * 1e3
918
1859
  };
919
1860
  }
920
1861
  }
@@ -926,23 +1867,37 @@ function scanSkills(repoPath) {
926
1867
  const seen = /* @__PURE__ */ new Set();
927
1868
  for (const dir of SKILL_SCAN_DIRS) {
928
1869
  const fullPath = join7(repoPath, dir);
929
- if (!existsSync5(fullPath)) continue;
1870
+ if (!existsSync8(fullPath)) continue;
930
1871
  scanDirectory(fullPath, repoPath, dir, skills, seen);
931
1872
  }
932
1873
  for (const pluginDir of PLUGIN_SCAN_DIRS) {
933
1874
  const pluginParent = join7(repoPath, pluginDir);
934
- if (!existsSync5(pluginParent)) continue;
1875
+ if (!existsSync8(pluginParent)) continue;
935
1876
  for (const plugin of safeReaddir(pluginParent)) {
936
1877
  const pluginPath = join7(pluginParent, plugin);
937
1878
  if (!isDirectory(pluginPath)) continue;
938
1879
  const skillsDir = join7(pluginPath, "skills");
939
- if (existsSync5(skillsDir)) {
1880
+ if (existsSync8(skillsDir)) {
940
1881
  scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
941
1882
  }
942
1883
  const skillDir = join7(pluginPath, "skill");
943
- if (existsSync5(skillDir)) {
1884
+ if (existsSync8(skillDir)) {
944
1885
  scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
945
1886
  }
1887
+ const rootSkillMd = join7(pluginPath, "SKILL.md");
1888
+ const initSh = join7(pluginPath, "init.sh");
1889
+ if (existsSync8(rootSkillMd) && !existsSync8(initSh)) {
1890
+ const frontmatter = parseFrontmatter(rootSkillMd);
1891
+ if (frontmatter && !seen.has(frontmatter.name)) {
1892
+ seen.add(frontmatter.name);
1893
+ skills.push({
1894
+ id: frontmatter.name,
1895
+ description: frontmatter.description,
1896
+ source: `${pluginDir}/${plugin}`,
1897
+ filePath: rootSkillMd
1898
+ });
1899
+ }
1900
+ }
946
1901
  }
947
1902
  }
948
1903
  return skills;
@@ -965,8 +1920,12 @@ function parseFrontmatter(filePath) {
965
1920
  const parsed = parseYaml2(yamlBlock);
966
1921
  if (!parsed || typeof parsed !== "object") return null;
967
1922
  if (!parsed.name || typeof parsed.name !== "string") return null;
1923
+ const trimmedName = parsed.name.trim();
1924
+ if (!trimmedName || /[\/\\]|\.\./.test(trimmedName) || !/^[a-zA-Z0-9._-]+$/.test(trimmedName)) {
1925
+ return null;
1926
+ }
968
1927
  return {
969
- name: parsed.name.trim(),
1928
+ name: trimmedName,
970
1929
  description: (parsed.description || "").trim()
971
1930
  };
972
1931
  } catch {
@@ -976,15 +1935,15 @@ function parseFrontmatter(filePath) {
976
1935
  function getCurrentCommit(repoPath) {
977
1936
  try {
978
1937
  const headFile = join7(repoPath, ".git", "HEAD");
979
- if (!existsSync5(headFile)) return "unknown";
1938
+ if (!existsSync8(headFile)) return "unknown";
980
1939
  const headContent = readFileSync5(headFile, "utf-8").trim();
981
1940
  if (headContent.startsWith("ref: ")) {
982
1941
  const refPath = join7(repoPath, ".git", headContent.slice(5));
983
- if (existsSync5(refPath)) {
1942
+ if (existsSync8(refPath)) {
984
1943
  return readFileSync5(refPath, "utf-8").trim();
985
1944
  }
986
1945
  const packedRefsPath = join7(repoPath, ".git", "packed-refs");
987
- if (existsSync5(packedRefsPath)) {
1946
+ if (existsSync8(packedRefsPath)) {
988
1947
  const packedRefs = readFileSync5(packedRefsPath, "utf-8");
989
1948
  const refName = headContent.slice(5);
990
1949
  const lines = packedRefs.split("\n");
@@ -1003,12 +1962,12 @@ function getCurrentCommit(repoPath) {
1003
1962
  function readScanCache() {
1004
1963
  try {
1005
1964
  const cachePath = getCachePath();
1006
- if (!existsSync5(cachePath)) return null;
1965
+ if (!existsSync8(cachePath)) return null;
1007
1966
  const content = readFileSync5(cachePath, "utf-8");
1008
1967
  const cache = JSON.parse(content);
1009
1968
  if (!cache.skills || !cache.timestamp) return null;
1010
1969
  const age = Date.now() - cache.timestamp;
1011
- if (age > 24 * 60 * 60 * 1e3) return null;
1970
+ if (age > scanConfig.cacheTtlMs) return null;
1012
1971
  return cache;
1013
1972
  } catch {
1014
1973
  return null;
@@ -1017,11 +1976,11 @@ function readScanCache() {
1017
1976
  function writeScanCache(cache) {
1018
1977
  try {
1019
1978
  const cachePath = getCachePath();
1020
- const cacheDir = dirname3(cachePath);
1021
- if (!existsSync5(cacheDir)) {
1979
+ const cacheDir = dirname4(cachePath);
1980
+ if (!existsSync8(cacheDir)) {
1022
1981
  mkdirSync2(cacheDir, { recursive: true });
1023
1982
  }
1024
- writeFileSync2(cachePath, JSON.stringify(cache, null, 2));
1983
+ atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
1025
1984
  } catch {
1026
1985
  }
1027
1986
  }
@@ -1034,7 +1993,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1034
1993
  if (EXCLUDE_DIRS.has(entry)) continue;
1035
1994
  if (!isDirectory(fullPath)) continue;
1036
1995
  const skillMd = join7(fullPath, "SKILL.md");
1037
- if (existsSync5(skillMd)) {
1996
+ if (existsSync8(skillMd)) {
1038
1997
  const frontmatter = parseFrontmatter(skillMd);
1039
1998
  if (frontmatter && !seen.has(frontmatter.name)) {
1040
1999
  seen.add(frontmatter.name);
@@ -1057,28 +2016,22 @@ function safeReaddir(dir) {
1057
2016
  return [];
1058
2017
  }
1059
2018
  }
1060
- function isDirectory(path) {
1061
- try {
1062
- return statSync(path).isDirectory();
1063
- } catch {
1064
- return false;
1065
- }
1066
- }
1067
2019
 
1068
2020
  // src/core/skill-registry.ts
1069
2021
  var CATEGORY_DEFS = [
1070
- { id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
1071
- { id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
1072
- { id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
1073
- { id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
1074
- { id: "ascendc", name: "AscendC \u5F00\u53D1" },
1075
- { id: "pypto", name: "PyPTO \u5F00\u53D1" },
1076
- { id: "tilelang", name: "TileLang \u5F00\u53D1" },
1077
- { id: "triton", name: "Triton \u5F00\u53D1" },
1078
- { id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
1079
- { id: "graph", name: "\u56FE\u6A21\u5F0F" },
1080
- { id: "platform", name: "\u5E73\u53F0\u5DE5\u5177" },
1081
- { id: "other", name: "\u5176\u4ED6 Skills" }
2022
+ { id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
2023
+ { id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
2024
+ { id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
2025
+ { id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
2026
+ { id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
2027
+ { id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
2028
+ { id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
2029
+ { id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
2030
+ { id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
2031
+ { id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
2032
+ { id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
2033
+ { id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
2034
+ { id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
1082
2035
  ];
1083
2036
  var dynamicSkills = [];
1084
2037
  var initialized = false;
@@ -1086,7 +2039,8 @@ function initFromScan(scanned) {
1086
2039
  dynamicSkills = scanned.map((s) => ({
1087
2040
  id: s.id,
1088
2041
  description: s.description,
1089
- source: s.source
2042
+ source: s.source,
2043
+ filePath: s.filePath
1090
2044
  }));
1091
2045
  initialized = true;
1092
2046
  }
@@ -1162,9 +2116,7 @@ var STATIC_SKILL_CATEGORIES = [
1162
2116
  { id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
1163
2117
  { id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
1164
2118
  { id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
1165
- { id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" },
1166
- { id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
1167
- { id: "ops-easyasc-dsl", description: "EasyASC DSL \u7B97\u5B50\u5F00\u53D1", source: "plugins-community/ops-easyasc-dsl/skill" }
2119
+ { id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
1168
2120
  ]
1169
2121
  },
1170
2122
  {
@@ -1221,7 +2173,9 @@ var STATIC_SKILL_CATEGORIES = [
1221
2173
  { id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
1222
2174
  { id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
1223
2175
  { id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
1224
- { id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" }
2176
+ { id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
2177
+ { id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
2178
+ { id: "science-model-npu-migration", description: "NPU \u4EE3\u7801\u7EA7\u8FC1\u79FB\uFF08\u73AF\u5883\u95E8\u7981/\u811A\u672C\u9002\u914D/\u7CBE\u5EA6\u6027\u80FD\u5BF9\u6BD4\uFF09", source: "plugins-community/science-model-npu-migration", filePath: "plugins-community/science-model-npu-migration/SKILL.md" }
1225
2179
  ]
1226
2180
  },
1227
2181
  {
@@ -1237,6 +2191,13 @@ var STATIC_SKILL_CATEGORIES = [
1237
2191
  { id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
1238
2192
  ]
1239
2193
  },
2194
+ {
2195
+ id: "runtime",
2196
+ name: "Runtime \u8FC1\u79FB",
2197
+ skills: [
2198
+ { id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
2199
+ ]
2200
+ },
1240
2201
  {
1241
2202
  id: "platform",
1242
2203
  name: "\u5E73\u53F0\u5DE5\u5177",
@@ -1280,9 +2241,10 @@ function getAllSkills() {
1280
2241
  }
1281
2242
  function getAllCategories() {
1282
2243
  const skills = getActiveSkills();
2244
+ const isEn = t("list_name") === "Name";
1283
2245
  return CATEGORY_DEFS.map((def) => ({
1284
2246
  id: def.id,
1285
- name: def.name,
2247
+ name: isEn ? def.nameEn : def.name,
1286
2248
  skills: skills.filter((s) => {
1287
2249
  const catId = CATEGORY_MAP[s.id];
1288
2250
  if (catId) return catId === def.id;
@@ -1293,7 +2255,7 @@ function getAllCategories() {
1293
2255
 
1294
2256
  // src/core/repository.ts
1295
2257
  var __filename4 = fileURLToPath4(import.meta.url);
1296
- var __dirname4 = dirname4(__filename4);
2258
+ var __dirname4 = dirname5(__filename4);
1297
2259
  function loadRepoUrl() {
1298
2260
  if (process.env.CANNBOT_REPO_URL) {
1299
2261
  return process.env.CANNBOT_REPO_URL;
@@ -1309,8 +2271,10 @@ function loadRepoUrl() {
1309
2271
  var REPO_URL = loadRepoUrl();
1310
2272
  var RepositoryManager = class {
1311
2273
  repoPath;
2274
+ customPath;
1312
2275
  constructor(customPath) {
1313
2276
  this.repoPath = customPath;
2277
+ this.customPath = customPath;
1314
2278
  }
1315
2279
  async ensureRepo() {
1316
2280
  if (this.repoPath && this.isValidRepo(this.repoPath)) {
@@ -1331,10 +2295,16 @@ var RepositoryManager = class {
1331
2295
  this.repoPath = cachedPath;
1332
2296
  return cachedPath;
1333
2297
  }
1334
- return await this.cloneRepo();
2298
+ const clonedPath = await this.cloneRepo();
2299
+ return clonedPath;
1335
2300
  }
1336
2301
  async ensureRepoAndScan() {
1337
2302
  const repoPath = await this.ensureRepo();
2303
+ const isUserProvided = !!this.customPath || !!process.env.CANNBOT_REPO_PATH;
2304
+ if (!isUserProvided) {
2305
+ await this.updateRepo();
2306
+ }
2307
+ mergeDynamicPlugins(repoPath);
1338
2308
  const cache = readScanCache();
1339
2309
  if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
1340
2310
  initFromScan(cache.skills);
@@ -1354,7 +2324,9 @@ var RepositoryManager = class {
1354
2324
  try {
1355
2325
  await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
1356
2326
  } catch {
2327
+ logger.warn(t("repo_pull_failed"));
1357
2328
  }
2329
+ mergeDynamicPlugins(repoPath);
1358
2330
  }
1359
2331
  getRepoPath() {
1360
2332
  if (!this.repoPath) {
@@ -1363,24 +2335,25 @@ var RepositoryManager = class {
1363
2335
  return this.repoPath;
1364
2336
  }
1365
2337
  isValidRepo(path) {
1366
- if (!existsSync6(path)) return false;
2338
+ if (!existsSync9(path)) return false;
1367
2339
  const gitDir = join8(path, ".git");
1368
2340
  const pluginsDir = join8(path, "plugins-official");
1369
- return existsSync6(gitDir) && existsSync6(pluginsDir);
2341
+ return existsSync9(gitDir) && existsSync9(pluginsDir);
1370
2342
  }
1371
2343
  async cloneRepo() {
1372
2344
  const configDir = getCannbotConfigDir();
1373
- if (!existsSync6(configDir)) {
2345
+ if (!existsSync9(configDir)) {
1374
2346
  mkdirSync3(configDir, { recursive: true });
1375
2347
  }
1376
2348
  const targetPath = getCannbotRepoPath();
1377
- if (existsSync6(targetPath)) {
2349
+ if (existsSync9(targetPath)) {
1378
2350
  if (this.isValidRepo(targetPath)) {
1379
2351
  this.repoPath = targetPath;
1380
2352
  return targetPath;
1381
2353
  }
1382
2354
  }
1383
2355
  try {
2356
+ logger.info(t("repo_cloning"));
1384
2357
  await execa2(
1385
2358
  "git",
1386
2359
  ["clone", "--depth", "1", REPO_URL, targetPath],
@@ -1390,7 +2363,7 @@ var RepositoryManager = class {
1390
2363
  return targetPath;
1391
2364
  } catch (error) {
1392
2365
  throw new Error(
1393
- `Failed to clone repository: ${error instanceof Error ? error.message : "Unknown error"}`
2366
+ `Failed to clone repository: ${error instanceof Error ? error.message : t("error_unknown")}`
1394
2367
  );
1395
2368
  }
1396
2369
  }
@@ -1400,12 +2373,12 @@ function createRepositoryManager(customPath) {
1400
2373
  }
1401
2374
 
1402
2375
  // src/core/installer.ts
1403
- import { existsSync as existsSync9 } from "fs";
1404
- import { join as join11 } from "path";
1405
- import { execa as execa3 } from "execa";
2376
+ import { existsSync as existsSync13 } from "fs";
2377
+ import { join as join12, basename as basename2 } from "path";
2378
+ import { execa as execa4, execaSync } from "execa";
1406
2379
 
1407
2380
  // src/core/record.ts
1408
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
2381
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
1409
2382
  import { join as join9 } from "path";
1410
2383
  function getInstallsDir() {
1411
2384
  return join9(getCannbotConfigDir(), "installs");
@@ -1415,53 +2388,54 @@ function getRecordPath(pluginId) {
1415
2388
  }
1416
2389
  function readRecord(pluginId) {
1417
2390
  const recordPath = getRecordPath(pluginId);
1418
- if (!existsSync7(recordPath)) {
2391
+ if (!existsSync10(recordPath)) {
1419
2392
  return null;
1420
2393
  }
1421
2394
  try {
1422
2395
  const content = readFileSync7(recordPath, "utf-8");
1423
2396
  return JSON.parse(content);
1424
2397
  } catch {
2398
+ logger.warn(t("record_corrupted").replace("{file}", recordPath));
1425
2399
  return null;
1426
2400
  }
1427
2401
  }
1428
2402
  function writeRecord(record) {
1429
2403
  const installsDir = getInstallsDir();
1430
- if (!existsSync7(installsDir)) {
2404
+ if (!existsSync10(installsDir)) {
1431
2405
  mkdirSync4(installsDir, { recursive: true });
1432
2406
  }
1433
2407
  const recordPath = getRecordPath(record.pluginId);
1434
- writeFileSync4(recordPath, JSON.stringify(record, null, 2), "utf-8");
2408
+ atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
1435
2409
  }
1436
2410
  function deleteRecord(pluginId) {
1437
2411
  const recordPath = getRecordPath(pluginId);
1438
- if (existsSync7(recordPath)) {
1439
- unlinkSync(recordPath);
2412
+ if (existsSync10(recordPath)) {
2413
+ unlinkSync2(recordPath);
1440
2414
  }
1441
2415
  }
1442
- function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
2416
+ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames) {
1443
2417
  const files = [];
1444
2418
  const directories = [];
1445
2419
  if (manifest) {
1446
2420
  const skillsDir = join9(configRoot, "skills");
1447
- if (existsSync7(skillsDir)) {
2421
+ if (existsSync10(skillsDir)) {
1448
2422
  directories.push(skillsDir);
1449
2423
  for (const skillName of manifest.installed_skills || []) {
1450
2424
  const skillPath = join9(skillsDir, skillName);
1451
- if (existsSync7(skillPath) || isSymlink(skillPath)) {
2425
+ if (existsSync10(skillPath) || isSymlink(skillPath)) {
1452
2426
  files.push(skillPath);
1453
2427
  }
1454
2428
  }
1455
2429
  }
1456
2430
  const agentsDir = join9(configRoot, "agents");
1457
- if (existsSync7(agentsDir)) {
2431
+ if (existsSync10(agentsDir)) {
1458
2432
  directories.push(agentsDir);
1459
2433
  for (const agentName of manifest.installed_agents || []) {
1460
2434
  const agentPath = join9(agentsDir, agentName);
1461
2435
  const agentPathMd = join9(agentsDir, agentName + ".md");
1462
- if (existsSync7(agentPath) || isSymlink(agentPath)) {
2436
+ if (existsSync10(agentPath) || isSymlink(agentPath)) {
1463
2437
  files.push(agentPath);
1464
- } else if (existsSync7(agentPathMd) || isSymlink(agentPathMd)) {
2438
+ } else if (existsSync10(agentPathMd) || isSymlink(agentPathMd)) {
1465
2439
  files.push(agentPathMd);
1466
2440
  }
1467
2441
  }
@@ -1472,19 +2446,19 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1472
2446
  files.push(workflowsLink);
1473
2447
  }
1474
2448
  const manifestPath = join9(configRoot, "cannbot-manifest.json");
1475
- if (existsSync7(manifestPath)) {
2449
+ if (existsSync10(manifestPath)) {
1476
2450
  files.push(manifestPath);
1477
2451
  }
1478
2452
  const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
1479
- if (existsSync7(pluginManifestPath)) {
2453
+ if (existsSync10(pluginManifestPath)) {
1480
2454
  files.push(pluginManifestPath);
1481
2455
  }
1482
2456
  const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1483
2457
  const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
1484
- if (existsSync7(configFilePath)) {
2458
+ if (existsSync10(configFilePath)) {
1485
2459
  files.push(configFilePath);
1486
2460
  }
1487
- const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
2461
+ const repoLinks = externalRepoNames && externalRepoNames.length > 0 ? externalRepoNames : ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
1488
2462
  for (const repoName of repoLinks) {
1489
2463
  const repoLinkPath = join9(installPath, repoName);
1490
2464
  if (isSymlink(repoLinkPath)) {
@@ -1507,35 +2481,29 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1507
2481
  directories
1508
2482
  };
1509
2483
  }
1510
- function isSymlink(path) {
1511
- try {
1512
- return lstatSync(path).isSymbolicLink();
1513
- } catch {
1514
- return false;
1515
- }
1516
- }
1517
2484
  function getSkillRecordPath() {
1518
2485
  return join9(getInstallsDir(), "skills.json");
1519
2486
  }
1520
2487
  function readSkillRecord() {
1521
2488
  const recordPath = getSkillRecordPath();
1522
- if (!existsSync7(recordPath)) {
2489
+ if (!existsSync10(recordPath)) {
1523
2490
  return {};
1524
2491
  }
1525
2492
  try {
1526
2493
  const content = readFileSync7(recordPath, "utf-8");
1527
2494
  return JSON.parse(content);
1528
2495
  } catch {
2496
+ logger.warn(t("skill_record_corrupted"));
1529
2497
  return {};
1530
2498
  }
1531
2499
  }
1532
2500
  function writeSkillRecord(record) {
1533
2501
  const installsDir = getInstallsDir();
1534
- if (!existsSync7(installsDir)) {
2502
+ if (!existsSync10(installsDir)) {
1535
2503
  mkdirSync4(installsDir, { recursive: true });
1536
2504
  }
1537
2505
  const recordPath = getSkillRecordPath();
1538
- writeFileSync4(recordPath, JSON.stringify(record, null, 2), "utf-8");
2506
+ atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
1539
2507
  }
1540
2508
  function addSkillsToRecord(skillIds, tool, level, installPath) {
1541
2509
  const record = readSkillRecord();
@@ -1576,16 +2544,13 @@ function getInstalledSkills(tool, level, installPath) {
1576
2544
  const skillsDir = join9(configRoot, "skills");
1577
2545
  return recordedSkills.filter((skillId) => {
1578
2546
  const skillPath = join9(skillsDir, skillId);
1579
- return existsSync7(skillPath) || isSymlink(skillPath);
2547
+ return existsSync10(skillPath) || isSymlink(skillPath);
1580
2548
  });
1581
2549
  }
1582
2550
 
1583
2551
  // src/core/backup.ts
1584
- import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
2552
+ import { existsSync as existsSync11, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
1585
2553
  import { join as join10 } from "path";
1586
- function getAgentsFileName(tool) {
1587
- return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1588
- }
1589
2554
  function detectCurrentPlugin(configRoot, tool) {
1590
2555
  const manifest = readManifest(configRoot);
1591
2556
  if (manifest?.team) {
@@ -1598,7 +2563,7 @@ function detectCurrentPlugin(configRoot, tool) {
1598
2563
  }
1599
2564
  }
1600
2565
  const agentsFile = join10(configRoot, getAgentsFileName(tool));
1601
- if (!existsSync8(agentsFile)) {
2566
+ if (!existsSync11(agentsFile)) {
1602
2567
  return null;
1603
2568
  }
1604
2569
  const allRecords = readAllRecords();
@@ -1617,7 +2582,7 @@ function detectCurrentPlugin(configRoot, tool) {
1617
2582
  }
1618
2583
  function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
1619
2584
  const agentsFile = join10(configRoot, getAgentsFileName(tool));
1620
- if (!existsSync8(agentsFile)) {
2585
+ if (!existsSync11(agentsFile)) {
1621
2586
  return null;
1622
2587
  }
1623
2588
  const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
@@ -1636,7 +2601,7 @@ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
1636
2601
  }
1637
2602
  }
1638
2603
  function findBackups(configRoot) {
1639
- if (!existsSync8(configRoot)) {
2604
+ if (!existsSync11(configRoot)) {
1640
2605
  return [];
1641
2606
  }
1642
2607
  const files = readdirSync3(configRoot);
@@ -1674,8 +2639,8 @@ function restoreBackup(backupPath, configRoot, tool) {
1674
2639
  }
1675
2640
  function deleteBackup(backupPath) {
1676
2641
  try {
1677
- if (existsSync8(backupPath)) {
1678
- unlinkSync2(backupPath);
2642
+ if (existsSync11(backupPath)) {
2643
+ unlinkSync3(backupPath);
1679
2644
  return true;
1680
2645
  }
1681
2646
  return false;
@@ -1694,20 +2659,9 @@ function formatTimestamp(date) {
1694
2659
  }
1695
2660
  function readAllRecords() {
1696
2661
  const records = [];
1697
- const plugins = [
1698
- "ops-direct-invoke",
1699
- "ops-direct-invoke-flash",
1700
- "ops-registry-invoke",
1701
- "pypto-op-orchestrator",
1702
- "triton-op-generator",
1703
- "tilelang-op-orchestrator",
1704
- "model-infer-optimize",
1705
- "catlass-op-generator",
1706
- "ops-code-reviewer",
1707
- "torch-compile"
1708
- ];
1709
- for (const pluginId of plugins) {
1710
- const record = readRecord(pluginId);
2662
+ const plugins = getAllPlugins();
2663
+ for (const plugin of plugins) {
2664
+ const record = readRecord(plugin.id);
1711
2665
  if (record) {
1712
2666
  records.push(record);
1713
2667
  }
@@ -1753,39 +2707,282 @@ async function showRestorePrompt(backups) {
1753
2707
  return choice;
1754
2708
  }
1755
2709
 
1756
- // src/core/installer.ts
1757
- async function installPlugin(opts) {
1758
- const plugin = getPluginById(opts.pluginId);
1759
- if (!plugin) {
2710
+ // src/core/plugin-installer.ts
2711
+ import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, realpathSync, copyFileSync as copyFileSync2, cpSync, writeFileSync as writeFileSync2, rmSync } from "fs";
2712
+ import { join as join11, basename } from "path";
2713
+ import { execa as execa3 } from "execa";
2714
+ function installLink(source, target) {
2715
+ try {
2716
+ const resolved = realpathSync(source);
2717
+ if (existsSync12(target) || isSymlink(target)) {
2718
+ unlinkSync4(target);
2719
+ }
2720
+ try {
2721
+ symlinkSync(resolved, target);
2722
+ return "symlink";
2723
+ } catch (e) {
2724
+ if (e.code === "EPERM") {
2725
+ cpSync(resolved, target, { recursive: true });
2726
+ return "copy";
2727
+ }
2728
+ throw e;
2729
+ }
2730
+ } catch {
2731
+ return "failed";
2732
+ }
2733
+ }
2734
+ function installConfigFile(pluginDir, configFile, tool, level, configRoot, installPath, configRootConfigLink) {
2735
+ const sourcePath = join11(pluginDir, configFile);
2736
+ if (!existsSync12(sourcePath)) return;
2737
+ const agentsFileName = getAgentsFileName(tool);
2738
+ const isProjectLevel = level === "project";
2739
+ let primaryTarget;
2740
+ if (isProjectLevel) {
2741
+ primaryTarget = join11(installPath, agentsFileName);
2742
+ } else {
2743
+ primaryTarget = join11(configRoot, agentsFileName);
2744
+ }
2745
+ try {
2746
+ const resolvedSource = realpathSync(sourcePath);
2747
+ if (existsSync12(primaryTarget) || isSymlink(primaryTarget)) {
2748
+ unlinkSync4(primaryTarget);
2749
+ }
2750
+ try {
2751
+ symlinkSync(resolvedSource, primaryTarget);
2752
+ } catch (e) {
2753
+ if (e.code === "EPERM") {
2754
+ copyFileSync2(sourcePath, primaryTarget);
2755
+ } else {
2756
+ throw e;
2757
+ }
2758
+ }
2759
+ } catch (e) {
2760
+ logger.warn(t("install_config_failed").replace("{error}", e.message));
2761
+ }
2762
+ if (isProjectLevel && primaryTarget !== join11(configRoot, agentsFileName)) {
2763
+ if (configRootConfigLink === false) return;
2764
+ const configRootTarget = join11(configRoot, agentsFileName);
2765
+ if (existsSync12(configRootTarget) || isSymlink(configRootTarget)) {
2766
+ unlinkSync4(configRootTarget);
2767
+ }
2768
+ try {
2769
+ symlinkSync(realpathSync(sourcePath), configRootTarget);
2770
+ } catch (e) {
2771
+ if (e.code === "EPERM") {
2772
+ copyFileSync2(sourcePath, configRootTarget);
2773
+ }
2774
+ }
2775
+ }
2776
+ }
2777
+ async function installExternalRepos(repos, repoPath, configRoot, installPath, level) {
2778
+ if (!repos || repos.length === 0) return;
2779
+ for (const repo of repos) {
2780
+ const targetDir = join11(repoPath, repo.dir);
2781
+ if (!existsSync12(targetDir)) {
2782
+ const args = ["clone"];
2783
+ if (repo.depth) {
2784
+ args.push("--depth", String(repo.depth));
2785
+ }
2786
+ if (repo.recursive) {
2787
+ args.push("--recursive");
2788
+ }
2789
+ args.push(repo.url, targetDir);
2790
+ try {
2791
+ await execa3("git", args, { timeout: 12e4 });
2792
+ } catch {
2793
+ logger.warn(t("install_clone_failed").replace("{url}", repo.url).replace("{dir}", repo.dir));
2794
+ try {
2795
+ rmSync(targetDir, { recursive: true, force: true });
2796
+ } catch {
2797
+ }
2798
+ continue;
2799
+ }
2800
+ }
2801
+ const linkName = basename(repo.dir);
2802
+ const resolved = realpathSync(targetDir);
2803
+ const shouldConfigRootLink = repo.configRootLink !== false && (level === "global" || repo.configRootLink === true);
2804
+ if (shouldConfigRootLink) {
2805
+ const configRootLink = join11(configRoot, linkName);
2806
+ try {
2807
+ if (existsSync12(configRootLink) || isSymlink(configRootLink)) {
2808
+ unlinkSync4(configRootLink);
2809
+ }
2810
+ try {
2811
+ symlinkSync(resolved, configRootLink);
2812
+ } catch (e) {
2813
+ if (e.code === "EPERM") {
2814
+ cpSync(resolved, configRootLink, { recursive: true });
2815
+ }
2816
+ }
2817
+ } catch {
2818
+ logger.warn(t("install_link_config_failed").replace("{name}", linkName));
2819
+ }
2820
+ }
2821
+ if (level === "project") {
2822
+ const projectLink = join11(installPath, linkName);
2823
+ try {
2824
+ if (existsSync12(projectLink) || isSymlink(projectLink)) {
2825
+ unlinkSync4(projectLink);
2826
+ }
2827
+ try {
2828
+ symlinkSync(resolved, projectLink);
2829
+ } catch (e) {
2830
+ if (e.code === "EPERM") {
2831
+ cpSync(resolved, projectLink, { recursive: true });
2832
+ }
2833
+ }
2834
+ } catch {
2835
+ logger.warn(t("install_link_project_failed").replace("{name}", linkName));
2836
+ }
2837
+ }
2838
+ }
2839
+ }
2840
+ function installWorkflows(pluginDir, configRoot) {
2841
+ const workflowsDir = join11(pluginDir, "workflows");
2842
+ if (!existsSync12(workflowsDir)) return;
2843
+ const targetPath = join11(configRoot, "workflows");
2844
+ try {
2845
+ const resolved = realpathSync(workflowsDir);
2846
+ if (existsSync12(targetPath) || isSymlink(targetPath)) {
2847
+ unlinkSync4(targetPath);
2848
+ }
2849
+ try {
2850
+ symlinkSync(resolved, targetPath);
2851
+ } catch (e) {
2852
+ if (e.code === "EPERM") {
2853
+ cpSync(resolved, targetPath, { recursive: true });
2854
+ }
2855
+ }
2856
+ } catch {
2857
+ logger.warn(t("install_link_workflows_failed"));
2858
+ }
2859
+ }
2860
+ function writeCannbotManifest(configRoot, pluginId, version, level, tool, skills, agents) {
2861
+ const manifest = {
2862
+ brand: "CANNBot",
2863
+ version,
2864
+ team: pluginId,
2865
+ level,
2866
+ tool,
2867
+ installed_skills: skills,
2868
+ installed_agents: agents,
2869
+ brand_dir: configRoot,
2870
+ install_time: (/* @__PURE__ */ new Date()).toISOString()
2871
+ };
2872
+ const manifestPath = join11(configRoot, "cannbot-manifest.json");
2873
+ writeFileSync2(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
2874
+ return manifest;
2875
+ }
2876
+ async function installViaManifest(plugin, repoPath, tool, level, installPath) {
2877
+ const cwd = installPath || process.cwd();
2878
+ const configRoot = getConfigRoot(tool, level, installPath);
2879
+ const pluginDir = join11(repoPath, plugin.dir);
2880
+ try {
2881
+ const skillsDir = join11(configRoot, "skills");
2882
+ const agentsDir = join11(configRoot, "agents");
2883
+ mkdirSync5(skillsDir, { recursive: true });
2884
+ mkdirSync5(agentsDir, { recursive: true });
2885
+ const installedSkills = [];
2886
+ const installedAgents = [];
2887
+ for (const source of plugin.installSkills || []) {
2888
+ const sourceDir = join11(repoPath, source.dir);
2889
+ if (!existsSync12(sourceDir)) {
2890
+ logger.warn(t("install_skill_source_not_found").replace("{dir}", source.dir));
2891
+ continue;
2892
+ }
2893
+ for (const skillEntry of source.skills) {
2894
+ const skillName = typeof skillEntry === "string" ? skillEntry : skillEntry.name;
2895
+ const skillAlias = typeof skillEntry === "string" ? skillName : skillEntry.as || skillName;
2896
+ const skillPath = join11(sourceDir, skillName);
2897
+ if (!existsSync12(skillPath)) {
2898
+ logger.warn(t("install_skill_not_found").replace("{name}", skillName).replace("{dir}", source.dir));
2899
+ continue;
2900
+ }
2901
+ const targetPath = join11(skillsDir, skillAlias);
2902
+ const result = installLink(skillPath, targetPath);
2903
+ if (result !== "failed") {
2904
+ installedSkills.push(skillAlias);
2905
+ } else {
2906
+ logger.warn(t("install_skill_failed").replace("{name}", skillAlias));
2907
+ }
2908
+ }
2909
+ }
2910
+ for (const agentName of plugin.installAgents || []) {
2911
+ const agentFile = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
2912
+ const agentPath = join11(pluginDir, "agents", agentFile);
2913
+ if (!existsSync12(agentPath)) {
2914
+ logger.warn(t("install_agent_not_found").replace("{name}", agentFile));
2915
+ continue;
2916
+ }
2917
+ const targetPath = join11(agentsDir, agentFile);
2918
+ const result = installLink(agentPath, targetPath);
2919
+ if (result !== "failed") {
2920
+ installedAgents.push(agentFile);
2921
+ } else {
2922
+ logger.warn(t("install_agent_failed").replace("{name}", agentFile));
2923
+ }
2924
+ }
2925
+ installConfigFile(pluginDir, plugin.configFile || "AGENTS.md", tool, level, configRoot, cwd, plugin.configRootConfigLink);
2926
+ installWorkflows(pluginDir, configRoot);
2927
+ await installExternalRepos(plugin.externalRepos, repoPath, configRoot, cwd, level);
2928
+ const cannbotManifest = writeCannbotManifest(
2929
+ configRoot,
2930
+ plugin.id,
2931
+ plugin.version || "1.0.0",
2932
+ level,
2933
+ tool,
2934
+ installedSkills,
2935
+ installedAgents
2936
+ );
2937
+ return {
2938
+ success: true,
2939
+ skillsCount: installedSkills.length,
2940
+ agentsCount: installedAgents.length,
2941
+ errors: [],
2942
+ manifest: cannbotManifest
2943
+ };
2944
+ } catch (error) {
1760
2945
  return {
1761
2946
  success: false,
1762
- pluginId: opts.pluginId,
1763
2947
  skillsCount: 0,
1764
2948
  agentsCount: 0,
1765
- errors: [`Plugin not found: ${opts.pluginId}`],
1766
- warnings: []
2949
+ errors: [error instanceof Error ? error.message : t("error_unknown")],
2950
+ manifest: null
1767
2951
  };
1768
2952
  }
1769
- const scriptPath = join11(opts.repoPath, plugin.dir, plugin.script);
1770
- if (!existsSync9(scriptPath)) {
2953
+ }
2954
+
2955
+ // src/core/installer.ts
2956
+ function findShell() {
2957
+ const candidates = ["bash", "sh"];
2958
+ for (const cmd of candidates) {
2959
+ try {
2960
+ const detectCmd = process.platform === "win32" ? "where" : "which";
2961
+ execaSync(detectCmd, [cmd], { timeout: 3e3 });
2962
+ return cmd;
2963
+ } catch {
2964
+ }
2965
+ }
2966
+ return null;
2967
+ }
2968
+ async function installPlugin(opts) {
2969
+ const plugin = getPluginById(opts.pluginId);
2970
+ if (!plugin) {
1771
2971
  return {
1772
2972
  success: false,
1773
2973
  pluginId: opts.pluginId,
1774
2974
  skillsCount: 0,
1775
2975
  agentsCount: 0,
1776
- errors: [`Script not found: ${scriptPath}`],
2976
+ errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
1777
2977
  warnings: []
1778
2978
  };
1779
2979
  }
1780
- const args = [opts.level, opts.tool];
1781
- if (opts.installPath) {
1782
- args.push(opts.installPath);
1783
- }
2980
+ const pluginDir = join12(opts.repoPath, plugin.dir);
1784
2981
  const cwd = opts.installPath || process.cwd();
1785
2982
  const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
1786
- const agentsFile = join11(configRoot, getAgentsFileName(opts.tool));
2983
+ const agentsFile = join12(configRoot, getAgentsFileName(opts.tool));
1787
2984
  let backupInfo = null;
1788
- if (existsSync9(agentsFile)) {
2985
+ if (existsSync13(agentsFile)) {
1789
2986
  const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
1790
2987
  if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
1791
2988
  let choice = "overwrite";
@@ -1812,19 +3009,105 @@ async function installPlugin(opts) {
1812
3009
  currentPlugin.pluginId,
1813
3010
  currentPlugin.pluginName
1814
3011
  );
1815
- if (backupInfo) {
1816
- logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
3012
+ if (!backupInfo) {
3013
+ logger.error(t("backup_failed"));
3014
+ return {
3015
+ success: false,
3016
+ pluginId: opts.pluginId,
3017
+ skillsCount: 0,
3018
+ agentsCount: 0,
3019
+ errors: [t("backup_failed")],
3020
+ warnings: []
3021
+ };
1817
3022
  }
3023
+ logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
1818
3024
  }
1819
3025
  }
3026
+ if (plugin.installSkills && plugin.installSkills.length > 0) {
3027
+ const result = await installViaManifest(
3028
+ plugin,
3029
+ opts.repoPath,
3030
+ opts.tool,
3031
+ opts.level,
3032
+ opts.installPath
3033
+ );
3034
+ if (result.success) {
3035
+ try {
3036
+ const record = scanInstalledFiles(
3037
+ opts.pluginId,
3038
+ plugin.displayName,
3039
+ opts.tool,
3040
+ opts.level,
3041
+ cwd,
3042
+ configRoot,
3043
+ result.manifest,
3044
+ plugin.externalRepos?.map((r) => basename2(r.dir))
3045
+ );
3046
+ if (backupInfo) {
3047
+ record.backup = {
3048
+ filePath: backupInfo.filePath,
3049
+ fromPluginId: backupInfo.pluginId,
3050
+ fromPluginName: backupInfo.pluginName,
3051
+ backupTime: backupInfo.backupTime
3052
+ };
3053
+ }
3054
+ writeRecord(record);
3055
+ } catch {
3056
+ logger.warn(t("record_write_failed"));
3057
+ }
3058
+ return {
3059
+ success: true,
3060
+ pluginId: opts.pluginId,
3061
+ skillsCount: result.skillsCount,
3062
+ agentsCount: result.agentsCount,
3063
+ errors: result.errors,
3064
+ warnings: []
3065
+ };
3066
+ } else {
3067
+ return {
3068
+ success: false,
3069
+ pluginId: opts.pluginId,
3070
+ skillsCount: 0,
3071
+ agentsCount: 0,
3072
+ errors: result.errors,
3073
+ warnings: []
3074
+ };
3075
+ }
3076
+ }
3077
+ const scriptPath = join12(pluginDir, plugin.script);
3078
+ if (!existsSync13(scriptPath)) {
3079
+ return {
3080
+ success: false,
3081
+ pluginId: opts.pluginId,
3082
+ skillsCount: 0,
3083
+ agentsCount: 0,
3084
+ errors: [t("error_script_not_found").replace("{path}", scriptPath)],
3085
+ warnings: []
3086
+ };
3087
+ }
3088
+ const args = [opts.level, opts.tool];
3089
+ if (opts.installPath) {
3090
+ args.push(opts.installPath);
3091
+ }
1820
3092
  try {
3093
+ const shell = findShell();
3094
+ if (!shell) {
3095
+ return {
3096
+ success: false,
3097
+ pluginId: opts.pluginId,
3098
+ skillsCount: 0,
3099
+ agentsCount: 0,
3100
+ errors: [t("error_no_shell")],
3101
+ warnings: []
3102
+ };
3103
+ }
1821
3104
  const execOptions = {
1822
3105
  cwd,
1823
3106
  timeout: 3e5,
1824
3107
  stdio: "pipe",
1825
3108
  input: opts.yes ? "y\n" : void 0
1826
3109
  };
1827
- await execa3("bash", [scriptPath, ...args], execOptions);
3110
+ await execa4(shell, [scriptPath, ...args], execOptions);
1828
3111
  const manifest = readManifest(configRoot);
1829
3112
  let skillsCount = 0;
1830
3113
  let agentsCount = 0;
@@ -1843,7 +3126,8 @@ async function installPlugin(opts) {
1843
3126
  opts.level,
1844
3127
  cwd,
1845
3128
  configRoot,
1846
- manifest
3129
+ manifest,
3130
+ plugin.externalRepos?.map((r) => basename2(r.dir))
1847
3131
  );
1848
3132
  if (backupInfo) {
1849
3133
  record.backup = {
@@ -1855,6 +3139,7 @@ async function installPlugin(opts) {
1855
3139
  }
1856
3140
  writeRecord(record);
1857
3141
  } catch {
3142
+ logger.warn(t("record_write_failed"));
1858
3143
  }
1859
3144
  return {
1860
3145
  success: true,
@@ -1865,7 +3150,7 @@ async function installPlugin(opts) {
1865
3150
  warnings: []
1866
3151
  };
1867
3152
  } catch (error) {
1868
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
3153
+ const errorMessage = error instanceof Error ? error.message : t("error_unknown");
1869
3154
  return {
1870
3155
  success: false,
1871
3156
  pluginId: opts.pluginId,
@@ -1878,41 +3163,46 @@ async function installPlugin(opts) {
1878
3163
  }
1879
3164
 
1880
3165
  // src/core/skill-installer.ts
1881
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
1882
- import { join as join12 } from "path";
1883
- import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
3166
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, unlinkSync as unlinkSync5, readdirSync as readdirSync4, rmdirSync, realpathSync as realpathSync2 } from "fs";
3167
+ import { join as join13, dirname as dirname6 } from "path";
3168
+ import { select as select3, checkbox, Separator as Separator2 } from "@inquirer/prompts";
1884
3169
  import chalk5 from "chalk";
1885
3170
  import Table from "cli-table3";
1886
3171
  async function installSkills(skillIds, tool, level, repoPath) {
1887
3172
  const configRoot = getConfigRoot(tool, level);
1888
- const skillsDir = join12(configRoot, "skills");
3173
+ const skillsDir = join13(configRoot, "skills");
1889
3174
  const installPath = level === "project" ? process.cwd() : configRoot;
1890
- if (!existsSync10(skillsDir)) {
1891
- mkdirSync5(skillsDir, { recursive: true });
3175
+ if (!existsSync14(skillsDir)) {
3176
+ mkdirSync6(skillsDir, { recursive: true });
1892
3177
  }
1893
3178
  const results = [];
1894
3179
  const installedIds = [];
1895
3180
  for (const skillId of skillIds) {
1896
3181
  const skill = findSkill(skillId);
1897
3182
  if (!skill) {
1898
- results.push({ skillId, success: false, error: "Skill not found in registry" });
3183
+ results.push({ skillId, success: false, error: t("error_skill_not_found") });
1899
3184
  continue;
1900
3185
  }
1901
- const sourcePath = join12(repoPath, skill.source, skill.id);
1902
- const targetPath = join12(skillsDir, skillId);
1903
- if (!existsSync10(sourcePath)) {
1904
- results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
3186
+ const sourcePath = resolveSkillSourcePath(skill, repoPath);
3187
+ const targetPath = join13(skillsDir, skillId);
3188
+ if (!existsSync14(sourcePath)) {
3189
+ results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
1905
3190
  continue;
1906
3191
  }
1907
3192
  try {
1908
- if (existsSync10(targetPath) || isSymlink2(targetPath)) {
1909
- unlinkSync3(targetPath);
3193
+ const resolvedSource = realpathSync2(sourcePath);
3194
+ if (existsSync14(targetPath) || isSymlink(targetPath)) {
3195
+ unlinkSync5(targetPath);
1910
3196
  }
1911
- symlinkSync(realpathSync(sourcePath), targetPath);
3197
+ symlinkSync2(resolvedSource, targetPath);
1912
3198
  results.push({ skillId, success: true });
1913
3199
  installedIds.push(skillId);
1914
3200
  } catch (error) {
1915
- results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
3201
+ let msg = error instanceof Error ? error.message : t("error_unknown");
3202
+ if (process.platform === "win32" && msg.includes("EPERM")) {
3203
+ msg = t("error_symlink_windows");
3204
+ }
3205
+ results.push({ skillId, success: false, error: msg });
1916
3206
  }
1917
3207
  }
1918
3208
  if (installedIds.length > 0) {
@@ -1922,30 +3212,30 @@ async function installSkills(skillIds, tool, level, repoPath) {
1922
3212
  }
1923
3213
  async function uninstallSkills(skillIds, tool, level) {
1924
3214
  const configRoot = getConfigRoot(tool, level);
1925
- const skillsDir = join12(configRoot, "skills");
3215
+ const skillsDir = join13(configRoot, "skills");
1926
3216
  const installPath = level === "project" ? process.cwd() : configRoot;
1927
3217
  const results = [];
1928
3218
  const removedIds = [];
1929
3219
  for (const skillId of skillIds) {
1930
- const targetPath = join12(skillsDir, skillId);
3220
+ const targetPath = join13(skillsDir, skillId);
1931
3221
  try {
1932
- if (existsSync10(targetPath) || isSymlink2(targetPath)) {
1933
- unlinkSync3(targetPath);
3222
+ if (existsSync14(targetPath) || isSymlink(targetPath)) {
3223
+ unlinkSync5(targetPath);
1934
3224
  results.push({ skillId, success: true });
1935
3225
  removedIds.push(skillId);
1936
3226
  logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
1937
3227
  } else {
1938
- results.push({ skillId, success: false, error: "Not installed" });
3228
+ results.push({ skillId, success: false, error: t("error_not_installed") });
1939
3229
  logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
1940
3230
  }
1941
3231
  } catch (error) {
1942
- results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
3232
+ results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
1943
3233
  }
1944
3234
  }
1945
3235
  if (removedIds.length > 0) {
1946
3236
  removeSkillsFromRecord(removedIds, tool, level, installPath);
1947
3237
  }
1948
- if (existsSync10(skillsDir)) {
3238
+ if (existsSync14(skillsDir)) {
1949
3239
  try {
1950
3240
  const entries = readdirSync4(skillsDir);
1951
3241
  if (entries.length === 0) {
@@ -1961,8 +3251,6 @@ var SELECT_ALL = "__select_all__";
1961
3251
  var SELECT_ALL_CATEGORY = "__select_all_category__";
1962
3252
  var CONTINUE_SELECT = "__continue_select__";
1963
3253
  async function interactiveSkillSelect(tool, level) {
1964
- const BACK3 = "__back__";
1965
- const CANCEL3 = "__cancel__";
1966
3254
  const categories = getAllCategories();
1967
3255
  const allSkillIds = getAllSkills().map((s) => s.id);
1968
3256
  let accumulated = [];
@@ -1985,8 +3273,8 @@ async function interactiveSkillSelect(tool, level) {
1985
3273
  })),
1986
3274
  new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
1987
3275
  ...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
1988
- { name: "<- " + t("wizard_back"), value: BACK3 },
1989
- { name: "x " + t("wizard_cancel"), value: CANCEL3 }
3276
+ { name: "<- " + t("wizard_back"), value: BACK },
3277
+ { name: "x " + t("wizard_cancel"), value: CANCEL }
1990
3278
  ];
1991
3279
  showOperationHints();
1992
3280
  selectedCategoryId = await select3({
@@ -1996,8 +3284,8 @@ async function interactiveSkillSelect(tool, level) {
1996
3284
  theme: selectTheme,
1997
3285
  pageSize: 15
1998
3286
  });
1999
- if (selectedCategoryId === BACK3) return "back";
2000
- if (selectedCategoryId === CANCEL3) return "cancel";
3287
+ if (selectedCategoryId === BACK) return "back";
3288
+ if (selectedCategoryId === CANCEL) return "cancel";
2001
3289
  if (selectedCategoryId === SELECT_ALL) {
2002
3290
  return allSkillIds;
2003
3291
  }
@@ -2037,7 +3325,7 @@ async function interactiveSkillSelect(tool, level) {
2037
3325
  })
2038
3326
  ];
2039
3327
  showOperationHints(true);
2040
- const selectedSkills = await checkbox2({
3328
+ const selectedSkills = await checkbox({
2041
3329
  message: t("skill_select_items"),
2042
3330
  choices: skillChoices,
2043
3331
  loop: false,
@@ -2128,12 +3416,14 @@ function listAllSkills() {
2128
3416
  console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
2129
3417
  console.log();
2130
3418
  }
2131
- function isSymlink2(path) {
2132
- try {
2133
- return lstatSync2(path).isSymbolicLink();
2134
- } catch {
2135
- return false;
3419
+ function resolveSkillSourcePath(skill, repoPath) {
3420
+ if (skill.filePath) {
3421
+ const absPath = skill.filePath.startsWith("/") ? skill.filePath : join13(repoPath, skill.filePath);
3422
+ if (existsSync14(absPath)) {
3423
+ return dirname6(absPath);
3424
+ }
2136
3425
  }
3426
+ return join13(repoPath, skill.source, skill.id);
2137
3427
  }
2138
3428
 
2139
3429
  // src/ui/display.ts
@@ -2145,10 +3435,11 @@ function printPluginList(plugins, installed) {
2145
3435
  chalk6.cyan(t("list_id")),
2146
3436
  chalk6.cyan(t("list_name")),
2147
3437
  chalk6.cyan(t("list_status")),
2148
- chalk6.cyan(t("list_skills")),
2149
- chalk6.cyan(t("list_agents"))
3438
+ chalk6.cyan(t("list_description"))
2150
3439
  ],
2151
- style: { head: [], border: [] }
3440
+ style: { head: [], border: [] },
3441
+ colWidths: [6, 32, 8, 50],
3442
+ wordWrap: true
2152
3443
  });
2153
3444
  plugins.forEach((plugin, index) => {
2154
3445
  const isInstalled = installed.has(plugin.id);
@@ -2157,8 +3448,7 @@ function printPluginList(plugins, installed) {
2157
3448
  String(index + 1),
2158
3449
  plugin.displayName,
2159
3450
  status,
2160
- String(plugin.skills),
2161
- String(plugin.agents)
3451
+ plugin.description || chalk6.dim("\u2014")
2162
3452
  ]);
2163
3453
  });
2164
3454
  console.log();
@@ -2225,15 +3515,13 @@ function printEnhancedSummary(results, tool, configRoot) {
2225
3515
  }
2226
3516
 
2227
3517
  // src/commands/init.ts
2228
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
2229
- import { join as join13 } from "path";
2230
- var BACK2 = "__back__";
2231
- var CANCEL2 = "__cancel__";
3518
+ import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
3519
+ import { join as join14 } from "path";
2232
3520
  async function initCommand() {
2233
3521
  const cwd = process.cwd();
2234
- if (existsSync11(join13(cwd, "package.json")) && (() => {
3522
+ if (existsSync15(join14(cwd, "package.json")) && (() => {
2235
3523
  try {
2236
- const pkg = JSON.parse(readFileSync8(join13(cwd, "package.json"), "utf-8"));
3524
+ const pkg = JSON.parse(readFileSync8(join14(cwd, "package.json"), "utf-8"));
2237
3525
  return pkg.name === "@cannbot-ai/install-helper";
2238
3526
  } catch {
2239
3527
  return false;
@@ -2287,7 +3575,6 @@ async function initCommand() {
2287
3575
  }
2288
3576
  async function selectMode() {
2289
3577
  printBoxTitle(t("wizard_select_mode_title"));
2290
- const CANCEL3 = "__cancel__";
2291
3578
  const choices = [
2292
3579
  {
2293
3580
  name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
@@ -2298,7 +3585,7 @@ async function selectMode() {
2298
3585
  value: "skill"
2299
3586
  },
2300
3587
  new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
2301
- { name: "x " + t("wizard_cancel"), value: CANCEL3 }
3588
+ { name: "x " + t("wizard_cancel"), value: CANCEL }
2302
3589
  ];
2303
3590
  showOperationHints();
2304
3591
  const result = await select4({
@@ -2307,7 +3594,7 @@ async function selectMode() {
2307
3594
  loop: false,
2308
3595
  theme: selectTheme
2309
3596
  });
2310
- if (result === CANCEL3) return null;
3597
+ if (result === CANCEL) return null;
2311
3598
  return result;
2312
3599
  }
2313
3600
  async function pluginInstallFlow() {
@@ -2384,11 +3671,11 @@ async function skillInstallFlow() {
2384
3671
  }
2385
3672
  case 1: {
2386
3673
  const result = await stepLevel();
2387
- if (result === BACK2) {
3674
+ if (result === BACK) {
2388
3675
  step = 0;
2389
3676
  break;
2390
3677
  }
2391
- if (result === CANCEL2) {
3678
+ if (result === CANCEL) {
2392
3679
  logger.info(t("init_cancelled"));
2393
3680
  return "done";
2394
3681
  }
@@ -2435,7 +3722,7 @@ async function skillInstallFlow() {
2435
3722
  logger.blank();
2436
3723
  logger.success(`${t("skill_install_done")}: ${chalk7.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk7.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk7.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
2437
3724
  logger.blank();
2438
- logger.info(`${t("install_to")}: ${chalk7.cyan(configRoot + "/skills/")}`);
3725
+ logger.info(`${t("install_to")}: ${chalk7.cyan(join14(configRoot, "skills"))}`);
2439
3726
  logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
2440
3727
  logger.blank();
2441
3728
  return "done";
@@ -2459,13 +3746,12 @@ async function listCommand() {
2459
3746
 
2460
3747
  // src/commands/doctor.ts
2461
3748
  import chalk8 from "chalk";
2462
- import { existsSync as existsSync12, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2463
- import { join as join14 } from "path";
3749
+ import { existsSync as existsSync16, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync6, mkdirSync as mkdirSync7 } from "fs";
3750
+ import { join as join15 } from "path";
2464
3751
  async function doctorCommand(options = {}) {
2465
3752
  console.log();
2466
3753
  console.log(chalk8.bold(` ${t("doctor_title")}`));
2467
3754
  console.log(chalk8.dim(" " + "\u2500".repeat(46)));
2468
- let errors = 0;
2469
3755
  let warnings = 0;
2470
3756
  let fixes = 0;
2471
3757
  console.log();
@@ -2503,7 +3789,7 @@ async function doctorCommand(options = {}) {
2503
3789
  const configRoot = getConfigRoot(primaryTool, "project");
2504
3790
  const skillsDir = getSkillsDir(configRoot);
2505
3791
  const agentsDir = getAgentsDir(configRoot);
2506
- if (existsSync12(skillsDir)) {
3792
+ if (existsSync16(skillsDir)) {
2507
3793
  const brokenLinks = checkBrokenLinks(skillsDir);
2508
3794
  if (brokenLinks === 0) {
2509
3795
  const count = readdirSync5(skillsDir).length;
@@ -2520,12 +3806,12 @@ async function doctorCommand(options = {}) {
2520
3806
  } else {
2521
3807
  console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
2522
3808
  if (options.fix) {
2523
- mkdirSync6(skillsDir, { recursive: true });
3809
+ mkdirSync7(skillsDir, { recursive: true });
2524
3810
  fixes++;
2525
3811
  console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
2526
3812
  }
2527
3813
  }
2528
- if (existsSync12(agentsDir)) {
3814
+ if (existsSync16(agentsDir)) {
2529
3815
  const brokenLinks = checkBrokenLinks(agentsDir);
2530
3816
  if (brokenLinks === 0) {
2531
3817
  const count = readdirSync5(agentsDir).length;
@@ -2542,7 +3828,7 @@ async function doctorCommand(options = {}) {
2542
3828
  } else {
2543
3829
  console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
2544
3830
  if (options.fix) {
2545
- mkdirSync6(agentsDir, { recursive: true });
3831
+ mkdirSync7(agentsDir, { recursive: true });
2546
3832
  fixes++;
2547
3833
  console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
2548
3834
  }
@@ -2550,8 +3836,8 @@ async function doctorCommand(options = {}) {
2550
3836
  console.log();
2551
3837
  console.log(chalk8.bold(` ${t("doctor_config")}`));
2552
3838
  const configFile = getConfigFileName(primaryTool);
2553
- const configPath = join14(process.cwd(), configFile);
2554
- if (existsSync12(configPath)) {
3839
+ const configPath = join15(process.cwd(), configFile);
3840
+ if (existsSync16(configPath)) {
2555
3841
  console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
2556
3842
  } else {
2557
3843
  console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
@@ -2560,11 +3846,11 @@ async function doctorCommand(options = {}) {
2560
3846
  console.log(chalk8.dim(" " + "\u2500".repeat(46)));
2561
3847
  if (options.fix && fixes > 0) {
2562
3848
  console.log(
2563
- ` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
3849
+ ` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
2564
3850
  );
2565
3851
  } else {
2566
3852
  console.log(
2567
- ` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
3853
+ ` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
2568
3854
  );
2569
3855
  }
2570
3856
  console.log();
@@ -2574,11 +3860,11 @@ function checkBrokenLinks(dir) {
2574
3860
  try {
2575
3861
  const entries = readdirSync5(dir);
2576
3862
  for (const entry of entries) {
2577
- const fullPath = join14(dir, entry);
3863
+ const fullPath = join15(dir, entry);
2578
3864
  try {
2579
- const stats = lstatSync3(fullPath);
3865
+ const stats = lstatSync2(fullPath);
2580
3866
  if (stats.isSymbolicLink()) {
2581
- if (!existsSync12(fullPath)) {
3867
+ if (!existsSync16(fullPath)) {
2582
3868
  broken++;
2583
3869
  }
2584
3870
  }
@@ -2595,12 +3881,12 @@ function fixBrokenLinks(dir) {
2595
3881
  try {
2596
3882
  const entries = readdirSync5(dir);
2597
3883
  for (const entry of entries) {
2598
- const fullPath = join14(dir, entry);
3884
+ const fullPath = join15(dir, entry);
2599
3885
  try {
2600
- const stats = lstatSync3(fullPath);
3886
+ const stats = lstatSync2(fullPath);
2601
3887
  if (stats.isSymbolicLink()) {
2602
- if (!existsSync12(fullPath)) {
2603
- unlinkSync4(fullPath);
3888
+ if (!existsSync16(fullPath)) {
3889
+ unlinkSync6(fullPath);
2604
3890
  fixed++;
2605
3891
  }
2606
3892
  }
@@ -2615,7 +3901,6 @@ function fixBrokenLinks(dir) {
2615
3901
  // src/commands/status.ts
2616
3902
  import chalk9 from "chalk";
2617
3903
  async function statusCommand() {
2618
- const plugins = getAllPlugins();
2619
3904
  const installed = scanInstalled();
2620
3905
  const installedMap = new Map(installed.map((p) => [p.id, p]));
2621
3906
  console.log();
@@ -2641,6 +3926,7 @@ async function statusCommand() {
2641
3926
  }
2642
3927
 
2643
3928
  // src/commands/install.ts
3929
+ import { join as join16 } from "path";
2644
3930
  import { confirm } from "@inquirer/prompts";
2645
3931
  import chalk10 from "chalk";
2646
3932
  async function installCommand(names, options) {
@@ -2674,7 +3960,10 @@ async function installCommand(names, options) {
2674
3960
  tool2 = validateTool(options.tool);
2675
3961
  } else {
2676
3962
  const detected = await selectToolWithDetection();
2677
- if (detected === "back" || detected === "cancel") return;
3963
+ if (detected === "back" || detected === "cancel") {
3964
+ logger.info(t("init_cancelled"));
3965
+ return;
3966
+ }
2678
3967
  tool2 = detected;
2679
3968
  }
2680
3969
  const level2 = options.level ? validateLevel(options.level) : "project";
@@ -2703,7 +3992,7 @@ async function installCommand(names, options) {
2703
3992
  logger.blank();
2704
3993
  logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
2705
3994
  logger.blank();
2706
- logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
3995
+ logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
2707
3996
  logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
2708
3997
  logger.blank();
2709
3998
  return;
@@ -2723,7 +4012,10 @@ async function installCommand(names, options) {
2723
4012
  tool2 = validateTool(options.tool);
2724
4013
  } else {
2725
4014
  const detected = await selectToolWithDetection();
2726
- if (detected === "back" || detected === "cancel") return;
4015
+ if (detected === "back" || detected === "cancel") {
4016
+ logger.info(t("init_cancelled"));
4017
+ return;
4018
+ }
2727
4019
  tool2 = detected;
2728
4020
  }
2729
4021
  const level2 = options.level ? validateLevel(options.level) : "project";
@@ -2734,11 +4026,17 @@ async function installCommand(names, options) {
2734
4026
  const selectedSkills = await interactiveSkillSelect(tool2, level2);
2735
4027
  if (selectedSkills === "back") {
2736
4028
  const redetected = await selectToolWithDetection();
2737
- if (redetected === "back" || redetected === "cancel") return;
4029
+ if (redetected === "back" || redetected === "cancel") {
4030
+ logger.info(t("init_cancelled"));
4031
+ return;
4032
+ }
2738
4033
  tool2 = redetected;
2739
4034
  break;
2740
4035
  }
2741
- if (selectedSkills === "cancel") return;
4036
+ if (selectedSkills === "cancel") {
4037
+ logger.info(t("init_cancelled"));
4038
+ return;
4039
+ }
2742
4040
  if (selectedSkills.length === 0) {
2743
4041
  logger.info(t("install_no_skill_selected"));
2744
4042
  return;
@@ -2769,7 +4067,7 @@ async function installCommand(names, options) {
2769
4067
  logger.blank();
2770
4068
  logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
2771
4069
  logger.blank();
2772
- logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
4070
+ logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
2773
4071
  logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
2774
4072
  logger.blank();
2775
4073
  return;
@@ -2813,7 +4111,10 @@ async function installCommand(names, options) {
2813
4111
  tool = validateTool(options.tool);
2814
4112
  } else {
2815
4113
  const detected = await selectToolWithDetection();
2816
- if (detected === "back" || detected === "cancel") return;
4114
+ if (detected === "back" || detected === "cancel") {
4115
+ logger.info(t("init_cancelled"));
4116
+ return;
4117
+ }
2817
4118
  tool = detected;
2818
4119
  }
2819
4120
  const level = options.level ? validateLevel(options.level) : "project";
@@ -2896,7 +4197,7 @@ async function installCommand(names, options) {
2896
4197
  logger.blank();
2897
4198
  logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
2898
4199
  logger.blank();
2899
- logger.info(`${t("install_to")}: ${chalk10.cyan(getConfigRoot(tool, level) + "/skills/")}`);
4200
+ logger.info(`${t("install_to")}: ${chalk10.cyan(join16(getConfigRoot(tool, level), "skills"))}`);
2900
4201
  logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
2901
4202
  logger.blank();
2902
4203
  }
@@ -2931,7 +4232,8 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
2931
4232
  }
2932
4233
 
2933
4234
  // src/commands/uninstall.ts
2934
- import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
4235
+ import { existsSync as existsSync17, unlinkSync as unlinkSync7, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
4236
+ import { resolve, basename as basename3, sep } from "path";
2935
4237
  import chalk11 from "chalk";
2936
4238
  async function uninstallCommand(names, options) {
2937
4239
  const tool = options.tool ? validateTool(options.tool) : "opencode";
@@ -2961,31 +4263,37 @@ async function uninstallCommand(names, options) {
2961
4263
  continue;
2962
4264
  }
2963
4265
  logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
4266
+ const configRoot = getConfigRoot(record.tool, record.level);
4267
+ const allowedBases = [resolve(configRoot), resolve(record.installPath)];
4268
+ const isSafePath = (p) => {
4269
+ const resolved = resolve(p);
4270
+ return allowedBases.some((base) => resolved === base || resolved.startsWith(base + sep));
4271
+ };
2964
4272
  let removedFiles = 0;
2965
4273
  let removedDirs = 0;
2966
4274
  for (const filePath of record.files) {
4275
+ if (!isSafePath(filePath)) continue;
2967
4276
  try {
2968
- if (existsSync13(filePath) || isSymlink3(filePath)) {
2969
- unlinkSync5(filePath);
2970
- const name = filePath.split("/").pop() || filePath;
4277
+ if (existsSync17(filePath) || isSymlink(filePath)) {
4278
+ unlinkSync7(filePath);
4279
+ const name = basename3(filePath);
2971
4280
  logger.step(` ${t("uninstall_remove_label")}: ${name}`);
2972
4281
  removedFiles++;
2973
4282
  }
2974
4283
  } catch {
2975
4284
  }
2976
4285
  }
2977
- const configRoot = getConfigRoot(record.tool, record.level);
2978
- const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
4286
+ const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
2979
4287
  for (const dirPath of sortedDirs) {
2980
4288
  try {
2981
- if (dirPath === configRoot) {
4289
+ if (resolve(dirPath) === resolve(configRoot)) {
2982
4290
  continue;
2983
4291
  }
2984
- if (existsSync13(dirPath)) {
4292
+ if (existsSync17(dirPath)) {
2985
4293
  const entries = readdirSync6(dirPath);
2986
4294
  if (entries.length === 0) {
2987
4295
  rmdirSync2(dirPath);
2988
- const name = dirPath.split("/").pop() || dirPath;
4296
+ const name = basename3(dirPath);
2989
4297
  logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
2990
4298
  removedDirs++;
2991
4299
  }
@@ -3014,7 +4322,7 @@ async function uninstallCommand(names, options) {
3014
4322
  }
3015
4323
  const totalRemoved = removedFiles + removedDirs;
3016
4324
  if (totalRemoved > 0) {
3017
- 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`);
4325
+ logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
3018
4326
  } else {
3019
4327
  logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
3020
4328
  }
@@ -3036,13 +4344,6 @@ async function uninstallCommand(names, options) {
3036
4344
  logger.blank();
3037
4345
  }
3038
4346
  }
3039
- function isSymlink3(path) {
3040
- try {
3041
- return lstatSync4(path).isSymbolicLink();
3042
- } catch {
3043
- return false;
3044
- }
3045
- }
3046
4347
 
3047
4348
  // src/commands/update.ts
3048
4349
  import chalk12 from "chalk";
@@ -3052,7 +4353,10 @@ async function updateCommand(pluginNames, options) {
3052
4353
  tool = validateTool(options.tool);
3053
4354
  } else {
3054
4355
  const detected = await selectToolWithDetection();
3055
- if (detected === "back" || detected === "cancel") return;
4356
+ if (detected === "back" || detected === "cancel") {
4357
+ logger.info(t("init_cancelled"));
4358
+ return;
4359
+ }
3056
4360
  tool = detected;
3057
4361
  }
3058
4362
  const level = options.level ? validateLevel(options.level) : "project";
@@ -3079,9 +4383,7 @@ async function updateCommand(pluginNames, options) {
3079
4383
  const repoManager = createRepositoryManager();
3080
4384
  const updateSpinner = createSpinner(t("update_updating") + "...");
3081
4385
  updateSpinner.start();
3082
- await repoManager.updateRepo();
3083
- const repoPath = repoManager.getRepoPath();
3084
- await repoManager.ensureRepoAndScan();
4386
+ const repoPath = await repoManager.ensureRepoAndScan();
3085
4387
  updateSpinner.succeed(t("install_repo_ready"));
3086
4388
  const allPlugins = getAllPlugins();
3087
4389
  const results = [];
@@ -3126,8 +4428,8 @@ async function updateCommand(pluginNames, options) {
3126
4428
 
3127
4429
  // src/commands/info.ts
3128
4430
  import chalk13 from "chalk";
3129
- import { existsSync as existsSync14, readdirSync as readdirSync7 } from "fs";
3130
- import { join as join15 } from "path";
4431
+ import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
4432
+ import { join as join17 } from "path";
3131
4433
  async function infoCommand(pluginName) {
3132
4434
  const plugin = findPlugin(pluginName);
3133
4435
  if (!plugin) {
@@ -3135,7 +4437,8 @@ async function infoCommand(pluginName) {
3135
4437
  return;
3136
4438
  }
3137
4439
  const installed = scanInstalled();
3138
- const isInstalled = installed.some((p) => p.id === plugin.id);
4440
+ const inst = installed.find((p) => p.id === plugin.id);
4441
+ const isInstalled = !!inst;
3139
4442
  const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
3140
4443
  console.log();
3141
4444
  console.log(chalk13.bold(` ${plugin.displayName}`));
@@ -3144,19 +4447,19 @@ async function infoCommand(pluginName) {
3144
4447
  console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
3145
4448
  console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
3146
4449
  console.log(` ${chalk13.dim(t("info_status") + ":")} ${statusText}`);
3147
- console.log(` ${chalk13.dim(t("info_skills") + ":")} ${plugin.skills}`);
3148
- console.log(` ${chalk13.dim(t("info_agents") + ":")} ${plugin.agents}`);
4450
+ console.log(` ${chalk13.dim(t("info_skills") + ":")} ${inst ? inst.skillsCount : plugin.skills}`);
4451
+ console.log(` ${chalk13.dim(t("info_agents") + ":")} ${inst ? inst.agentsCount : plugin.agents}`);
3149
4452
  console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
3150
4453
  console.log();
3151
4454
  try {
3152
4455
  const repoManager = createRepositoryManager();
3153
4456
  const repoPath = await repoManager.ensureRepo();
3154
- const quickstartPath = join15(repoPath, plugin.dir, "quickstart.md");
3155
- if (existsSync14(quickstartPath)) {
4457
+ const quickstartPath = join17(repoPath, plugin.dir, "quickstart.md");
4458
+ if (existsSync18(quickstartPath)) {
3156
4459
  console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
3157
4460
  }
3158
- const agentsDir = join15(repoPath, plugin.dir, "agents");
3159
- if (existsSync14(agentsDir)) {
4461
+ const agentsDir = join17(repoPath, plugin.dir, "agents");
4462
+ if (existsSync18(agentsDir)) {
3160
4463
  const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
3161
4464
  if (agentFiles.length > 0) {
3162
4465
  console.log();
@@ -3208,13 +4511,44 @@ async function langCommand(action, value) {
3208
4511
  }
3209
4512
 
3210
4513
  // src/cli.ts
4514
+ import { readFileSync as readFileSync9, existsSync as existsSync19 } from "fs";
4515
+ import { fileURLToPath as fileURLToPath5 } from "url";
4516
+ import { dirname as dirname7, join as join18 } from "path";
4517
+ function readVersion() {
4518
+ try {
4519
+ if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
4520
+ return INSTALL_HELPER_VERSION;
4521
+ }
4522
+ } catch {
4523
+ }
4524
+ try {
4525
+ const __filename5 = fileURLToPath5(import.meta.url);
4526
+ const __dirname5 = dirname7(__filename5);
4527
+ const candidates = [
4528
+ join18(__dirname5, "..", "package.json"),
4529
+ join18(__dirname5, "package.json"),
4530
+ join18(process.cwd(), "package.json")
4531
+ ];
4532
+ for (const pkgPath of candidates) {
4533
+ if (existsSync19(pkgPath)) {
4534
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
4535
+ if (pkg.version && (pkg.name === "@cannbot-ai/install-helper" || pkg.name?.startsWith("@cannbot-ai/install-helper-"))) {
4536
+ return pkg.version;
4537
+ }
4538
+ }
4539
+ }
4540
+ return "0.0.0";
4541
+ } catch {
4542
+ return "0.0.0";
4543
+ }
4544
+ }
3211
4545
  function createCLI() {
3212
4546
  const config = readConfig();
3213
4547
  if (config.language) {
3214
4548
  setLanguage(config.language);
3215
4549
  }
3216
4550
  const program2 = new Command();
3217
- program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("1.0.8");
4551
+ program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
3218
4552
  program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
3219
4553
  await initCommand();
3220
4554
  });
@@ -3249,13 +4583,28 @@ function createCLI() {
3249
4583
  }
3250
4584
 
3251
4585
  // src/index.ts
3252
- process.on("uncaughtException", (error) => {
3253
- if (error.name === "ExitPromptError") {
4586
+ function isExitError(error) {
4587
+ if (!error) return false;
4588
+ return error.name === "ExitPromptError" || error.message?.includes("force closed") || error.message?.includes("SIGINT") || error.constructor?.name === "ExitPromptError";
4589
+ }
4590
+ function handleExit(error) {
4591
+ if (isExitError(error)) {
3254
4592
  console.log("\n" + t("init_cancelled"));
3255
4593
  process.exit(0);
3256
4594
  }
4595
+ }
4596
+ process.on("SIGINT", () => {
4597
+ console.log("\n" + t("init_cancelled"));
4598
+ process.exit(130);
4599
+ });
4600
+ process.on("uncaughtException", (error) => {
4601
+ handleExit(error);
3257
4602
  throw error;
3258
4603
  });
4604
+ process.on("unhandledRejection", (error) => {
4605
+ handleExit(error);
4606
+ process.exit(1);
4607
+ });
3259
4608
  var program = createCLI();
3260
4609
  program.parse();
3261
4610
  //# sourceMappingURL=index.js.map