@cannbot-ai/install-helper 1.0.7 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +1831 -482
- package/dist/index.js.map +1 -1
- package/dist/install.ps1 +232 -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 +11 -0
- package/dist/plugins.d/model-infer-optimize.yml +12 -0
- package/dist/plugins.d/ops-code-reviewer.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +11 -0
- package/dist/plugins.d/ops-direct-invoke.yml +14 -1
- package/dist/plugins.d/ops-registry-invoke.yml +17 -0
- 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 +7 -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: 16,
|
|
599
|
+
agents: 3,
|
|
600
|
+
description: "Ascend C \u76F4\u8C03\u5168\u6D41\u7A0B\uFF1A\u73AF\u5883\u2192\u8BBE\u8BA1\u2192\u5F00\u53D1\u2192\u5BA1\u67E5\u2192\u6027\u80FD\u9A8C\u6536",
|
|
601
|
+
version: "1.2.2",
|
|
602
|
+
configFile: "AGENTS.md",
|
|
603
|
+
configRootConfigLink: false,
|
|
604
|
+
installSkills: [
|
|
605
|
+
{
|
|
606
|
+
dir: "ops",
|
|
607
|
+
skills: [
|
|
608
|
+
"ascendc-tiling-design",
|
|
609
|
+
"npu-arch",
|
|
610
|
+
"ascendc-api-best-practices",
|
|
611
|
+
"ascendc-regbase-best-practice",
|
|
612
|
+
"ascendc-blaze-best-practice",
|
|
613
|
+
"ops-precision-standard",
|
|
614
|
+
"ascendc-docs-search",
|
|
615
|
+
"ascendc-env-check",
|
|
616
|
+
"ascendc-precision-debug",
|
|
617
|
+
"ops-profiling",
|
|
618
|
+
"ascendc-direct-invoke-template",
|
|
619
|
+
"torch-ascendc-op-extension",
|
|
620
|
+
"ascendc-runtime-debug",
|
|
621
|
+
"ascendc-crash-debug",
|
|
622
|
+
"ascendc-code-review"
|
|
623
|
+
]
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
dir: "infra",
|
|
627
|
+
skills: [
|
|
628
|
+
"gitcode-toolkit",
|
|
629
|
+
"gitcode-pr-handler",
|
|
630
|
+
"gitcode-issue-gen",
|
|
631
|
+
"gitcode-issue-handler"
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
],
|
|
635
|
+
installAgents: [
|
|
636
|
+
"ascendc-kernel-architect",
|
|
637
|
+
"ascendc-kernel-design-reviewer",
|
|
638
|
+
"ascendc-kernel-developer",
|
|
639
|
+
"ascendc-kernel-reviewer"
|
|
640
|
+
],
|
|
641
|
+
externalRepos: [
|
|
642
|
+
{
|
|
643
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
644
|
+
dir: "plugins-official/ops-direct-invoke/asc-devkit",
|
|
645
|
+
configRootLink: false
|
|
646
|
+
}
|
|
647
|
+
]
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
id: "ops-direct-invoke-flash",
|
|
651
|
+
dir: "plugins-official/ops-direct-invoke-flash",
|
|
652
|
+
displayName: "AscendC Kernel \u76F4\u8C03\uFF08\u5FEB\u901F\u7248\uFF09",
|
|
653
|
+
script: "init.sh",
|
|
654
|
+
aliases: [
|
|
655
|
+
"flash",
|
|
656
|
+
"kernel-flash"
|
|
657
|
+
],
|
|
658
|
+
skills: 1,
|
|
659
|
+
agents: 1,
|
|
660
|
+
description: "\u4ECE\u6570\u5B66\u516C\u5F0F\u6216\u4EE3\u7801\u7247\u6BB5\u5FEB\u901F\u6784\u5EFA\u5E76\u9A8C\u8BC1 Ascend NPU \u6838\u51FD\u6570",
|
|
661
|
+
version: "1.2.2",
|
|
662
|
+
configFile: "AGENTS.md",
|
|
663
|
+
configRootConfigLink: true,
|
|
664
|
+
installSkills: [
|
|
665
|
+
{
|
|
666
|
+
dir: "plugins-official/ops-direct-invoke-flash/skills",
|
|
667
|
+
skills: [
|
|
668
|
+
"ops-direct-invoke-flash"
|
|
669
|
+
]
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
dir: "ops",
|
|
673
|
+
skills: [
|
|
674
|
+
"ascendc-st-design",
|
|
675
|
+
"ascendc-whitebox-design"
|
|
676
|
+
]
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
dir: "infra",
|
|
680
|
+
skills: [
|
|
681
|
+
"gitcode-toolkit",
|
|
682
|
+
"gitcode-pr-handler",
|
|
683
|
+
"gitcode-issue-gen",
|
|
684
|
+
"gitcode-issue-handler"
|
|
685
|
+
]
|
|
686
|
+
}
|
|
687
|
+
],
|
|
688
|
+
installAgents: [
|
|
689
|
+
"ops-direct-invoke-flash-reviewer"
|
|
690
|
+
]
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
id: "ops-registry-invoke",
|
|
694
|
+
dir: "plugins-official/ops-registry-invoke",
|
|
695
|
+
displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
|
|
696
|
+
script: "init.sh",
|
|
697
|
+
aliases: [
|
|
698
|
+
"ops-registry",
|
|
699
|
+
"ascendc-registry",
|
|
700
|
+
"registry"
|
|
701
|
+
],
|
|
702
|
+
skills: 12,
|
|
703
|
+
agents: 4,
|
|
704
|
+
description: "\u751F\u4EA7\u7EA7 ACLNN \u6CE8\u518C\u7B97\u5B50\u5168\u6D41\u7A0B\uFF1A\u4EE3\u7801\u2192UT/ST\u2192\u4E0A\u5E93",
|
|
705
|
+
version: "1.2.2",
|
|
706
|
+
configFile: "AGENTS.md",
|
|
707
|
+
configRootConfigLink: false,
|
|
708
|
+
installSkills: [
|
|
709
|
+
{
|
|
710
|
+
dir: "ops",
|
|
711
|
+
skills: [
|
|
712
|
+
"ascendc-blaze-best-practice",
|
|
713
|
+
"ascendc-api-best-practices",
|
|
714
|
+
"ascendc-code-review",
|
|
715
|
+
"ascendc-crash-debug",
|
|
716
|
+
"ascendc-direct-invoke-template",
|
|
717
|
+
"ascendc-docs-gen",
|
|
718
|
+
"ascendc-docs-search",
|
|
719
|
+
"ascendc-env-check",
|
|
720
|
+
"npu-arch",
|
|
721
|
+
"ascendc-performance-best-practices",
|
|
722
|
+
"ascendc-precision-debug",
|
|
723
|
+
"ascendc-regbase-best-practice",
|
|
724
|
+
"ascendc-registry-invoke-template",
|
|
725
|
+
"ascendc-runtime-debug",
|
|
726
|
+
"ascendc-st-design",
|
|
727
|
+
"ascendc-tiling-design",
|
|
728
|
+
"ascendc-ut-develop",
|
|
729
|
+
"ascendc-whitebox-design",
|
|
730
|
+
"ops-precision-standard",
|
|
731
|
+
"ops-profiling",
|
|
732
|
+
"ops-spec-gen"
|
|
733
|
+
]
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
dir: "infra",
|
|
737
|
+
skills: [
|
|
738
|
+
"gitcode-toolkit",
|
|
739
|
+
"gitcode-pr-handler",
|
|
740
|
+
"gitcode-issue-gen",
|
|
741
|
+
"gitcode-issue-handler"
|
|
742
|
+
]
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
dir: "plugins-official/ops-registry-invoke",
|
|
746
|
+
skills: [
|
|
747
|
+
{
|
|
748
|
+
name: "workflow",
|
|
749
|
+
as: "ops-registry-invoke-workflow"
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
}
|
|
753
|
+
],
|
|
754
|
+
installAgents: [
|
|
755
|
+
"ascendc-ops-architect",
|
|
756
|
+
"ascendc-ops-developer",
|
|
757
|
+
"ascendc-ops-tester"
|
|
758
|
+
],
|
|
759
|
+
externalRepos: [
|
|
760
|
+
{
|
|
761
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
762
|
+
dir: "plugins-official/ops-registry-invoke/asc-devkit",
|
|
763
|
+
configRootLink: false
|
|
764
|
+
}
|
|
765
|
+
]
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
id: "catlass-op-generator",
|
|
769
|
+
dir: "plugins-official/catlass-op-generator",
|
|
770
|
+
displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
|
|
771
|
+
script: "init.sh",
|
|
772
|
+
aliases: [
|
|
773
|
+
"catlass"
|
|
774
|
+
],
|
|
775
|
+
skills: 10,
|
|
776
|
+
agents: 3,
|
|
777
|
+
description: "Catlass \u6A21\u677F\u62FC\u88C5 GEMM/Matmul/Epilogue \u7B97\u5B50\u5F00\u53D1",
|
|
778
|
+
version: "1.2.2",
|
|
779
|
+
configFile: "AGENTS.md",
|
|
780
|
+
configRootConfigLink: true,
|
|
781
|
+
installSkills: [
|
|
782
|
+
{
|
|
783
|
+
dir: "ops",
|
|
784
|
+
skills: [
|
|
785
|
+
"catlass-op-design",
|
|
786
|
+
"catlass-op-develop",
|
|
787
|
+
"catlass-op-perf-tune",
|
|
788
|
+
"ascendc-tiling-design",
|
|
789
|
+
"npu-arch",
|
|
790
|
+
"ascendc-api-best-practices",
|
|
791
|
+
"ops-precision-standard",
|
|
792
|
+
"ascendc-docs-search",
|
|
793
|
+
"ascendc-env-check",
|
|
794
|
+
"ascendc-precision-debug",
|
|
795
|
+
"ops-profiling",
|
|
796
|
+
"ascendc-runtime-debug",
|
|
797
|
+
"ascendc-code-review",
|
|
798
|
+
"torch-ascendc-op-extension"
|
|
799
|
+
]
|
|
800
|
+
}
|
|
801
|
+
],
|
|
802
|
+
installAgents: [
|
|
803
|
+
"catlass-op-architect",
|
|
804
|
+
"catlass-op-generator",
|
|
805
|
+
"catlass-op-reviewer"
|
|
806
|
+
],
|
|
807
|
+
externalRepos: [
|
|
808
|
+
{
|
|
809
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
810
|
+
dir: "plugins-official/catlass-op-generator/asc-devkit",
|
|
811
|
+
configRootLink: false
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
id: "model-infer-optimize",
|
|
817
|
+
dir: "plugins-official/model-infer-optimize",
|
|
818
|
+
displayName: "NPU \u63A8\u7406\u4F18\u5316",
|
|
819
|
+
script: "init.sh",
|
|
820
|
+
aliases: [
|
|
821
|
+
"model-infer",
|
|
822
|
+
"infer",
|
|
823
|
+
"inference"
|
|
824
|
+
],
|
|
825
|
+
skills: 11,
|
|
826
|
+
agents: 3,
|
|
827
|
+
description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316\uFF08\u5E76\u884C/KVCache/\u878D\u5408/\u91CF\u5316/\u56FE\u6A21\u5F0F/\u591A\u6D41/SuperKernel\uFF09",
|
|
828
|
+
version: "1.2.2",
|
|
829
|
+
configFile: "AGENTS.md",
|
|
830
|
+
configRootConfigLink: true,
|
|
831
|
+
installSkills: [
|
|
832
|
+
{
|
|
833
|
+
dir: "model",
|
|
834
|
+
skills: [
|
|
835
|
+
"model-infer-migrator",
|
|
836
|
+
"model-infer-parallel-analysis",
|
|
837
|
+
"model-infer-parallel-impl",
|
|
838
|
+
"model-infer-kvcache",
|
|
839
|
+
"model-infer-fusion",
|
|
840
|
+
"model-infer-quantization",
|
|
841
|
+
"model-infer-graph-mode",
|
|
842
|
+
"model-infer-precision-debug",
|
|
843
|
+
"model-infer-runtime-debug",
|
|
844
|
+
"model-infer-multi-stream",
|
|
845
|
+
"model-infer-prefetch",
|
|
846
|
+
"model-infer-superkernel"
|
|
847
|
+
]
|
|
848
|
+
}
|
|
849
|
+
],
|
|
850
|
+
installAgents: [
|
|
851
|
+
"model-infer-analyzer",
|
|
852
|
+
"model-infer-implementer",
|
|
853
|
+
"model-infer-reviewer"
|
|
854
|
+
],
|
|
855
|
+
externalRepos: [
|
|
856
|
+
{
|
|
857
|
+
url: "https://gitcode.com/cann/cann-recipes-infer.git",
|
|
858
|
+
dir: "plugins-official/model-infer-optimize/cann-recipes-infer",
|
|
859
|
+
depth: 1,
|
|
860
|
+
configRootLink: true
|
|
861
|
+
}
|
|
862
|
+
]
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
id: "pypto-op-orchestrator",
|
|
866
|
+
dir: "plugins-official/pypto-op-orchestrator",
|
|
867
|
+
displayName: "PyPTO \u7B97\u5B50\u5F00\u53D1",
|
|
868
|
+
script: "init.sh",
|
|
869
|
+
aliases: [
|
|
870
|
+
"pypto",
|
|
871
|
+
"pytorch"
|
|
872
|
+
],
|
|
873
|
+
skills: 8,
|
|
874
|
+
agents: 3,
|
|
875
|
+
description: "PyPTO Python API \u7B97\u5B50\u5168\u6D41\u7A0B\u5F00\u53D1\uFF0C\u8986\u76D6\u9700\u6C42\u7406\u89E3\u3001API \u63A2\u7D22\u3001\u5B9E\u73B0\u4E0E\u8C03\u4F18",
|
|
876
|
+
version: "1.2.2",
|
|
877
|
+
configFile: "AGENTS.md",
|
|
878
|
+
configRootConfigLink: false,
|
|
879
|
+
installSkills: [
|
|
880
|
+
{
|
|
881
|
+
dir: "ops",
|
|
882
|
+
skills: [
|
|
883
|
+
"pypto-intent-understand",
|
|
884
|
+
"pypto-api-explore",
|
|
885
|
+
"pypto-op-design",
|
|
886
|
+
"pypto-op-develop",
|
|
887
|
+
"pypto-precision-debug",
|
|
888
|
+
"pypto-precision-compare",
|
|
889
|
+
"pypto-op-perf-tune",
|
|
890
|
+
"pypto-golden-generate"
|
|
891
|
+
]
|
|
892
|
+
}
|
|
893
|
+
],
|
|
894
|
+
installAgents: [
|
|
895
|
+
"pypto-op-analyst",
|
|
896
|
+
"pypto-op-developer",
|
|
897
|
+
"pypto-op-perf-tuner"
|
|
898
|
+
],
|
|
899
|
+
externalRepos: [
|
|
900
|
+
{
|
|
901
|
+
url: "https://gitcode.com/cann/pypto.git",
|
|
902
|
+
dir: "plugins-official/pypto-op-orchestrator/pypto",
|
|
903
|
+
configRootLink: false
|
|
904
|
+
}
|
|
905
|
+
]
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
id: "tilelang-op-orchestrator",
|
|
909
|
+
dir: "plugins-official/tilelang-op-orchestrator",
|
|
910
|
+
displayName: "TileLang \u7B97\u5B50\u5F00\u53D1",
|
|
911
|
+
script: "init.sh",
|
|
912
|
+
aliases: [
|
|
913
|
+
"tilelang"
|
|
914
|
+
],
|
|
915
|
+
skills: 9,
|
|
916
|
+
agents: 3,
|
|
917
|
+
description: "TileLang Python DSL + @tilelang.jit \u7B97\u5B50\u5F00\u53D1\uFF08\u652F\u6301 Developer/Expert \u53CC\u6A21\u5F0F\uFF09",
|
|
918
|
+
version: "1.2.2",
|
|
919
|
+
configFile: "AGENTS.md",
|
|
920
|
+
configRootConfigLink: false,
|
|
921
|
+
installSkills: [
|
|
922
|
+
{
|
|
923
|
+
dir: "ops",
|
|
924
|
+
skills: [
|
|
925
|
+
"tilelang-api-best-practices",
|
|
926
|
+
"tilelang-env-check",
|
|
927
|
+
"tilelang-op-design",
|
|
928
|
+
"tilelang-op-develop",
|
|
929
|
+
"tilelang-op-test-design",
|
|
930
|
+
"tilelang-perf-optimization",
|
|
931
|
+
"tilelang-programming-model-guide",
|
|
932
|
+
"tilelang-review",
|
|
933
|
+
"tilelang-submodule-pull"
|
|
934
|
+
]
|
|
935
|
+
}
|
|
936
|
+
],
|
|
937
|
+
installAgents: [
|
|
938
|
+
"tilelang-op-analyst",
|
|
939
|
+
"tilelang-op-developer",
|
|
940
|
+
"tilelang-op-perf-tuner"
|
|
941
|
+
],
|
|
942
|
+
externalRepos: [
|
|
943
|
+
{
|
|
944
|
+
url: "https://github.com/tile-ai/tilelang-ascend.git",
|
|
945
|
+
dir: "plugins-official/tilelang-op-orchestrator/tilelang-ascend",
|
|
946
|
+
depth: 1,
|
|
947
|
+
recursive: true,
|
|
948
|
+
configRootLink: false
|
|
949
|
+
}
|
|
950
|
+
]
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
id: "triton-op-generator",
|
|
954
|
+
dir: "plugins-official/triton-op-generator",
|
|
955
|
+
displayName: "Triton \u7B97\u5B50\u5F00\u53D1",
|
|
956
|
+
script: "init.sh",
|
|
957
|
+
aliases: [
|
|
958
|
+
"triton"
|
|
959
|
+
],
|
|
960
|
+
skills: 6,
|
|
961
|
+
agents: 1,
|
|
962
|
+
description: "Triton-Ascend \u7B97\u5B50 6 \u9636\u6BB5\u81EA\u52A8\u751F\u6210\u4E0E\u4F18\u5316\uFF08\u63CF\u8FF0\u2192\u4EE3\u7801\u2192\u9A8C\u8BC1\u2192\u4F18\u5316\uFF09",
|
|
963
|
+
version: "1.2.2",
|
|
964
|
+
configFile: "AGENTS.md",
|
|
965
|
+
configRootConfigLink: false,
|
|
966
|
+
installSkills: [
|
|
967
|
+
{
|
|
968
|
+
dir: "ops",
|
|
969
|
+
skills: [
|
|
970
|
+
"triton-task-extractor",
|
|
971
|
+
"triton-op-designer",
|
|
972
|
+
"triton-op-coding",
|
|
973
|
+
"triton-op-verifier",
|
|
974
|
+
"triton-latency-optimizer",
|
|
975
|
+
"npu-arch"
|
|
976
|
+
]
|
|
977
|
+
}
|
|
978
|
+
],
|
|
979
|
+
installAgents: []
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
id: "torch-compile",
|
|
983
|
+
dir: "plugins-official/torch-compile",
|
|
984
|
+
displayName: "torch.compile \u56FE\u6A21\u5F0F",
|
|
985
|
+
script: "init.sh",
|
|
986
|
+
aliases: [
|
|
987
|
+
"torch",
|
|
988
|
+
"compile",
|
|
989
|
+
"graph"
|
|
990
|
+
],
|
|
991
|
+
skills: 6,
|
|
992
|
+
agents: 1,
|
|
993
|
+
description: "NPU \u56FE\u6A21\u5F0F npugraph_ex \u914D\u7F6E\u3001\u811A\u672C\u8FC1\u79FB\u3001\u7F16\u8BD1\u8BCA\u65AD\u4E0E\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE",
|
|
994
|
+
version: "1.2.2",
|
|
995
|
+
configFile: "AGENTS.md",
|
|
996
|
+
configRootConfigLink: true,
|
|
997
|
+
installSkills: [
|
|
998
|
+
{
|
|
999
|
+
dir: "graph",
|
|
1000
|
+
skills: [
|
|
1001
|
+
"torch-npugraph-ex-knowledge",
|
|
1002
|
+
"torch-npugraph-ex-template",
|
|
1003
|
+
"torch-npugraph-ex-dfx-triage",
|
|
1004
|
+
"torch-npugraph-ex-compile-error-diagnosis",
|
|
1005
|
+
"torch-npugraph-ex-runtime-error-diagnosis",
|
|
1006
|
+
"torch-custom-ops-guide"
|
|
1007
|
+
]
|
|
1008
|
+
}
|
|
1009
|
+
],
|
|
1010
|
+
installAgents: [
|
|
1011
|
+
"torch-npugraph-ex"
|
|
1012
|
+
]
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
id: "ops-code-reviewer",
|
|
1016
|
+
dir: "plugins-official/ops-code-reviewer",
|
|
1017
|
+
displayName: "\u4EE3\u7801\u68C0\u89C6",
|
|
1018
|
+
script: "init.sh",
|
|
1019
|
+
aliases: [
|
|
1020
|
+
"code-review",
|
|
1021
|
+
"reviewer",
|
|
1022
|
+
"review"
|
|
1023
|
+
],
|
|
1024
|
+
skills: 5,
|
|
1025
|
+
agents: 1,
|
|
1026
|
+
description: "Ascend C \u7B97\u5B50\u4EE3\u7801 4 \u9636\u6BB5\u5168\u91CF\u68C0\u89C6\uFF08\u4E3B Agent \u7F16\u6392 + \u5B50 Agent \u5E76\u884C\u68C0\u89C6\uFF09",
|
|
1027
|
+
version: "1.2.2",
|
|
1028
|
+
configFile: "AGENTS.md",
|
|
1029
|
+
configRootConfigLink: false,
|
|
1030
|
+
installSkills: [
|
|
1031
|
+
{
|
|
1032
|
+
dir: "ops",
|
|
1033
|
+
skills: [
|
|
1034
|
+
"ascendc-code-review",
|
|
1035
|
+
"ascendc-docs-search",
|
|
1036
|
+
"ascendc-task-focus",
|
|
1037
|
+
"ascendc-api-best-practices",
|
|
1038
|
+
"npu-arch"
|
|
1039
|
+
]
|
|
1040
|
+
}
|
|
1041
|
+
],
|
|
1042
|
+
installAgents: [
|
|
1043
|
+
"ascendc-code-summarizer",
|
|
1044
|
+
"ascendc-ops-reviewer"
|
|
1045
|
+
]
|
|
1046
|
+
}
|
|
1047
|
+
];
|
|
1048
|
+
|
|
1049
|
+
// src/core/registry.ts
|
|
1050
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
1051
|
+
var __dirname2 = dirname2(__filename2);
|
|
1052
|
+
function findPluginsDir() {
|
|
1053
|
+
const candidates = [
|
|
1054
|
+
join2(__dirname2, "plugins.d"),
|
|
1055
|
+
join2(__dirname2, "..", "plugins.d"),
|
|
1056
|
+
join2(__dirname2, "..", "..", "plugins.d")
|
|
1057
|
+
];
|
|
1058
|
+
for (const dir of candidates) {
|
|
1059
|
+
if (existsSync2(dir)) return dir;
|
|
1060
|
+
}
|
|
1061
|
+
return candidates[0];
|
|
1062
|
+
}
|
|
21
1063
|
function loadPluginsFromYaml() {
|
|
22
|
-
const pluginsDir =
|
|
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,14 +1462,14 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
|
398
1462
|
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
399
1463
|
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
400
1464
|
const skillPath = join5(skillsDir, skillId);
|
|
401
|
-
return
|
|
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
|
}
|
|
@@ -415,86 +1479,44 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
|
415
1479
|
} else {
|
|
416
1480
|
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
417
1481
|
}
|
|
418
|
-
if (!
|
|
1482
|
+
if (!existsSync5(agentsMdPath)) return false;
|
|
419
1483
|
return true;
|
|
420
1484
|
}
|
|
421
1485
|
|
|
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;
|
|
1486
|
+
// src/utils/config.ts
|
|
1487
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
1488
|
+
import { join as join6 } from "path";
|
|
1489
|
+
import { parse, stringify } from "yaml";
|
|
1490
|
+
|
|
1491
|
+
// src/utils/fs.ts
|
|
1492
|
+
import { writeFileSync, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
1493
|
+
import { dirname as dirname3 } from "path";
|
|
1494
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
1495
|
+
const dir = dirname3(filePath);
|
|
1496
|
+
if (!existsSync6(dir)) {
|
|
1497
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
1498
|
+
}
|
|
1499
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1500
|
+
writeFileSync(tmpPath, data, encoding);
|
|
1501
|
+
if (existsSync6(filePath)) {
|
|
1502
|
+
try {
|
|
1503
|
+
unlinkSync(filePath);
|
|
1504
|
+
} catch {
|
|
457
1505
|
}
|
|
458
1506
|
}
|
|
459
|
-
|
|
460
|
-
}
|
|
461
|
-
function printBoxTitle(title, width = 65) {
|
|
462
|
-
const border = "\u2550".repeat(width);
|
|
463
|
-
const displayWidth = getDisplayWidth(title);
|
|
464
|
-
const totalPadding = width - displayWidth;
|
|
465
|
-
const leftPadding = Math.floor(totalPadding / 2);
|
|
466
|
-
const rightPadding = totalPadding - leftPadding;
|
|
467
|
-
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
468
|
-
console.log();
|
|
469
|
-
console.log(chalk.cyan.bold(` \u2554${border}\u2557`));
|
|
470
|
-
console.log(chalk.cyan.bold(` \u2551`) + chalk.cyan.bold(paddedTitle) + chalk.cyan.bold(`\u2551`));
|
|
471
|
-
console.log(chalk.cyan.bold(` \u255A${border}\u255D`));
|
|
472
|
-
console.log();
|
|
473
|
-
}
|
|
474
|
-
function showOperationHints(isCheckbox = false) {
|
|
475
|
-
if (isCheckbox) {
|
|
476
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m${t("hint_select_space")}\x1B[0m ${t("hint_select_check")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}\uFF08${t("hint_checkbox_suffix")}\uFF09
|
|
477
|
-
`);
|
|
478
|
-
} else {
|
|
479
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
480
|
-
`);
|
|
481
|
-
}
|
|
1507
|
+
renameSync(tmpPath, filePath);
|
|
482
1508
|
}
|
|
483
1509
|
|
|
484
1510
|
// src/utils/config.ts
|
|
485
|
-
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
486
|
-
import { join as join6 } from "path";
|
|
487
|
-
import { homedir as homedir3 } from "os";
|
|
488
|
-
import { parse, stringify } from "yaml";
|
|
489
1511
|
function getConfigDir() {
|
|
490
|
-
return
|
|
1512
|
+
return getCannbotConfigDir();
|
|
491
1513
|
}
|
|
492
1514
|
function getConfigPath() {
|
|
493
1515
|
return join6(getConfigDir(), "config.yaml");
|
|
494
1516
|
}
|
|
495
1517
|
function readConfig() {
|
|
496
1518
|
const configPath = getConfigPath();
|
|
497
|
-
if (!
|
|
1519
|
+
if (!existsSync7(configPath)) {
|
|
498
1520
|
return {
|
|
499
1521
|
language: "zh_CN",
|
|
500
1522
|
installedPlugins: []
|
|
@@ -511,6 +1533,7 @@ function readConfig() {
|
|
|
511
1533
|
installedPlugins: parsed.installedPlugins || []
|
|
512
1534
|
};
|
|
513
1535
|
} catch {
|
|
1536
|
+
logger.warn(t("config_corrupted"));
|
|
514
1537
|
return {
|
|
515
1538
|
language: "zh_CN",
|
|
516
1539
|
installedPlugins: []
|
|
@@ -519,112 +1542,39 @@ function readConfig() {
|
|
|
519
1542
|
}
|
|
520
1543
|
function writeConfig(config) {
|
|
521
1544
|
const configDir = getConfigDir();
|
|
522
|
-
if (!
|
|
1545
|
+
if (!existsSync7(configDir)) {
|
|
523
1546
|
mkdirSync(configDir, { recursive: true });
|
|
524
1547
|
}
|
|
525
1548
|
const configPath = getConfigPath();
|
|
526
1549
|
const content = stringify(config);
|
|
527
|
-
|
|
1550
|
+
atomicWriteFileSync(configPath, content);
|
|
528
1551
|
}
|
|
529
1552
|
function updateConfig(updates) {
|
|
530
1553
|
const config = readConfig();
|
|
531
1554
|
const updated = { ...config, ...updates };
|
|
532
|
-
writeConfig(updated);
|
|
533
|
-
return updated;
|
|
534
|
-
}
|
|
535
|
-
function addInstalledPlugin(pluginId) {
|
|
536
|
-
const config = readConfig();
|
|
537
|
-
if (!config.installedPlugins.includes(pluginId)) {
|
|
538
|
-
config.installedPlugins.push(pluginId);
|
|
539
|
-
writeConfig(config);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
function removeInstalledPlugin(pluginId) {
|
|
543
|
-
const config = readConfig();
|
|
544
|
-
config.installedPlugins = config.installedPlugins.filter(
|
|
545
|
-
(id) => id !== pluginId
|
|
546
|
-
);
|
|
547
|
-
writeConfig(config);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// src/ui/theme.ts
|
|
551
|
-
import chalk2 from "chalk";
|
|
552
|
-
var selectTheme = {
|
|
553
|
-
helpMode: "never"
|
|
554
|
-
};
|
|
555
|
-
function isCJK(code) {
|
|
556
|
-
return code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65519 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983;
|
|
557
|
-
}
|
|
558
|
-
function isEmoji(code) {
|
|
559
|
-
return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
|
|
560
|
-
}
|
|
561
|
-
function getDisplayWidth2(str) {
|
|
562
|
-
let width = 0;
|
|
563
|
-
for (const ch of str) {
|
|
564
|
-
const code = ch.codePointAt(0);
|
|
565
|
-
if (isCJK(code) || isEmoji(code)) {
|
|
566
|
-
width += 2;
|
|
567
|
-
} else {
|
|
568
|
-
width += 1;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return width;
|
|
572
|
-
}
|
|
573
|
-
function wrapDescription(text) {
|
|
574
|
-
const prefix = " \u{1F4A1} ";
|
|
575
|
-
const indent = " ";
|
|
576
|
-
const cols = process.stdout.columns || 120;
|
|
577
|
-
const maxLen = Math.max(20, cols - getDisplayWidth2(prefix) - 2);
|
|
578
|
-
const tokens = [];
|
|
579
|
-
let i = 0;
|
|
580
|
-
while (i < text.length) {
|
|
581
|
-
const code = text[i].codePointAt(0);
|
|
582
|
-
if (isCJK(code)) {
|
|
583
|
-
tokens.push({ text: text[i], width: 2 });
|
|
584
|
-
i++;
|
|
585
|
-
} else {
|
|
586
|
-
let j = i;
|
|
587
|
-
while (j < text.length) {
|
|
588
|
-
const c = text[j].codePointAt(0);
|
|
589
|
-
if (isCJK(c)) break;
|
|
590
|
-
j++;
|
|
591
|
-
}
|
|
592
|
-
const word = text.slice(i, j);
|
|
593
|
-
tokens.push({ text: word, width: getDisplayWidth2(word) });
|
|
594
|
-
i = j;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
const lines = [];
|
|
598
|
-
let line = "";
|
|
599
|
-
let lineWidth = 0;
|
|
600
|
-
for (const token of tokens) {
|
|
601
|
-
if (lineWidth + token.width > maxLen && line) {
|
|
602
|
-
lines.push(line.trimEnd());
|
|
603
|
-
line = token.text;
|
|
604
|
-
lineWidth = token.width;
|
|
605
|
-
} else {
|
|
606
|
-
line += token.text;
|
|
607
|
-
lineWidth += token.width;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
if (line) lines.push(line.trimEnd());
|
|
611
|
-
return lines.map((l, i2) => chalk2.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
|
|
1555
|
+
writeConfig(updated);
|
|
1556
|
+
return updated;
|
|
612
1557
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
},
|
|
619
|
-
helpMode: "never",
|
|
620
|
-
style: {
|
|
621
|
-
description: (text) => wrapDescription(text)
|
|
1558
|
+
function addInstalledPlugin(pluginId) {
|
|
1559
|
+
const config = readConfig();
|
|
1560
|
+
if (!config.installedPlugins.includes(pluginId)) {
|
|
1561
|
+
config.installedPlugins.push(pluginId);
|
|
1562
|
+
writeConfig(config);
|
|
622
1563
|
}
|
|
623
|
-
}
|
|
1564
|
+
}
|
|
1565
|
+
function removeInstalledPlugin(pluginId) {
|
|
1566
|
+
const config = readConfig();
|
|
1567
|
+
config.installedPlugins = config.installedPlugins.filter(
|
|
1568
|
+
(id) => id !== pluginId
|
|
1569
|
+
);
|
|
1570
|
+
writeConfig(config);
|
|
1571
|
+
}
|
|
624
1572
|
|
|
625
|
-
// src/
|
|
1573
|
+
// src/utils/constants.ts
|
|
626
1574
|
var BACK = "__back__";
|
|
627
1575
|
var CANCEL = "__cancel__";
|
|
1576
|
+
|
|
1577
|
+
// src/ui/wizard.ts
|
|
628
1578
|
async function selectToolWithDetection() {
|
|
629
1579
|
const config = readConfig();
|
|
630
1580
|
logger.blank();
|
|
@@ -708,7 +1658,7 @@ async function stepTool(detectedTools, lastTool) {
|
|
|
708
1658
|
if (detectedTools.length === 0) {
|
|
709
1659
|
logger.error(t("error_tool_not_found"));
|
|
710
1660
|
const choices2 = [
|
|
711
|
-
...
|
|
1661
|
+
...VALID_TOOLS.map((tool) => ({
|
|
712
1662
|
name: `> ${getToolDisplayName(tool)}`,
|
|
713
1663
|
value: tool
|
|
714
1664
|
})),
|
|
@@ -809,37 +1759,25 @@ async function stepPlugins(tool, level) {
|
|
|
809
1759
|
const isInstalled = installedSet.has(p.id);
|
|
810
1760
|
const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
|
|
811
1761
|
return {
|
|
812
|
-
name:
|
|
1762
|
+
name: `> ${chalk3.cyan(p.displayName)}${suffix}`,
|
|
813
1763
|
value: p.id,
|
|
814
|
-
checked: false,
|
|
815
1764
|
description: p.description
|
|
816
1765
|
};
|
|
817
1766
|
});
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1767
|
+
choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1768
|
+
choices.push({ name: "<- " + t("wizard_back"), value: BACK });
|
|
1769
|
+
choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL });
|
|
1770
|
+
showOperationHints();
|
|
1771
|
+
const selected = await select({
|
|
1772
|
+
message: t("wizard_select_plugin"),
|
|
821
1773
|
choices,
|
|
822
1774
|
loop: false,
|
|
823
|
-
|
|
824
|
-
theme: checkboxTheme,
|
|
1775
|
+
theme: selectTheme,
|
|
825
1776
|
pageSize: 15
|
|
826
1777
|
});
|
|
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;
|
|
1778
|
+
if (selected === BACK) return BACK;
|
|
1779
|
+
if (selected === CANCEL) return CANCEL;
|
|
1780
|
+
return [selected];
|
|
843
1781
|
}
|
|
844
1782
|
async function stepConfirm(tool, level, plugins) {
|
|
845
1783
|
printBoxTitle(t("wizard_step_4_title"));
|
|
@@ -870,19 +1808,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
870
1808
|
}
|
|
871
1809
|
|
|
872
1810
|
// src/core/repository.ts
|
|
873
|
-
import { existsSync as
|
|
874
|
-
import { join as join8, dirname as
|
|
1811
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1812
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
875
1813
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
876
1814
|
import { execa as execa2 } from "execa";
|
|
877
1815
|
import { parse as parseYaml3 } from "yaml";
|
|
878
1816
|
|
|
879
1817
|
// src/core/scanner.ts
|
|
880
|
-
import { existsSync as
|
|
881
|
-
import { join as join7, dirname as
|
|
1818
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
1819
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
882
1820
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
883
1821
|
import { parse as parseYaml2 } from "yaml";
|
|
884
1822
|
var __filename3 = fileURLToPath3(import.meta.url);
|
|
885
|
-
var __dirname3 =
|
|
1823
|
+
var __dirname3 = dirname4(__filename3);
|
|
886
1824
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
887
1825
|
"node_modules",
|
|
888
1826
|
".git",
|
|
@@ -907,14 +1845,17 @@ function loadScanConfig() {
|
|
|
907
1845
|
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
908
1846
|
try {
|
|
909
1847
|
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
1848
|
+
const ttlHours = config.scanCacheTtlHours || 24;
|
|
910
1849
|
return {
|
|
911
|
-
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
|
|
912
|
-
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
|
|
1850
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1851
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"],
|
|
1852
|
+
cacheTtlMs: ttlHours * 60 * 60 * 1e3
|
|
913
1853
|
};
|
|
914
1854
|
} catch {
|
|
915
1855
|
return {
|
|
916
|
-
skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
|
|
917
|
-
pluginDirs: ["plugins-official", "plugins-community"]
|
|
1856
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1857
|
+
pluginDirs: ["plugins-official", "plugins-community"],
|
|
1858
|
+
cacheTtlMs: 24 * 60 * 60 * 1e3
|
|
918
1859
|
};
|
|
919
1860
|
}
|
|
920
1861
|
}
|
|
@@ -926,23 +1867,37 @@ function scanSkills(repoPath) {
|
|
|
926
1867
|
const seen = /* @__PURE__ */ new Set();
|
|
927
1868
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
928
1869
|
const fullPath = join7(repoPath, dir);
|
|
929
|
-
if (!
|
|
1870
|
+
if (!existsSync8(fullPath)) continue;
|
|
930
1871
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
931
1872
|
}
|
|
932
1873
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
933
1874
|
const pluginParent = join7(repoPath, pluginDir);
|
|
934
|
-
if (!
|
|
1875
|
+
if (!existsSync8(pluginParent)) continue;
|
|
935
1876
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
936
1877
|
const pluginPath = join7(pluginParent, plugin);
|
|
937
1878
|
if (!isDirectory(pluginPath)) continue;
|
|
938
1879
|
const skillsDir = join7(pluginPath, "skills");
|
|
939
|
-
if (
|
|
1880
|
+
if (existsSync8(skillsDir)) {
|
|
940
1881
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
941
1882
|
}
|
|
942
1883
|
const skillDir = join7(pluginPath, "skill");
|
|
943
|
-
if (
|
|
1884
|
+
if (existsSync8(skillDir)) {
|
|
944
1885
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
945
1886
|
}
|
|
1887
|
+
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
1888
|
+
const initSh = join7(pluginPath, "init.sh");
|
|
1889
|
+
if (existsSync8(rootSkillMd) && !existsSync8(initSh)) {
|
|
1890
|
+
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
1891
|
+
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1892
|
+
seen.add(frontmatter.name);
|
|
1893
|
+
skills.push({
|
|
1894
|
+
id: frontmatter.name,
|
|
1895
|
+
description: frontmatter.description,
|
|
1896
|
+
source: `${pluginDir}/${plugin}`,
|
|
1897
|
+
filePath: rootSkillMd
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
946
1901
|
}
|
|
947
1902
|
}
|
|
948
1903
|
return skills;
|
|
@@ -965,8 +1920,12 @@ function parseFrontmatter(filePath) {
|
|
|
965
1920
|
const parsed = parseYaml2(yamlBlock);
|
|
966
1921
|
if (!parsed || typeof parsed !== "object") return null;
|
|
967
1922
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1923
|
+
const trimmedName = parsed.name.trim();
|
|
1924
|
+
if (!trimmedName || /[\/\\]|\.\./.test(trimmedName) || !/^[a-zA-Z0-9._-]+$/.test(trimmedName)) {
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
968
1927
|
return {
|
|
969
|
-
name:
|
|
1928
|
+
name: trimmedName,
|
|
970
1929
|
description: (parsed.description || "").trim()
|
|
971
1930
|
};
|
|
972
1931
|
} catch {
|
|
@@ -976,15 +1935,15 @@ function parseFrontmatter(filePath) {
|
|
|
976
1935
|
function getCurrentCommit(repoPath) {
|
|
977
1936
|
try {
|
|
978
1937
|
const headFile = join7(repoPath, ".git", "HEAD");
|
|
979
|
-
if (!
|
|
1938
|
+
if (!existsSync8(headFile)) return "unknown";
|
|
980
1939
|
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
981
1940
|
if (headContent.startsWith("ref: ")) {
|
|
982
1941
|
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
983
|
-
if (
|
|
1942
|
+
if (existsSync8(refPath)) {
|
|
984
1943
|
return readFileSync5(refPath, "utf-8").trim();
|
|
985
1944
|
}
|
|
986
1945
|
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
987
|
-
if (
|
|
1946
|
+
if (existsSync8(packedRefsPath)) {
|
|
988
1947
|
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
989
1948
|
const refName = headContent.slice(5);
|
|
990
1949
|
const lines = packedRefs.split("\n");
|
|
@@ -1003,12 +1962,12 @@ function getCurrentCommit(repoPath) {
|
|
|
1003
1962
|
function readScanCache() {
|
|
1004
1963
|
try {
|
|
1005
1964
|
const cachePath = getCachePath();
|
|
1006
|
-
if (!
|
|
1965
|
+
if (!existsSync8(cachePath)) return null;
|
|
1007
1966
|
const content = readFileSync5(cachePath, "utf-8");
|
|
1008
1967
|
const cache = JSON.parse(content);
|
|
1009
1968
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1010
1969
|
const age = Date.now() - cache.timestamp;
|
|
1011
|
-
if (age >
|
|
1970
|
+
if (age > scanConfig.cacheTtlMs) return null;
|
|
1012
1971
|
return cache;
|
|
1013
1972
|
} catch {
|
|
1014
1973
|
return null;
|
|
@@ -1017,11 +1976,11 @@ function readScanCache() {
|
|
|
1017
1976
|
function writeScanCache(cache) {
|
|
1018
1977
|
try {
|
|
1019
1978
|
const cachePath = getCachePath();
|
|
1020
|
-
const cacheDir =
|
|
1021
|
-
if (!
|
|
1979
|
+
const cacheDir = dirname4(cachePath);
|
|
1980
|
+
if (!existsSync8(cacheDir)) {
|
|
1022
1981
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1023
1982
|
}
|
|
1024
|
-
|
|
1983
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1025
1984
|
} catch {
|
|
1026
1985
|
}
|
|
1027
1986
|
}
|
|
@@ -1034,7 +1993,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1034
1993
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1035
1994
|
if (!isDirectory(fullPath)) continue;
|
|
1036
1995
|
const skillMd = join7(fullPath, "SKILL.md");
|
|
1037
|
-
if (
|
|
1996
|
+
if (existsSync8(skillMd)) {
|
|
1038
1997
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1039
1998
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1040
1999
|
seen.add(frontmatter.name);
|
|
@@ -1057,28 +2016,22 @@ function safeReaddir(dir) {
|
|
|
1057
2016
|
return [];
|
|
1058
2017
|
}
|
|
1059
2018
|
}
|
|
1060
|
-
function isDirectory(path) {
|
|
1061
|
-
try {
|
|
1062
|
-
return statSync(path).isDirectory();
|
|
1063
|
-
} catch {
|
|
1064
|
-
return false;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
2019
|
|
|
1068
2020
|
// src/core/skill-registry.ts
|
|
1069
2021
|
var CATEGORY_DEFS = [
|
|
1070
|
-
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
|
|
1071
|
-
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
|
|
1072
|
-
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
|
|
1073
|
-
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
|
|
1074
|
-
{ id: "ascendc", name: "AscendC \u5F00\u53D1" },
|
|
1075
|
-
{ id: "pypto", name: "PyPTO \u5F00\u53D1" },
|
|
1076
|
-
{ id: "tilelang", name: "TileLang \u5F00\u53D1" },
|
|
1077
|
-
{ id: "triton", name: "Triton \u5F00\u53D1" },
|
|
1078
|
-
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
|
|
1079
|
-
{ id: "
|
|
1080
|
-
{ id: "
|
|
1081
|
-
{ id: "
|
|
2022
|
+
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
|
|
2023
|
+
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
|
|
2024
|
+
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
|
|
2025
|
+
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
|
|
2026
|
+
{ id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
|
|
2027
|
+
{ id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
|
|
2028
|
+
{ id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
|
|
2029
|
+
{ id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
|
|
2030
|
+
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
|
|
2031
|
+
{ id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
|
|
2032
|
+
{ id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
|
|
2033
|
+
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
|
|
2034
|
+
{ id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
|
|
1082
2035
|
];
|
|
1083
2036
|
var dynamicSkills = [];
|
|
1084
2037
|
var initialized = false;
|
|
@@ -1086,7 +2039,8 @@ function initFromScan(scanned) {
|
|
|
1086
2039
|
dynamicSkills = scanned.map((s) => ({
|
|
1087
2040
|
id: s.id,
|
|
1088
2041
|
description: s.description,
|
|
1089
|
-
source: s.source
|
|
2042
|
+
source: s.source,
|
|
2043
|
+
filePath: s.filePath
|
|
1090
2044
|
}));
|
|
1091
2045
|
initialized = true;
|
|
1092
2046
|
}
|
|
@@ -1162,9 +2116,7 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1162
2116
|
{ id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
|
|
1163
2117
|
{ id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
|
|
1164
2118
|
{ id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
|
|
1165
|
-
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1166
|
-
{ id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
|
|
1167
|
-
{ id: "ops-easyasc-dsl", description: "EasyASC DSL \u7B97\u5B50\u5F00\u53D1", source: "plugins-community/ops-easyasc-dsl/skill" }
|
|
2119
|
+
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1168
2120
|
]
|
|
1169
2121
|
},
|
|
1170
2122
|
{
|
|
@@ -1221,7 +2173,9 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1221
2173
|
{ id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
|
|
1222
2174
|
{ id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
|
|
1223
2175
|
{ id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
|
|
1224
|
-
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" }
|
|
2176
|
+
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
|
|
2177
|
+
{ id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
|
|
2178
|
+
{ id: "science-model-npu-migration", description: "NPU \u4EE3\u7801\u7EA7\u8FC1\u79FB\uFF08\u73AF\u5883\u95E8\u7981/\u811A\u672C\u9002\u914D/\u7CBE\u5EA6\u6027\u80FD\u5BF9\u6BD4\uFF09", source: "plugins-community/science-model-npu-migration", filePath: "plugins-community/science-model-npu-migration/SKILL.md" }
|
|
1225
2179
|
]
|
|
1226
2180
|
},
|
|
1227
2181
|
{
|
|
@@ -1237,6 +2191,13 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1237
2191
|
{ id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
|
|
1238
2192
|
]
|
|
1239
2193
|
},
|
|
2194
|
+
{
|
|
2195
|
+
id: "runtime",
|
|
2196
|
+
name: "Runtime \u8FC1\u79FB",
|
|
2197
|
+
skills: [
|
|
2198
|
+
{ id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
|
|
2199
|
+
]
|
|
2200
|
+
},
|
|
1240
2201
|
{
|
|
1241
2202
|
id: "platform",
|
|
1242
2203
|
name: "\u5E73\u53F0\u5DE5\u5177",
|
|
@@ -1280,9 +2241,10 @@ function getAllSkills() {
|
|
|
1280
2241
|
}
|
|
1281
2242
|
function getAllCategories() {
|
|
1282
2243
|
const skills = getActiveSkills();
|
|
2244
|
+
const isEn = t("list_name") === "Name";
|
|
1283
2245
|
return CATEGORY_DEFS.map((def) => ({
|
|
1284
2246
|
id: def.id,
|
|
1285
|
-
name: def.name,
|
|
2247
|
+
name: isEn ? def.nameEn : def.name,
|
|
1286
2248
|
skills: skills.filter((s) => {
|
|
1287
2249
|
const catId = CATEGORY_MAP[s.id];
|
|
1288
2250
|
if (catId) return catId === def.id;
|
|
@@ -1293,7 +2255,7 @@ function getAllCategories() {
|
|
|
1293
2255
|
|
|
1294
2256
|
// src/core/repository.ts
|
|
1295
2257
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1296
|
-
var __dirname4 =
|
|
2258
|
+
var __dirname4 = dirname5(__filename4);
|
|
1297
2259
|
function loadRepoUrl() {
|
|
1298
2260
|
if (process.env.CANNBOT_REPO_URL) {
|
|
1299
2261
|
return process.env.CANNBOT_REPO_URL;
|
|
@@ -1309,8 +2271,10 @@ function loadRepoUrl() {
|
|
|
1309
2271
|
var REPO_URL = loadRepoUrl();
|
|
1310
2272
|
var RepositoryManager = class {
|
|
1311
2273
|
repoPath;
|
|
2274
|
+
customPath;
|
|
1312
2275
|
constructor(customPath) {
|
|
1313
2276
|
this.repoPath = customPath;
|
|
2277
|
+
this.customPath = customPath;
|
|
1314
2278
|
}
|
|
1315
2279
|
async ensureRepo() {
|
|
1316
2280
|
if (this.repoPath && this.isValidRepo(this.repoPath)) {
|
|
@@ -1331,10 +2295,16 @@ var RepositoryManager = class {
|
|
|
1331
2295
|
this.repoPath = cachedPath;
|
|
1332
2296
|
return cachedPath;
|
|
1333
2297
|
}
|
|
1334
|
-
|
|
2298
|
+
const clonedPath = await this.cloneRepo();
|
|
2299
|
+
return clonedPath;
|
|
1335
2300
|
}
|
|
1336
2301
|
async ensureRepoAndScan() {
|
|
1337
2302
|
const repoPath = await this.ensureRepo();
|
|
2303
|
+
const isUserProvided = !!this.customPath || !!process.env.CANNBOT_REPO_PATH;
|
|
2304
|
+
if (!isUserProvided) {
|
|
2305
|
+
await this.updateRepo();
|
|
2306
|
+
}
|
|
2307
|
+
mergeDynamicPlugins(repoPath);
|
|
1338
2308
|
const cache = readScanCache();
|
|
1339
2309
|
if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
|
|
1340
2310
|
initFromScan(cache.skills);
|
|
@@ -1354,7 +2324,9 @@ var RepositoryManager = class {
|
|
|
1354
2324
|
try {
|
|
1355
2325
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1356
2326
|
} catch {
|
|
2327
|
+
logger.warn(t("repo_pull_failed"));
|
|
1357
2328
|
}
|
|
2329
|
+
mergeDynamicPlugins(repoPath);
|
|
1358
2330
|
}
|
|
1359
2331
|
getRepoPath() {
|
|
1360
2332
|
if (!this.repoPath) {
|
|
@@ -1363,24 +2335,25 @@ var RepositoryManager = class {
|
|
|
1363
2335
|
return this.repoPath;
|
|
1364
2336
|
}
|
|
1365
2337
|
isValidRepo(path) {
|
|
1366
|
-
if (!
|
|
2338
|
+
if (!existsSync9(path)) return false;
|
|
1367
2339
|
const gitDir = join8(path, ".git");
|
|
1368
2340
|
const pluginsDir = join8(path, "plugins-official");
|
|
1369
|
-
return
|
|
2341
|
+
return existsSync9(gitDir) && existsSync9(pluginsDir);
|
|
1370
2342
|
}
|
|
1371
2343
|
async cloneRepo() {
|
|
1372
2344
|
const configDir = getCannbotConfigDir();
|
|
1373
|
-
if (!
|
|
2345
|
+
if (!existsSync9(configDir)) {
|
|
1374
2346
|
mkdirSync3(configDir, { recursive: true });
|
|
1375
2347
|
}
|
|
1376
2348
|
const targetPath = getCannbotRepoPath();
|
|
1377
|
-
if (
|
|
2349
|
+
if (existsSync9(targetPath)) {
|
|
1378
2350
|
if (this.isValidRepo(targetPath)) {
|
|
1379
2351
|
this.repoPath = targetPath;
|
|
1380
2352
|
return targetPath;
|
|
1381
2353
|
}
|
|
1382
2354
|
}
|
|
1383
2355
|
try {
|
|
2356
|
+
logger.info(t("repo_cloning"));
|
|
1384
2357
|
await execa2(
|
|
1385
2358
|
"git",
|
|
1386
2359
|
["clone", "--depth", "1", REPO_URL, targetPath],
|
|
@@ -1390,7 +2363,7 @@ var RepositoryManager = class {
|
|
|
1390
2363
|
return targetPath;
|
|
1391
2364
|
} catch (error) {
|
|
1392
2365
|
throw new Error(
|
|
1393
|
-
`Failed to clone repository: ${error instanceof Error ? error.message : "
|
|
2366
|
+
`Failed to clone repository: ${error instanceof Error ? error.message : t("error_unknown")}`
|
|
1394
2367
|
);
|
|
1395
2368
|
}
|
|
1396
2369
|
}
|
|
@@ -1400,12 +2373,12 @@ function createRepositoryManager(customPath) {
|
|
|
1400
2373
|
}
|
|
1401
2374
|
|
|
1402
2375
|
// src/core/installer.ts
|
|
1403
|
-
import { existsSync as
|
|
1404
|
-
import { join as
|
|
1405
|
-
import { execa as
|
|
2376
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2377
|
+
import { join as join12, basename as basename2 } from "path";
|
|
2378
|
+
import { execa as execa4, execaSync } from "execa";
|
|
1406
2379
|
|
|
1407
2380
|
// src/core/record.ts
|
|
1408
|
-
import { existsSync as
|
|
2381
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
1409
2382
|
import { join as join9 } from "path";
|
|
1410
2383
|
function getInstallsDir() {
|
|
1411
2384
|
return join9(getCannbotConfigDir(), "installs");
|
|
@@ -1415,53 +2388,54 @@ function getRecordPath(pluginId) {
|
|
|
1415
2388
|
}
|
|
1416
2389
|
function readRecord(pluginId) {
|
|
1417
2390
|
const recordPath = getRecordPath(pluginId);
|
|
1418
|
-
if (!
|
|
2391
|
+
if (!existsSync10(recordPath)) {
|
|
1419
2392
|
return null;
|
|
1420
2393
|
}
|
|
1421
2394
|
try {
|
|
1422
2395
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1423
2396
|
return JSON.parse(content);
|
|
1424
2397
|
} catch {
|
|
2398
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1425
2399
|
return null;
|
|
1426
2400
|
}
|
|
1427
2401
|
}
|
|
1428
2402
|
function writeRecord(record) {
|
|
1429
2403
|
const installsDir = getInstallsDir();
|
|
1430
|
-
if (!
|
|
2404
|
+
if (!existsSync10(installsDir)) {
|
|
1431
2405
|
mkdirSync4(installsDir, { recursive: true });
|
|
1432
2406
|
}
|
|
1433
2407
|
const recordPath = getRecordPath(record.pluginId);
|
|
1434
|
-
|
|
2408
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1435
2409
|
}
|
|
1436
2410
|
function deleteRecord(pluginId) {
|
|
1437
2411
|
const recordPath = getRecordPath(pluginId);
|
|
1438
|
-
if (
|
|
1439
|
-
|
|
2412
|
+
if (existsSync10(recordPath)) {
|
|
2413
|
+
unlinkSync2(recordPath);
|
|
1440
2414
|
}
|
|
1441
2415
|
}
|
|
1442
|
-
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
2416
|
+
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames) {
|
|
1443
2417
|
const files = [];
|
|
1444
2418
|
const directories = [];
|
|
1445
2419
|
if (manifest) {
|
|
1446
2420
|
const skillsDir = join9(configRoot, "skills");
|
|
1447
|
-
if (
|
|
2421
|
+
if (existsSync10(skillsDir)) {
|
|
1448
2422
|
directories.push(skillsDir);
|
|
1449
2423
|
for (const skillName of manifest.installed_skills || []) {
|
|
1450
2424
|
const skillPath = join9(skillsDir, skillName);
|
|
1451
|
-
if (
|
|
2425
|
+
if (existsSync10(skillPath) || isSymlink(skillPath)) {
|
|
1452
2426
|
files.push(skillPath);
|
|
1453
2427
|
}
|
|
1454
2428
|
}
|
|
1455
2429
|
}
|
|
1456
2430
|
const agentsDir = join9(configRoot, "agents");
|
|
1457
|
-
if (
|
|
2431
|
+
if (existsSync10(agentsDir)) {
|
|
1458
2432
|
directories.push(agentsDir);
|
|
1459
2433
|
for (const agentName of manifest.installed_agents || []) {
|
|
1460
2434
|
const agentPath = join9(agentsDir, agentName);
|
|
1461
2435
|
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
1462
|
-
if (
|
|
2436
|
+
if (existsSync10(agentPath) || isSymlink(agentPath)) {
|
|
1463
2437
|
files.push(agentPath);
|
|
1464
|
-
} else if (
|
|
2438
|
+
} else if (existsSync10(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1465
2439
|
files.push(agentPathMd);
|
|
1466
2440
|
}
|
|
1467
2441
|
}
|
|
@@ -1472,19 +2446,19 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1472
2446
|
files.push(workflowsLink);
|
|
1473
2447
|
}
|
|
1474
2448
|
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
1475
|
-
if (
|
|
2449
|
+
if (existsSync10(manifestPath)) {
|
|
1476
2450
|
files.push(manifestPath);
|
|
1477
2451
|
}
|
|
1478
2452
|
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
1479
|
-
if (
|
|
2453
|
+
if (existsSync10(pluginManifestPath)) {
|
|
1480
2454
|
files.push(pluginManifestPath);
|
|
1481
2455
|
}
|
|
1482
2456
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1483
2457
|
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
1484
|
-
if (
|
|
2458
|
+
if (existsSync10(configFilePath)) {
|
|
1485
2459
|
files.push(configFilePath);
|
|
1486
2460
|
}
|
|
1487
|
-
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
2461
|
+
const repoLinks = externalRepoNames && externalRepoNames.length > 0 ? externalRepoNames : ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1488
2462
|
for (const repoName of repoLinks) {
|
|
1489
2463
|
const repoLinkPath = join9(installPath, repoName);
|
|
1490
2464
|
if (isSymlink(repoLinkPath)) {
|
|
@@ -1507,35 +2481,29 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1507
2481
|
directories
|
|
1508
2482
|
};
|
|
1509
2483
|
}
|
|
1510
|
-
function isSymlink(path) {
|
|
1511
|
-
try {
|
|
1512
|
-
return lstatSync(path).isSymbolicLink();
|
|
1513
|
-
} catch {
|
|
1514
|
-
return false;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
2484
|
function getSkillRecordPath() {
|
|
1518
2485
|
return join9(getInstallsDir(), "skills.json");
|
|
1519
2486
|
}
|
|
1520
2487
|
function readSkillRecord() {
|
|
1521
2488
|
const recordPath = getSkillRecordPath();
|
|
1522
|
-
if (!
|
|
2489
|
+
if (!existsSync10(recordPath)) {
|
|
1523
2490
|
return {};
|
|
1524
2491
|
}
|
|
1525
2492
|
try {
|
|
1526
2493
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1527
2494
|
return JSON.parse(content);
|
|
1528
2495
|
} catch {
|
|
2496
|
+
logger.warn(t("skill_record_corrupted"));
|
|
1529
2497
|
return {};
|
|
1530
2498
|
}
|
|
1531
2499
|
}
|
|
1532
2500
|
function writeSkillRecord(record) {
|
|
1533
2501
|
const installsDir = getInstallsDir();
|
|
1534
|
-
if (!
|
|
2502
|
+
if (!existsSync10(installsDir)) {
|
|
1535
2503
|
mkdirSync4(installsDir, { recursive: true });
|
|
1536
2504
|
}
|
|
1537
2505
|
const recordPath = getSkillRecordPath();
|
|
1538
|
-
|
|
2506
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1539
2507
|
}
|
|
1540
2508
|
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
1541
2509
|
const record = readSkillRecord();
|
|
@@ -1576,16 +2544,13 @@ function getInstalledSkills(tool, level, installPath) {
|
|
|
1576
2544
|
const skillsDir = join9(configRoot, "skills");
|
|
1577
2545
|
return recordedSkills.filter((skillId) => {
|
|
1578
2546
|
const skillPath = join9(skillsDir, skillId);
|
|
1579
|
-
return
|
|
2547
|
+
return existsSync10(skillPath) || isSymlink(skillPath);
|
|
1580
2548
|
});
|
|
1581
2549
|
}
|
|
1582
2550
|
|
|
1583
2551
|
// src/core/backup.ts
|
|
1584
|
-
import { existsSync as
|
|
2552
|
+
import { existsSync as existsSync11, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
1585
2553
|
import { join as join10 } from "path";
|
|
1586
|
-
function getAgentsFileName(tool) {
|
|
1587
|
-
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1588
|
-
}
|
|
1589
2554
|
function detectCurrentPlugin(configRoot, tool) {
|
|
1590
2555
|
const manifest = readManifest(configRoot);
|
|
1591
2556
|
if (manifest?.team) {
|
|
@@ -1598,7 +2563,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1598
2563
|
}
|
|
1599
2564
|
}
|
|
1600
2565
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1601
|
-
if (!
|
|
2566
|
+
if (!existsSync11(agentsFile)) {
|
|
1602
2567
|
return null;
|
|
1603
2568
|
}
|
|
1604
2569
|
const allRecords = readAllRecords();
|
|
@@ -1617,7 +2582,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1617
2582
|
}
|
|
1618
2583
|
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1619
2584
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1620
|
-
if (!
|
|
2585
|
+
if (!existsSync11(agentsFile)) {
|
|
1621
2586
|
return null;
|
|
1622
2587
|
}
|
|
1623
2588
|
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
@@ -1636,7 +2601,7 @@ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
|
1636
2601
|
}
|
|
1637
2602
|
}
|
|
1638
2603
|
function findBackups(configRoot) {
|
|
1639
|
-
if (!
|
|
2604
|
+
if (!existsSync11(configRoot)) {
|
|
1640
2605
|
return [];
|
|
1641
2606
|
}
|
|
1642
2607
|
const files = readdirSync3(configRoot);
|
|
@@ -1674,8 +2639,8 @@ function restoreBackup(backupPath, configRoot, tool) {
|
|
|
1674
2639
|
}
|
|
1675
2640
|
function deleteBackup(backupPath) {
|
|
1676
2641
|
try {
|
|
1677
|
-
if (
|
|
1678
|
-
|
|
2642
|
+
if (existsSync11(backupPath)) {
|
|
2643
|
+
unlinkSync3(backupPath);
|
|
1679
2644
|
return true;
|
|
1680
2645
|
}
|
|
1681
2646
|
return false;
|
|
@@ -1694,20 +2659,9 @@ function formatTimestamp(date) {
|
|
|
1694
2659
|
}
|
|
1695
2660
|
function readAllRecords() {
|
|
1696
2661
|
const records = [];
|
|
1697
|
-
const plugins =
|
|
1698
|
-
|
|
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);
|
|
2662
|
+
const plugins = getAllPlugins();
|
|
2663
|
+
for (const plugin of plugins) {
|
|
2664
|
+
const record = readRecord(plugin.id);
|
|
1711
2665
|
if (record) {
|
|
1712
2666
|
records.push(record);
|
|
1713
2667
|
}
|
|
@@ -1753,39 +2707,282 @@ async function showRestorePrompt(backups) {
|
|
|
1753
2707
|
return choice;
|
|
1754
2708
|
}
|
|
1755
2709
|
|
|
1756
|
-
// src/core/installer.ts
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2710
|
+
// src/core/plugin-installer.ts
|
|
2711
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, realpathSync, copyFileSync as copyFileSync2, cpSync, writeFileSync as writeFileSync2, rmSync } from "fs";
|
|
2712
|
+
import { join as join11, basename } from "path";
|
|
2713
|
+
import { execa as execa3 } from "execa";
|
|
2714
|
+
function installLink(source, target) {
|
|
2715
|
+
try {
|
|
2716
|
+
const resolved = realpathSync(source);
|
|
2717
|
+
if (existsSync12(target) || isSymlink(target)) {
|
|
2718
|
+
unlinkSync4(target);
|
|
2719
|
+
}
|
|
2720
|
+
try {
|
|
2721
|
+
symlinkSync(resolved, target);
|
|
2722
|
+
return "symlink";
|
|
2723
|
+
} catch (e) {
|
|
2724
|
+
if (e.code === "EPERM") {
|
|
2725
|
+
cpSync(resolved, target, { recursive: true });
|
|
2726
|
+
return "copy";
|
|
2727
|
+
}
|
|
2728
|
+
throw e;
|
|
2729
|
+
}
|
|
2730
|
+
} catch {
|
|
2731
|
+
return "failed";
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
function installConfigFile(pluginDir, configFile, tool, level, configRoot, installPath, configRootConfigLink) {
|
|
2735
|
+
const sourcePath = join11(pluginDir, configFile);
|
|
2736
|
+
if (!existsSync12(sourcePath)) return;
|
|
2737
|
+
const agentsFileName = getAgentsFileName(tool);
|
|
2738
|
+
const isProjectLevel = level === "project";
|
|
2739
|
+
let primaryTarget;
|
|
2740
|
+
if (isProjectLevel) {
|
|
2741
|
+
primaryTarget = join11(installPath, agentsFileName);
|
|
2742
|
+
} else {
|
|
2743
|
+
primaryTarget = join11(configRoot, agentsFileName);
|
|
2744
|
+
}
|
|
2745
|
+
try {
|
|
2746
|
+
const resolvedSource = realpathSync(sourcePath);
|
|
2747
|
+
if (existsSync12(primaryTarget) || isSymlink(primaryTarget)) {
|
|
2748
|
+
unlinkSync4(primaryTarget);
|
|
2749
|
+
}
|
|
2750
|
+
try {
|
|
2751
|
+
symlinkSync(resolvedSource, primaryTarget);
|
|
2752
|
+
} catch (e) {
|
|
2753
|
+
if (e.code === "EPERM") {
|
|
2754
|
+
copyFileSync2(sourcePath, primaryTarget);
|
|
2755
|
+
} else {
|
|
2756
|
+
throw e;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
} catch (e) {
|
|
2760
|
+
logger.warn(t("install_config_failed").replace("{error}", e.message));
|
|
2761
|
+
}
|
|
2762
|
+
if (isProjectLevel && primaryTarget !== join11(configRoot, agentsFileName)) {
|
|
2763
|
+
if (configRootConfigLink === false) return;
|
|
2764
|
+
const configRootTarget = join11(configRoot, agentsFileName);
|
|
2765
|
+
if (existsSync12(configRootTarget) || isSymlink(configRootTarget)) {
|
|
2766
|
+
unlinkSync4(configRootTarget);
|
|
2767
|
+
}
|
|
2768
|
+
try {
|
|
2769
|
+
symlinkSync(realpathSync(sourcePath), configRootTarget);
|
|
2770
|
+
} catch (e) {
|
|
2771
|
+
if (e.code === "EPERM") {
|
|
2772
|
+
copyFileSync2(sourcePath, configRootTarget);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
async function installExternalRepos(repos, repoPath, configRoot, installPath, level) {
|
|
2778
|
+
if (!repos || repos.length === 0) return;
|
|
2779
|
+
for (const repo of repos) {
|
|
2780
|
+
const targetDir = join11(repoPath, repo.dir);
|
|
2781
|
+
if (!existsSync12(targetDir)) {
|
|
2782
|
+
const args = ["clone"];
|
|
2783
|
+
if (repo.depth) {
|
|
2784
|
+
args.push("--depth", String(repo.depth));
|
|
2785
|
+
}
|
|
2786
|
+
if (repo.recursive) {
|
|
2787
|
+
args.push("--recursive");
|
|
2788
|
+
}
|
|
2789
|
+
args.push(repo.url, targetDir);
|
|
2790
|
+
try {
|
|
2791
|
+
await execa3("git", args, { timeout: 12e4 });
|
|
2792
|
+
} catch {
|
|
2793
|
+
logger.warn(t("install_clone_failed").replace("{url}", repo.url).replace("{dir}", repo.dir));
|
|
2794
|
+
try {
|
|
2795
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
2796
|
+
} catch {
|
|
2797
|
+
}
|
|
2798
|
+
continue;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
const linkName = basename(repo.dir);
|
|
2802
|
+
const resolved = realpathSync(targetDir);
|
|
2803
|
+
const shouldConfigRootLink = repo.configRootLink !== false && (level === "global" || repo.configRootLink === true);
|
|
2804
|
+
if (shouldConfigRootLink) {
|
|
2805
|
+
const configRootLink = join11(configRoot, linkName);
|
|
2806
|
+
try {
|
|
2807
|
+
if (existsSync12(configRootLink) || isSymlink(configRootLink)) {
|
|
2808
|
+
unlinkSync4(configRootLink);
|
|
2809
|
+
}
|
|
2810
|
+
try {
|
|
2811
|
+
symlinkSync(resolved, configRootLink);
|
|
2812
|
+
} catch (e) {
|
|
2813
|
+
if (e.code === "EPERM") {
|
|
2814
|
+
cpSync(resolved, configRootLink, { recursive: true });
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
} catch {
|
|
2818
|
+
logger.warn(t("install_link_config_failed").replace("{name}", linkName));
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (level === "project") {
|
|
2822
|
+
const projectLink = join11(installPath, linkName);
|
|
2823
|
+
try {
|
|
2824
|
+
if (existsSync12(projectLink) || isSymlink(projectLink)) {
|
|
2825
|
+
unlinkSync4(projectLink);
|
|
2826
|
+
}
|
|
2827
|
+
try {
|
|
2828
|
+
symlinkSync(resolved, projectLink);
|
|
2829
|
+
} catch (e) {
|
|
2830
|
+
if (e.code === "EPERM") {
|
|
2831
|
+
cpSync(resolved, projectLink, { recursive: true });
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
} catch {
|
|
2835
|
+
logger.warn(t("install_link_project_failed").replace("{name}", linkName));
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
function installWorkflows(pluginDir, configRoot) {
|
|
2841
|
+
const workflowsDir = join11(pluginDir, "workflows");
|
|
2842
|
+
if (!existsSync12(workflowsDir)) return;
|
|
2843
|
+
const targetPath = join11(configRoot, "workflows");
|
|
2844
|
+
try {
|
|
2845
|
+
const resolved = realpathSync(workflowsDir);
|
|
2846
|
+
if (existsSync12(targetPath) || isSymlink(targetPath)) {
|
|
2847
|
+
unlinkSync4(targetPath);
|
|
2848
|
+
}
|
|
2849
|
+
try {
|
|
2850
|
+
symlinkSync(resolved, targetPath);
|
|
2851
|
+
} catch (e) {
|
|
2852
|
+
if (e.code === "EPERM") {
|
|
2853
|
+
cpSync(resolved, targetPath, { recursive: true });
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
} catch {
|
|
2857
|
+
logger.warn(t("install_link_workflows_failed"));
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
function writeCannbotManifest(configRoot, pluginId, version, level, tool, skills, agents) {
|
|
2861
|
+
const manifest = {
|
|
2862
|
+
brand: "CANNBot",
|
|
2863
|
+
version,
|
|
2864
|
+
team: pluginId,
|
|
2865
|
+
level,
|
|
2866
|
+
tool,
|
|
2867
|
+
installed_skills: skills,
|
|
2868
|
+
installed_agents: agents,
|
|
2869
|
+
brand_dir: configRoot,
|
|
2870
|
+
install_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2871
|
+
};
|
|
2872
|
+
const manifestPath = join11(configRoot, "cannbot-manifest.json");
|
|
2873
|
+
writeFileSync2(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
2874
|
+
return manifest;
|
|
2875
|
+
}
|
|
2876
|
+
async function installViaManifest(plugin, repoPath, tool, level, installPath) {
|
|
2877
|
+
const cwd = installPath || process.cwd();
|
|
2878
|
+
const configRoot = getConfigRoot(tool, level, installPath);
|
|
2879
|
+
const pluginDir = join11(repoPath, plugin.dir);
|
|
2880
|
+
try {
|
|
2881
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2882
|
+
const agentsDir = join11(configRoot, "agents");
|
|
2883
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
2884
|
+
mkdirSync5(agentsDir, { recursive: true });
|
|
2885
|
+
const installedSkills = [];
|
|
2886
|
+
const installedAgents = [];
|
|
2887
|
+
for (const source of plugin.installSkills || []) {
|
|
2888
|
+
const sourceDir = join11(repoPath, source.dir);
|
|
2889
|
+
if (!existsSync12(sourceDir)) {
|
|
2890
|
+
logger.warn(t("install_skill_source_not_found").replace("{dir}", source.dir));
|
|
2891
|
+
continue;
|
|
2892
|
+
}
|
|
2893
|
+
for (const skillEntry of source.skills) {
|
|
2894
|
+
const skillName = typeof skillEntry === "string" ? skillEntry : skillEntry.name;
|
|
2895
|
+
const skillAlias = typeof skillEntry === "string" ? skillName : skillEntry.as || skillName;
|
|
2896
|
+
const skillPath = join11(sourceDir, skillName);
|
|
2897
|
+
if (!existsSync12(skillPath)) {
|
|
2898
|
+
logger.warn(t("install_skill_not_found").replace("{name}", skillName).replace("{dir}", source.dir));
|
|
2899
|
+
continue;
|
|
2900
|
+
}
|
|
2901
|
+
const targetPath = join11(skillsDir, skillAlias);
|
|
2902
|
+
const result = installLink(skillPath, targetPath);
|
|
2903
|
+
if (result !== "failed") {
|
|
2904
|
+
installedSkills.push(skillAlias);
|
|
2905
|
+
} else {
|
|
2906
|
+
logger.warn(t("install_skill_failed").replace("{name}", skillAlias));
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
for (const agentName of plugin.installAgents || []) {
|
|
2911
|
+
const agentFile = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
|
|
2912
|
+
const agentPath = join11(pluginDir, "agents", agentFile);
|
|
2913
|
+
if (!existsSync12(agentPath)) {
|
|
2914
|
+
logger.warn(t("install_agent_not_found").replace("{name}", agentFile));
|
|
2915
|
+
continue;
|
|
2916
|
+
}
|
|
2917
|
+
const targetPath = join11(agentsDir, agentFile);
|
|
2918
|
+
const result = installLink(agentPath, targetPath);
|
|
2919
|
+
if (result !== "failed") {
|
|
2920
|
+
installedAgents.push(agentFile);
|
|
2921
|
+
} else {
|
|
2922
|
+
logger.warn(t("install_agent_failed").replace("{name}", agentFile));
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
installConfigFile(pluginDir, plugin.configFile || "AGENTS.md", tool, level, configRoot, cwd, plugin.configRootConfigLink);
|
|
2926
|
+
installWorkflows(pluginDir, configRoot);
|
|
2927
|
+
await installExternalRepos(plugin.externalRepos, repoPath, configRoot, cwd, level);
|
|
2928
|
+
const cannbotManifest = writeCannbotManifest(
|
|
2929
|
+
configRoot,
|
|
2930
|
+
plugin.id,
|
|
2931
|
+
plugin.version || "1.0.0",
|
|
2932
|
+
level,
|
|
2933
|
+
tool,
|
|
2934
|
+
installedSkills,
|
|
2935
|
+
installedAgents
|
|
2936
|
+
);
|
|
2937
|
+
return {
|
|
2938
|
+
success: true,
|
|
2939
|
+
skillsCount: installedSkills.length,
|
|
2940
|
+
agentsCount: installedAgents.length,
|
|
2941
|
+
errors: [],
|
|
2942
|
+
manifest: cannbotManifest
|
|
2943
|
+
};
|
|
2944
|
+
} catch (error) {
|
|
1760
2945
|
return {
|
|
1761
2946
|
success: false,
|
|
1762
|
-
pluginId: opts.pluginId,
|
|
1763
2947
|
skillsCount: 0,
|
|
1764
2948
|
agentsCount: 0,
|
|
1765
|
-
errors: [
|
|
1766
|
-
|
|
2949
|
+
errors: [error instanceof Error ? error.message : t("error_unknown")],
|
|
2950
|
+
manifest: null
|
|
1767
2951
|
};
|
|
1768
2952
|
}
|
|
1769
|
-
|
|
1770
|
-
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
// src/core/installer.ts
|
|
2956
|
+
function findShell() {
|
|
2957
|
+
const candidates = ["bash", "sh"];
|
|
2958
|
+
for (const cmd of candidates) {
|
|
2959
|
+
try {
|
|
2960
|
+
const detectCmd = process.platform === "win32" ? "where" : "which";
|
|
2961
|
+
execaSync(detectCmd, [cmd], { timeout: 3e3 });
|
|
2962
|
+
return cmd;
|
|
2963
|
+
} catch {
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
return null;
|
|
2967
|
+
}
|
|
2968
|
+
async function installPlugin(opts) {
|
|
2969
|
+
const plugin = getPluginById(opts.pluginId);
|
|
2970
|
+
if (!plugin) {
|
|
1771
2971
|
return {
|
|
1772
2972
|
success: false,
|
|
1773
2973
|
pluginId: opts.pluginId,
|
|
1774
2974
|
skillsCount: 0,
|
|
1775
2975
|
agentsCount: 0,
|
|
1776
|
-
errors: [
|
|
2976
|
+
errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
|
|
1777
2977
|
warnings: []
|
|
1778
2978
|
};
|
|
1779
2979
|
}
|
|
1780
|
-
const
|
|
1781
|
-
if (opts.installPath) {
|
|
1782
|
-
args.push(opts.installPath);
|
|
1783
|
-
}
|
|
2980
|
+
const pluginDir = join12(opts.repoPath, plugin.dir);
|
|
1784
2981
|
const cwd = opts.installPath || process.cwd();
|
|
1785
2982
|
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1786
|
-
const agentsFile =
|
|
2983
|
+
const agentsFile = join12(configRoot, getAgentsFileName(opts.tool));
|
|
1787
2984
|
let backupInfo = null;
|
|
1788
|
-
if (
|
|
2985
|
+
if (existsSync13(agentsFile)) {
|
|
1789
2986
|
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
1790
2987
|
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
1791
2988
|
let choice = "overwrite";
|
|
@@ -1812,19 +3009,105 @@ async function installPlugin(opts) {
|
|
|
1812
3009
|
currentPlugin.pluginId,
|
|
1813
3010
|
currentPlugin.pluginName
|
|
1814
3011
|
);
|
|
1815
|
-
if (backupInfo) {
|
|
1816
|
-
logger.
|
|
3012
|
+
if (!backupInfo) {
|
|
3013
|
+
logger.error(t("backup_failed"));
|
|
3014
|
+
return {
|
|
3015
|
+
success: false,
|
|
3016
|
+
pluginId: opts.pluginId,
|
|
3017
|
+
skillsCount: 0,
|
|
3018
|
+
agentsCount: 0,
|
|
3019
|
+
errors: [t("backup_failed")],
|
|
3020
|
+
warnings: []
|
|
3021
|
+
};
|
|
1817
3022
|
}
|
|
3023
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
1818
3024
|
}
|
|
1819
3025
|
}
|
|
3026
|
+
if (plugin.installSkills && plugin.installSkills.length > 0) {
|
|
3027
|
+
const result = await installViaManifest(
|
|
3028
|
+
plugin,
|
|
3029
|
+
opts.repoPath,
|
|
3030
|
+
opts.tool,
|
|
3031
|
+
opts.level,
|
|
3032
|
+
opts.installPath
|
|
3033
|
+
);
|
|
3034
|
+
if (result.success) {
|
|
3035
|
+
try {
|
|
3036
|
+
const record = scanInstalledFiles(
|
|
3037
|
+
opts.pluginId,
|
|
3038
|
+
plugin.displayName,
|
|
3039
|
+
opts.tool,
|
|
3040
|
+
opts.level,
|
|
3041
|
+
cwd,
|
|
3042
|
+
configRoot,
|
|
3043
|
+
result.manifest,
|
|
3044
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
3045
|
+
);
|
|
3046
|
+
if (backupInfo) {
|
|
3047
|
+
record.backup = {
|
|
3048
|
+
filePath: backupInfo.filePath,
|
|
3049
|
+
fromPluginId: backupInfo.pluginId,
|
|
3050
|
+
fromPluginName: backupInfo.pluginName,
|
|
3051
|
+
backupTime: backupInfo.backupTime
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
writeRecord(record);
|
|
3055
|
+
} catch {
|
|
3056
|
+
logger.warn(t("record_write_failed"));
|
|
3057
|
+
}
|
|
3058
|
+
return {
|
|
3059
|
+
success: true,
|
|
3060
|
+
pluginId: opts.pluginId,
|
|
3061
|
+
skillsCount: result.skillsCount,
|
|
3062
|
+
agentsCount: result.agentsCount,
|
|
3063
|
+
errors: result.errors,
|
|
3064
|
+
warnings: []
|
|
3065
|
+
};
|
|
3066
|
+
} else {
|
|
3067
|
+
return {
|
|
3068
|
+
success: false,
|
|
3069
|
+
pluginId: opts.pluginId,
|
|
3070
|
+
skillsCount: 0,
|
|
3071
|
+
agentsCount: 0,
|
|
3072
|
+
errors: result.errors,
|
|
3073
|
+
warnings: []
|
|
3074
|
+
};
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
const scriptPath = join12(pluginDir, plugin.script);
|
|
3078
|
+
if (!existsSync13(scriptPath)) {
|
|
3079
|
+
return {
|
|
3080
|
+
success: false,
|
|
3081
|
+
pluginId: opts.pluginId,
|
|
3082
|
+
skillsCount: 0,
|
|
3083
|
+
agentsCount: 0,
|
|
3084
|
+
errors: [t("error_script_not_found").replace("{path}", scriptPath)],
|
|
3085
|
+
warnings: []
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
const args = [opts.level, opts.tool];
|
|
3089
|
+
if (opts.installPath) {
|
|
3090
|
+
args.push(opts.installPath);
|
|
3091
|
+
}
|
|
1820
3092
|
try {
|
|
3093
|
+
const shell = findShell();
|
|
3094
|
+
if (!shell) {
|
|
3095
|
+
return {
|
|
3096
|
+
success: false,
|
|
3097
|
+
pluginId: opts.pluginId,
|
|
3098
|
+
skillsCount: 0,
|
|
3099
|
+
agentsCount: 0,
|
|
3100
|
+
errors: [t("error_no_shell")],
|
|
3101
|
+
warnings: []
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
1821
3104
|
const execOptions = {
|
|
1822
3105
|
cwd,
|
|
1823
3106
|
timeout: 3e5,
|
|
1824
3107
|
stdio: "pipe",
|
|
1825
3108
|
input: opts.yes ? "y\n" : void 0
|
|
1826
3109
|
};
|
|
1827
|
-
await
|
|
3110
|
+
await execa4(shell, [scriptPath, ...args], execOptions);
|
|
1828
3111
|
const manifest = readManifest(configRoot);
|
|
1829
3112
|
let skillsCount = 0;
|
|
1830
3113
|
let agentsCount = 0;
|
|
@@ -1843,7 +3126,8 @@ async function installPlugin(opts) {
|
|
|
1843
3126
|
opts.level,
|
|
1844
3127
|
cwd,
|
|
1845
3128
|
configRoot,
|
|
1846
|
-
manifest
|
|
3129
|
+
manifest,
|
|
3130
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
1847
3131
|
);
|
|
1848
3132
|
if (backupInfo) {
|
|
1849
3133
|
record.backup = {
|
|
@@ -1855,6 +3139,7 @@ async function installPlugin(opts) {
|
|
|
1855
3139
|
}
|
|
1856
3140
|
writeRecord(record);
|
|
1857
3141
|
} catch {
|
|
3142
|
+
logger.warn(t("record_write_failed"));
|
|
1858
3143
|
}
|
|
1859
3144
|
return {
|
|
1860
3145
|
success: true,
|
|
@@ -1865,7 +3150,7 @@ async function installPlugin(opts) {
|
|
|
1865
3150
|
warnings: []
|
|
1866
3151
|
};
|
|
1867
3152
|
} catch (error) {
|
|
1868
|
-
const errorMessage = error instanceof Error ? error.message : "
|
|
3153
|
+
const errorMessage = error instanceof Error ? error.message : t("error_unknown");
|
|
1869
3154
|
return {
|
|
1870
3155
|
success: false,
|
|
1871
3156
|
pluginId: opts.pluginId,
|
|
@@ -1878,41 +3163,46 @@ async function installPlugin(opts) {
|
|
|
1878
3163
|
}
|
|
1879
3164
|
|
|
1880
3165
|
// src/core/skill-installer.ts
|
|
1881
|
-
import { existsSync as
|
|
1882
|
-
import { join as
|
|
1883
|
-
import { select as select3, checkbox
|
|
3166
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, unlinkSync as unlinkSync5, readdirSync as readdirSync4, rmdirSync, realpathSync as realpathSync2 } from "fs";
|
|
3167
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
3168
|
+
import { select as select3, checkbox, Separator as Separator2 } from "@inquirer/prompts";
|
|
1884
3169
|
import chalk5 from "chalk";
|
|
1885
3170
|
import Table from "cli-table3";
|
|
1886
3171
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
1887
3172
|
const configRoot = getConfigRoot(tool, level);
|
|
1888
|
-
const skillsDir =
|
|
3173
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1889
3174
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1890
|
-
if (!
|
|
1891
|
-
|
|
3175
|
+
if (!existsSync14(skillsDir)) {
|
|
3176
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
1892
3177
|
}
|
|
1893
3178
|
const results = [];
|
|
1894
3179
|
const installedIds = [];
|
|
1895
3180
|
for (const skillId of skillIds) {
|
|
1896
3181
|
const skill = findSkill(skillId);
|
|
1897
3182
|
if (!skill) {
|
|
1898
|
-
results.push({ skillId, success: false, error: "
|
|
3183
|
+
results.push({ skillId, success: false, error: t("error_skill_not_found") });
|
|
1899
3184
|
continue;
|
|
1900
3185
|
}
|
|
1901
|
-
const sourcePath =
|
|
1902
|
-
const targetPath =
|
|
1903
|
-
if (!
|
|
1904
|
-
results.push({ skillId, success: false, error:
|
|
3186
|
+
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
3187
|
+
const targetPath = join13(skillsDir, skillId);
|
|
3188
|
+
if (!existsSync14(sourcePath)) {
|
|
3189
|
+
results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
|
|
1905
3190
|
continue;
|
|
1906
3191
|
}
|
|
1907
3192
|
try {
|
|
1908
|
-
|
|
1909
|
-
|
|
3193
|
+
const resolvedSource = realpathSync2(sourcePath);
|
|
3194
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3195
|
+
unlinkSync5(targetPath);
|
|
1910
3196
|
}
|
|
1911
|
-
|
|
3197
|
+
symlinkSync2(resolvedSource, targetPath);
|
|
1912
3198
|
results.push({ skillId, success: true });
|
|
1913
3199
|
installedIds.push(skillId);
|
|
1914
3200
|
} catch (error) {
|
|
1915
|
-
|
|
3201
|
+
let msg = error instanceof Error ? error.message : t("error_unknown");
|
|
3202
|
+
if (process.platform === "win32" && msg.includes("EPERM")) {
|
|
3203
|
+
msg = t("error_symlink_windows");
|
|
3204
|
+
}
|
|
3205
|
+
results.push({ skillId, success: false, error: msg });
|
|
1916
3206
|
}
|
|
1917
3207
|
}
|
|
1918
3208
|
if (installedIds.length > 0) {
|
|
@@ -1922,30 +3212,30 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1922
3212
|
}
|
|
1923
3213
|
async function uninstallSkills(skillIds, tool, level) {
|
|
1924
3214
|
const configRoot = getConfigRoot(tool, level);
|
|
1925
|
-
const skillsDir =
|
|
3215
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1926
3216
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1927
3217
|
const results = [];
|
|
1928
3218
|
const removedIds = [];
|
|
1929
3219
|
for (const skillId of skillIds) {
|
|
1930
|
-
const targetPath =
|
|
3220
|
+
const targetPath = join13(skillsDir, skillId);
|
|
1931
3221
|
try {
|
|
1932
|
-
if (
|
|
1933
|
-
|
|
3222
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3223
|
+
unlinkSync5(targetPath);
|
|
1934
3224
|
results.push({ skillId, success: true });
|
|
1935
3225
|
removedIds.push(skillId);
|
|
1936
3226
|
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
1937
3227
|
} else {
|
|
1938
|
-
results.push({ skillId, success: false, error: "
|
|
3228
|
+
results.push({ skillId, success: false, error: t("error_not_installed") });
|
|
1939
3229
|
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
1940
3230
|
}
|
|
1941
3231
|
} catch (error) {
|
|
1942
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
3232
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
1943
3233
|
}
|
|
1944
3234
|
}
|
|
1945
3235
|
if (removedIds.length > 0) {
|
|
1946
3236
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
1947
3237
|
}
|
|
1948
|
-
if (
|
|
3238
|
+
if (existsSync14(skillsDir)) {
|
|
1949
3239
|
try {
|
|
1950
3240
|
const entries = readdirSync4(skillsDir);
|
|
1951
3241
|
if (entries.length === 0) {
|
|
@@ -1961,8 +3251,6 @@ var SELECT_ALL = "__select_all__";
|
|
|
1961
3251
|
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
1962
3252
|
var CONTINUE_SELECT = "__continue_select__";
|
|
1963
3253
|
async function interactiveSkillSelect(tool, level) {
|
|
1964
|
-
const BACK3 = "__back__";
|
|
1965
|
-
const CANCEL3 = "__cancel__";
|
|
1966
3254
|
const categories = getAllCategories();
|
|
1967
3255
|
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
1968
3256
|
let accumulated = [];
|
|
@@ -1985,8 +3273,8 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
1985
3273
|
})),
|
|
1986
3274
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1987
3275
|
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
1988
|
-
{ name: "<- " + t("wizard_back"), value:
|
|
1989
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3276
|
+
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
3277
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
1990
3278
|
];
|
|
1991
3279
|
showOperationHints();
|
|
1992
3280
|
selectedCategoryId = await select3({
|
|
@@ -1996,8 +3284,8 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
1996
3284
|
theme: selectTheme,
|
|
1997
3285
|
pageSize: 15
|
|
1998
3286
|
});
|
|
1999
|
-
if (selectedCategoryId ===
|
|
2000
|
-
if (selectedCategoryId ===
|
|
3287
|
+
if (selectedCategoryId === BACK) return "back";
|
|
3288
|
+
if (selectedCategoryId === CANCEL) return "cancel";
|
|
2001
3289
|
if (selectedCategoryId === SELECT_ALL) {
|
|
2002
3290
|
return allSkillIds;
|
|
2003
3291
|
}
|
|
@@ -2037,7 +3325,7 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
2037
3325
|
})
|
|
2038
3326
|
];
|
|
2039
3327
|
showOperationHints(true);
|
|
2040
|
-
const selectedSkills = await
|
|
3328
|
+
const selectedSkills = await checkbox({
|
|
2041
3329
|
message: t("skill_select_items"),
|
|
2042
3330
|
choices: skillChoices,
|
|
2043
3331
|
loop: false,
|
|
@@ -2128,12 +3416,14 @@ function listAllSkills() {
|
|
|
2128
3416
|
console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
2129
3417
|
console.log();
|
|
2130
3418
|
}
|
|
2131
|
-
function
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
3419
|
+
function resolveSkillSourcePath(skill, repoPath) {
|
|
3420
|
+
if (skill.filePath) {
|
|
3421
|
+
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join13(repoPath, skill.filePath);
|
|
3422
|
+
if (existsSync14(absPath)) {
|
|
3423
|
+
return dirname6(absPath);
|
|
3424
|
+
}
|
|
2136
3425
|
}
|
|
3426
|
+
return join13(repoPath, skill.source, skill.id);
|
|
2137
3427
|
}
|
|
2138
3428
|
|
|
2139
3429
|
// src/ui/display.ts
|
|
@@ -2145,10 +3435,11 @@ function printPluginList(plugins, installed) {
|
|
|
2145
3435
|
chalk6.cyan(t("list_id")),
|
|
2146
3436
|
chalk6.cyan(t("list_name")),
|
|
2147
3437
|
chalk6.cyan(t("list_status")),
|
|
2148
|
-
chalk6.cyan(t("
|
|
2149
|
-
chalk6.cyan(t("list_agents"))
|
|
3438
|
+
chalk6.cyan(t("list_description"))
|
|
2150
3439
|
],
|
|
2151
|
-
style: { head: [], border: [] }
|
|
3440
|
+
style: { head: [], border: [] },
|
|
3441
|
+
colWidths: [6, 32, 8, 50],
|
|
3442
|
+
wordWrap: true
|
|
2152
3443
|
});
|
|
2153
3444
|
plugins.forEach((plugin, index) => {
|
|
2154
3445
|
const isInstalled = installed.has(plugin.id);
|
|
@@ -2157,8 +3448,7 @@ function printPluginList(plugins, installed) {
|
|
|
2157
3448
|
String(index + 1),
|
|
2158
3449
|
plugin.displayName,
|
|
2159
3450
|
status,
|
|
2160
|
-
|
|
2161
|
-
String(plugin.agents)
|
|
3451
|
+
plugin.description || chalk6.dim("\u2014")
|
|
2162
3452
|
]);
|
|
2163
3453
|
});
|
|
2164
3454
|
console.log();
|
|
@@ -2225,15 +3515,13 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2225
3515
|
}
|
|
2226
3516
|
|
|
2227
3517
|
// src/commands/init.ts
|
|
2228
|
-
import { existsSync as
|
|
2229
|
-
import { join as
|
|
2230
|
-
var BACK2 = "__back__";
|
|
2231
|
-
var CANCEL2 = "__cancel__";
|
|
3518
|
+
import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
|
|
3519
|
+
import { join as join14 } from "path";
|
|
2232
3520
|
async function initCommand() {
|
|
2233
3521
|
const cwd = process.cwd();
|
|
2234
|
-
if (
|
|
3522
|
+
if (existsSync15(join14(cwd, "package.json")) && (() => {
|
|
2235
3523
|
try {
|
|
2236
|
-
const pkg = JSON.parse(readFileSync8(
|
|
3524
|
+
const pkg = JSON.parse(readFileSync8(join14(cwd, "package.json"), "utf-8"));
|
|
2237
3525
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2238
3526
|
} catch {
|
|
2239
3527
|
return false;
|
|
@@ -2287,7 +3575,6 @@ async function initCommand() {
|
|
|
2287
3575
|
}
|
|
2288
3576
|
async function selectMode() {
|
|
2289
3577
|
printBoxTitle(t("wizard_select_mode_title"));
|
|
2290
|
-
const CANCEL3 = "__cancel__";
|
|
2291
3578
|
const choices = [
|
|
2292
3579
|
{
|
|
2293
3580
|
name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
|
|
@@ -2298,7 +3585,7 @@ async function selectMode() {
|
|
|
2298
3585
|
value: "skill"
|
|
2299
3586
|
},
|
|
2300
3587
|
new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2301
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3588
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2302
3589
|
];
|
|
2303
3590
|
showOperationHints();
|
|
2304
3591
|
const result = await select4({
|
|
@@ -2307,7 +3594,7 @@ async function selectMode() {
|
|
|
2307
3594
|
loop: false,
|
|
2308
3595
|
theme: selectTheme
|
|
2309
3596
|
});
|
|
2310
|
-
if (result ===
|
|
3597
|
+
if (result === CANCEL) return null;
|
|
2311
3598
|
return result;
|
|
2312
3599
|
}
|
|
2313
3600
|
async function pluginInstallFlow() {
|
|
@@ -2384,11 +3671,11 @@ async function skillInstallFlow() {
|
|
|
2384
3671
|
}
|
|
2385
3672
|
case 1: {
|
|
2386
3673
|
const result = await stepLevel();
|
|
2387
|
-
if (result ===
|
|
3674
|
+
if (result === BACK) {
|
|
2388
3675
|
step = 0;
|
|
2389
3676
|
break;
|
|
2390
3677
|
}
|
|
2391
|
-
if (result ===
|
|
3678
|
+
if (result === CANCEL) {
|
|
2392
3679
|
logger.info(t("init_cancelled"));
|
|
2393
3680
|
return "done";
|
|
2394
3681
|
}
|
|
@@ -2435,7 +3722,7 @@ async function skillInstallFlow() {
|
|
|
2435
3722
|
logger.blank();
|
|
2436
3723
|
logger.success(`${t("skill_install_done")}: ${chalk7.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk7.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk7.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2437
3724
|
logger.blank();
|
|
2438
|
-
logger.info(`${t("install_to")}: ${chalk7.cyan(configRoot
|
|
3725
|
+
logger.info(`${t("install_to")}: ${chalk7.cyan(join14(configRoot, "skills"))}`);
|
|
2439
3726
|
logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
|
|
2440
3727
|
logger.blank();
|
|
2441
3728
|
return "done";
|
|
@@ -2459,13 +3746,12 @@ async function listCommand() {
|
|
|
2459
3746
|
|
|
2460
3747
|
// src/commands/doctor.ts
|
|
2461
3748
|
import chalk8 from "chalk";
|
|
2462
|
-
import { existsSync as
|
|
2463
|
-
import { join as
|
|
3749
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
3750
|
+
import { join as join15 } from "path";
|
|
2464
3751
|
async function doctorCommand(options = {}) {
|
|
2465
3752
|
console.log();
|
|
2466
3753
|
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
2467
3754
|
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2468
|
-
let errors = 0;
|
|
2469
3755
|
let warnings = 0;
|
|
2470
3756
|
let fixes = 0;
|
|
2471
3757
|
console.log();
|
|
@@ -2503,7 +3789,7 @@ async function doctorCommand(options = {}) {
|
|
|
2503
3789
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2504
3790
|
const skillsDir = getSkillsDir(configRoot);
|
|
2505
3791
|
const agentsDir = getAgentsDir(configRoot);
|
|
2506
|
-
if (
|
|
3792
|
+
if (existsSync16(skillsDir)) {
|
|
2507
3793
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2508
3794
|
if (brokenLinks === 0) {
|
|
2509
3795
|
const count = readdirSync5(skillsDir).length;
|
|
@@ -2520,12 +3806,12 @@ async function doctorCommand(options = {}) {
|
|
|
2520
3806
|
} else {
|
|
2521
3807
|
console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2522
3808
|
if (options.fix) {
|
|
2523
|
-
|
|
3809
|
+
mkdirSync7(skillsDir, { recursive: true });
|
|
2524
3810
|
fixes++;
|
|
2525
3811
|
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2526
3812
|
}
|
|
2527
3813
|
}
|
|
2528
|
-
if (
|
|
3814
|
+
if (existsSync16(agentsDir)) {
|
|
2529
3815
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2530
3816
|
if (brokenLinks === 0) {
|
|
2531
3817
|
const count = readdirSync5(agentsDir).length;
|
|
@@ -2542,7 +3828,7 @@ async function doctorCommand(options = {}) {
|
|
|
2542
3828
|
} else {
|
|
2543
3829
|
console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2544
3830
|
if (options.fix) {
|
|
2545
|
-
|
|
3831
|
+
mkdirSync7(agentsDir, { recursive: true });
|
|
2546
3832
|
fixes++;
|
|
2547
3833
|
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2548
3834
|
}
|
|
@@ -2550,8 +3836,8 @@ async function doctorCommand(options = {}) {
|
|
|
2550
3836
|
console.log();
|
|
2551
3837
|
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2552
3838
|
const configFile = getConfigFileName(primaryTool);
|
|
2553
|
-
const configPath =
|
|
2554
|
-
if (
|
|
3839
|
+
const configPath = join15(process.cwd(), configFile);
|
|
3840
|
+
if (existsSync16(configPath)) {
|
|
2555
3841
|
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2556
3842
|
} else {
|
|
2557
3843
|
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
@@ -2560,11 +3846,11 @@ async function doctorCommand(options = {}) {
|
|
|
2560
3846
|
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2561
3847
|
if (options.fix && fixes > 0) {
|
|
2562
3848
|
console.log(
|
|
2563
|
-
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{
|
|
3849
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
|
|
2564
3850
|
);
|
|
2565
3851
|
} else {
|
|
2566
3852
|
console.log(
|
|
2567
|
-
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))
|
|
3853
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
|
|
2568
3854
|
);
|
|
2569
3855
|
}
|
|
2570
3856
|
console.log();
|
|
@@ -2574,11 +3860,11 @@ function checkBrokenLinks(dir) {
|
|
|
2574
3860
|
try {
|
|
2575
3861
|
const entries = readdirSync5(dir);
|
|
2576
3862
|
for (const entry of entries) {
|
|
2577
|
-
const fullPath =
|
|
3863
|
+
const fullPath = join15(dir, entry);
|
|
2578
3864
|
try {
|
|
2579
|
-
const stats =
|
|
3865
|
+
const stats = lstatSync2(fullPath);
|
|
2580
3866
|
if (stats.isSymbolicLink()) {
|
|
2581
|
-
if (!
|
|
3867
|
+
if (!existsSync16(fullPath)) {
|
|
2582
3868
|
broken++;
|
|
2583
3869
|
}
|
|
2584
3870
|
}
|
|
@@ -2595,12 +3881,12 @@ function fixBrokenLinks(dir) {
|
|
|
2595
3881
|
try {
|
|
2596
3882
|
const entries = readdirSync5(dir);
|
|
2597
3883
|
for (const entry of entries) {
|
|
2598
|
-
const fullPath =
|
|
3884
|
+
const fullPath = join15(dir, entry);
|
|
2599
3885
|
try {
|
|
2600
|
-
const stats =
|
|
3886
|
+
const stats = lstatSync2(fullPath);
|
|
2601
3887
|
if (stats.isSymbolicLink()) {
|
|
2602
|
-
if (!
|
|
2603
|
-
|
|
3888
|
+
if (!existsSync16(fullPath)) {
|
|
3889
|
+
unlinkSync6(fullPath);
|
|
2604
3890
|
fixed++;
|
|
2605
3891
|
}
|
|
2606
3892
|
}
|
|
@@ -2615,7 +3901,6 @@ function fixBrokenLinks(dir) {
|
|
|
2615
3901
|
// src/commands/status.ts
|
|
2616
3902
|
import chalk9 from "chalk";
|
|
2617
3903
|
async function statusCommand() {
|
|
2618
|
-
const plugins = getAllPlugins();
|
|
2619
3904
|
const installed = scanInstalled();
|
|
2620
3905
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2621
3906
|
console.log();
|
|
@@ -2641,6 +3926,7 @@ async function statusCommand() {
|
|
|
2641
3926
|
}
|
|
2642
3927
|
|
|
2643
3928
|
// src/commands/install.ts
|
|
3929
|
+
import { join as join16 } from "path";
|
|
2644
3930
|
import { confirm } from "@inquirer/prompts";
|
|
2645
3931
|
import chalk10 from "chalk";
|
|
2646
3932
|
async function installCommand(names, options) {
|
|
@@ -2674,7 +3960,10 @@ async function installCommand(names, options) {
|
|
|
2674
3960
|
tool2 = validateTool(options.tool);
|
|
2675
3961
|
} else {
|
|
2676
3962
|
const detected = await selectToolWithDetection();
|
|
2677
|
-
if (detected === "back" || detected === "cancel")
|
|
3963
|
+
if (detected === "back" || detected === "cancel") {
|
|
3964
|
+
logger.info(t("init_cancelled"));
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
2678
3967
|
tool2 = detected;
|
|
2679
3968
|
}
|
|
2680
3969
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2703,7 +3992,7 @@ async function installCommand(names, options) {
|
|
|
2703
3992
|
logger.blank();
|
|
2704
3993
|
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2705
3994
|
logger.blank();
|
|
2706
|
-
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot
|
|
3995
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
|
|
2707
3996
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2708
3997
|
logger.blank();
|
|
2709
3998
|
return;
|
|
@@ -2723,7 +4012,10 @@ async function installCommand(names, options) {
|
|
|
2723
4012
|
tool2 = validateTool(options.tool);
|
|
2724
4013
|
} else {
|
|
2725
4014
|
const detected = await selectToolWithDetection();
|
|
2726
|
-
if (detected === "back" || detected === "cancel")
|
|
4015
|
+
if (detected === "back" || detected === "cancel") {
|
|
4016
|
+
logger.info(t("init_cancelled"));
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
2727
4019
|
tool2 = detected;
|
|
2728
4020
|
}
|
|
2729
4021
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2734,11 +4026,17 @@ async function installCommand(names, options) {
|
|
|
2734
4026
|
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2735
4027
|
if (selectedSkills === "back") {
|
|
2736
4028
|
const redetected = await selectToolWithDetection();
|
|
2737
|
-
if (redetected === "back" || redetected === "cancel")
|
|
4029
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
4030
|
+
logger.info(t("init_cancelled"));
|
|
4031
|
+
return;
|
|
4032
|
+
}
|
|
2738
4033
|
tool2 = redetected;
|
|
2739
4034
|
break;
|
|
2740
4035
|
}
|
|
2741
|
-
if (selectedSkills === "cancel")
|
|
4036
|
+
if (selectedSkills === "cancel") {
|
|
4037
|
+
logger.info(t("init_cancelled"));
|
|
4038
|
+
return;
|
|
4039
|
+
}
|
|
2742
4040
|
if (selectedSkills.length === 0) {
|
|
2743
4041
|
logger.info(t("install_no_skill_selected"));
|
|
2744
4042
|
return;
|
|
@@ -2769,7 +4067,7 @@ async function installCommand(names, options) {
|
|
|
2769
4067
|
logger.blank();
|
|
2770
4068
|
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2771
4069
|
logger.blank();
|
|
2772
|
-
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot
|
|
4070
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
|
|
2773
4071
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2774
4072
|
logger.blank();
|
|
2775
4073
|
return;
|
|
@@ -2813,7 +4111,10 @@ async function installCommand(names, options) {
|
|
|
2813
4111
|
tool = validateTool(options.tool);
|
|
2814
4112
|
} else {
|
|
2815
4113
|
const detected = await selectToolWithDetection();
|
|
2816
|
-
if (detected === "back" || detected === "cancel")
|
|
4114
|
+
if (detected === "back" || detected === "cancel") {
|
|
4115
|
+
logger.info(t("init_cancelled"));
|
|
4116
|
+
return;
|
|
4117
|
+
}
|
|
2817
4118
|
tool = detected;
|
|
2818
4119
|
}
|
|
2819
4120
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2896,7 +4197,7 @@ async function installCommand(names, options) {
|
|
|
2896
4197
|
logger.blank();
|
|
2897
4198
|
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2898
4199
|
logger.blank();
|
|
2899
|
-
logger.info(`${t("install_to")}: ${chalk10.cyan(getConfigRoot(tool, level)
|
|
4200
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(getConfigRoot(tool, level), "skills"))}`);
|
|
2900
4201
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
2901
4202
|
logger.blank();
|
|
2902
4203
|
}
|
|
@@ -2931,7 +4232,8 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
|
|
|
2931
4232
|
}
|
|
2932
4233
|
|
|
2933
4234
|
// src/commands/uninstall.ts
|
|
2934
|
-
import { existsSync as
|
|
4235
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync7, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
|
|
4236
|
+
import { resolve, basename as basename3, sep } from "path";
|
|
2935
4237
|
import chalk11 from "chalk";
|
|
2936
4238
|
async function uninstallCommand(names, options) {
|
|
2937
4239
|
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
@@ -2961,31 +4263,37 @@ async function uninstallCommand(names, options) {
|
|
|
2961
4263
|
continue;
|
|
2962
4264
|
}
|
|
2963
4265
|
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
4266
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
4267
|
+
const allowedBases = [resolve(configRoot), resolve(record.installPath)];
|
|
4268
|
+
const isSafePath = (p) => {
|
|
4269
|
+
const resolved = resolve(p);
|
|
4270
|
+
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + sep));
|
|
4271
|
+
};
|
|
2964
4272
|
let removedFiles = 0;
|
|
2965
4273
|
let removedDirs = 0;
|
|
2966
4274
|
for (const filePath of record.files) {
|
|
4275
|
+
if (!isSafePath(filePath)) continue;
|
|
2967
4276
|
try {
|
|
2968
|
-
if (
|
|
2969
|
-
|
|
2970
|
-
const name = filePath
|
|
4277
|
+
if (existsSync17(filePath) || isSymlink(filePath)) {
|
|
4278
|
+
unlinkSync7(filePath);
|
|
4279
|
+
const name = basename3(filePath);
|
|
2971
4280
|
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2972
4281
|
removedFiles++;
|
|
2973
4282
|
}
|
|
2974
4283
|
} catch {
|
|
2975
4284
|
}
|
|
2976
4285
|
}
|
|
2977
|
-
const
|
|
2978
|
-
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
4286
|
+
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
2979
4287
|
for (const dirPath of sortedDirs) {
|
|
2980
4288
|
try {
|
|
2981
|
-
if (dirPath === configRoot) {
|
|
4289
|
+
if (resolve(dirPath) === resolve(configRoot)) {
|
|
2982
4290
|
continue;
|
|
2983
4291
|
}
|
|
2984
|
-
if (
|
|
4292
|
+
if (existsSync17(dirPath)) {
|
|
2985
4293
|
const entries = readdirSync6(dirPath);
|
|
2986
4294
|
if (entries.length === 0) {
|
|
2987
4295
|
rmdirSync2(dirPath);
|
|
2988
|
-
const name = dirPath
|
|
4296
|
+
const name = basename3(dirPath);
|
|
2989
4297
|
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
2990
4298
|
removedDirs++;
|
|
2991
4299
|
}
|
|
@@ -3014,7 +4322,7 @@ async function uninstallCommand(names, options) {
|
|
|
3014
4322
|
}
|
|
3015
4323
|
const totalRemoved = removedFiles + removedDirs;
|
|
3016
4324
|
if (totalRemoved > 0) {
|
|
3017
|
-
logger.success(
|
|
4325
|
+
logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
|
|
3018
4326
|
} else {
|
|
3019
4327
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3020
4328
|
}
|
|
@@ -3036,13 +4344,6 @@ async function uninstallCommand(names, options) {
|
|
|
3036
4344
|
logger.blank();
|
|
3037
4345
|
}
|
|
3038
4346
|
}
|
|
3039
|
-
function isSymlink3(path) {
|
|
3040
|
-
try {
|
|
3041
|
-
return lstatSync4(path).isSymbolicLink();
|
|
3042
|
-
} catch {
|
|
3043
|
-
return false;
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
4347
|
|
|
3047
4348
|
// src/commands/update.ts
|
|
3048
4349
|
import chalk12 from "chalk";
|
|
@@ -3052,7 +4353,10 @@ async function updateCommand(pluginNames, options) {
|
|
|
3052
4353
|
tool = validateTool(options.tool);
|
|
3053
4354
|
} else {
|
|
3054
4355
|
const detected = await selectToolWithDetection();
|
|
3055
|
-
if (detected === "back" || detected === "cancel")
|
|
4356
|
+
if (detected === "back" || detected === "cancel") {
|
|
4357
|
+
logger.info(t("init_cancelled"));
|
|
4358
|
+
return;
|
|
4359
|
+
}
|
|
3056
4360
|
tool = detected;
|
|
3057
4361
|
}
|
|
3058
4362
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -3079,9 +4383,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3079
4383
|
const repoManager = createRepositoryManager();
|
|
3080
4384
|
const updateSpinner = createSpinner(t("update_updating") + "...");
|
|
3081
4385
|
updateSpinner.start();
|
|
3082
|
-
await repoManager.
|
|
3083
|
-
const repoPath = repoManager.getRepoPath();
|
|
3084
|
-
await repoManager.ensureRepoAndScan();
|
|
4386
|
+
const repoPath = await repoManager.ensureRepoAndScan();
|
|
3085
4387
|
updateSpinner.succeed(t("install_repo_ready"));
|
|
3086
4388
|
const allPlugins = getAllPlugins();
|
|
3087
4389
|
const results = [];
|
|
@@ -3126,8 +4428,8 @@ async function updateCommand(pluginNames, options) {
|
|
|
3126
4428
|
|
|
3127
4429
|
// src/commands/info.ts
|
|
3128
4430
|
import chalk13 from "chalk";
|
|
3129
|
-
import { existsSync as
|
|
3130
|
-
import { join as
|
|
4431
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
|
|
4432
|
+
import { join as join17 } from "path";
|
|
3131
4433
|
async function infoCommand(pluginName) {
|
|
3132
4434
|
const plugin = findPlugin(pluginName);
|
|
3133
4435
|
if (!plugin) {
|
|
@@ -3135,7 +4437,8 @@ async function infoCommand(pluginName) {
|
|
|
3135
4437
|
return;
|
|
3136
4438
|
}
|
|
3137
4439
|
const installed = scanInstalled();
|
|
3138
|
-
const
|
|
4440
|
+
const inst = installed.find((p) => p.id === plugin.id);
|
|
4441
|
+
const isInstalled = !!inst;
|
|
3139
4442
|
const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
|
|
3140
4443
|
console.log();
|
|
3141
4444
|
console.log(chalk13.bold(` ${plugin.displayName}`));
|
|
@@ -3144,19 +4447,19 @@ async function infoCommand(pluginName) {
|
|
|
3144
4447
|
console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
3145
4448
|
console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
|
|
3146
4449
|
console.log(` ${chalk13.dim(t("info_status") + ":")} ${statusText}`);
|
|
3147
|
-
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${plugin.skills}`);
|
|
3148
|
-
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${plugin.agents}`);
|
|
4450
|
+
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${inst ? inst.skillsCount : plugin.skills}`);
|
|
4451
|
+
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${inst ? inst.agentsCount : plugin.agents}`);
|
|
3149
4452
|
console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
3150
4453
|
console.log();
|
|
3151
4454
|
try {
|
|
3152
4455
|
const repoManager = createRepositoryManager();
|
|
3153
4456
|
const repoPath = await repoManager.ensureRepo();
|
|
3154
|
-
const quickstartPath =
|
|
3155
|
-
if (
|
|
4457
|
+
const quickstartPath = join17(repoPath, plugin.dir, "quickstart.md");
|
|
4458
|
+
if (existsSync18(quickstartPath)) {
|
|
3156
4459
|
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
3157
4460
|
}
|
|
3158
|
-
const agentsDir =
|
|
3159
|
-
if (
|
|
4461
|
+
const agentsDir = join17(repoPath, plugin.dir, "agents");
|
|
4462
|
+
if (existsSync18(agentsDir)) {
|
|
3160
4463
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3161
4464
|
if (agentFiles.length > 0) {
|
|
3162
4465
|
console.log();
|
|
@@ -3208,13 +4511,44 @@ async function langCommand(action, value) {
|
|
|
3208
4511
|
}
|
|
3209
4512
|
|
|
3210
4513
|
// src/cli.ts
|
|
4514
|
+
import { readFileSync as readFileSync9, existsSync as existsSync19 } from "fs";
|
|
4515
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
4516
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
4517
|
+
function readVersion() {
|
|
4518
|
+
try {
|
|
4519
|
+
if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
|
|
4520
|
+
return INSTALL_HELPER_VERSION;
|
|
4521
|
+
}
|
|
4522
|
+
} catch {
|
|
4523
|
+
}
|
|
4524
|
+
try {
|
|
4525
|
+
const __filename5 = fileURLToPath5(import.meta.url);
|
|
4526
|
+
const __dirname5 = dirname7(__filename5);
|
|
4527
|
+
const candidates = [
|
|
4528
|
+
join18(__dirname5, "..", "package.json"),
|
|
4529
|
+
join18(__dirname5, "package.json"),
|
|
4530
|
+
join18(process.cwd(), "package.json")
|
|
4531
|
+
];
|
|
4532
|
+
for (const pkgPath of candidates) {
|
|
4533
|
+
if (existsSync19(pkgPath)) {
|
|
4534
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
4535
|
+
if (pkg.version && (pkg.name === "@cannbot-ai/install-helper" || pkg.name?.startsWith("@cannbot-ai/install-helper-"))) {
|
|
4536
|
+
return pkg.version;
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
return "0.0.0";
|
|
4541
|
+
} catch {
|
|
4542
|
+
return "0.0.0";
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
3211
4545
|
function createCLI() {
|
|
3212
4546
|
const config = readConfig();
|
|
3213
4547
|
if (config.language) {
|
|
3214
4548
|
setLanguage(config.language);
|
|
3215
4549
|
}
|
|
3216
4550
|
const program2 = new Command();
|
|
3217
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(
|
|
4551
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
|
|
3218
4552
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3219
4553
|
await initCommand();
|
|
3220
4554
|
});
|
|
@@ -3249,13 +4583,28 @@ function createCLI() {
|
|
|
3249
4583
|
}
|
|
3250
4584
|
|
|
3251
4585
|
// src/index.ts
|
|
3252
|
-
|
|
3253
|
-
if (error
|
|
4586
|
+
function isExitError(error) {
|
|
4587
|
+
if (!error) return false;
|
|
4588
|
+
return error.name === "ExitPromptError" || error.message?.includes("force closed") || error.message?.includes("SIGINT") || error.constructor?.name === "ExitPromptError";
|
|
4589
|
+
}
|
|
4590
|
+
function handleExit(error) {
|
|
4591
|
+
if (isExitError(error)) {
|
|
3254
4592
|
console.log("\n" + t("init_cancelled"));
|
|
3255
4593
|
process.exit(0);
|
|
3256
4594
|
}
|
|
4595
|
+
}
|
|
4596
|
+
process.on("SIGINT", () => {
|
|
4597
|
+
console.log("\n" + t("init_cancelled"));
|
|
4598
|
+
process.exit(130);
|
|
4599
|
+
});
|
|
4600
|
+
process.on("uncaughtException", (error) => {
|
|
4601
|
+
handleExit(error);
|
|
3257
4602
|
throw error;
|
|
3258
4603
|
});
|
|
4604
|
+
process.on("unhandledRejection", (error) => {
|
|
4605
|
+
handleExit(error);
|
|
4606
|
+
process.exit(1);
|
|
4607
|
+
});
|
|
3259
4608
|
var program = createCLI();
|
|
3260
4609
|
program.parse();
|
|
3261
4610
|
//# sourceMappingURL=index.js.map
|