@cannbot-ai/install-helper 1.0.8 → 1.1.3
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/README.md +37 -191
- package/bin/install-helper.js +59 -0
- package/bin/package.json +3 -0
- package/dist/config/repository.yaml +3 -0
- package/dist/index.js +1864 -489
- package/dist/index.js.map +1 -1
- package/dist/install.ps1 +223 -0
- package/dist/locales/en_US.json +30 -17
- package/dist/locales/zh_CN.json +30 -17
- package/dist/plugins.d/catlass-op-generator.yml +12 -1
- package/dist/plugins.d/model-infer-optimize.yml +13 -1
- package/dist/plugins.d/ops-code-reviewer.yml +8 -1
- package/dist/plugins.d/ops-direct-invoke-flash.yml +12 -1
- package/dist/plugins.d/ops-direct-invoke.yml +16 -3
- package/dist/plugins.d/ops-registry-invoke.yml +19 -2
- package/dist/plugins.d/pypto-op-orchestrator.yml +11 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +13 -0
- package/dist/plugins.d/torch-compile.yml +7 -0
- package/dist/plugins.d/triton-op-generator.yml +8 -1
- package/package.json +17 -6
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,
|
|
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
|
-
|
|
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: 19,
|
|
599
|
+
agents: 4,
|
|
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: 7,
|
|
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: 26,
|
|
703
|
+
agents: 3,
|
|
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: 14,
|
|
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: 12,
|
|
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: 0,
|
|
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: 2,
|
|
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 =
|
|
23
|
-
const defaultsPath =
|
|
1064
|
+
const pluginsDir = findPluginsDir();
|
|
1065
|
+
const defaultsPath = join2(pluginsDir, "_defaults.yml");
|
|
24
1066
|
let defaults = {};
|
|
25
1067
|
try {
|
|
26
|
-
defaults = parseYaml(
|
|
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(
|
|
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
|
|
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 (
|
|
142
|
-
if (
|
|
143
|
-
if (
|
|
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
|
|
235
|
-
|
|
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" &&
|
|
1320
|
+
if (variant === "ide" && existsSync4(join4(home, ".trae-cn"))) {
|
|
257
1321
|
path = join4(home, ".trae-cn");
|
|
258
|
-
} else if (variant === "plugin" &&
|
|
1322
|
+
} else if (variant === "plugin" && existsSync4(join4(home, ".marscode"))) {
|
|
259
1323
|
path = join4(home, ".marscode");
|
|
260
|
-
} else if (variant === "cli" &&
|
|
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 (
|
|
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
|
|
1385
|
+
return VALID_TOOLS;
|
|
322
1386
|
}
|
|
323
1387
|
|
|
324
1388
|
// src/core/manifest.ts
|
|
325
|
-
import { existsSync as
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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,103 +1462,57 @@ 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
|
|
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
|
|
1472
|
+
return existsSync5(agentPath);
|
|
409
1473
|
});
|
|
410
1474
|
if (!hasAnyAgent) return false;
|
|
411
1475
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
} else {
|
|
416
|
-
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
417
|
-
}
|
|
418
|
-
if (!existsSync3(agentsMdPath)) return false;
|
|
1476
|
+
const agentsFileName = getAgentsFileName(tool);
|
|
1477
|
+
const configFilePath = level === "project" ? join5(configRoot, "..", agentsFileName) : join5(configRoot, agentsFileName);
|
|
1478
|
+
if (!existsSync5(configFilePath)) return false;
|
|
419
1479
|
return true;
|
|
420
1480
|
}
|
|
421
1481
|
|
|
422
|
-
// src/utils/
|
|
423
|
-
import
|
|
424
|
-
import
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
|
|
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;
|
|
1482
|
+
// src/utils/config.ts
|
|
1483
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
1484
|
+
import { join as join6 } from "path";
|
|
1485
|
+
import { parse, stringify } from "yaml";
|
|
1486
|
+
|
|
1487
|
+
// src/utils/fs.ts
|
|
1488
|
+
import { writeFileSync, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
1489
|
+
import { dirname as dirname3 } from "path";
|
|
1490
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
1491
|
+
const dir = dirname3(filePath);
|
|
1492
|
+
if (!existsSync6(dir)) {
|
|
1493
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
1494
|
+
}
|
|
1495
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1496
|
+
writeFileSync(tmpPath, data, encoding);
|
|
1497
|
+
if (existsSync6(filePath)) {
|
|
1498
|
+
try {
|
|
1499
|
+
unlinkSync(filePath);
|
|
1500
|
+
} catch {
|
|
457
1501
|
}
|
|
458
1502
|
}
|
|
459
|
-
|
|
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
|
-
}
|
|
1503
|
+
renameSync(tmpPath, filePath);
|
|
482
1504
|
}
|
|
483
1505
|
|
|
484
1506
|
// 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
1507
|
function getConfigDir() {
|
|
490
|
-
return
|
|
1508
|
+
return getCannbotConfigDir();
|
|
491
1509
|
}
|
|
492
1510
|
function getConfigPath() {
|
|
493
1511
|
return join6(getConfigDir(), "config.yaml");
|
|
494
1512
|
}
|
|
495
1513
|
function readConfig() {
|
|
496
1514
|
const configPath = getConfigPath();
|
|
497
|
-
if (!
|
|
1515
|
+
if (!existsSync7(configPath)) {
|
|
498
1516
|
return {
|
|
499
1517
|
language: "zh_CN",
|
|
500
1518
|
installedPlugins: []
|
|
@@ -511,6 +1529,7 @@ function readConfig() {
|
|
|
511
1529
|
installedPlugins: parsed.installedPlugins || []
|
|
512
1530
|
};
|
|
513
1531
|
} catch {
|
|
1532
|
+
logger.warn(t("config_corrupted"));
|
|
514
1533
|
return {
|
|
515
1534
|
language: "zh_CN",
|
|
516
1535
|
installedPlugins: []
|
|
@@ -519,112 +1538,39 @@ function readConfig() {
|
|
|
519
1538
|
}
|
|
520
1539
|
function writeConfig(config) {
|
|
521
1540
|
const configDir = getConfigDir();
|
|
522
|
-
if (!
|
|
1541
|
+
if (!existsSync7(configDir)) {
|
|
523
1542
|
mkdirSync(configDir, { recursive: true });
|
|
524
1543
|
}
|
|
525
1544
|
const configPath = getConfigPath();
|
|
526
1545
|
const content = stringify(config);
|
|
527
|
-
|
|
1546
|
+
atomicWriteFileSync(configPath, content);
|
|
528
1547
|
}
|
|
529
1548
|
function updateConfig(updates) {
|
|
530
1549
|
const config = readConfig();
|
|
531
1550
|
const updated = { ...config, ...updates };
|
|
532
1551
|
writeConfig(updated);
|
|
533
1552
|
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");
|
|
612
|
-
}
|
|
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)
|
|
1553
|
+
}
|
|
1554
|
+
function addInstalledPlugin(pluginId) {
|
|
1555
|
+
const config = readConfig();
|
|
1556
|
+
if (!config.installedPlugins.includes(pluginId)) {
|
|
1557
|
+
config.installedPlugins.push(pluginId);
|
|
1558
|
+
writeConfig(config);
|
|
622
1559
|
}
|
|
623
|
-
}
|
|
1560
|
+
}
|
|
1561
|
+
function removeInstalledPlugin(pluginId) {
|
|
1562
|
+
const config = readConfig();
|
|
1563
|
+
config.installedPlugins = config.installedPlugins.filter(
|
|
1564
|
+
(id) => id !== pluginId
|
|
1565
|
+
);
|
|
1566
|
+
writeConfig(config);
|
|
1567
|
+
}
|
|
624
1568
|
|
|
625
|
-
// src/
|
|
1569
|
+
// src/utils/constants.ts
|
|
626
1570
|
var BACK = "__back__";
|
|
627
1571
|
var CANCEL = "__cancel__";
|
|
1572
|
+
|
|
1573
|
+
// src/ui/wizard.ts
|
|
628
1574
|
async function selectToolWithDetection() {
|
|
629
1575
|
const config = readConfig();
|
|
630
1576
|
logger.blank();
|
|
@@ -708,7 +1654,7 @@ async function stepTool(detectedTools, lastTool) {
|
|
|
708
1654
|
if (detectedTools.length === 0) {
|
|
709
1655
|
logger.error(t("error_tool_not_found"));
|
|
710
1656
|
const choices2 = [
|
|
711
|
-
...
|
|
1657
|
+
...VALID_TOOLS.map((tool) => ({
|
|
712
1658
|
name: `> ${getToolDisplayName(tool)}`,
|
|
713
1659
|
value: tool
|
|
714
1660
|
})),
|
|
@@ -809,37 +1755,25 @@ async function stepPlugins(tool, level) {
|
|
|
809
1755
|
const isInstalled = installedSet.has(p.id);
|
|
810
1756
|
const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
|
|
811
1757
|
return {
|
|
812
|
-
name:
|
|
1758
|
+
name: `> ${chalk3.cyan(p.displayName)}${suffix}`,
|
|
813
1759
|
value: p.id,
|
|
814
|
-
checked: false,
|
|
815
1760
|
description: p.description
|
|
816
1761
|
};
|
|
817
1762
|
});
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1763
|
+
choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1764
|
+
choices.push({ name: "<- " + t("wizard_back"), value: BACK });
|
|
1765
|
+
choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL });
|
|
1766
|
+
showOperationHints();
|
|
1767
|
+
const selected = await select({
|
|
1768
|
+
message: t("wizard_select_plugin"),
|
|
821
1769
|
choices,
|
|
822
1770
|
loop: false,
|
|
823
|
-
|
|
824
|
-
theme: checkboxTheme,
|
|
1771
|
+
theme: selectTheme,
|
|
825
1772
|
pageSize: 15
|
|
826
1773
|
});
|
|
827
|
-
|
|
828
|
-
if (
|
|
829
|
-
|
|
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;
|
|
1774
|
+
if (selected === BACK) return BACK;
|
|
1775
|
+
if (selected === CANCEL) return CANCEL;
|
|
1776
|
+
return [selected];
|
|
843
1777
|
}
|
|
844
1778
|
async function stepConfirm(tool, level, plugins) {
|
|
845
1779
|
printBoxTitle(t("wizard_step_4_title"));
|
|
@@ -870,19 +1804,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
870
1804
|
}
|
|
871
1805
|
|
|
872
1806
|
// src/core/repository.ts
|
|
873
|
-
import { existsSync as
|
|
874
|
-
import { join as join8, dirname as
|
|
1807
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1808
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
875
1809
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
876
1810
|
import { execa as execa2 } from "execa";
|
|
877
1811
|
import { parse as parseYaml3 } from "yaml";
|
|
878
1812
|
|
|
879
1813
|
// src/core/scanner.ts
|
|
880
|
-
import { existsSync as
|
|
881
|
-
import { join as join7, dirname as
|
|
1814
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
1815
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
882
1816
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
883
1817
|
import { parse as parseYaml2 } from "yaml";
|
|
884
1818
|
var __filename3 = fileURLToPath3(import.meta.url);
|
|
885
|
-
var __dirname3 =
|
|
1819
|
+
var __dirname3 = dirname4(__filename3);
|
|
886
1820
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
887
1821
|
"node_modules",
|
|
888
1822
|
".git",
|
|
@@ -907,14 +1841,17 @@ function loadScanConfig() {
|
|
|
907
1841
|
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
908
1842
|
try {
|
|
909
1843
|
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
1844
|
+
const ttlHours = config.scanCacheTtlHours || 24;
|
|
910
1845
|
return {
|
|
911
|
-
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
|
|
912
|
-
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
|
|
1846
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1847
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"],
|
|
1848
|
+
cacheTtlMs: ttlHours * 60 * 60 * 1e3
|
|
913
1849
|
};
|
|
914
1850
|
} catch {
|
|
915
1851
|
return {
|
|
916
|
-
skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
|
|
917
|
-
pluginDirs: ["plugins-official", "plugins-community"]
|
|
1852
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1853
|
+
pluginDirs: ["plugins-official", "plugins-community"],
|
|
1854
|
+
cacheTtlMs: 24 * 60 * 60 * 1e3
|
|
918
1855
|
};
|
|
919
1856
|
}
|
|
920
1857
|
}
|
|
@@ -926,23 +1863,37 @@ function scanSkills(repoPath) {
|
|
|
926
1863
|
const seen = /* @__PURE__ */ new Set();
|
|
927
1864
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
928
1865
|
const fullPath = join7(repoPath, dir);
|
|
929
|
-
if (!
|
|
1866
|
+
if (!existsSync8(fullPath)) continue;
|
|
930
1867
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
931
1868
|
}
|
|
932
1869
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
933
1870
|
const pluginParent = join7(repoPath, pluginDir);
|
|
934
|
-
if (!
|
|
1871
|
+
if (!existsSync8(pluginParent)) continue;
|
|
935
1872
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
936
1873
|
const pluginPath = join7(pluginParent, plugin);
|
|
937
1874
|
if (!isDirectory(pluginPath)) continue;
|
|
938
1875
|
const skillsDir = join7(pluginPath, "skills");
|
|
939
|
-
if (
|
|
1876
|
+
if (existsSync8(skillsDir)) {
|
|
940
1877
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
941
1878
|
}
|
|
942
1879
|
const skillDir = join7(pluginPath, "skill");
|
|
943
|
-
if (
|
|
1880
|
+
if (existsSync8(skillDir)) {
|
|
944
1881
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
945
1882
|
}
|
|
1883
|
+
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
1884
|
+
const initSh = join7(pluginPath, "init.sh");
|
|
1885
|
+
if (existsSync8(rootSkillMd) && !existsSync8(initSh)) {
|
|
1886
|
+
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
1887
|
+
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1888
|
+
seen.add(frontmatter.name);
|
|
1889
|
+
skills.push({
|
|
1890
|
+
id: frontmatter.name,
|
|
1891
|
+
description: frontmatter.description,
|
|
1892
|
+
source: `${pluginDir}/${plugin}`,
|
|
1893
|
+
filePath: rootSkillMd
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
946
1897
|
}
|
|
947
1898
|
}
|
|
948
1899
|
return skills;
|
|
@@ -965,8 +1916,12 @@ function parseFrontmatter(filePath) {
|
|
|
965
1916
|
const parsed = parseYaml2(yamlBlock);
|
|
966
1917
|
if (!parsed || typeof parsed !== "object") return null;
|
|
967
1918
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1919
|
+
const trimmedName = parsed.name.trim();
|
|
1920
|
+
if (!trimmedName || /[\/\\]|\.\./.test(trimmedName) || !/^[a-zA-Z0-9._-]+$/.test(trimmedName)) {
|
|
1921
|
+
return null;
|
|
1922
|
+
}
|
|
968
1923
|
return {
|
|
969
|
-
name:
|
|
1924
|
+
name: trimmedName,
|
|
970
1925
|
description: (parsed.description || "").trim()
|
|
971
1926
|
};
|
|
972
1927
|
} catch {
|
|
@@ -976,15 +1931,15 @@ function parseFrontmatter(filePath) {
|
|
|
976
1931
|
function getCurrentCommit(repoPath) {
|
|
977
1932
|
try {
|
|
978
1933
|
const headFile = join7(repoPath, ".git", "HEAD");
|
|
979
|
-
if (!
|
|
1934
|
+
if (!existsSync8(headFile)) return "unknown";
|
|
980
1935
|
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
981
1936
|
if (headContent.startsWith("ref: ")) {
|
|
982
1937
|
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
983
|
-
if (
|
|
1938
|
+
if (existsSync8(refPath)) {
|
|
984
1939
|
return readFileSync5(refPath, "utf-8").trim();
|
|
985
1940
|
}
|
|
986
1941
|
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
987
|
-
if (
|
|
1942
|
+
if (existsSync8(packedRefsPath)) {
|
|
988
1943
|
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
989
1944
|
const refName = headContent.slice(5);
|
|
990
1945
|
const lines = packedRefs.split("\n");
|
|
@@ -1003,12 +1958,12 @@ function getCurrentCommit(repoPath) {
|
|
|
1003
1958
|
function readScanCache() {
|
|
1004
1959
|
try {
|
|
1005
1960
|
const cachePath = getCachePath();
|
|
1006
|
-
if (!
|
|
1961
|
+
if (!existsSync8(cachePath)) return null;
|
|
1007
1962
|
const content = readFileSync5(cachePath, "utf-8");
|
|
1008
1963
|
const cache = JSON.parse(content);
|
|
1009
1964
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1010
1965
|
const age = Date.now() - cache.timestamp;
|
|
1011
|
-
if (age >
|
|
1966
|
+
if (age > scanConfig.cacheTtlMs) return null;
|
|
1012
1967
|
return cache;
|
|
1013
1968
|
} catch {
|
|
1014
1969
|
return null;
|
|
@@ -1017,11 +1972,11 @@ function readScanCache() {
|
|
|
1017
1972
|
function writeScanCache(cache) {
|
|
1018
1973
|
try {
|
|
1019
1974
|
const cachePath = getCachePath();
|
|
1020
|
-
const cacheDir =
|
|
1021
|
-
if (!
|
|
1975
|
+
const cacheDir = dirname4(cachePath);
|
|
1976
|
+
if (!existsSync8(cacheDir)) {
|
|
1022
1977
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1023
1978
|
}
|
|
1024
|
-
|
|
1979
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1025
1980
|
} catch {
|
|
1026
1981
|
}
|
|
1027
1982
|
}
|
|
@@ -1034,7 +1989,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1034
1989
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1035
1990
|
if (!isDirectory(fullPath)) continue;
|
|
1036
1991
|
const skillMd = join7(fullPath, "SKILL.md");
|
|
1037
|
-
if (
|
|
1992
|
+
if (existsSync8(skillMd)) {
|
|
1038
1993
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1039
1994
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1040
1995
|
seen.add(frontmatter.name);
|
|
@@ -1057,28 +2012,22 @@ function safeReaddir(dir) {
|
|
|
1057
2012
|
return [];
|
|
1058
2013
|
}
|
|
1059
2014
|
}
|
|
1060
|
-
function isDirectory(path) {
|
|
1061
|
-
try {
|
|
1062
|
-
return statSync(path).isDirectory();
|
|
1063
|
-
} catch {
|
|
1064
|
-
return false;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
2015
|
|
|
1068
2016
|
// src/core/skill-registry.ts
|
|
1069
2017
|
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: "
|
|
1080
|
-
{ id: "
|
|
1081
|
-
{ id: "
|
|
2018
|
+
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
|
|
2019
|
+
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
|
|
2020
|
+
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
|
|
2021
|
+
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
|
|
2022
|
+
{ id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
|
|
2023
|
+
{ id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
|
|
2024
|
+
{ id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
|
|
2025
|
+
{ id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
|
|
2026
|
+
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
|
|
2027
|
+
{ id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
|
|
2028
|
+
{ id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
|
|
2029
|
+
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
|
|
2030
|
+
{ id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
|
|
1082
2031
|
];
|
|
1083
2032
|
var dynamicSkills = [];
|
|
1084
2033
|
var initialized = false;
|
|
@@ -1086,7 +2035,8 @@ function initFromScan(scanned) {
|
|
|
1086
2035
|
dynamicSkills = scanned.map((s) => ({
|
|
1087
2036
|
id: s.id,
|
|
1088
2037
|
description: s.description,
|
|
1089
|
-
source: s.source
|
|
2038
|
+
source: s.source,
|
|
2039
|
+
filePath: s.filePath
|
|
1090
2040
|
}));
|
|
1091
2041
|
initialized = true;
|
|
1092
2042
|
}
|
|
@@ -1162,9 +2112,7 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1162
2112
|
{ id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
|
|
1163
2113
|
{ id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
|
|
1164
2114
|
{ 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" }
|
|
2115
|
+
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1168
2116
|
]
|
|
1169
2117
|
},
|
|
1170
2118
|
{
|
|
@@ -1221,7 +2169,9 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1221
2169
|
{ id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
|
|
1222
2170
|
{ id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
|
|
1223
2171
|
{ 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" }
|
|
2172
|
+
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
|
|
2173
|
+
{ id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
|
|
2174
|
+
{ 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
2175
|
]
|
|
1226
2176
|
},
|
|
1227
2177
|
{
|
|
@@ -1237,6 +2187,13 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1237
2187
|
{ id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
|
|
1238
2188
|
]
|
|
1239
2189
|
},
|
|
2190
|
+
{
|
|
2191
|
+
id: "runtime",
|
|
2192
|
+
name: "Runtime \u8FC1\u79FB",
|
|
2193
|
+
skills: [
|
|
2194
|
+
{ id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
|
|
2195
|
+
]
|
|
2196
|
+
},
|
|
1240
2197
|
{
|
|
1241
2198
|
id: "platform",
|
|
1242
2199
|
name: "\u5E73\u53F0\u5DE5\u5177",
|
|
@@ -1280,9 +2237,10 @@ function getAllSkills() {
|
|
|
1280
2237
|
}
|
|
1281
2238
|
function getAllCategories() {
|
|
1282
2239
|
const skills = getActiveSkills();
|
|
2240
|
+
const isEn = t("list_name") === "Name";
|
|
1283
2241
|
return CATEGORY_DEFS.map((def) => ({
|
|
1284
2242
|
id: def.id,
|
|
1285
|
-
name: def.name,
|
|
2243
|
+
name: isEn ? def.nameEn : def.name,
|
|
1286
2244
|
skills: skills.filter((s) => {
|
|
1287
2245
|
const catId = CATEGORY_MAP[s.id];
|
|
1288
2246
|
if (catId) return catId === def.id;
|
|
@@ -1293,7 +2251,7 @@ function getAllCategories() {
|
|
|
1293
2251
|
|
|
1294
2252
|
// src/core/repository.ts
|
|
1295
2253
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1296
|
-
var __dirname4 =
|
|
2254
|
+
var __dirname4 = dirname5(__filename4);
|
|
1297
2255
|
function loadRepoUrl() {
|
|
1298
2256
|
if (process.env.CANNBOT_REPO_URL) {
|
|
1299
2257
|
return process.env.CANNBOT_REPO_URL;
|
|
@@ -1309,8 +2267,10 @@ function loadRepoUrl() {
|
|
|
1309
2267
|
var REPO_URL = loadRepoUrl();
|
|
1310
2268
|
var RepositoryManager = class {
|
|
1311
2269
|
repoPath;
|
|
2270
|
+
customPath;
|
|
1312
2271
|
constructor(customPath) {
|
|
1313
2272
|
this.repoPath = customPath;
|
|
2273
|
+
this.customPath = customPath;
|
|
1314
2274
|
}
|
|
1315
2275
|
async ensureRepo() {
|
|
1316
2276
|
if (this.repoPath && this.isValidRepo(this.repoPath)) {
|
|
@@ -1331,10 +2291,16 @@ var RepositoryManager = class {
|
|
|
1331
2291
|
this.repoPath = cachedPath;
|
|
1332
2292
|
return cachedPath;
|
|
1333
2293
|
}
|
|
1334
|
-
|
|
2294
|
+
const clonedPath = await this.cloneRepo();
|
|
2295
|
+
return clonedPath;
|
|
1335
2296
|
}
|
|
1336
2297
|
async ensureRepoAndScan() {
|
|
1337
2298
|
const repoPath = await this.ensureRepo();
|
|
2299
|
+
const isUserProvided = !!this.customPath || !!process.env.CANNBOT_REPO_PATH;
|
|
2300
|
+
if (!isUserProvided) {
|
|
2301
|
+
await this.updateRepo();
|
|
2302
|
+
}
|
|
2303
|
+
mergeDynamicPlugins(repoPath);
|
|
1338
2304
|
const cache = readScanCache();
|
|
1339
2305
|
if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
|
|
1340
2306
|
initFromScan(cache.skills);
|
|
@@ -1354,7 +2320,9 @@ var RepositoryManager = class {
|
|
|
1354
2320
|
try {
|
|
1355
2321
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1356
2322
|
} catch {
|
|
2323
|
+
logger.warn(t("repo_pull_failed"));
|
|
1357
2324
|
}
|
|
2325
|
+
mergeDynamicPlugins(repoPath);
|
|
1358
2326
|
}
|
|
1359
2327
|
getRepoPath() {
|
|
1360
2328
|
if (!this.repoPath) {
|
|
@@ -1363,24 +2331,25 @@ var RepositoryManager = class {
|
|
|
1363
2331
|
return this.repoPath;
|
|
1364
2332
|
}
|
|
1365
2333
|
isValidRepo(path) {
|
|
1366
|
-
if (!
|
|
2334
|
+
if (!existsSync9(path)) return false;
|
|
1367
2335
|
const gitDir = join8(path, ".git");
|
|
1368
2336
|
const pluginsDir = join8(path, "plugins-official");
|
|
1369
|
-
return
|
|
2337
|
+
return existsSync9(gitDir) && existsSync9(pluginsDir);
|
|
1370
2338
|
}
|
|
1371
2339
|
async cloneRepo() {
|
|
1372
2340
|
const configDir = getCannbotConfigDir();
|
|
1373
|
-
if (!
|
|
2341
|
+
if (!existsSync9(configDir)) {
|
|
1374
2342
|
mkdirSync3(configDir, { recursive: true });
|
|
1375
2343
|
}
|
|
1376
2344
|
const targetPath = getCannbotRepoPath();
|
|
1377
|
-
if (
|
|
2345
|
+
if (existsSync9(targetPath)) {
|
|
1378
2346
|
if (this.isValidRepo(targetPath)) {
|
|
1379
2347
|
this.repoPath = targetPath;
|
|
1380
2348
|
return targetPath;
|
|
1381
2349
|
}
|
|
1382
2350
|
}
|
|
1383
2351
|
try {
|
|
2352
|
+
logger.info(t("repo_cloning"));
|
|
1384
2353
|
await execa2(
|
|
1385
2354
|
"git",
|
|
1386
2355
|
["clone", "--depth", "1", REPO_URL, targetPath],
|
|
@@ -1390,7 +2359,7 @@ var RepositoryManager = class {
|
|
|
1390
2359
|
return targetPath;
|
|
1391
2360
|
} catch (error) {
|
|
1392
2361
|
throw new Error(
|
|
1393
|
-
`Failed to clone repository: ${error instanceof Error ? error.message : "
|
|
2362
|
+
`Failed to clone repository: ${error instanceof Error ? error.message : t("error_unknown")}`
|
|
1394
2363
|
);
|
|
1395
2364
|
}
|
|
1396
2365
|
}
|
|
@@ -1400,12 +2369,12 @@ function createRepositoryManager(customPath) {
|
|
|
1400
2369
|
}
|
|
1401
2370
|
|
|
1402
2371
|
// src/core/installer.ts
|
|
1403
|
-
import { existsSync as
|
|
1404
|
-
import { join as
|
|
1405
|
-
import { execa as
|
|
2372
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2373
|
+
import { join as join12, basename as basename2 } from "path";
|
|
2374
|
+
import { execa as execa4, execaSync } from "execa";
|
|
1406
2375
|
|
|
1407
2376
|
// src/core/record.ts
|
|
1408
|
-
import { existsSync as
|
|
2377
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
1409
2378
|
import { join as join9 } from "path";
|
|
1410
2379
|
function getInstallsDir() {
|
|
1411
2380
|
return join9(getCannbotConfigDir(), "installs");
|
|
@@ -1415,53 +2384,54 @@ function getRecordPath(pluginId) {
|
|
|
1415
2384
|
}
|
|
1416
2385
|
function readRecord(pluginId) {
|
|
1417
2386
|
const recordPath = getRecordPath(pluginId);
|
|
1418
|
-
if (!
|
|
2387
|
+
if (!existsSync10(recordPath)) {
|
|
1419
2388
|
return null;
|
|
1420
2389
|
}
|
|
1421
2390
|
try {
|
|
1422
2391
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1423
2392
|
return JSON.parse(content);
|
|
1424
2393
|
} catch {
|
|
2394
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1425
2395
|
return null;
|
|
1426
2396
|
}
|
|
1427
2397
|
}
|
|
1428
2398
|
function writeRecord(record) {
|
|
1429
2399
|
const installsDir = getInstallsDir();
|
|
1430
|
-
if (!
|
|
2400
|
+
if (!existsSync10(installsDir)) {
|
|
1431
2401
|
mkdirSync4(installsDir, { recursive: true });
|
|
1432
2402
|
}
|
|
1433
2403
|
const recordPath = getRecordPath(record.pluginId);
|
|
1434
|
-
|
|
2404
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1435
2405
|
}
|
|
1436
2406
|
function deleteRecord(pluginId) {
|
|
1437
2407
|
const recordPath = getRecordPath(pluginId);
|
|
1438
|
-
if (
|
|
1439
|
-
|
|
2408
|
+
if (existsSync10(recordPath)) {
|
|
2409
|
+
unlinkSync2(recordPath);
|
|
1440
2410
|
}
|
|
1441
2411
|
}
|
|
1442
|
-
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
2412
|
+
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames) {
|
|
1443
2413
|
const files = [];
|
|
1444
2414
|
const directories = [];
|
|
1445
2415
|
if (manifest) {
|
|
1446
2416
|
const skillsDir = join9(configRoot, "skills");
|
|
1447
|
-
if (
|
|
2417
|
+
if (existsSync10(skillsDir)) {
|
|
1448
2418
|
directories.push(skillsDir);
|
|
1449
2419
|
for (const skillName of manifest.installed_skills || []) {
|
|
1450
2420
|
const skillPath = join9(skillsDir, skillName);
|
|
1451
|
-
if (
|
|
2421
|
+
if (existsSync10(skillPath) || isSymlink(skillPath)) {
|
|
1452
2422
|
files.push(skillPath);
|
|
1453
2423
|
}
|
|
1454
2424
|
}
|
|
1455
2425
|
}
|
|
1456
2426
|
const agentsDir = join9(configRoot, "agents");
|
|
1457
|
-
if (
|
|
2427
|
+
if (existsSync10(agentsDir)) {
|
|
1458
2428
|
directories.push(agentsDir);
|
|
1459
2429
|
for (const agentName of manifest.installed_agents || []) {
|
|
1460
2430
|
const agentPath = join9(agentsDir, agentName);
|
|
1461
2431
|
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
1462
|
-
if (
|
|
2432
|
+
if (existsSync10(agentPath) || isSymlink(agentPath)) {
|
|
1463
2433
|
files.push(agentPath);
|
|
1464
|
-
} else if (
|
|
2434
|
+
} else if (existsSync10(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1465
2435
|
files.push(agentPathMd);
|
|
1466
2436
|
}
|
|
1467
2437
|
}
|
|
@@ -1472,19 +2442,19 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1472
2442
|
files.push(workflowsLink);
|
|
1473
2443
|
}
|
|
1474
2444
|
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
1475
|
-
if (
|
|
2445
|
+
if (existsSync10(manifestPath)) {
|
|
1476
2446
|
files.push(manifestPath);
|
|
1477
2447
|
}
|
|
1478
2448
|
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
1479
|
-
if (
|
|
2449
|
+
if (existsSync10(pluginManifestPath)) {
|
|
1480
2450
|
files.push(pluginManifestPath);
|
|
1481
2451
|
}
|
|
1482
2452
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1483
2453
|
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
1484
|
-
if (
|
|
2454
|
+
if (existsSync10(configFilePath)) {
|
|
1485
2455
|
files.push(configFilePath);
|
|
1486
2456
|
}
|
|
1487
|
-
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
2457
|
+
const repoLinks = externalRepoNames && externalRepoNames.length > 0 ? externalRepoNames : ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1488
2458
|
for (const repoName of repoLinks) {
|
|
1489
2459
|
const repoLinkPath = join9(installPath, repoName);
|
|
1490
2460
|
if (isSymlink(repoLinkPath)) {
|
|
@@ -1507,35 +2477,29 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1507
2477
|
directories
|
|
1508
2478
|
};
|
|
1509
2479
|
}
|
|
1510
|
-
function isSymlink(path) {
|
|
1511
|
-
try {
|
|
1512
|
-
return lstatSync(path).isSymbolicLink();
|
|
1513
|
-
} catch {
|
|
1514
|
-
return false;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
2480
|
function getSkillRecordPath() {
|
|
1518
2481
|
return join9(getInstallsDir(), "skills.json");
|
|
1519
2482
|
}
|
|
1520
2483
|
function readSkillRecord() {
|
|
1521
2484
|
const recordPath = getSkillRecordPath();
|
|
1522
|
-
if (!
|
|
2485
|
+
if (!existsSync10(recordPath)) {
|
|
1523
2486
|
return {};
|
|
1524
2487
|
}
|
|
1525
2488
|
try {
|
|
1526
2489
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1527
2490
|
return JSON.parse(content);
|
|
1528
2491
|
} catch {
|
|
2492
|
+
logger.warn(t("skill_record_corrupted"));
|
|
1529
2493
|
return {};
|
|
1530
2494
|
}
|
|
1531
2495
|
}
|
|
1532
2496
|
function writeSkillRecord(record) {
|
|
1533
2497
|
const installsDir = getInstallsDir();
|
|
1534
|
-
if (!
|
|
2498
|
+
if (!existsSync10(installsDir)) {
|
|
1535
2499
|
mkdirSync4(installsDir, { recursive: true });
|
|
1536
2500
|
}
|
|
1537
2501
|
const recordPath = getSkillRecordPath();
|
|
1538
|
-
|
|
2502
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1539
2503
|
}
|
|
1540
2504
|
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
1541
2505
|
const record = readSkillRecord();
|
|
@@ -1576,16 +2540,13 @@ function getInstalledSkills(tool, level, installPath) {
|
|
|
1576
2540
|
const skillsDir = join9(configRoot, "skills");
|
|
1577
2541
|
return recordedSkills.filter((skillId) => {
|
|
1578
2542
|
const skillPath = join9(skillsDir, skillId);
|
|
1579
|
-
return
|
|
2543
|
+
return existsSync10(skillPath) || isSymlink(skillPath);
|
|
1580
2544
|
});
|
|
1581
2545
|
}
|
|
1582
2546
|
|
|
1583
2547
|
// src/core/backup.ts
|
|
1584
|
-
import { existsSync as
|
|
2548
|
+
import { existsSync as existsSync11, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
1585
2549
|
import { join as join10 } from "path";
|
|
1586
|
-
function getAgentsFileName(tool) {
|
|
1587
|
-
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1588
|
-
}
|
|
1589
2550
|
function detectCurrentPlugin(configRoot, tool) {
|
|
1590
2551
|
const manifest = readManifest(configRoot);
|
|
1591
2552
|
if (manifest?.team) {
|
|
@@ -1598,7 +2559,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1598
2559
|
}
|
|
1599
2560
|
}
|
|
1600
2561
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1601
|
-
if (!
|
|
2562
|
+
if (!existsSync11(agentsFile)) {
|
|
1602
2563
|
return null;
|
|
1603
2564
|
}
|
|
1604
2565
|
const allRecords = readAllRecords();
|
|
@@ -1617,7 +2578,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1617
2578
|
}
|
|
1618
2579
|
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1619
2580
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1620
|
-
if (!
|
|
2581
|
+
if (!existsSync11(agentsFile)) {
|
|
1621
2582
|
return null;
|
|
1622
2583
|
}
|
|
1623
2584
|
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
@@ -1636,7 +2597,7 @@ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
|
1636
2597
|
}
|
|
1637
2598
|
}
|
|
1638
2599
|
function findBackups(configRoot) {
|
|
1639
|
-
if (!
|
|
2600
|
+
if (!existsSync11(configRoot)) {
|
|
1640
2601
|
return [];
|
|
1641
2602
|
}
|
|
1642
2603
|
const files = readdirSync3(configRoot);
|
|
@@ -1674,8 +2635,8 @@ function restoreBackup(backupPath, configRoot, tool) {
|
|
|
1674
2635
|
}
|
|
1675
2636
|
function deleteBackup(backupPath) {
|
|
1676
2637
|
try {
|
|
1677
|
-
if (
|
|
1678
|
-
|
|
2638
|
+
if (existsSync11(backupPath)) {
|
|
2639
|
+
unlinkSync3(backupPath);
|
|
1679
2640
|
return true;
|
|
1680
2641
|
}
|
|
1681
2642
|
return false;
|
|
@@ -1694,20 +2655,9 @@ function formatTimestamp(date) {
|
|
|
1694
2655
|
}
|
|
1695
2656
|
function readAllRecords() {
|
|
1696
2657
|
const records = [];
|
|
1697
|
-
const plugins =
|
|
1698
|
-
|
|
1699
|
-
|
|
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);
|
|
2658
|
+
const plugins = getAllPlugins();
|
|
2659
|
+
for (const plugin of plugins) {
|
|
2660
|
+
const record = readRecord(plugin.id);
|
|
1711
2661
|
if (record) {
|
|
1712
2662
|
records.push(record);
|
|
1713
2663
|
}
|
|
@@ -1753,39 +2703,294 @@ async function showRestorePrompt(backups) {
|
|
|
1753
2703
|
return choice;
|
|
1754
2704
|
}
|
|
1755
2705
|
|
|
1756
|
-
// src/core/installer.ts
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2706
|
+
// src/core/plugin-installer.ts
|
|
2707
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, realpathSync, readlinkSync, copyFileSync as copyFileSync2, cpSync, writeFileSync as writeFileSync2, rmSync } from "fs";
|
|
2708
|
+
import { join as join11, basename } from "path";
|
|
2709
|
+
import { execa as execa3 } from "execa";
|
|
2710
|
+
function installLink(source, target) {
|
|
2711
|
+
try {
|
|
2712
|
+
const resolved = realpathSync(source);
|
|
2713
|
+
if (isSymlink(target)) {
|
|
2714
|
+
try {
|
|
2715
|
+
const currentTarget = readlinkSync(target);
|
|
2716
|
+
const normCurrent = currentTarget.replace(/\\/g, "/").toLowerCase();
|
|
2717
|
+
const normSource = resolved.replace(/\\/g, "/").toLowerCase();
|
|
2718
|
+
if (normCurrent === normSource) {
|
|
2719
|
+
return "skipped";
|
|
2720
|
+
}
|
|
2721
|
+
} catch {
|
|
2722
|
+
return "skipped";
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
if (existsSync12(target) || isSymlink(target)) {
|
|
2726
|
+
unlinkSync4(target);
|
|
2727
|
+
}
|
|
2728
|
+
try {
|
|
2729
|
+
symlinkSync(resolved, target);
|
|
2730
|
+
return "symlink";
|
|
2731
|
+
} catch (e) {
|
|
2732
|
+
if (e.code === "EPERM") {
|
|
2733
|
+
cpSync(resolved, target, { recursive: true });
|
|
2734
|
+
return "copy";
|
|
2735
|
+
}
|
|
2736
|
+
throw e;
|
|
2737
|
+
}
|
|
2738
|
+
} catch {
|
|
2739
|
+
return "failed";
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
function installConfigFile(pluginDir, configFile, tool, level, configRoot, installPath, configRootConfigLink) {
|
|
2743
|
+
const sourcePath = join11(pluginDir, configFile);
|
|
2744
|
+
if (!existsSync12(sourcePath)) return;
|
|
2745
|
+
const agentsFileName = getAgentsFileName(tool);
|
|
2746
|
+
const isProjectLevel = level === "project";
|
|
2747
|
+
let primaryTarget;
|
|
2748
|
+
if (isProjectLevel) {
|
|
2749
|
+
primaryTarget = join11(installPath, agentsFileName);
|
|
2750
|
+
} else {
|
|
2751
|
+
primaryTarget = join11(configRoot, agentsFileName);
|
|
2752
|
+
}
|
|
2753
|
+
try {
|
|
2754
|
+
const resolvedSource = realpathSync(sourcePath);
|
|
2755
|
+
if (existsSync12(primaryTarget) || isSymlink(primaryTarget)) {
|
|
2756
|
+
unlinkSync4(primaryTarget);
|
|
2757
|
+
}
|
|
2758
|
+
try {
|
|
2759
|
+
symlinkSync(resolvedSource, primaryTarget);
|
|
2760
|
+
} catch (e) {
|
|
2761
|
+
if (e.code === "EPERM") {
|
|
2762
|
+
copyFileSync2(sourcePath, primaryTarget);
|
|
2763
|
+
} else {
|
|
2764
|
+
throw e;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
} catch (e) {
|
|
2768
|
+
logger.warn(t("install_config_failed").replace("{error}", e.message));
|
|
2769
|
+
}
|
|
2770
|
+
if (isProjectLevel && primaryTarget !== join11(configRoot, agentsFileName)) {
|
|
2771
|
+
if (configRootConfigLink === false) return;
|
|
2772
|
+
const configRootTarget = join11(configRoot, agentsFileName);
|
|
2773
|
+
if (existsSync12(configRootTarget) || isSymlink(configRootTarget)) {
|
|
2774
|
+
unlinkSync4(configRootTarget);
|
|
2775
|
+
}
|
|
2776
|
+
try {
|
|
2777
|
+
symlinkSync(realpathSync(sourcePath), configRootTarget);
|
|
2778
|
+
} catch (e) {
|
|
2779
|
+
if (e.code === "EPERM") {
|
|
2780
|
+
copyFileSync2(sourcePath, configRootTarget);
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
async function installExternalRepos(repos, repoPath, configRoot, installPath, level) {
|
|
2786
|
+
if (!repos || repos.length === 0) return;
|
|
2787
|
+
for (const repo of repos) {
|
|
2788
|
+
const targetDir = join11(repoPath, repo.dir);
|
|
2789
|
+
if (!existsSync12(targetDir)) {
|
|
2790
|
+
const args = ["clone"];
|
|
2791
|
+
if (repo.depth) {
|
|
2792
|
+
args.push("--depth", String(repo.depth));
|
|
2793
|
+
}
|
|
2794
|
+
if (repo.recursive) {
|
|
2795
|
+
args.push("--recursive");
|
|
2796
|
+
}
|
|
2797
|
+
args.push(repo.url, targetDir);
|
|
2798
|
+
try {
|
|
2799
|
+
await execa3("git", args, { timeout: 12e4 });
|
|
2800
|
+
} catch {
|
|
2801
|
+
logger.warn(t("install_clone_failed").replace("{url}", repo.url).replace("{dir}", repo.dir));
|
|
2802
|
+
try {
|
|
2803
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
2804
|
+
} catch {
|
|
2805
|
+
}
|
|
2806
|
+
continue;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
const linkName = basename(repo.dir);
|
|
2810
|
+
const resolved = realpathSync(targetDir);
|
|
2811
|
+
const shouldConfigRootLink = repo.configRootLink !== false && (level === "global" || repo.configRootLink === true);
|
|
2812
|
+
if (shouldConfigRootLink) {
|
|
2813
|
+
const configRootLink = join11(configRoot, linkName);
|
|
2814
|
+
try {
|
|
2815
|
+
if (existsSync12(configRootLink) || isSymlink(configRootLink)) {
|
|
2816
|
+
unlinkSync4(configRootLink);
|
|
2817
|
+
}
|
|
2818
|
+
try {
|
|
2819
|
+
symlinkSync(resolved, configRootLink);
|
|
2820
|
+
} catch (e) {
|
|
2821
|
+
if (e.code === "EPERM") {
|
|
2822
|
+
cpSync(resolved, configRootLink, { recursive: true });
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
} catch {
|
|
2826
|
+
logger.warn(t("install_link_config_failed").replace("{name}", linkName));
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
if (level === "project") {
|
|
2830
|
+
const projectLink = join11(installPath, linkName);
|
|
2831
|
+
try {
|
|
2832
|
+
if (existsSync12(projectLink) || isSymlink(projectLink)) {
|
|
2833
|
+
unlinkSync4(projectLink);
|
|
2834
|
+
}
|
|
2835
|
+
try {
|
|
2836
|
+
symlinkSync(resolved, projectLink);
|
|
2837
|
+
} catch (e) {
|
|
2838
|
+
if (e.code === "EPERM") {
|
|
2839
|
+
cpSync(resolved, projectLink, { recursive: true });
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
} catch {
|
|
2843
|
+
logger.warn(t("install_link_project_failed").replace("{name}", linkName));
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
function installWorkflows(pluginDir, configRoot) {
|
|
2849
|
+
const workflowsDir = join11(pluginDir, "workflows");
|
|
2850
|
+
if (!existsSync12(workflowsDir)) return;
|
|
2851
|
+
const targetPath = join11(configRoot, "workflows");
|
|
2852
|
+
try {
|
|
2853
|
+
const resolved = realpathSync(workflowsDir);
|
|
2854
|
+
if (existsSync12(targetPath) || isSymlink(targetPath)) {
|
|
2855
|
+
unlinkSync4(targetPath);
|
|
2856
|
+
}
|
|
2857
|
+
try {
|
|
2858
|
+
symlinkSync(resolved, targetPath);
|
|
2859
|
+
} catch (e) {
|
|
2860
|
+
if (e.code === "EPERM") {
|
|
2861
|
+
cpSync(resolved, targetPath, { recursive: true });
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
} catch {
|
|
2865
|
+
logger.warn(t("install_link_workflows_failed"));
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
function writeCannbotManifest(configRoot, pluginId, version, level, tool, skills, agents) {
|
|
2869
|
+
const manifest = {
|
|
2870
|
+
brand: "CANNBot",
|
|
2871
|
+
version,
|
|
2872
|
+
team: pluginId,
|
|
2873
|
+
level,
|
|
2874
|
+
tool,
|
|
2875
|
+
installed_skills: skills,
|
|
2876
|
+
installed_agents: agents,
|
|
2877
|
+
brand_dir: configRoot,
|
|
2878
|
+
install_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2879
|
+
};
|
|
2880
|
+
const manifestPath = join11(configRoot, `${pluginId}-manifest.json`);
|
|
2881
|
+
writeFileSync2(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
2882
|
+
return manifest;
|
|
2883
|
+
}
|
|
2884
|
+
async function installViaManifest(plugin, repoPath, tool, level, installPath) {
|
|
2885
|
+
const cwd = installPath || process.cwd();
|
|
2886
|
+
const configRoot = getConfigRoot(tool, level, installPath);
|
|
2887
|
+
const pluginDir = join11(repoPath, plugin.dir);
|
|
2888
|
+
try {
|
|
2889
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2890
|
+
const agentsDir = join11(configRoot, "agents");
|
|
2891
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
2892
|
+
mkdirSync5(agentsDir, { recursive: true });
|
|
2893
|
+
const installedSkills = [];
|
|
2894
|
+
const installedAgents = [];
|
|
2895
|
+
for (const source of plugin.installSkills || []) {
|
|
2896
|
+
const sourceDir = join11(repoPath, source.dir);
|
|
2897
|
+
if (!existsSync12(sourceDir)) {
|
|
2898
|
+
logger.warn(t("install_skill_source_not_found").replace("{dir}", source.dir));
|
|
2899
|
+
continue;
|
|
2900
|
+
}
|
|
2901
|
+
for (const skillEntry of source.skills) {
|
|
2902
|
+
const skillName = typeof skillEntry === "string" ? skillEntry : skillEntry.name;
|
|
2903
|
+
const skillAlias = typeof skillEntry === "string" ? skillName : skillEntry.as || skillName;
|
|
2904
|
+
const skillPath = join11(sourceDir, skillName);
|
|
2905
|
+
if (!existsSync12(skillPath)) {
|
|
2906
|
+
logger.warn(t("install_skill_not_found").replace("{name}", skillName).replace("{dir}", source.dir));
|
|
2907
|
+
continue;
|
|
2908
|
+
}
|
|
2909
|
+
const targetPath = join11(skillsDir, skillAlias);
|
|
2910
|
+
const result = installLink(skillPath, targetPath);
|
|
2911
|
+
if (result !== "failed") {
|
|
2912
|
+
installedSkills.push(skillAlias);
|
|
2913
|
+
} else {
|
|
2914
|
+
logger.warn(t("install_skill_failed").replace("{name}", skillAlias));
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
for (const agentName of plugin.installAgents || []) {
|
|
2919
|
+
const agentFile = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
|
|
2920
|
+
const agentPath = join11(pluginDir, "agents", agentFile);
|
|
2921
|
+
if (!existsSync12(agentPath)) {
|
|
2922
|
+
logger.warn(t("install_agent_not_found").replace("{name}", agentFile));
|
|
2923
|
+
continue;
|
|
2924
|
+
}
|
|
2925
|
+
const targetPath = join11(agentsDir, agentFile);
|
|
2926
|
+
const result = installLink(agentPath, targetPath);
|
|
2927
|
+
if (result !== "failed") {
|
|
2928
|
+
installedAgents.push(agentFile);
|
|
2929
|
+
} else {
|
|
2930
|
+
logger.warn(t("install_agent_failed").replace("{name}", agentFile));
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
installConfigFile(pluginDir, plugin.configFile || "AGENTS.md", tool, level, configRoot, cwd, plugin.configRootConfigLink);
|
|
2934
|
+
installWorkflows(pluginDir, configRoot);
|
|
2935
|
+
await installExternalRepos(plugin.externalRepos, repoPath, configRoot, cwd, level);
|
|
2936
|
+
const cannbotManifest = writeCannbotManifest(
|
|
2937
|
+
configRoot,
|
|
2938
|
+
plugin.id,
|
|
2939
|
+
plugin.version || "1.0.0",
|
|
2940
|
+
level,
|
|
2941
|
+
tool,
|
|
2942
|
+
installedSkills,
|
|
2943
|
+
installedAgents
|
|
2944
|
+
);
|
|
2945
|
+
return {
|
|
2946
|
+
success: true,
|
|
2947
|
+
skillsCount: installedSkills.length,
|
|
2948
|
+
agentsCount: installedAgents.length,
|
|
2949
|
+
errors: [],
|
|
2950
|
+
manifest: cannbotManifest
|
|
2951
|
+
};
|
|
2952
|
+
} catch (error) {
|
|
1760
2953
|
return {
|
|
1761
2954
|
success: false,
|
|
1762
|
-
pluginId: opts.pluginId,
|
|
1763
2955
|
skillsCount: 0,
|
|
1764
2956
|
agentsCount: 0,
|
|
1765
|
-
errors: [
|
|
1766
|
-
|
|
2957
|
+
errors: [error instanceof Error ? error.message : t("error_unknown")],
|
|
2958
|
+
manifest: null
|
|
1767
2959
|
};
|
|
1768
2960
|
}
|
|
1769
|
-
|
|
1770
|
-
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
// src/core/installer.ts
|
|
2964
|
+
function findShell() {
|
|
2965
|
+
const candidates = ["bash", "sh"];
|
|
2966
|
+
for (const cmd of candidates) {
|
|
2967
|
+
try {
|
|
2968
|
+
const detectCmd = process.platform === "win32" ? "where" : "which";
|
|
2969
|
+
execaSync(detectCmd, [cmd], { timeout: 3e3 });
|
|
2970
|
+
return cmd;
|
|
2971
|
+
} catch {
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
return null;
|
|
2975
|
+
}
|
|
2976
|
+
async function installPlugin(opts) {
|
|
2977
|
+
const plugin = getPluginById(opts.pluginId);
|
|
2978
|
+
if (!plugin) {
|
|
1771
2979
|
return {
|
|
1772
2980
|
success: false,
|
|
1773
2981
|
pluginId: opts.pluginId,
|
|
1774
2982
|
skillsCount: 0,
|
|
1775
2983
|
agentsCount: 0,
|
|
1776
|
-
errors: [
|
|
2984
|
+
errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
|
|
1777
2985
|
warnings: []
|
|
1778
2986
|
};
|
|
1779
2987
|
}
|
|
1780
|
-
const
|
|
1781
|
-
if (opts.installPath) {
|
|
1782
|
-
args.push(opts.installPath);
|
|
1783
|
-
}
|
|
2988
|
+
const pluginDir = join12(opts.repoPath, plugin.dir);
|
|
1784
2989
|
const cwd = opts.installPath || process.cwd();
|
|
1785
2990
|
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1786
|
-
const agentsFile =
|
|
2991
|
+
const agentsFile = join12(configRoot, getAgentsFileName(opts.tool));
|
|
1787
2992
|
let backupInfo = null;
|
|
1788
|
-
if (
|
|
2993
|
+
if (existsSync13(agentsFile)) {
|
|
1789
2994
|
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
1790
2995
|
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
1791
2996
|
let choice = "overwrite";
|
|
@@ -1806,25 +3011,113 @@ async function installPlugin(opts) {
|
|
|
1806
3011
|
warnings: []
|
|
1807
3012
|
};
|
|
1808
3013
|
}
|
|
3014
|
+
}
|
|
3015
|
+
if (currentPlugin) {
|
|
1809
3016
|
backupInfo = createBackup(
|
|
1810
3017
|
configRoot,
|
|
1811
3018
|
opts.tool,
|
|
1812
3019
|
currentPlugin.pluginId,
|
|
1813
3020
|
currentPlugin.pluginName
|
|
1814
3021
|
);
|
|
1815
|
-
if (backupInfo) {
|
|
1816
|
-
logger.
|
|
3022
|
+
if (!backupInfo) {
|
|
3023
|
+
logger.error(t("backup_failed"));
|
|
3024
|
+
return {
|
|
3025
|
+
success: false,
|
|
3026
|
+
pluginId: opts.pluginId,
|
|
3027
|
+
skillsCount: 0,
|
|
3028
|
+
agentsCount: 0,
|
|
3029
|
+
errors: [t("backup_failed")],
|
|
3030
|
+
warnings: []
|
|
3031
|
+
};
|
|
3032
|
+
}
|
|
3033
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
if (plugin.installSkills && plugin.installSkills.length > 0) {
|
|
3037
|
+
const result = await installViaManifest(
|
|
3038
|
+
plugin,
|
|
3039
|
+
opts.repoPath,
|
|
3040
|
+
opts.tool,
|
|
3041
|
+
opts.level,
|
|
3042
|
+
opts.installPath
|
|
3043
|
+
);
|
|
3044
|
+
if (result.success) {
|
|
3045
|
+
try {
|
|
3046
|
+
const record = scanInstalledFiles(
|
|
3047
|
+
opts.pluginId,
|
|
3048
|
+
plugin.displayName,
|
|
3049
|
+
opts.tool,
|
|
3050
|
+
opts.level,
|
|
3051
|
+
cwd,
|
|
3052
|
+
configRoot,
|
|
3053
|
+
result.manifest,
|
|
3054
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
3055
|
+
);
|
|
3056
|
+
if (backupInfo) {
|
|
3057
|
+
record.backup = {
|
|
3058
|
+
filePath: backupInfo.filePath,
|
|
3059
|
+
fromPluginId: backupInfo.pluginId,
|
|
3060
|
+
fromPluginName: backupInfo.pluginName,
|
|
3061
|
+
backupTime: backupInfo.backupTime
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
writeRecord(record);
|
|
3065
|
+
} catch {
|
|
3066
|
+
logger.warn(t("record_write_failed"));
|
|
1817
3067
|
}
|
|
3068
|
+
return {
|
|
3069
|
+
success: true,
|
|
3070
|
+
pluginId: opts.pluginId,
|
|
3071
|
+
skillsCount: result.skillsCount,
|
|
3072
|
+
agentsCount: result.agentsCount,
|
|
3073
|
+
errors: result.errors,
|
|
3074
|
+
warnings: []
|
|
3075
|
+
};
|
|
3076
|
+
} else {
|
|
3077
|
+
return {
|
|
3078
|
+
success: false,
|
|
3079
|
+
pluginId: opts.pluginId,
|
|
3080
|
+
skillsCount: 0,
|
|
3081
|
+
agentsCount: 0,
|
|
3082
|
+
errors: result.errors,
|
|
3083
|
+
warnings: []
|
|
3084
|
+
};
|
|
1818
3085
|
}
|
|
1819
3086
|
}
|
|
3087
|
+
const scriptPath = join12(pluginDir, plugin.script);
|
|
3088
|
+
if (!existsSync13(scriptPath)) {
|
|
3089
|
+
return {
|
|
3090
|
+
success: false,
|
|
3091
|
+
pluginId: opts.pluginId,
|
|
3092
|
+
skillsCount: 0,
|
|
3093
|
+
agentsCount: 0,
|
|
3094
|
+
errors: [t("error_script_not_found").replace("{path}", scriptPath)],
|
|
3095
|
+
warnings: []
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
const args = [opts.level, opts.tool];
|
|
3099
|
+
if (opts.installPath) {
|
|
3100
|
+
args.push(opts.installPath);
|
|
3101
|
+
}
|
|
1820
3102
|
try {
|
|
3103
|
+
const shell = findShell();
|
|
3104
|
+
if (!shell) {
|
|
3105
|
+
return {
|
|
3106
|
+
success: false,
|
|
3107
|
+
pluginId: opts.pluginId,
|
|
3108
|
+
skillsCount: 0,
|
|
3109
|
+
agentsCount: 0,
|
|
3110
|
+
errors: [t("error_no_shell")],
|
|
3111
|
+
warnings: []
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
1821
3114
|
const execOptions = {
|
|
1822
3115
|
cwd,
|
|
1823
3116
|
timeout: 3e5,
|
|
1824
3117
|
stdio: "pipe",
|
|
1825
3118
|
input: opts.yes ? "y\n" : void 0
|
|
1826
3119
|
};
|
|
1827
|
-
await
|
|
3120
|
+
await execa4(shell, [scriptPath, ...args], execOptions);
|
|
1828
3121
|
const manifest = readManifest(configRoot);
|
|
1829
3122
|
let skillsCount = 0;
|
|
1830
3123
|
let agentsCount = 0;
|
|
@@ -1843,7 +3136,8 @@ async function installPlugin(opts) {
|
|
|
1843
3136
|
opts.level,
|
|
1844
3137
|
cwd,
|
|
1845
3138
|
configRoot,
|
|
1846
|
-
manifest
|
|
3139
|
+
manifest,
|
|
3140
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
1847
3141
|
);
|
|
1848
3142
|
if (backupInfo) {
|
|
1849
3143
|
record.backup = {
|
|
@@ -1855,6 +3149,7 @@ async function installPlugin(opts) {
|
|
|
1855
3149
|
}
|
|
1856
3150
|
writeRecord(record);
|
|
1857
3151
|
} catch {
|
|
3152
|
+
logger.warn(t("record_write_failed"));
|
|
1858
3153
|
}
|
|
1859
3154
|
return {
|
|
1860
3155
|
success: true,
|
|
@@ -1865,7 +3160,7 @@ async function installPlugin(opts) {
|
|
|
1865
3160
|
warnings: []
|
|
1866
3161
|
};
|
|
1867
3162
|
} catch (error) {
|
|
1868
|
-
const errorMessage = error instanceof Error ? error.message : "
|
|
3163
|
+
const errorMessage = error instanceof Error ? error.message : t("error_unknown");
|
|
1869
3164
|
return {
|
|
1870
3165
|
success: false,
|
|
1871
3166
|
pluginId: opts.pluginId,
|
|
@@ -1878,41 +3173,62 @@ async function installPlugin(opts) {
|
|
|
1878
3173
|
}
|
|
1879
3174
|
|
|
1880
3175
|
// src/core/skill-installer.ts
|
|
1881
|
-
import { existsSync as
|
|
1882
|
-
import { join as
|
|
1883
|
-
import { select as select3, checkbox
|
|
3176
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, unlinkSync as unlinkSync5, readdirSync as readdirSync4, rmSync as rmSync2, realpathSync as realpathSync2, readlinkSync as readlinkSync2 } from "fs";
|
|
3177
|
+
import { join as join13, dirname as dirname6, isAbsolute } from "path";
|
|
3178
|
+
import { select as select3, checkbox, Separator as Separator2 } from "@inquirer/prompts";
|
|
1884
3179
|
import chalk5 from "chalk";
|
|
1885
3180
|
import Table from "cli-table3";
|
|
1886
3181
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
1887
3182
|
const configRoot = getConfigRoot(tool, level);
|
|
1888
|
-
const skillsDir =
|
|
3183
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1889
3184
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1890
|
-
if (!
|
|
1891
|
-
|
|
3185
|
+
if (!existsSync14(skillsDir)) {
|
|
3186
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
1892
3187
|
}
|
|
1893
3188
|
const results = [];
|
|
1894
3189
|
const installedIds = [];
|
|
1895
3190
|
for (const skillId of skillIds) {
|
|
1896
3191
|
const skill = findSkill(skillId);
|
|
1897
3192
|
if (!skill) {
|
|
1898
|
-
results.push({ skillId, success: false, error: "
|
|
3193
|
+
results.push({ skillId, success: false, error: t("error_skill_not_found") });
|
|
1899
3194
|
continue;
|
|
1900
3195
|
}
|
|
1901
|
-
const sourcePath =
|
|
1902
|
-
const targetPath =
|
|
1903
|
-
if (!
|
|
1904
|
-
results.push({ skillId, success: false, error:
|
|
3196
|
+
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
3197
|
+
const targetPath = join13(skillsDir, skillId);
|
|
3198
|
+
if (!existsSync14(sourcePath)) {
|
|
3199
|
+
results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
|
|
1905
3200
|
continue;
|
|
1906
3201
|
}
|
|
1907
3202
|
try {
|
|
1908
|
-
|
|
1909
|
-
|
|
3203
|
+
const resolvedSource = realpathSync2(sourcePath);
|
|
3204
|
+
if (isSymlink(targetPath)) {
|
|
3205
|
+
try {
|
|
3206
|
+
const currentTarget = readlinkSync2(targetPath);
|
|
3207
|
+
const normCurrent = currentTarget.replace(/\\/g, "/").toLowerCase();
|
|
3208
|
+
const normSource = resolvedSource.replace(/\\/g, "/").toLowerCase();
|
|
3209
|
+
if (normCurrent === normSource) {
|
|
3210
|
+
results.push({ skillId, success: true });
|
|
3211
|
+
installedIds.push(skillId);
|
|
3212
|
+
continue;
|
|
3213
|
+
}
|
|
3214
|
+
} catch {
|
|
3215
|
+
results.push({ skillId, success: true });
|
|
3216
|
+
installedIds.push(skillId);
|
|
3217
|
+
continue;
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3221
|
+
unlinkSync5(targetPath);
|
|
1910
3222
|
}
|
|
1911
|
-
|
|
3223
|
+
symlinkSync2(resolvedSource, targetPath);
|
|
1912
3224
|
results.push({ skillId, success: true });
|
|
1913
3225
|
installedIds.push(skillId);
|
|
1914
3226
|
} catch (error) {
|
|
1915
|
-
|
|
3227
|
+
let msg = error instanceof Error ? error.message : t("error_unknown");
|
|
3228
|
+
if (process.platform === "win32" && msg.includes("EPERM")) {
|
|
3229
|
+
msg = t("error_symlink_windows");
|
|
3230
|
+
}
|
|
3231
|
+
results.push({ skillId, success: false, error: msg });
|
|
1916
3232
|
}
|
|
1917
3233
|
}
|
|
1918
3234
|
if (installedIds.length > 0) {
|
|
@@ -1922,34 +3238,34 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1922
3238
|
}
|
|
1923
3239
|
async function uninstallSkills(skillIds, tool, level) {
|
|
1924
3240
|
const configRoot = getConfigRoot(tool, level);
|
|
1925
|
-
const skillsDir =
|
|
3241
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1926
3242
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1927
3243
|
const results = [];
|
|
1928
3244
|
const removedIds = [];
|
|
1929
3245
|
for (const skillId of skillIds) {
|
|
1930
|
-
const targetPath =
|
|
3246
|
+
const targetPath = join13(skillsDir, skillId);
|
|
1931
3247
|
try {
|
|
1932
|
-
if (
|
|
1933
|
-
|
|
3248
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3249
|
+
unlinkSync5(targetPath);
|
|
1934
3250
|
results.push({ skillId, success: true });
|
|
1935
3251
|
removedIds.push(skillId);
|
|
1936
3252
|
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
1937
3253
|
} else {
|
|
1938
|
-
results.push({ skillId, success: false, error: "
|
|
3254
|
+
results.push({ skillId, success: false, error: t("error_not_installed") });
|
|
1939
3255
|
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
1940
3256
|
}
|
|
1941
3257
|
} catch (error) {
|
|
1942
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
3258
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
1943
3259
|
}
|
|
1944
3260
|
}
|
|
1945
3261
|
if (removedIds.length > 0) {
|
|
1946
3262
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
1947
3263
|
}
|
|
1948
|
-
if (
|
|
3264
|
+
if (existsSync14(skillsDir)) {
|
|
1949
3265
|
try {
|
|
1950
3266
|
const entries = readdirSync4(skillsDir);
|
|
1951
3267
|
if (entries.length === 0) {
|
|
1952
|
-
|
|
3268
|
+
rmSync2(skillsDir, { recursive: true });
|
|
1953
3269
|
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
1954
3270
|
}
|
|
1955
3271
|
} catch {
|
|
@@ -1961,8 +3277,6 @@ var SELECT_ALL = "__select_all__";
|
|
|
1961
3277
|
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
1962
3278
|
var CONTINUE_SELECT = "__continue_select__";
|
|
1963
3279
|
async function interactiveSkillSelect(tool, level) {
|
|
1964
|
-
const BACK3 = "__back__";
|
|
1965
|
-
const CANCEL3 = "__cancel__";
|
|
1966
3280
|
const categories = getAllCategories();
|
|
1967
3281
|
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
1968
3282
|
let accumulated = [];
|
|
@@ -1985,8 +3299,8 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
1985
3299
|
})),
|
|
1986
3300
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1987
3301
|
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
1988
|
-
{ name: "<- " + t("wizard_back"), value:
|
|
1989
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3302
|
+
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
3303
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
1990
3304
|
];
|
|
1991
3305
|
showOperationHints();
|
|
1992
3306
|
selectedCategoryId = await select3({
|
|
@@ -1996,8 +3310,8 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
1996
3310
|
theme: selectTheme,
|
|
1997
3311
|
pageSize: 15
|
|
1998
3312
|
});
|
|
1999
|
-
if (selectedCategoryId ===
|
|
2000
|
-
if (selectedCategoryId ===
|
|
3313
|
+
if (selectedCategoryId === BACK) return "back";
|
|
3314
|
+
if (selectedCategoryId === CANCEL) return "cancel";
|
|
2001
3315
|
if (selectedCategoryId === SELECT_ALL) {
|
|
2002
3316
|
return allSkillIds;
|
|
2003
3317
|
}
|
|
@@ -2037,7 +3351,7 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
2037
3351
|
})
|
|
2038
3352
|
];
|
|
2039
3353
|
showOperationHints(true);
|
|
2040
|
-
const selectedSkills = await
|
|
3354
|
+
const selectedSkills = await checkbox({
|
|
2041
3355
|
message: t("skill_select_items"),
|
|
2042
3356
|
choices: skillChoices,
|
|
2043
3357
|
loop: false,
|
|
@@ -2128,12 +3442,14 @@ function listAllSkills() {
|
|
|
2128
3442
|
console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
2129
3443
|
console.log();
|
|
2130
3444
|
}
|
|
2131
|
-
function
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
3445
|
+
function resolveSkillSourcePath(skill, repoPath) {
|
|
3446
|
+
if (skill.filePath) {
|
|
3447
|
+
const absPath = isAbsolute(skill.filePath) ? skill.filePath : join13(repoPath, skill.filePath);
|
|
3448
|
+
if (existsSync14(absPath)) {
|
|
3449
|
+
return dirname6(absPath);
|
|
3450
|
+
}
|
|
2136
3451
|
}
|
|
3452
|
+
return join13(repoPath, skill.source, skill.id);
|
|
2137
3453
|
}
|
|
2138
3454
|
|
|
2139
3455
|
// src/ui/display.ts
|
|
@@ -2145,10 +3461,11 @@ function printPluginList(plugins, installed) {
|
|
|
2145
3461
|
chalk6.cyan(t("list_id")),
|
|
2146
3462
|
chalk6.cyan(t("list_name")),
|
|
2147
3463
|
chalk6.cyan(t("list_status")),
|
|
2148
|
-
chalk6.cyan(t("
|
|
2149
|
-
chalk6.cyan(t("list_agents"))
|
|
3464
|
+
chalk6.cyan(t("list_description"))
|
|
2150
3465
|
],
|
|
2151
|
-
style: { head: [], border: [] }
|
|
3466
|
+
style: { head: [], border: [] },
|
|
3467
|
+
colWidths: [6, 32, 8, 50],
|
|
3468
|
+
wordWrap: true
|
|
2152
3469
|
});
|
|
2153
3470
|
plugins.forEach((plugin, index) => {
|
|
2154
3471
|
const isInstalled = installed.has(plugin.id);
|
|
@@ -2157,8 +3474,7 @@ function printPluginList(plugins, installed) {
|
|
|
2157
3474
|
String(index + 1),
|
|
2158
3475
|
plugin.displayName,
|
|
2159
3476
|
status,
|
|
2160
|
-
|
|
2161
|
-
String(plugin.agents)
|
|
3477
|
+
plugin.description || chalk6.dim("\u2014")
|
|
2162
3478
|
]);
|
|
2163
3479
|
});
|
|
2164
3480
|
console.log();
|
|
@@ -2225,15 +3541,13 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2225
3541
|
}
|
|
2226
3542
|
|
|
2227
3543
|
// src/commands/init.ts
|
|
2228
|
-
import { existsSync as
|
|
2229
|
-
import { join as
|
|
2230
|
-
var BACK2 = "__back__";
|
|
2231
|
-
var CANCEL2 = "__cancel__";
|
|
3544
|
+
import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
|
|
3545
|
+
import { join as join14 } from "path";
|
|
2232
3546
|
async function initCommand() {
|
|
2233
3547
|
const cwd = process.cwd();
|
|
2234
|
-
if (
|
|
3548
|
+
if (existsSync15(join14(cwd, "package.json")) && (() => {
|
|
2235
3549
|
try {
|
|
2236
|
-
const pkg = JSON.parse(readFileSync8(
|
|
3550
|
+
const pkg = JSON.parse(readFileSync8(join14(cwd, "package.json"), "utf-8"));
|
|
2237
3551
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2238
3552
|
} catch {
|
|
2239
3553
|
return false;
|
|
@@ -2287,7 +3601,6 @@ async function initCommand() {
|
|
|
2287
3601
|
}
|
|
2288
3602
|
async function selectMode() {
|
|
2289
3603
|
printBoxTitle(t("wizard_select_mode_title"));
|
|
2290
|
-
const CANCEL3 = "__cancel__";
|
|
2291
3604
|
const choices = [
|
|
2292
3605
|
{
|
|
2293
3606
|
name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
|
|
@@ -2298,7 +3611,7 @@ async function selectMode() {
|
|
|
2298
3611
|
value: "skill"
|
|
2299
3612
|
},
|
|
2300
3613
|
new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2301
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3614
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2302
3615
|
];
|
|
2303
3616
|
showOperationHints();
|
|
2304
3617
|
const result = await select4({
|
|
@@ -2307,7 +3620,7 @@ async function selectMode() {
|
|
|
2307
3620
|
loop: false,
|
|
2308
3621
|
theme: selectTheme
|
|
2309
3622
|
});
|
|
2310
|
-
if (result ===
|
|
3623
|
+
if (result === CANCEL) return null;
|
|
2311
3624
|
return result;
|
|
2312
3625
|
}
|
|
2313
3626
|
async function pluginInstallFlow() {
|
|
@@ -2384,11 +3697,11 @@ async function skillInstallFlow() {
|
|
|
2384
3697
|
}
|
|
2385
3698
|
case 1: {
|
|
2386
3699
|
const result = await stepLevel();
|
|
2387
|
-
if (result ===
|
|
3700
|
+
if (result === BACK) {
|
|
2388
3701
|
step = 0;
|
|
2389
3702
|
break;
|
|
2390
3703
|
}
|
|
2391
|
-
if (result ===
|
|
3704
|
+
if (result === CANCEL) {
|
|
2392
3705
|
logger.info(t("init_cancelled"));
|
|
2393
3706
|
return "done";
|
|
2394
3707
|
}
|
|
@@ -2435,7 +3748,7 @@ async function skillInstallFlow() {
|
|
|
2435
3748
|
logger.blank();
|
|
2436
3749
|
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
3750
|
logger.blank();
|
|
2438
|
-
logger.info(`${t("install_to")}: ${chalk7.cyan(configRoot
|
|
3751
|
+
logger.info(`${t("install_to")}: ${chalk7.cyan(join14(configRoot, "skills"))}`);
|
|
2439
3752
|
logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
|
|
2440
3753
|
logger.blank();
|
|
2441
3754
|
return "done";
|
|
@@ -2459,13 +3772,12 @@ async function listCommand() {
|
|
|
2459
3772
|
|
|
2460
3773
|
// src/commands/doctor.ts
|
|
2461
3774
|
import chalk8 from "chalk";
|
|
2462
|
-
import { existsSync as
|
|
2463
|
-
import { join as
|
|
3775
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
3776
|
+
import { join as join15 } from "path";
|
|
2464
3777
|
async function doctorCommand(options = {}) {
|
|
2465
3778
|
console.log();
|
|
2466
3779
|
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
2467
3780
|
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2468
|
-
let errors = 0;
|
|
2469
3781
|
let warnings = 0;
|
|
2470
3782
|
let fixes = 0;
|
|
2471
3783
|
console.log();
|
|
@@ -2503,7 +3815,7 @@ async function doctorCommand(options = {}) {
|
|
|
2503
3815
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2504
3816
|
const skillsDir = getSkillsDir(configRoot);
|
|
2505
3817
|
const agentsDir = getAgentsDir(configRoot);
|
|
2506
|
-
if (
|
|
3818
|
+
if (existsSync16(skillsDir)) {
|
|
2507
3819
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2508
3820
|
if (brokenLinks === 0) {
|
|
2509
3821
|
const count = readdirSync5(skillsDir).length;
|
|
@@ -2520,12 +3832,12 @@ async function doctorCommand(options = {}) {
|
|
|
2520
3832
|
} else {
|
|
2521
3833
|
console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2522
3834
|
if (options.fix) {
|
|
2523
|
-
|
|
3835
|
+
mkdirSync7(skillsDir, { recursive: true });
|
|
2524
3836
|
fixes++;
|
|
2525
3837
|
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2526
3838
|
}
|
|
2527
3839
|
}
|
|
2528
|
-
if (
|
|
3840
|
+
if (existsSync16(agentsDir)) {
|
|
2529
3841
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2530
3842
|
if (brokenLinks === 0) {
|
|
2531
3843
|
const count = readdirSync5(agentsDir).length;
|
|
@@ -2542,7 +3854,7 @@ async function doctorCommand(options = {}) {
|
|
|
2542
3854
|
} else {
|
|
2543
3855
|
console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2544
3856
|
if (options.fix) {
|
|
2545
|
-
|
|
3857
|
+
mkdirSync7(agentsDir, { recursive: true });
|
|
2546
3858
|
fixes++;
|
|
2547
3859
|
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2548
3860
|
}
|
|
@@ -2550,8 +3862,8 @@ async function doctorCommand(options = {}) {
|
|
|
2550
3862
|
console.log();
|
|
2551
3863
|
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2552
3864
|
const configFile = getConfigFileName(primaryTool);
|
|
2553
|
-
const configPath =
|
|
2554
|
-
if (
|
|
3865
|
+
const configPath = join15(process.cwd(), configFile);
|
|
3866
|
+
if (existsSync16(configPath)) {
|
|
2555
3867
|
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2556
3868
|
} else {
|
|
2557
3869
|
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
@@ -2560,11 +3872,11 @@ async function doctorCommand(options = {}) {
|
|
|
2560
3872
|
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2561
3873
|
if (options.fix && fixes > 0) {
|
|
2562
3874
|
console.log(
|
|
2563
|
-
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{
|
|
3875
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
|
|
2564
3876
|
);
|
|
2565
3877
|
} else {
|
|
2566
3878
|
console.log(
|
|
2567
|
-
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))
|
|
3879
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
|
|
2568
3880
|
);
|
|
2569
3881
|
}
|
|
2570
3882
|
console.log();
|
|
@@ -2574,11 +3886,11 @@ function checkBrokenLinks(dir) {
|
|
|
2574
3886
|
try {
|
|
2575
3887
|
const entries = readdirSync5(dir);
|
|
2576
3888
|
for (const entry of entries) {
|
|
2577
|
-
const fullPath =
|
|
3889
|
+
const fullPath = join15(dir, entry);
|
|
2578
3890
|
try {
|
|
2579
|
-
const stats =
|
|
3891
|
+
const stats = lstatSync2(fullPath);
|
|
2580
3892
|
if (stats.isSymbolicLink()) {
|
|
2581
|
-
if (!
|
|
3893
|
+
if (!existsSync16(fullPath)) {
|
|
2582
3894
|
broken++;
|
|
2583
3895
|
}
|
|
2584
3896
|
}
|
|
@@ -2595,12 +3907,12 @@ function fixBrokenLinks(dir) {
|
|
|
2595
3907
|
try {
|
|
2596
3908
|
const entries = readdirSync5(dir);
|
|
2597
3909
|
for (const entry of entries) {
|
|
2598
|
-
const fullPath =
|
|
3910
|
+
const fullPath = join15(dir, entry);
|
|
2599
3911
|
try {
|
|
2600
|
-
const stats =
|
|
3912
|
+
const stats = lstatSync2(fullPath);
|
|
2601
3913
|
if (stats.isSymbolicLink()) {
|
|
2602
|
-
if (!
|
|
2603
|
-
|
|
3914
|
+
if (!existsSync16(fullPath)) {
|
|
3915
|
+
unlinkSync6(fullPath);
|
|
2604
3916
|
fixed++;
|
|
2605
3917
|
}
|
|
2606
3918
|
}
|
|
@@ -2615,7 +3927,6 @@ function fixBrokenLinks(dir) {
|
|
|
2615
3927
|
// src/commands/status.ts
|
|
2616
3928
|
import chalk9 from "chalk";
|
|
2617
3929
|
async function statusCommand() {
|
|
2618
|
-
const plugins = getAllPlugins();
|
|
2619
3930
|
const installed = scanInstalled();
|
|
2620
3931
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2621
3932
|
console.log();
|
|
@@ -2641,6 +3952,7 @@ async function statusCommand() {
|
|
|
2641
3952
|
}
|
|
2642
3953
|
|
|
2643
3954
|
// src/commands/install.ts
|
|
3955
|
+
import { join as join16 } from "path";
|
|
2644
3956
|
import { confirm } from "@inquirer/prompts";
|
|
2645
3957
|
import chalk10 from "chalk";
|
|
2646
3958
|
async function installCommand(names, options) {
|
|
@@ -2674,7 +3986,10 @@ async function installCommand(names, options) {
|
|
|
2674
3986
|
tool2 = validateTool(options.tool);
|
|
2675
3987
|
} else {
|
|
2676
3988
|
const detected = await selectToolWithDetection();
|
|
2677
|
-
if (detected === "back" || detected === "cancel")
|
|
3989
|
+
if (detected === "back" || detected === "cancel") {
|
|
3990
|
+
logger.info(t("init_cancelled"));
|
|
3991
|
+
return;
|
|
3992
|
+
}
|
|
2678
3993
|
tool2 = detected;
|
|
2679
3994
|
}
|
|
2680
3995
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2703,7 +4018,7 @@ async function installCommand(names, options) {
|
|
|
2703
4018
|
logger.blank();
|
|
2704
4019
|
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
4020
|
logger.blank();
|
|
2706
|
-
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot
|
|
4021
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
|
|
2707
4022
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2708
4023
|
logger.blank();
|
|
2709
4024
|
return;
|
|
@@ -2723,7 +4038,10 @@ async function installCommand(names, options) {
|
|
|
2723
4038
|
tool2 = validateTool(options.tool);
|
|
2724
4039
|
} else {
|
|
2725
4040
|
const detected = await selectToolWithDetection();
|
|
2726
|
-
if (detected === "back" || detected === "cancel")
|
|
4041
|
+
if (detected === "back" || detected === "cancel") {
|
|
4042
|
+
logger.info(t("init_cancelled"));
|
|
4043
|
+
return;
|
|
4044
|
+
}
|
|
2727
4045
|
tool2 = detected;
|
|
2728
4046
|
}
|
|
2729
4047
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2734,11 +4052,17 @@ async function installCommand(names, options) {
|
|
|
2734
4052
|
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2735
4053
|
if (selectedSkills === "back") {
|
|
2736
4054
|
const redetected = await selectToolWithDetection();
|
|
2737
|
-
if (redetected === "back" || redetected === "cancel")
|
|
4055
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
4056
|
+
logger.info(t("init_cancelled"));
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
2738
4059
|
tool2 = redetected;
|
|
2739
4060
|
break;
|
|
2740
4061
|
}
|
|
2741
|
-
if (selectedSkills === "cancel")
|
|
4062
|
+
if (selectedSkills === "cancel") {
|
|
4063
|
+
logger.info(t("init_cancelled"));
|
|
4064
|
+
return;
|
|
4065
|
+
}
|
|
2742
4066
|
if (selectedSkills.length === 0) {
|
|
2743
4067
|
logger.info(t("install_no_skill_selected"));
|
|
2744
4068
|
return;
|
|
@@ -2769,7 +4093,7 @@ async function installCommand(names, options) {
|
|
|
2769
4093
|
logger.blank();
|
|
2770
4094
|
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
4095
|
logger.blank();
|
|
2772
|
-
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot
|
|
4096
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
|
|
2773
4097
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2774
4098
|
logger.blank();
|
|
2775
4099
|
return;
|
|
@@ -2813,7 +4137,10 @@ async function installCommand(names, options) {
|
|
|
2813
4137
|
tool = validateTool(options.tool);
|
|
2814
4138
|
} else {
|
|
2815
4139
|
const detected = await selectToolWithDetection();
|
|
2816
|
-
if (detected === "back" || detected === "cancel")
|
|
4140
|
+
if (detected === "back" || detected === "cancel") {
|
|
4141
|
+
logger.info(t("init_cancelled"));
|
|
4142
|
+
return;
|
|
4143
|
+
}
|
|
2817
4144
|
tool = detected;
|
|
2818
4145
|
}
|
|
2819
4146
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2896,7 +4223,7 @@ async function installCommand(names, options) {
|
|
|
2896
4223
|
logger.blank();
|
|
2897
4224
|
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
4225
|
logger.blank();
|
|
2899
|
-
logger.info(`${t("install_to")}: ${chalk10.cyan(getConfigRoot(tool, level)
|
|
4226
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(getConfigRoot(tool, level), "skills"))}`);
|
|
2900
4227
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
2901
4228
|
logger.blank();
|
|
2902
4229
|
}
|
|
@@ -2931,7 +4258,8 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
|
|
|
2931
4258
|
}
|
|
2932
4259
|
|
|
2933
4260
|
// src/commands/uninstall.ts
|
|
2934
|
-
import { existsSync as
|
|
4261
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync7, rmdirSync, readdirSync as readdirSync6 } from "fs";
|
|
4262
|
+
import { resolve, basename as basename3, sep } from "path";
|
|
2935
4263
|
import chalk11 from "chalk";
|
|
2936
4264
|
async function uninstallCommand(names, options) {
|
|
2937
4265
|
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
@@ -2961,31 +4289,37 @@ async function uninstallCommand(names, options) {
|
|
|
2961
4289
|
continue;
|
|
2962
4290
|
}
|
|
2963
4291
|
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
4292
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
4293
|
+
const allowedBases = [resolve(configRoot), resolve(record.installPath)];
|
|
4294
|
+
const isSafePath = (p) => {
|
|
4295
|
+
const resolved = resolve(p);
|
|
4296
|
+
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + sep));
|
|
4297
|
+
};
|
|
2964
4298
|
let removedFiles = 0;
|
|
2965
4299
|
let removedDirs = 0;
|
|
2966
4300
|
for (const filePath of record.files) {
|
|
4301
|
+
if (!isSafePath(filePath)) continue;
|
|
2967
4302
|
try {
|
|
2968
|
-
if (
|
|
2969
|
-
|
|
2970
|
-
const name = filePath
|
|
4303
|
+
if (existsSync17(filePath) || isSymlink(filePath)) {
|
|
4304
|
+
unlinkSync7(filePath);
|
|
4305
|
+
const name = basename3(filePath);
|
|
2971
4306
|
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2972
4307
|
removedFiles++;
|
|
2973
4308
|
}
|
|
2974
4309
|
} catch {
|
|
2975
4310
|
}
|
|
2976
4311
|
}
|
|
2977
|
-
const
|
|
2978
|
-
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
4312
|
+
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
2979
4313
|
for (const dirPath of sortedDirs) {
|
|
2980
4314
|
try {
|
|
2981
|
-
if (dirPath === configRoot) {
|
|
4315
|
+
if (resolve(dirPath) === resolve(configRoot)) {
|
|
2982
4316
|
continue;
|
|
2983
4317
|
}
|
|
2984
|
-
if (
|
|
4318
|
+
if (existsSync17(dirPath)) {
|
|
2985
4319
|
const entries = readdirSync6(dirPath);
|
|
2986
4320
|
if (entries.length === 0) {
|
|
2987
|
-
|
|
2988
|
-
const name = dirPath
|
|
4321
|
+
rmdirSync(dirPath);
|
|
4322
|
+
const name = basename3(dirPath);
|
|
2989
4323
|
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
2990
4324
|
removedDirs++;
|
|
2991
4325
|
}
|
|
@@ -3014,7 +4348,7 @@ async function uninstallCommand(names, options) {
|
|
|
3014
4348
|
}
|
|
3015
4349
|
const totalRemoved = removedFiles + removedDirs;
|
|
3016
4350
|
if (totalRemoved > 0) {
|
|
3017
|
-
logger.success(
|
|
4351
|
+
logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
|
|
3018
4352
|
} else {
|
|
3019
4353
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3020
4354
|
}
|
|
@@ -3036,13 +4370,6 @@ async function uninstallCommand(names, options) {
|
|
|
3036
4370
|
logger.blank();
|
|
3037
4371
|
}
|
|
3038
4372
|
}
|
|
3039
|
-
function isSymlink3(path) {
|
|
3040
|
-
try {
|
|
3041
|
-
return lstatSync4(path).isSymbolicLink();
|
|
3042
|
-
} catch {
|
|
3043
|
-
return false;
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
4373
|
|
|
3047
4374
|
// src/commands/update.ts
|
|
3048
4375
|
import chalk12 from "chalk";
|
|
@@ -3052,7 +4379,10 @@ async function updateCommand(pluginNames, options) {
|
|
|
3052
4379
|
tool = validateTool(options.tool);
|
|
3053
4380
|
} else {
|
|
3054
4381
|
const detected = await selectToolWithDetection();
|
|
3055
|
-
if (detected === "back" || detected === "cancel")
|
|
4382
|
+
if (detected === "back" || detected === "cancel") {
|
|
4383
|
+
logger.info(t("init_cancelled"));
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
3056
4386
|
tool = detected;
|
|
3057
4387
|
}
|
|
3058
4388
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -3079,9 +4409,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3079
4409
|
const repoManager = createRepositoryManager();
|
|
3080
4410
|
const updateSpinner = createSpinner(t("update_updating") + "...");
|
|
3081
4411
|
updateSpinner.start();
|
|
3082
|
-
await repoManager.
|
|
3083
|
-
const repoPath = repoManager.getRepoPath();
|
|
3084
|
-
await repoManager.ensureRepoAndScan();
|
|
4412
|
+
const repoPath = await repoManager.ensureRepoAndScan();
|
|
3085
4413
|
updateSpinner.succeed(t("install_repo_ready"));
|
|
3086
4414
|
const allPlugins = getAllPlugins();
|
|
3087
4415
|
const results = [];
|
|
@@ -3126,8 +4454,8 @@ async function updateCommand(pluginNames, options) {
|
|
|
3126
4454
|
|
|
3127
4455
|
// src/commands/info.ts
|
|
3128
4456
|
import chalk13 from "chalk";
|
|
3129
|
-
import { existsSync as
|
|
3130
|
-
import { join as
|
|
4457
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
|
|
4458
|
+
import { join as join17 } from "path";
|
|
3131
4459
|
async function infoCommand(pluginName) {
|
|
3132
4460
|
const plugin = findPlugin(pluginName);
|
|
3133
4461
|
if (!plugin) {
|
|
@@ -3135,7 +4463,8 @@ async function infoCommand(pluginName) {
|
|
|
3135
4463
|
return;
|
|
3136
4464
|
}
|
|
3137
4465
|
const installed = scanInstalled();
|
|
3138
|
-
const
|
|
4466
|
+
const inst = installed.find((p) => p.id === plugin.id);
|
|
4467
|
+
const isInstalled = !!inst;
|
|
3139
4468
|
const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
|
|
3140
4469
|
console.log();
|
|
3141
4470
|
console.log(chalk13.bold(` ${plugin.displayName}`));
|
|
@@ -3144,19 +4473,19 @@ async function infoCommand(pluginName) {
|
|
|
3144
4473
|
console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
3145
4474
|
console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
|
|
3146
4475
|
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}`);
|
|
4476
|
+
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${inst ? inst.skillsCount : plugin.skills}`);
|
|
4477
|
+
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${inst ? inst.agentsCount : plugin.agents}`);
|
|
3149
4478
|
console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
3150
4479
|
console.log();
|
|
3151
4480
|
try {
|
|
3152
4481
|
const repoManager = createRepositoryManager();
|
|
3153
4482
|
const repoPath = await repoManager.ensureRepo();
|
|
3154
|
-
const quickstartPath =
|
|
3155
|
-
if (
|
|
4483
|
+
const quickstartPath = join17(repoPath, plugin.dir, "quickstart.md");
|
|
4484
|
+
if (existsSync18(quickstartPath)) {
|
|
3156
4485
|
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
3157
4486
|
}
|
|
3158
|
-
const agentsDir =
|
|
3159
|
-
if (
|
|
4487
|
+
const agentsDir = join17(repoPath, plugin.dir, "agents");
|
|
4488
|
+
if (existsSync18(agentsDir)) {
|
|
3160
4489
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3161
4490
|
if (agentFiles.length > 0) {
|
|
3162
4491
|
console.log();
|
|
@@ -3208,13 +4537,44 @@ async function langCommand(action, value) {
|
|
|
3208
4537
|
}
|
|
3209
4538
|
|
|
3210
4539
|
// src/cli.ts
|
|
4540
|
+
import { readFileSync as readFileSync9, existsSync as existsSync19 } from "fs";
|
|
4541
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
4542
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
4543
|
+
function readVersion() {
|
|
4544
|
+
try {
|
|
4545
|
+
if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
|
|
4546
|
+
return INSTALL_HELPER_VERSION;
|
|
4547
|
+
}
|
|
4548
|
+
} catch {
|
|
4549
|
+
}
|
|
4550
|
+
try {
|
|
4551
|
+
const __filename5 = fileURLToPath5(import.meta.url);
|
|
4552
|
+
const __dirname5 = dirname7(__filename5);
|
|
4553
|
+
const candidates = [
|
|
4554
|
+
join18(__dirname5, "..", "package.json"),
|
|
4555
|
+
join18(__dirname5, "package.json"),
|
|
4556
|
+
join18(process.cwd(), "package.json")
|
|
4557
|
+
];
|
|
4558
|
+
for (const pkgPath of candidates) {
|
|
4559
|
+
if (existsSync19(pkgPath)) {
|
|
4560
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
4561
|
+
if (pkg.version && (pkg.name === "@cannbot-ai/install-helper" || pkg.name?.startsWith("@cannbot-ai/install-helper-"))) {
|
|
4562
|
+
return pkg.version;
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
return "0.0.0";
|
|
4567
|
+
} catch {
|
|
4568
|
+
return "0.0.0";
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
3211
4571
|
function createCLI() {
|
|
3212
4572
|
const config = readConfig();
|
|
3213
4573
|
if (config.language) {
|
|
3214
4574
|
setLanguage(config.language);
|
|
3215
4575
|
}
|
|
3216
4576
|
const program2 = new Command();
|
|
3217
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(
|
|
4577
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
|
|
3218
4578
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3219
4579
|
await initCommand();
|
|
3220
4580
|
});
|
|
@@ -3249,13 +4609,28 @@ function createCLI() {
|
|
|
3249
4609
|
}
|
|
3250
4610
|
|
|
3251
4611
|
// src/index.ts
|
|
3252
|
-
|
|
3253
|
-
if (error
|
|
4612
|
+
function isExitError(error) {
|
|
4613
|
+
if (!error) return false;
|
|
4614
|
+
return error.name === "ExitPromptError" || error.message?.includes("force closed") || error.message?.includes("SIGINT") || error.constructor?.name === "ExitPromptError";
|
|
4615
|
+
}
|
|
4616
|
+
function handleExit(error) {
|
|
4617
|
+
if (isExitError(error)) {
|
|
3254
4618
|
console.log("\n" + t("init_cancelled"));
|
|
3255
4619
|
process.exit(0);
|
|
3256
4620
|
}
|
|
4621
|
+
}
|
|
4622
|
+
process.on("SIGINT", () => {
|
|
4623
|
+
console.log("\n" + t("init_cancelled"));
|
|
4624
|
+
process.exit(130);
|
|
4625
|
+
});
|
|
4626
|
+
process.on("uncaughtException", (error) => {
|
|
4627
|
+
handleExit(error);
|
|
3257
4628
|
throw error;
|
|
3258
4629
|
});
|
|
4630
|
+
process.on("unhandledRejection", (error) => {
|
|
4631
|
+
handleExit(error);
|
|
4632
|
+
process.exit(1);
|
|
4633
|
+
});
|
|
3259
4634
|
var program = createCLI();
|
|
3260
4635
|
program.parse();
|
|
3261
4636
|
//# sourceMappingURL=index.js.map
|