@cannbot-ai/install-helper 0.0.2-beta.6 → 0.0.2-beta.60
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 +134 -35
- package/bin/install-helper.js +59 -0
- package/bin/package.json +3 -0
- package/dist/config/repository.yaml +28 -0
- package/dist/index.js +2522 -896
- package/dist/index.js.map +1 -1
- package/dist/install.ps1 +155 -0
- package/dist/locales/en_US.json +181 -0
- package/dist/locales/zh_CN.json +181 -0
- package/dist/plugins.d/_defaults.yml +4 -0
- package/dist/plugins.d/catlass-op-generator.yml +18 -0
- package/dist/plugins.d/model-infer-optimize.yml +19 -0
- package/dist/plugins.d/ops-code-reviewer.yml +14 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +18 -0
- package/dist/plugins.d/ops-direct-invoke.yml +20 -0
- package/dist/plugins.d/ops-registry-invoke.yml +24 -0
- package/dist/plugins.d/pypto-op-orchestrator.yml +18 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +20 -0
- package/dist/plugins.d/torch-compile.yml +14 -0
- package/dist/plugins.d/triton-op-generator.yml +14 -0
- package/package.json +17 -6
package/dist/index.js
CHANGED
|
@@ -4,116 +4,1137 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
|
-
import { select as
|
|
8
|
-
import
|
|
7
|
+
import { select as select4, Separator as Separator3 } from "@inquirer/prompts";
|
|
8
|
+
import chalk7 from "chalk";
|
|
9
9
|
|
|
10
10
|
// src/ui/wizard.ts
|
|
11
11
|
import { select, checkbox, Separator } from "@inquirer/prompts";
|
|
12
|
-
import
|
|
12
|
+
import chalk3 from "chalk";
|
|
13
13
|
|
|
14
14
|
// src/core/registry.ts
|
|
15
|
-
|
|
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";
|
|
26
|
+
import { join, dirname } from "path";
|
|
27
|
+
import { fileURLToPath } from "url";
|
|
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_plugins: "\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
|
+
quick_start: "\u542F\u52A8\u547D\u4EE4",
|
|
94
|
+
quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
|
|
95
|
+
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
96
|
+
skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
|
|
97
|
+
skill_already_installed: "\u5DF2\u5B89\u88C5",
|
|
98
|
+
skill_select_all: "\u5168\u9009\u6240\u6709 Skills\uFF08\u5171 {count} \u4E2A\uFF09",
|
|
99
|
+
skill_select_all_category: "\u5168\u9009\u5F53\u524D\u7C7B\u522B",
|
|
100
|
+
skill_continue_or_install: "\u5DF2\u9009 {count} \u4E2A Skills\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
101
|
+
skill_continue_select: "\u7EE7\u7EED\u9009\u62E9\u5176\u4ED6\u7C7B\u522B",
|
|
102
|
+
skill_confirm_install_count: "\u786E\u8BA4\u5B89\u88C5\uFF08\u5DF2\u9009 {count} \u4E2A Skills\uFF09",
|
|
103
|
+
skill_all_install_progress: "\u6B63\u5728\u5B89\u88C5\u5168\u90E8 {count} \u4E2A Skills...",
|
|
104
|
+
skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
105
|
+
skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
|
|
106
|
+
skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
|
|
107
|
+
skill_list_title: "\u53EF\u7528 Skills",
|
|
108
|
+
wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
109
|
+
wizard_mode_plugin: "\u5B89\u88C5 Plugin",
|
|
110
|
+
wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
|
|
111
|
+
wizard_mode_skill: "\u5B89\u88C5 Skill",
|
|
112
|
+
wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
|
|
113
|
+
wizard_back: "\u8FD4\u56DE",
|
|
114
|
+
wizard_cancel: "\u9000\u51FA",
|
|
115
|
+
wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
116
|
+
wizard_back_to_reselect: "\u8FD4\u56DE",
|
|
117
|
+
status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
118
|
+
status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
119
|
+
status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
|
|
120
|
+
status_install_time: "\u5B89\u88C5\u65F6\u95F4",
|
|
121
|
+
install_installed_to: "\u5B89\u88C5\u5230",
|
|
122
|
+
install_skills_count: "Skills",
|
|
123
|
+
install_agents_count: "Agents",
|
|
124
|
+
lang_supported: "\u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US",
|
|
125
|
+
lang_usage: "\u7528\u6CD5: install-helper lang set <language>",
|
|
126
|
+
lang_specify: "\u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN",
|
|
127
|
+
lang_unknown_action: "\u672A\u77E5\u64CD\u4F5C",
|
|
128
|
+
lang_supported_actions: "\u652F\u6301\u7684\u64CD\u4F5C: show, set",
|
|
129
|
+
info_aliases: "\u522B\u540D",
|
|
130
|
+
uninstall_not_found: "\u672A\u627E\u5230",
|
|
131
|
+
uninstall_no_record: "\u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
|
|
132
|
+
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",
|
|
133
|
+
uninstall_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
134
|
+
uninstall_removed: "\u5DF2\u5378\u8F7D",
|
|
135
|
+
uninstall_summary_format: "{name}\uFF08\u79FB\u9664 {files} \u4E2A\u6587\u4EF6\uFF0C{dirs} \u4E2A\u76EE\u5F55\uFF09",
|
|
136
|
+
uninstall_files_gone: "\u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55",
|
|
137
|
+
uninstall_skills_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
138
|
+
init_pkg_dir_warn: "\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55",
|
|
139
|
+
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",
|
|
140
|
+
init_pkg_dir_example: "\u793A\u4F8B\uFF1Acd ~/my-project && install-helper",
|
|
141
|
+
init_cancelled: "\u5DF2\u53D6\u6D88\u5B89\u88C5",
|
|
142
|
+
backup_detected: "\u68C0\u6D4B\u5230\u5F53\u524D\u76EE\u5F55\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
143
|
+
backup_current: "\u5F53\u524D\u5B89\u88C5",
|
|
144
|
+
backup_installing: "\u5373\u5C06\u5B89\u88C5",
|
|
145
|
+
backup_overwrite_warning: "\u7EE7\u7EED\u5B89\u88C5\u5C06\u8986\u76D6\u73B0\u6709\u7684 AGENTS.md \u914D\u7F6E\u6587\u4EF6\u3002",
|
|
146
|
+
backup_auto_create: "\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u521B\u5EFA\u5907\u4EFD\uFF0C\u4EE5\u4FBF\u540E\u7EED\u6062\u590D\u3002",
|
|
147
|
+
backup_overwrite: "\u8986\u76D6\u5B89\u88C5\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09",
|
|
148
|
+
backup_cancel: "\u53D6\u6D88\u5B89\u88C5",
|
|
149
|
+
backup_restore_prompt: "\u68C0\u6D4B\u5230\u5907\u4EFD\u914D\u7F6E\uFF0C\u662F\u5426\u6062\u590D\uFF1F",
|
|
150
|
+
backup_restore: "\u6062\u590D",
|
|
151
|
+
backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
|
|
152
|
+
backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
153
|
+
backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
|
|
154
|
+
backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
|
|
155
|
+
backup_failed: "\u5907\u4EFD\u521B\u5EFA\u5931\u8D25\uFF0C\u5B89\u88C5\u5DF2\u4E2D\u6B62\u4EE5\u4FDD\u62A4\u73B0\u6709\u914D\u7F6E",
|
|
156
|
+
loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
|
|
157
|
+
loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
|
|
158
|
+
loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
|
|
159
|
+
loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
|
|
160
|
+
loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
|
|
161
|
+
repo_pull_failed: "\u65E0\u6CD5\u62C9\u53D6\u6700\u65B0\u5185\u5BB9\uFF0C\u4F7F\u7528\u7F13\u5B58\u6570\u636E",
|
|
162
|
+
config_corrupted: "\u914D\u7F6E\u6587\u4EF6\u5DF2\u635F\u574F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E",
|
|
163
|
+
record_corrupted: "\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F\uFF1A{file}",
|
|
164
|
+
skill_record_corrupted: "Skill \u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
|
|
165
|
+
plugin_yaml_error: "\u63D2\u4EF6\u914D\u7F6E\u89E3\u6790\u5931\u8D25\uFF1A{file}",
|
|
166
|
+
record_write_failed: "\u5B89\u88C5\u8BB0\u5F55\u5199\u5165\u5931\u8D25\uFF0C\u5378\u8F7D\u65F6\u53EF\u80FD\u65E0\u6CD5\u81EA\u52A8\u6E05\u7406",
|
|
167
|
+
install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
|
|
168
|
+
install_to: "\u5B89\u88C5\u5230",
|
|
169
|
+
start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
|
|
170
|
+
wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
171
|
+
wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
|
|
172
|
+
wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
173
|
+
wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
|
|
174
|
+
wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
|
|
175
|
+
wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
|
|
176
|
+
install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
|
|
177
|
+
install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
|
|
178
|
+
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
179
|
+
skill_count_format: "{count} \u4E2A Skills",
|
|
180
|
+
result_success_format: "{count} \u6210\u529F",
|
|
181
|
+
result_failed_format: "{count} \u5931\u8D25",
|
|
182
|
+
uninstall_remove_label: "\u79FB\u9664",
|
|
183
|
+
uninstall_remove_file_format: "\u79FB\u9664 {count} \u4E2A\u6587\u4EF6",
|
|
184
|
+
uninstall_remove_dir_format: "{count} \u4E2A\u76EE\u5F55",
|
|
185
|
+
uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
|
|
186
|
+
uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
|
|
187
|
+
available_plugins: "\u53EF\u7528\u63D2\u4EF6",
|
|
188
|
+
available_skills: "\u53EF\u7528 Skills",
|
|
189
|
+
skill_total_count: "\u5171 {count} \u4E2A Skills",
|
|
190
|
+
wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
|
|
191
|
+
wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
|
|
192
|
+
update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
|
|
193
|
+
hint_select_move: "\u79FB\u52A8",
|
|
194
|
+
hint_select_space: "\u7A7A\u683C",
|
|
195
|
+
hint_select_check: "\u52FE\u9009",
|
|
196
|
+
hint_select_confirm: "\u786E\u8BA4",
|
|
197
|
+
hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
|
|
198
|
+
doctor_not_installed: "\u672A\u5B89\u88C5",
|
|
199
|
+
doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
|
|
200
|
+
doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
201
|
+
doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
202
|
+
doctor_not_exist: "\u4E0D\u5B58\u5728",
|
|
203
|
+
doctor_created: "\u5DF2\u521B\u5EFA {path}",
|
|
204
|
+
doctor_config_exists: "\u5B58\u5728",
|
|
205
|
+
doctor_config_not_exist: "\u4E0D\u5B58\u5728",
|
|
206
|
+
doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {fixes} \u9879\u5DF2\u4FEE\u590D",
|
|
207
|
+
doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A",
|
|
208
|
+
count_suffix: "\u4E2A",
|
|
209
|
+
try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/locales/en_US.json
|
|
213
|
+
var en_US_default = {
|
|
214
|
+
wizard_title: "CANNBot Install Helper",
|
|
215
|
+
wizard_detect: "Detecting installed AI coding tools...",
|
|
216
|
+
wizard_select_tool: "Select AI coding tool",
|
|
217
|
+
wizard_select_level: "Select install level",
|
|
218
|
+
wizard_select_plugins: "Select scenarios to install",
|
|
219
|
+
wizard_confirm: "Confirm installation",
|
|
220
|
+
wizard_detected_tool: "Detected",
|
|
221
|
+
wizard_confirm_tool: "Confirm using",
|
|
222
|
+
wizard_last_used: "last used",
|
|
223
|
+
wizard_already_installed: "installed",
|
|
224
|
+
install_repo_ready: "Repository ready",
|
|
225
|
+
install_progress: "Installing",
|
|
226
|
+
install_done: "Installation complete",
|
|
227
|
+
install_success: "success",
|
|
228
|
+
install_already_installed: "already installed",
|
|
229
|
+
install_reinstall_confirm: "Reinstall (overwrite)",
|
|
230
|
+
install_skip: "skipped",
|
|
231
|
+
install_enhanced_next_steps: "Next steps",
|
|
232
|
+
install_enhanced_launch: "Launch",
|
|
233
|
+
install_enhanced_try: "Try",
|
|
234
|
+
install_enhanced_more: "More",
|
|
235
|
+
install_enhanced_check: "Check",
|
|
236
|
+
install_enhanced_docs: "Docs",
|
|
237
|
+
update_updating: "Updating",
|
|
238
|
+
update_done: "Update complete",
|
|
239
|
+
update_no_plugins: "No installed plugins",
|
|
240
|
+
doctor_title: "CANNBot Doctor",
|
|
241
|
+
doctor_tools: "AI Tools",
|
|
242
|
+
doctor_plugins: "Installed Plugins",
|
|
243
|
+
doctor_links: "Link Integrity",
|
|
244
|
+
doctor_config: "Configuration",
|
|
245
|
+
doctor_result: "Result",
|
|
246
|
+
doctor_fix_cleaning: "Cleaning broken links",
|
|
247
|
+
doctor_fix_rebuilding: "Rebuilding directories",
|
|
248
|
+
info_description: "Description",
|
|
249
|
+
info_skills: "Skills",
|
|
250
|
+
info_agents: "Agents",
|
|
251
|
+
info_status: "Status",
|
|
252
|
+
info_quickstart: "Quick Start",
|
|
253
|
+
info_not_found: "Plugin not found",
|
|
254
|
+
lang_title: "Language Settings",
|
|
255
|
+
lang_current: "Current language",
|
|
256
|
+
lang_set: "Language set to",
|
|
257
|
+
lang_invalid: "Invalid language, supported: zh_CN, en_US",
|
|
258
|
+
list_title: "Available Scenarios",
|
|
259
|
+
list_id: "#",
|
|
260
|
+
list_name: "Name",
|
|
261
|
+
list_status: "Status",
|
|
262
|
+
list_description: "Description",
|
|
263
|
+
status_installed: "installed",
|
|
264
|
+
status_not_installed: "not installed",
|
|
265
|
+
error_tool_not_found: "No AI coding tool detected",
|
|
266
|
+
error_plugin_not_found: "Plugin not found",
|
|
267
|
+
error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
|
|
268
|
+
error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
|
|
269
|
+
error_skill_not_found: "Skill not found in registry",
|
|
270
|
+
error_source_not_found: "Source not found: {path}",
|
|
271
|
+
error_script_not_found: "Script not found: {path}",
|
|
272
|
+
error_unknown: "Unknown error",
|
|
273
|
+
error_not_installed: "Not installed",
|
|
274
|
+
error_no_shell: "bash or sh not found, cannot execute install script. Please install Git Bash or WSL",
|
|
275
|
+
error_symlink_windows: "Failed to create symlink, please run as administrator or enable Windows Developer Mode",
|
|
276
|
+
quick_start: "Launch command",
|
|
277
|
+
quick_start_hint: "Try: help me develop an Abs operator",
|
|
278
|
+
skill_select_category: "Select Skill category",
|
|
279
|
+
skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
|
|
280
|
+
skill_already_installed: "installed",
|
|
281
|
+
skill_select_all: "Select ALL Skills ({count} total)",
|
|
282
|
+
skill_select_all_category: "Select all in this category",
|
|
283
|
+
skill_continue_or_install: "{count} Skills selected, choose an action",
|
|
284
|
+
skill_continue_select: "Continue selecting from other categories",
|
|
285
|
+
skill_confirm_install_count: "Install now ({count} Skills selected)",
|
|
286
|
+
skill_all_install_progress: "Installing all {count} Skills...",
|
|
287
|
+
skill_install_progress: "Installing",
|
|
288
|
+
skill_install_done: "Skill installation complete",
|
|
289
|
+
skill_uninstall_done: "Skill uninstallation complete",
|
|
290
|
+
skill_list_title: "Available Skills",
|
|
291
|
+
wizard_select_mode: "Select installation mode",
|
|
292
|
+
wizard_mode_plugin: "Install Plugin",
|
|
293
|
+
wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
|
|
294
|
+
wizard_mode_skill: "Install Skill",
|
|
295
|
+
wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
|
|
296
|
+
wizard_back: "Go back",
|
|
297
|
+
wizard_cancel: "Cancel installation",
|
|
298
|
+
wizard_no_selection: "No items selected, please choose an action",
|
|
299
|
+
wizard_back_to_reselect: "Go back",
|
|
300
|
+
status_title: "Installed Plugins",
|
|
301
|
+
status_none: "No installed plugins",
|
|
302
|
+
status_hint: "Run install-helper init to start",
|
|
303
|
+
status_install_time: "Installed at",
|
|
304
|
+
install_installed_to: "Installed to",
|
|
305
|
+
install_skills_count: "Skills",
|
|
306
|
+
install_agents_count: "Agents",
|
|
307
|
+
lang_supported: "Supported languages: zh_CN, en_US",
|
|
308
|
+
lang_usage: "Usage: install-helper lang set <language>",
|
|
309
|
+
lang_specify: "Please specify language: install-helper lang set zh_CN",
|
|
310
|
+
lang_unknown_action: "Unknown action",
|
|
311
|
+
lang_supported_actions: "Supported actions: show, set",
|
|
312
|
+
info_aliases: "Aliases",
|
|
313
|
+
uninstall_not_found: "Not found",
|
|
314
|
+
uninstall_no_record: "No install record found, may not be installed or record is corrupted",
|
|
315
|
+
uninstall_no_record_hint: "For manual cleanup, delete related files in .opencode/ or .claude/ directory",
|
|
316
|
+
uninstall_in_progress: "Uninstalling",
|
|
317
|
+
uninstall_removed: "Uninstalled",
|
|
318
|
+
uninstall_summary_format: "{name} (removed {files} files, {dirs} directories)",
|
|
319
|
+
uninstall_files_gone: " files no longer exist, install record cleaned",
|
|
320
|
+
uninstall_skills_in_progress: "Uninstalling",
|
|
321
|
+
init_pkg_dir_warn: "Current directory appears to be the install-helper package directory",
|
|
322
|
+
init_pkg_dir_hint: "Please run this command in your project directory, not in the install-helper package directory",
|
|
323
|
+
init_pkg_dir_example: "Example: cd ~/my-project && install-helper",
|
|
324
|
+
init_cancelled: "Installation cancelled",
|
|
325
|
+
backup_detected: "Plugin already installed in current directory",
|
|
326
|
+
backup_current: "Currently installed",
|
|
327
|
+
backup_installing: "About to install",
|
|
328
|
+
backup_overwrite_warning: "Continuing will overwrite existing AGENTS.md configuration.",
|
|
329
|
+
backup_auto_create: "System will automatically create backup for future restoration.",
|
|
330
|
+
backup_overwrite: "Overwrite (auto backup)",
|
|
331
|
+
backup_cancel: "Cancel installation",
|
|
332
|
+
backup_restore_prompt: "Backup configuration detected, restore?",
|
|
333
|
+
backup_restore: "Restore",
|
|
334
|
+
backup_only_uninstall: "Uninstall only (no restore)",
|
|
335
|
+
backup_select_action: "Please select an action",
|
|
336
|
+
backup_restore_success: "Configuration restored",
|
|
337
|
+
backup_created: "Backup created",
|
|
338
|
+
backup_failed: "Backup creation failed, installation aborted to protect existing config",
|
|
339
|
+
loading_skills_list: "Loading Skills list...",
|
|
340
|
+
loading_skills_list_complete: "Skills list loaded",
|
|
341
|
+
loading_plugin_data: "Loading plugin data...",
|
|
342
|
+
loading_plugin_data_complete: "Plugin data loaded",
|
|
343
|
+
loading_skills_list_failed: "Failed to load Skills list, using built-in data",
|
|
344
|
+
repo_pull_failed: "Failed to pull latest updates, using cached data",
|
|
345
|
+
config_corrupted: "Config file corrupted, using defaults",
|
|
346
|
+
record_corrupted: "Install record corrupted: {file}",
|
|
347
|
+
skill_record_corrupted: "Skill install record corrupted",
|
|
348
|
+
plugin_yaml_error: "Plugin config parse error: {file}",
|
|
349
|
+
record_write_failed: "Install record write failed, uninstall may not work properly",
|
|
350
|
+
install_no_skill_selected: "No Skill selected",
|
|
351
|
+
install_to: "Install to",
|
|
352
|
+
start_to_use: "Start {tool} to use",
|
|
353
|
+
wizard_step_1_title: "[1/4] Select AI coding tool",
|
|
354
|
+
wizard_step_2_title: "[2/4] Select installation location",
|
|
355
|
+
wizard_step_3_title: "[3/4] Select plugins to install",
|
|
356
|
+
wizard_step_4_title: "[4/4] Confirm installation",
|
|
357
|
+
wizard_step_3_skill_title: "[3/4] Select Skill category",
|
|
358
|
+
wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
|
|
359
|
+
install_level_project: "Install to current directory (recommended, current project only)",
|
|
360
|
+
install_level_global: "Install globally (all projects)",
|
|
361
|
+
wizard_select_mode_title: "Select installation type",
|
|
362
|
+
skill_count_format: "{count} Skills",
|
|
363
|
+
result_success_format: "{count} succeeded",
|
|
364
|
+
result_failed_format: "{count} failed",
|
|
365
|
+
uninstall_remove_label: "Removed",
|
|
366
|
+
uninstall_remove_file_format: "Removed {count} files",
|
|
367
|
+
uninstall_remove_dir_format: "{count} directories",
|
|
368
|
+
uninstall_clean_empty_dir: "Cleaned empty directory",
|
|
369
|
+
uninstall_skill_not_installed: "Not installed",
|
|
370
|
+
available_plugins: "Available plugins",
|
|
371
|
+
available_skills: "Available Skills",
|
|
372
|
+
skill_total_count: "Total {count} Skills",
|
|
373
|
+
wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
|
|
374
|
+
wizard_install_path: "Install path",
|
|
375
|
+
update_install_hint: "Use {cmd} to install plugins",
|
|
376
|
+
hint_select_move: "Navigate",
|
|
377
|
+
hint_select_space: "Space",
|
|
378
|
+
hint_select_check: "Toggle",
|
|
379
|
+
hint_select_confirm: "Confirm",
|
|
380
|
+
hint_checkbox_suffix: "Press enter without selection to go back",
|
|
381
|
+
doctor_not_installed: "Not installed",
|
|
382
|
+
doctor_links_valid: "{count} symlinks all valid",
|
|
383
|
+
doctor_broken_links: "{count} broken links",
|
|
384
|
+
doctor_fixed: "Cleaned {count} broken links",
|
|
385
|
+
doctor_not_exist: "Does not exist",
|
|
386
|
+
doctor_created: "Created {path}",
|
|
387
|
+
doctor_config_exists: "Exists",
|
|
388
|
+
doctor_config_not_exist: "Does not exist",
|
|
389
|
+
doctor_result_with_fix: "{warnings} warnings, {fixes} fixed",
|
|
390
|
+
doctor_result_without_fix: "{warnings} warnings",
|
|
391
|
+
count_suffix: "",
|
|
392
|
+
try_prompt: "help me develop an Abs operator"
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// src/utils/i18n.ts
|
|
396
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
397
|
+
var __dirname = dirname(__filename);
|
|
398
|
+
var EMBEDDED_LOCALES = {
|
|
399
|
+
zh_CN: zh_CN_default,
|
|
400
|
+
en_US: en_US_default
|
|
401
|
+
};
|
|
402
|
+
function loadLocale(locale) {
|
|
403
|
+
const embedded = EMBEDDED_LOCALES[locale];
|
|
404
|
+
if (embedded) return embedded;
|
|
405
|
+
const localePath = join(__dirname, "locales", `${locale}.json`);
|
|
406
|
+
if (existsSync(localePath)) {
|
|
407
|
+
try {
|
|
408
|
+
const content = readFileSync(localePath, "utf-8");
|
|
409
|
+
return JSON.parse(content);
|
|
410
|
+
} catch {
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const fallbackPath = join(__dirname, "locales", "zh_CN.json");
|
|
414
|
+
if (existsSync(fallbackPath)) {
|
|
415
|
+
try {
|
|
416
|
+
const content = readFileSync(fallbackPath, "utf-8");
|
|
417
|
+
return JSON.parse(content);
|
|
418
|
+
} catch {
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return EMBEDDED_LOCALES.zh_CN || {};
|
|
422
|
+
}
|
|
423
|
+
var currentLang = "zh_CN";
|
|
424
|
+
var messages = loadLocale("zh_CN");
|
|
425
|
+
function setLanguage(lang) {
|
|
426
|
+
currentLang = lang;
|
|
427
|
+
messages = loadLocale(lang);
|
|
428
|
+
}
|
|
429
|
+
function t(key) {
|
|
430
|
+
return messages[key] !== void 0 ? messages[key] : key;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/ui/theme.ts
|
|
434
|
+
import chalk from "chalk";
|
|
435
|
+
var selectTheme = {
|
|
436
|
+
helpMode: "never"
|
|
437
|
+
};
|
|
438
|
+
function isCJK(code) {
|
|
439
|
+
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;
|
|
440
|
+
}
|
|
441
|
+
function isEmoji(code) {
|
|
442
|
+
return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
|
|
443
|
+
}
|
|
444
|
+
function getDisplayWidth(str) {
|
|
445
|
+
let width = 0;
|
|
446
|
+
for (const ch of str) {
|
|
447
|
+
const code = ch.codePointAt(0);
|
|
448
|
+
if (isCJK(code) || isEmoji(code)) {
|
|
449
|
+
width += 2;
|
|
450
|
+
} else {
|
|
451
|
+
width += 1;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return width;
|
|
455
|
+
}
|
|
456
|
+
function wrapDescription(text) {
|
|
457
|
+
const prefix = " \u{1F4A1} ";
|
|
458
|
+
const indent = " ";
|
|
459
|
+
const cols = process.stdout.columns || 120;
|
|
460
|
+
const maxLen = Math.max(20, cols - getDisplayWidth(prefix) - 2);
|
|
461
|
+
const tokens = [];
|
|
462
|
+
let i = 0;
|
|
463
|
+
while (i < text.length) {
|
|
464
|
+
const code = text[i].codePointAt(0);
|
|
465
|
+
if (isCJK(code)) {
|
|
466
|
+
tokens.push({ text: text[i], width: 2 });
|
|
467
|
+
i++;
|
|
468
|
+
} else {
|
|
469
|
+
let j = i;
|
|
470
|
+
while (j < text.length) {
|
|
471
|
+
const c = text[j].codePointAt(0);
|
|
472
|
+
if (isCJK(c)) break;
|
|
473
|
+
j++;
|
|
474
|
+
}
|
|
475
|
+
const word = text.slice(i, j);
|
|
476
|
+
tokens.push({ text: word, width: getDisplayWidth(word) });
|
|
477
|
+
i = j;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const lines = [];
|
|
481
|
+
let line = "";
|
|
482
|
+
let lineWidth = 0;
|
|
483
|
+
for (const token of tokens) {
|
|
484
|
+
if (lineWidth + token.width > maxLen && line) {
|
|
485
|
+
lines.push(line.trimEnd());
|
|
486
|
+
line = token.text;
|
|
487
|
+
lineWidth = token.width;
|
|
488
|
+
} else {
|
|
489
|
+
line += token.text;
|
|
490
|
+
lineWidth += token.width;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (line) lines.push(line.trimEnd());
|
|
494
|
+
return lines.map((l, i2) => chalk.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
|
|
495
|
+
}
|
|
496
|
+
var checkboxTheme = {
|
|
497
|
+
icon: {
|
|
498
|
+
checked: "\x1B[32m\u2611\x1B[22m",
|
|
499
|
+
unchecked: "\u2610",
|
|
500
|
+
cursor: "\u276F"
|
|
501
|
+
},
|
|
502
|
+
helpMode: "never",
|
|
503
|
+
style: {
|
|
504
|
+
description: (text) => wrapDescription(text)
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// src/utils/logger.ts
|
|
509
|
+
var logger = {
|
|
510
|
+
success: (msg) => console.log(chalk2.green("\u2713") + " " + msg),
|
|
511
|
+
error: (msg) => console.log(chalk2.red("\u2717") + " " + msg),
|
|
512
|
+
warn: (msg) => console.log(chalk2.yellow("\u26A0") + " " + msg),
|
|
513
|
+
info: (msg) => console.log(chalk2.cyan("\u2192") + " " + msg),
|
|
514
|
+
step: (msg) => console.log(chalk2.dim(msg)),
|
|
515
|
+
blank: () => console.log()
|
|
516
|
+
};
|
|
517
|
+
function createSpinner(text) {
|
|
518
|
+
return ora({ text, color: "cyan" });
|
|
519
|
+
}
|
|
520
|
+
function printBanner(subtitle) {
|
|
521
|
+
console.log(chalk2.cyan(`
|
|
522
|
+
____ _ _ _ _ _ ____ _
|
|
523
|
+
/ ___| / \\ | \\ | | \\ | | __ ) ___ | |_
|
|
524
|
+
| | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
|
|
525
|
+
| |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
|
|
526
|
+
\\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
|
|
527
|
+
`));
|
|
528
|
+
if (subtitle) {
|
|
529
|
+
console.log(chalk2.bold(` ${subtitle}`));
|
|
530
|
+
console.log();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function printBoxTitle(title, width = 65) {
|
|
534
|
+
const border = "\u2550".repeat(width);
|
|
535
|
+
const displayWidth = getDisplayWidth(title);
|
|
536
|
+
const totalPadding = width - displayWidth;
|
|
537
|
+
const leftPadding = Math.floor(totalPadding / 2);
|
|
538
|
+
const rightPadding = totalPadding - leftPadding;
|
|
539
|
+
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
540
|
+
console.log();
|
|
541
|
+
console.log(chalk2.cyan.bold(` \u2554${border}\u2557`));
|
|
542
|
+
console.log(chalk2.cyan.bold(` \u2551`) + chalk2.cyan.bold(paddedTitle) + chalk2.cyan.bold(`\u2551`));
|
|
543
|
+
console.log(chalk2.cyan.bold(` \u255A${border}\u255D`));
|
|
544
|
+
console.log();
|
|
545
|
+
}
|
|
546
|
+
function showOperationHints(isCheckbox = false) {
|
|
547
|
+
if (isCheckbox) {
|
|
548
|
+
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
|
|
549
|
+
`);
|
|
550
|
+
} else {
|
|
551
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
552
|
+
`);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/utils/fs-helpers.ts
|
|
557
|
+
import { lstatSync, statSync } from "fs";
|
|
558
|
+
function isSymlink(path) {
|
|
559
|
+
try {
|
|
560
|
+
return lstatSync(path).isSymbolicLink();
|
|
561
|
+
} catch {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function isDirectory(path) {
|
|
566
|
+
try {
|
|
567
|
+
return statSync(path).isDirectory();
|
|
568
|
+
} catch {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/embedded-plugins.json
|
|
574
|
+
var embedded_plugins_default = [
|
|
16
575
|
{
|
|
17
576
|
id: "ops-direct-invoke",
|
|
18
577
|
dir: "plugins-official/ops-direct-invoke",
|
|
19
578
|
displayName: "AscendC Kernel \u76F4\u8C03",
|
|
20
579
|
script: "init.sh",
|
|
21
|
-
aliases: [
|
|
580
|
+
aliases: [
|
|
581
|
+
"ops-direct",
|
|
582
|
+
"ascendc-direct",
|
|
583
|
+
"direct",
|
|
584
|
+
"kernel"
|
|
585
|
+
],
|
|
22
586
|
skills: 16,
|
|
23
587
|
agents: 3,
|
|
24
|
-
description: "Ascend C
|
|
588
|
+
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",
|
|
589
|
+
version: "1.2.2",
|
|
590
|
+
configFile: "AGENTS.md",
|
|
591
|
+
configRootConfigLink: false,
|
|
592
|
+
installSkills: [
|
|
593
|
+
{
|
|
594
|
+
dir: "ops",
|
|
595
|
+
skills: [
|
|
596
|
+
"ascendc-tiling-design",
|
|
597
|
+
"npu-arch",
|
|
598
|
+
"ascendc-api-best-practices",
|
|
599
|
+
"ascendc-regbase-best-practice",
|
|
600
|
+
"ascendc-blaze-best-practice",
|
|
601
|
+
"ops-precision-standard",
|
|
602
|
+
"ascendc-docs-search",
|
|
603
|
+
"ascendc-env-check",
|
|
604
|
+
"ascendc-precision-debug",
|
|
605
|
+
"ops-profiling",
|
|
606
|
+
"ascendc-direct-invoke-template",
|
|
607
|
+
"torch-ascendc-op-extension",
|
|
608
|
+
"ascendc-runtime-debug",
|
|
609
|
+
"ascendc-crash-debug",
|
|
610
|
+
"ascendc-code-review"
|
|
611
|
+
]
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
dir: "infra",
|
|
615
|
+
skills: [
|
|
616
|
+
"gitcode-toolkit",
|
|
617
|
+
"gitcode-pr-handler",
|
|
618
|
+
"gitcode-issue-gen",
|
|
619
|
+
"gitcode-issue-handler"
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
],
|
|
623
|
+
installAgents: [
|
|
624
|
+
"ascendc-kernel-architect",
|
|
625
|
+
"ascendc-kernel-design-reviewer",
|
|
626
|
+
"ascendc-kernel-developer",
|
|
627
|
+
"ascendc-kernel-reviewer"
|
|
628
|
+
],
|
|
629
|
+
externalRepos: [
|
|
630
|
+
{
|
|
631
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
632
|
+
dir: "plugins-official/ops-direct-invoke/asc-devkit",
|
|
633
|
+
configRootLink: false
|
|
634
|
+
}
|
|
635
|
+
]
|
|
25
636
|
},
|
|
26
637
|
{
|
|
27
638
|
id: "ops-direct-invoke-flash",
|
|
28
639
|
dir: "plugins-official/ops-direct-invoke-flash",
|
|
29
|
-
displayName: "AscendC Kernel \
|
|
640
|
+
displayName: "AscendC Kernel \u76F4\u8C03\uFF08\u5FEB\u901F\u7248\uFF09",
|
|
30
641
|
script: "init.sh",
|
|
31
|
-
aliases: [
|
|
642
|
+
aliases: [
|
|
643
|
+
"flash",
|
|
644
|
+
"kernel-flash"
|
|
645
|
+
],
|
|
32
646
|
skills: 1,
|
|
33
647
|
agents: 1,
|
|
34
|
-
description: "Ascend
|
|
648
|
+
description: "\u4ECE\u6570\u5B66\u516C\u5F0F\u6216\u4EE3\u7801\u7247\u6BB5\u5FEB\u901F\u6784\u5EFA\u5E76\u9A8C\u8BC1 Ascend NPU \u6838\u51FD\u6570",
|
|
649
|
+
version: "1.2.2",
|
|
650
|
+
configFile: "AGENTS.md",
|
|
651
|
+
configRootConfigLink: true,
|
|
652
|
+
installSkills: [
|
|
653
|
+
{
|
|
654
|
+
dir: "plugins-official/ops-direct-invoke-flash/skills",
|
|
655
|
+
skills: [
|
|
656
|
+
"ops-direct-invoke-flash"
|
|
657
|
+
]
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
dir: "ops",
|
|
661
|
+
skills: [
|
|
662
|
+
"ascendc-st-design",
|
|
663
|
+
"ascendc-whitebox-design"
|
|
664
|
+
]
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
dir: "infra",
|
|
668
|
+
skills: [
|
|
669
|
+
"gitcode-toolkit",
|
|
670
|
+
"gitcode-pr-handler",
|
|
671
|
+
"gitcode-issue-gen",
|
|
672
|
+
"gitcode-issue-handler"
|
|
673
|
+
]
|
|
674
|
+
}
|
|
675
|
+
],
|
|
676
|
+
installAgents: [
|
|
677
|
+
"ops-direct-invoke-flash-reviewer"
|
|
678
|
+
]
|
|
35
679
|
},
|
|
36
680
|
{
|
|
37
681
|
id: "ops-registry-invoke",
|
|
38
682
|
dir: "plugins-official/ops-registry-invoke",
|
|
39
683
|
displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
|
|
40
684
|
script: "init.sh",
|
|
41
|
-
aliases: [
|
|
685
|
+
aliases: [
|
|
686
|
+
"ops-registry",
|
|
687
|
+
"ascendc-registry",
|
|
688
|
+
"registry"
|
|
689
|
+
],
|
|
42
690
|
skills: 12,
|
|
43
691
|
agents: 4,
|
|
44
|
-
description: "
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
692
|
+
description: "\u751F\u4EA7\u7EA7 ACLNN \u6CE8\u518C\u7B97\u5B50\u5168\u6D41\u7A0B\uFF1A\u4EE3\u7801\u2192UT/ST\u2192\u4E0A\u5E93",
|
|
693
|
+
version: "1.2.2",
|
|
694
|
+
configFile: "AGENTS.md",
|
|
695
|
+
configRootConfigLink: false,
|
|
696
|
+
installSkills: [
|
|
697
|
+
{
|
|
698
|
+
dir: "ops",
|
|
699
|
+
skills: [
|
|
700
|
+
"ascendc-blaze-best-practice",
|
|
701
|
+
"ascendc-api-best-practices",
|
|
702
|
+
"ascendc-code-review",
|
|
703
|
+
"ascendc-crash-debug",
|
|
704
|
+
"ascendc-direct-invoke-template",
|
|
705
|
+
"ascendc-docs-gen",
|
|
706
|
+
"ascendc-docs-search",
|
|
707
|
+
"ascendc-env-check",
|
|
708
|
+
"npu-arch",
|
|
709
|
+
"ascendc-performance-best-practices",
|
|
710
|
+
"ascendc-precision-debug",
|
|
711
|
+
"ascendc-regbase-best-practice",
|
|
712
|
+
"ascendc-registry-invoke-template",
|
|
713
|
+
"ascendc-runtime-debug",
|
|
714
|
+
"ascendc-st-design",
|
|
715
|
+
"ascendc-tiling-design",
|
|
716
|
+
"ascendc-ut-develop",
|
|
717
|
+
"ascendc-whitebox-design",
|
|
718
|
+
"ops-precision-standard",
|
|
719
|
+
"ops-profiling",
|
|
720
|
+
"ops-spec-gen"
|
|
721
|
+
]
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
dir: "infra",
|
|
725
|
+
skills: [
|
|
726
|
+
"gitcode-toolkit",
|
|
727
|
+
"gitcode-pr-handler",
|
|
728
|
+
"gitcode-issue-gen",
|
|
729
|
+
"gitcode-issue-handler"
|
|
730
|
+
]
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
dir: "plugins-official/ops-registry-invoke",
|
|
734
|
+
skills: [
|
|
735
|
+
{
|
|
736
|
+
name: "workflow",
|
|
737
|
+
as: "ops-registry-invoke-workflow"
|
|
738
|
+
}
|
|
739
|
+
]
|
|
740
|
+
}
|
|
741
|
+
],
|
|
742
|
+
installAgents: [
|
|
743
|
+
"ascendc-ops-architect",
|
|
744
|
+
"ascendc-ops-developer",
|
|
745
|
+
"ascendc-ops-tester"
|
|
746
|
+
],
|
|
747
|
+
externalRepos: [
|
|
748
|
+
{
|
|
749
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
750
|
+
dir: "plugins-official/ops-registry-invoke/asc-devkit",
|
|
751
|
+
configRootLink: false
|
|
752
|
+
}
|
|
753
|
+
]
|
|
65
754
|
},
|
|
66
755
|
{
|
|
67
|
-
id: "
|
|
68
|
-
dir: "plugins-official/
|
|
69
|
-
displayName: "
|
|
756
|
+
id: "catlass-op-generator",
|
|
757
|
+
dir: "plugins-official/catlass-op-generator",
|
|
758
|
+
displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
|
|
70
759
|
script: "init.sh",
|
|
71
|
-
aliases: [
|
|
72
|
-
|
|
760
|
+
aliases: [
|
|
761
|
+
"catlass"
|
|
762
|
+
],
|
|
763
|
+
skills: 10,
|
|
73
764
|
agents: 3,
|
|
74
|
-
description: "
|
|
765
|
+
description: "Catlass \u6A21\u677F\u62FC\u88C5 GEMM/Matmul/Epilogue \u7B97\u5B50\u5F00\u53D1",
|
|
766
|
+
version: "1.2.2",
|
|
767
|
+
configFile: "AGENTS.md",
|
|
768
|
+
configRootConfigLink: true,
|
|
769
|
+
installSkills: [
|
|
770
|
+
{
|
|
771
|
+
dir: "ops",
|
|
772
|
+
skills: [
|
|
773
|
+
"catlass-op-design",
|
|
774
|
+
"catlass-op-develop",
|
|
775
|
+
"catlass-op-perf-tune",
|
|
776
|
+
"ascendc-tiling-design",
|
|
777
|
+
"npu-arch",
|
|
778
|
+
"ascendc-api-best-practices",
|
|
779
|
+
"ops-precision-standard",
|
|
780
|
+
"ascendc-docs-search",
|
|
781
|
+
"ascendc-env-check",
|
|
782
|
+
"ascendc-precision-debug",
|
|
783
|
+
"ops-profiling",
|
|
784
|
+
"ascendc-runtime-debug",
|
|
785
|
+
"ascendc-code-review",
|
|
786
|
+
"torch-ascendc-op-extension"
|
|
787
|
+
]
|
|
788
|
+
}
|
|
789
|
+
],
|
|
790
|
+
installAgents: [
|
|
791
|
+
"catlass-op-architect",
|
|
792
|
+
"catlass-op-generator",
|
|
793
|
+
"catlass-op-reviewer"
|
|
794
|
+
],
|
|
795
|
+
externalRepos: [
|
|
796
|
+
{
|
|
797
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
798
|
+
dir: "plugins-official/catlass-op-generator/asc-devkit",
|
|
799
|
+
configRootLink: false
|
|
800
|
+
}
|
|
801
|
+
]
|
|
75
802
|
},
|
|
76
803
|
{
|
|
77
804
|
id: "model-infer-optimize",
|
|
78
805
|
dir: "plugins-official/model-infer-optimize",
|
|
79
806
|
displayName: "NPU \u63A8\u7406\u4F18\u5316",
|
|
80
807
|
script: "init.sh",
|
|
81
|
-
aliases: [
|
|
808
|
+
aliases: [
|
|
809
|
+
"model-infer",
|
|
810
|
+
"infer",
|
|
811
|
+
"inference"
|
|
812
|
+
],
|
|
82
813
|
skills: 11,
|
|
83
814
|
agents: 3,
|
|
84
|
-
description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316"
|
|
815
|
+
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",
|
|
816
|
+
version: "1.2.2",
|
|
817
|
+
configFile: "AGENTS.md",
|
|
818
|
+
configRootConfigLink: true,
|
|
819
|
+
installSkills: [
|
|
820
|
+
{
|
|
821
|
+
dir: "model",
|
|
822
|
+
skills: [
|
|
823
|
+
"model-infer-migrator",
|
|
824
|
+
"model-infer-parallel-analysis",
|
|
825
|
+
"model-infer-parallel-impl",
|
|
826
|
+
"model-infer-kvcache",
|
|
827
|
+
"model-infer-fusion",
|
|
828
|
+
"model-infer-quantization",
|
|
829
|
+
"model-infer-graph-mode",
|
|
830
|
+
"model-infer-precision-debug",
|
|
831
|
+
"model-infer-runtime-debug",
|
|
832
|
+
"model-infer-multi-stream",
|
|
833
|
+
"model-infer-prefetch",
|
|
834
|
+
"model-infer-superkernel"
|
|
835
|
+
]
|
|
836
|
+
}
|
|
837
|
+
],
|
|
838
|
+
installAgents: [
|
|
839
|
+
"model-infer-analyzer",
|
|
840
|
+
"model-infer-implementer",
|
|
841
|
+
"model-infer-reviewer"
|
|
842
|
+
],
|
|
843
|
+
externalRepos: [
|
|
844
|
+
{
|
|
845
|
+
url: "https://gitcode.com/cann/cann-recipes-infer.git",
|
|
846
|
+
dir: "plugins-official/model-infer-optimize/cann-recipes-infer",
|
|
847
|
+
depth: 1,
|
|
848
|
+
configRootLink: true
|
|
849
|
+
}
|
|
850
|
+
]
|
|
85
851
|
},
|
|
86
852
|
{
|
|
87
|
-
id: "
|
|
88
|
-
dir: "plugins-official/
|
|
89
|
-
displayName: "
|
|
853
|
+
id: "pypto-op-orchestrator",
|
|
854
|
+
dir: "plugins-official/pypto-op-orchestrator",
|
|
855
|
+
displayName: "PyPTO \u7B97\u5B50\u5F00\u53D1",
|
|
90
856
|
script: "init.sh",
|
|
91
|
-
aliases: [
|
|
92
|
-
|
|
857
|
+
aliases: [
|
|
858
|
+
"pypto",
|
|
859
|
+
"pytorch"
|
|
860
|
+
],
|
|
861
|
+
skills: 8,
|
|
93
862
|
agents: 3,
|
|
94
|
-
description: "
|
|
863
|
+
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",
|
|
864
|
+
version: "1.2.2",
|
|
865
|
+
configFile: "AGENTS.md",
|
|
866
|
+
configRootConfigLink: false,
|
|
867
|
+
installSkills: [
|
|
868
|
+
{
|
|
869
|
+
dir: "ops",
|
|
870
|
+
skills: [
|
|
871
|
+
"pypto-intent-understand",
|
|
872
|
+
"pypto-api-explore",
|
|
873
|
+
"pypto-op-design",
|
|
874
|
+
"pypto-op-develop",
|
|
875
|
+
"pypto-precision-debug",
|
|
876
|
+
"pypto-precision-compare",
|
|
877
|
+
"pypto-op-perf-tune",
|
|
878
|
+
"pypto-golden-generate"
|
|
879
|
+
]
|
|
880
|
+
}
|
|
881
|
+
],
|
|
882
|
+
installAgents: [
|
|
883
|
+
"pypto-op-analyst",
|
|
884
|
+
"pypto-op-developer",
|
|
885
|
+
"pypto-op-perf-tuner"
|
|
886
|
+
],
|
|
887
|
+
externalRepos: [
|
|
888
|
+
{
|
|
889
|
+
url: "https://gitcode.com/cann/pypto.git",
|
|
890
|
+
dir: "plugins-official/pypto-op-orchestrator/pypto",
|
|
891
|
+
configRootLink: false
|
|
892
|
+
}
|
|
893
|
+
]
|
|
95
894
|
},
|
|
96
895
|
{
|
|
97
|
-
id: "
|
|
98
|
-
dir: "plugins-official/
|
|
99
|
-
displayName: "\
|
|
896
|
+
id: "tilelang-op-orchestrator",
|
|
897
|
+
dir: "plugins-official/tilelang-op-orchestrator",
|
|
898
|
+
displayName: "TileLang \u7B97\u5B50\u5F00\u53D1",
|
|
100
899
|
script: "init.sh",
|
|
101
|
-
aliases: [
|
|
102
|
-
|
|
900
|
+
aliases: [
|
|
901
|
+
"tilelang"
|
|
902
|
+
],
|
|
903
|
+
skills: 9,
|
|
904
|
+
agents: 3,
|
|
905
|
+
description: "TileLang Python DSL + @tilelang.jit \u7B97\u5B50\u5F00\u53D1\uFF08\u652F\u6301 Developer/Expert \u53CC\u6A21\u5F0F\uFF09",
|
|
906
|
+
version: "1.2.2",
|
|
907
|
+
configFile: "AGENTS.md",
|
|
908
|
+
configRootConfigLink: false,
|
|
909
|
+
installSkills: [
|
|
910
|
+
{
|
|
911
|
+
dir: "ops",
|
|
912
|
+
skills: [
|
|
913
|
+
"tilelang-api-best-practices",
|
|
914
|
+
"tilelang-env-check",
|
|
915
|
+
"tilelang-op-design",
|
|
916
|
+
"tilelang-op-develop",
|
|
917
|
+
"tilelang-op-test-design",
|
|
918
|
+
"tilelang-perf-optimization",
|
|
919
|
+
"tilelang-programming-model-guide",
|
|
920
|
+
"tilelang-review",
|
|
921
|
+
"tilelang-submodule-pull"
|
|
922
|
+
]
|
|
923
|
+
}
|
|
924
|
+
],
|
|
925
|
+
installAgents: [
|
|
926
|
+
"tilelang-op-analyst",
|
|
927
|
+
"tilelang-op-developer",
|
|
928
|
+
"tilelang-op-perf-tuner"
|
|
929
|
+
],
|
|
930
|
+
externalRepos: [
|
|
931
|
+
{
|
|
932
|
+
url: "https://github.com/tile-ai/tilelang-ascend.git",
|
|
933
|
+
dir: "plugins-official/tilelang-op-orchestrator/tilelang-ascend",
|
|
934
|
+
depth: 1,
|
|
935
|
+
recursive: true,
|
|
936
|
+
configRootLink: false
|
|
937
|
+
}
|
|
938
|
+
]
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
id: "triton-op-generator",
|
|
942
|
+
dir: "plugins-official/triton-op-generator",
|
|
943
|
+
displayName: "Triton \u7B97\u5B50\u5F00\u53D1",
|
|
944
|
+
script: "init.sh",
|
|
945
|
+
aliases: [
|
|
946
|
+
"triton"
|
|
947
|
+
],
|
|
948
|
+
skills: 6,
|
|
103
949
|
agents: 1,
|
|
104
|
-
description: "\u4EE3\u7801\
|
|
950
|
+
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",
|
|
951
|
+
version: "1.2.2",
|
|
952
|
+
configFile: "AGENTS.md",
|
|
953
|
+
configRootConfigLink: false,
|
|
954
|
+
installSkills: [
|
|
955
|
+
{
|
|
956
|
+
dir: "ops",
|
|
957
|
+
skills: [
|
|
958
|
+
"triton-task-extractor",
|
|
959
|
+
"triton-op-designer",
|
|
960
|
+
"triton-op-coding",
|
|
961
|
+
"triton-op-verifier",
|
|
962
|
+
"triton-latency-optimizer",
|
|
963
|
+
"npu-arch"
|
|
964
|
+
]
|
|
965
|
+
}
|
|
966
|
+
],
|
|
967
|
+
installAgents: []
|
|
105
968
|
},
|
|
106
969
|
{
|
|
107
970
|
id: "torch-compile",
|
|
108
971
|
dir: "plugins-official/torch-compile",
|
|
109
972
|
displayName: "torch.compile \u56FE\u6A21\u5F0F",
|
|
110
973
|
script: "init.sh",
|
|
111
|
-
aliases: [
|
|
974
|
+
aliases: [
|
|
975
|
+
"torch",
|
|
976
|
+
"compile",
|
|
977
|
+
"graph"
|
|
978
|
+
],
|
|
112
979
|
skills: 6,
|
|
113
980
|
agents: 1,
|
|
114
|
-
description: "
|
|
981
|
+
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",
|
|
982
|
+
version: "1.2.2",
|
|
983
|
+
configFile: "AGENTS.md",
|
|
984
|
+
configRootConfigLink: true,
|
|
985
|
+
installSkills: [
|
|
986
|
+
{
|
|
987
|
+
dir: "graph",
|
|
988
|
+
skills: [
|
|
989
|
+
"torch-npugraph-ex-knowledge",
|
|
990
|
+
"torch-npugraph-ex-template",
|
|
991
|
+
"torch-npugraph-ex-dfx-triage",
|
|
992
|
+
"torch-npugraph-ex-compile-error-diagnosis",
|
|
993
|
+
"torch-npugraph-ex-runtime-error-diagnosis",
|
|
994
|
+
"torch-custom-ops-guide"
|
|
995
|
+
]
|
|
996
|
+
}
|
|
997
|
+
],
|
|
998
|
+
installAgents: [
|
|
999
|
+
"torch-npugraph-ex"
|
|
1000
|
+
]
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
id: "ops-code-reviewer",
|
|
1004
|
+
dir: "plugins-official/ops-code-reviewer",
|
|
1005
|
+
displayName: "\u4EE3\u7801\u68C0\u89C6",
|
|
1006
|
+
script: "init.sh",
|
|
1007
|
+
aliases: [
|
|
1008
|
+
"code-review",
|
|
1009
|
+
"reviewer",
|
|
1010
|
+
"review"
|
|
1011
|
+
],
|
|
1012
|
+
skills: 5,
|
|
1013
|
+
agents: 1,
|
|
1014
|
+
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",
|
|
1015
|
+
version: "1.2.2",
|
|
1016
|
+
configFile: "AGENTS.md",
|
|
1017
|
+
configRootConfigLink: false,
|
|
1018
|
+
installSkills: [
|
|
1019
|
+
{
|
|
1020
|
+
dir: "ops",
|
|
1021
|
+
skills: [
|
|
1022
|
+
"ascendc-code-review",
|
|
1023
|
+
"ascendc-docs-search",
|
|
1024
|
+
"ascendc-task-focus",
|
|
1025
|
+
"ascendc-api-best-practices",
|
|
1026
|
+
"npu-arch"
|
|
1027
|
+
]
|
|
1028
|
+
}
|
|
1029
|
+
],
|
|
1030
|
+
installAgents: [
|
|
1031
|
+
"ascendc-code-summarizer",
|
|
1032
|
+
"ascendc-ops-reviewer"
|
|
1033
|
+
]
|
|
115
1034
|
}
|
|
116
1035
|
];
|
|
1036
|
+
|
|
1037
|
+
// src/core/registry.ts
|
|
1038
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
1039
|
+
var __dirname2 = dirname2(__filename2);
|
|
1040
|
+
function findPluginsDir() {
|
|
1041
|
+
const candidates = [
|
|
1042
|
+
join2(__dirname2, "plugins.d"),
|
|
1043
|
+
join2(__dirname2, "..", "plugins.d"),
|
|
1044
|
+
join2(__dirname2, "..", "..", "plugins.d")
|
|
1045
|
+
];
|
|
1046
|
+
for (const dir of candidates) {
|
|
1047
|
+
if (existsSync2(dir)) return dir;
|
|
1048
|
+
}
|
|
1049
|
+
return candidates[0];
|
|
1050
|
+
}
|
|
1051
|
+
function loadPluginsFromYaml() {
|
|
1052
|
+
const pluginsDir = findPluginsDir();
|
|
1053
|
+
const defaultsPath = join2(pluginsDir, "_defaults.yml");
|
|
1054
|
+
let defaults = {};
|
|
1055
|
+
try {
|
|
1056
|
+
defaults = parseYaml(readFileSync2(defaultsPath, "utf-8")) || {};
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
1059
|
+
const plugins = [];
|
|
1060
|
+
try {
|
|
1061
|
+
for (const file of readdirSync(pluginsDir)) {
|
|
1062
|
+
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
1063
|
+
try {
|
|
1064
|
+
const content = parseYaml(readFileSync2(join2(pluginsDir, file), "utf-8"));
|
|
1065
|
+
if (!content || !content.id) continue;
|
|
1066
|
+
plugins.push({
|
|
1067
|
+
id: content.id,
|
|
1068
|
+
dir: content.dir,
|
|
1069
|
+
displayName: content.displayName || content.id,
|
|
1070
|
+
script: content.script || defaults.script || "init.sh",
|
|
1071
|
+
aliases: content.aliases || [],
|
|
1072
|
+
skills: content.skills ?? defaults.skills ?? 0,
|
|
1073
|
+
agents: content.agents ?? defaults.agents ?? 0,
|
|
1074
|
+
description: content.description || "",
|
|
1075
|
+
version: content.version,
|
|
1076
|
+
configFile: content.configFile,
|
|
1077
|
+
configRootConfigLink: content.configRootConfigLink,
|
|
1078
|
+
installSkills: content.installSkills,
|
|
1079
|
+
installAgents: content.installAgents,
|
|
1080
|
+
externalRepos: content.externalRepos
|
|
1081
|
+
});
|
|
1082
|
+
} catch {
|
|
1083
|
+
logger.warn(t("plugin_yaml_error").replace("{file}", file));
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
} catch {
|
|
1087
|
+
}
|
|
1088
|
+
if (plugins.length === 0) {
|
|
1089
|
+
return [...embedded_plugins_default];
|
|
1090
|
+
}
|
|
1091
|
+
plugins.sort(
|
|
1092
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
1093
|
+
);
|
|
1094
|
+
return plugins;
|
|
1095
|
+
}
|
|
1096
|
+
var PLUGIN_REGISTRY = loadPluginsFromYaml();
|
|
1097
|
+
var PLUGIN_DIRS = ["plugins-official", "plugins-community"];
|
|
1098
|
+
function mergeDynamicPlugins(repoPath) {
|
|
1099
|
+
if (!existsSync2(repoPath)) return;
|
|
1100
|
+
const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
|
|
1101
|
+
const dynamic = [];
|
|
1102
|
+
for (const pluginDir of PLUGIN_DIRS) {
|
|
1103
|
+
const parent = join2(repoPath, pluginDir);
|
|
1104
|
+
if (!existsSync2(parent)) continue;
|
|
1105
|
+
let entries = [];
|
|
1106
|
+
try {
|
|
1107
|
+
entries = readdirSync(parent);
|
|
1108
|
+
} catch {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
for (const entry of entries) {
|
|
1112
|
+
const pluginPath = join2(parent, entry);
|
|
1113
|
+
if (!isDirectory(pluginPath)) continue;
|
|
1114
|
+
if (entry === "install-helper") continue;
|
|
1115
|
+
const initSh = join2(pluginPath, "init.sh");
|
|
1116
|
+
if (!existsSync2(initSh)) continue;
|
|
1117
|
+
const pluginId = entry;
|
|
1118
|
+
if (existingIds.has(pluginId)) continue;
|
|
1119
|
+
dynamic.push({
|
|
1120
|
+
id: pluginId,
|
|
1121
|
+
dir: `${pluginDir}/${entry}`,
|
|
1122
|
+
displayName: entry,
|
|
1123
|
+
script: "init.sh",
|
|
1124
|
+
aliases: [],
|
|
1125
|
+
skills: 0,
|
|
1126
|
+
agents: 0,
|
|
1127
|
+
description: ""
|
|
1128
|
+
});
|
|
1129
|
+
existingIds.add(pluginId);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (dynamic.length > 0) {
|
|
1133
|
+
PLUGIN_REGISTRY = [...PLUGIN_REGISTRY, ...dynamic].sort(
|
|
1134
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
117
1138
|
function findPlugin(query) {
|
|
118
1139
|
const normalized = query.toLowerCase().trim();
|
|
119
1140
|
const exactMatch = PLUGIN_REGISTRY.find(
|
|
@@ -137,19 +1158,37 @@ function getPluginById(id) {
|
|
|
137
1158
|
|
|
138
1159
|
// src/core/detector.ts
|
|
139
1160
|
import { execa } from "execa";
|
|
140
|
-
import { existsSync as
|
|
1161
|
+
import { existsSync as existsSync4 } from "fs";
|
|
141
1162
|
import { homedir as homedir2 } from "os";
|
|
142
|
-
import { join as
|
|
1163
|
+
import { join as join4 } from "path";
|
|
143
1164
|
|
|
144
1165
|
// src/utils/paths.ts
|
|
145
1166
|
import { homedir } from "os";
|
|
146
|
-
import { join } from "path";
|
|
147
|
-
import { existsSync } from "fs";
|
|
1167
|
+
import { join as join3 } from "path";
|
|
1168
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1169
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
1170
|
+
var VALID_LEVELS = ["project", "global"];
|
|
1171
|
+
function validateTool(tool) {
|
|
1172
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
return tool;
|
|
1178
|
+
}
|
|
1179
|
+
function validateLevel(level) {
|
|
1180
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
1181
|
+
throw new Error(
|
|
1182
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
return level;
|
|
1186
|
+
}
|
|
148
1187
|
function detectTraeVariant() {
|
|
149
1188
|
const home = homedir();
|
|
150
|
-
if (
|
|
151
|
-
if (
|
|
152
|
-
if (
|
|
1189
|
+
if (existsSync3(join3(home, ".trae-cn"))) return "ide";
|
|
1190
|
+
if (existsSync3(join3(home, ".marscode"))) return "plugin";
|
|
1191
|
+
if (existsSync3(join3(home, ".traecli"))) return "cli";
|
|
153
1192
|
return "unknown";
|
|
154
1193
|
}
|
|
155
1194
|
function getConfigRoot(tool, level, base) {
|
|
@@ -157,66 +1196,77 @@ function getConfigRoot(tool, level, base) {
|
|
|
157
1196
|
if (level === "global") {
|
|
158
1197
|
switch (tool) {
|
|
159
1198
|
case "opencode":
|
|
160
|
-
return
|
|
1199
|
+
return join3(home, ".config", "opencode");
|
|
161
1200
|
case "claude":
|
|
162
|
-
return
|
|
1201
|
+
return join3(home, ".claude");
|
|
163
1202
|
case "trae": {
|
|
164
1203
|
const variant = detectTraeVariant();
|
|
165
1204
|
switch (variant) {
|
|
166
1205
|
case "plugin":
|
|
167
|
-
return
|
|
1206
|
+
return join3(home, ".marscode");
|
|
168
1207
|
case "cli":
|
|
169
|
-
return
|
|
1208
|
+
return join3(home, ".traecli");
|
|
170
1209
|
default:
|
|
171
|
-
return
|
|
1210
|
+
return join3(home, ".trae-cn");
|
|
172
1211
|
}
|
|
173
1212
|
}
|
|
174
1213
|
case "cursor":
|
|
175
|
-
return
|
|
1214
|
+
return join3(home, ".cursor");
|
|
176
1215
|
case "copilot":
|
|
177
|
-
return
|
|
1216
|
+
return join3(home, ".copilot");
|
|
1217
|
+
default:
|
|
1218
|
+
throw new Error(
|
|
1219
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1220
|
+
);
|
|
178
1221
|
}
|
|
179
1222
|
}
|
|
180
1223
|
const baseDir = base || process.cwd();
|
|
181
1224
|
switch (tool) {
|
|
182
1225
|
case "opencode":
|
|
183
|
-
return
|
|
1226
|
+
return join3(baseDir, ".opencode");
|
|
184
1227
|
case "claude":
|
|
185
|
-
return
|
|
1228
|
+
return join3(baseDir, ".claude");
|
|
186
1229
|
case "trae": {
|
|
187
1230
|
const variant = detectTraeVariant();
|
|
188
1231
|
switch (variant) {
|
|
189
1232
|
case "plugin":
|
|
190
|
-
return
|
|
1233
|
+
return join3(baseDir, ".marscode");
|
|
191
1234
|
case "cli":
|
|
192
|
-
return
|
|
1235
|
+
return join3(baseDir, ".traecli");
|
|
193
1236
|
default:
|
|
194
|
-
return
|
|
1237
|
+
return join3(baseDir, ".trae");
|
|
195
1238
|
}
|
|
196
1239
|
}
|
|
197
1240
|
case "cursor":
|
|
198
|
-
return
|
|
1241
|
+
return join3(baseDir, ".cursor");
|
|
199
1242
|
case "copilot":
|
|
200
|
-
return
|
|
1243
|
+
return join3(baseDir, ".github");
|
|
1244
|
+
default:
|
|
1245
|
+
throw new Error(
|
|
1246
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1247
|
+
);
|
|
201
1248
|
}
|
|
202
1249
|
}
|
|
203
1250
|
function getConfigFileName(tool) {
|
|
204
1251
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
1252
|
}
|
|
1253
|
+
function getAgentsFileName(tool) {
|
|
1254
|
+
return getConfigFileName(tool);
|
|
1255
|
+
}
|
|
206
1256
|
function getSkillsDir(configRoot) {
|
|
207
|
-
return
|
|
1257
|
+
return join3(configRoot, "skills");
|
|
208
1258
|
}
|
|
209
1259
|
function getAgentsDir(configRoot) {
|
|
210
|
-
return
|
|
1260
|
+
return join3(configRoot, "agents");
|
|
211
1261
|
}
|
|
212
1262
|
function getManifestPath(configRoot) {
|
|
213
|
-
return
|
|
1263
|
+
return join3(configRoot, "cannbot-manifest.json");
|
|
214
1264
|
}
|
|
215
1265
|
function getCannbotConfigDir() {
|
|
216
|
-
return
|
|
1266
|
+
return join3(homedir(), ".cannbot");
|
|
217
1267
|
}
|
|
218
1268
|
function getCannbotRepoPath() {
|
|
219
|
-
return
|
|
1269
|
+
return join3(getCannbotConfigDir(), "repo");
|
|
220
1270
|
}
|
|
221
1271
|
|
|
222
1272
|
// src/core/detector.ts
|
|
@@ -232,8 +1282,9 @@ async function getCommandVersion(cmd, args = ["--version"]) {
|
|
|
232
1282
|
}
|
|
233
1283
|
async function getCommandPath(cmd) {
|
|
234
1284
|
try {
|
|
235
|
-
const
|
|
236
|
-
|
|
1285
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1286
|
+
const result = await execa(whichCmd, [cmd], { timeout: 5e3 });
|
|
1287
|
+
return result.stdout.trim().split("\n")[0];
|
|
237
1288
|
} catch {
|
|
238
1289
|
return void 0;
|
|
239
1290
|
}
|
|
@@ -254,12 +1305,12 @@ async function detectTrae() {
|
|
|
254
1305
|
const home = homedir2();
|
|
255
1306
|
const variant = detectTraeVariant();
|
|
256
1307
|
let path;
|
|
257
|
-
if (variant === "ide" &&
|
|
258
|
-
path =
|
|
259
|
-
} else if (variant === "plugin" &&
|
|
260
|
-
path =
|
|
261
|
-
} else if (variant === "cli" &&
|
|
262
|
-
path =
|
|
1308
|
+
if (variant === "ide" && existsSync4(join4(home, ".trae-cn"))) {
|
|
1309
|
+
path = join4(home, ".trae-cn");
|
|
1310
|
+
} else if (variant === "plugin" && existsSync4(join4(home, ".marscode"))) {
|
|
1311
|
+
path = join4(home, ".marscode");
|
|
1312
|
+
} else if (variant === "cli" && existsSync4(join4(home, ".traecli"))) {
|
|
1313
|
+
path = join4(home, ".traecli");
|
|
263
1314
|
} else if (variant === "unknown") {
|
|
264
1315
|
const cmdPath = await getCommandPath("trae");
|
|
265
1316
|
if (cmdPath) path = cmdPath;
|
|
@@ -275,7 +1326,7 @@ async function detectCursor() {
|
|
|
275
1326
|
}
|
|
276
1327
|
if (process.platform === "darwin") {
|
|
277
1328
|
const appPath = "/Applications/Cursor.app";
|
|
278
|
-
if (
|
|
1329
|
+
if (existsSync4(appPath)) {
|
|
279
1330
|
return { name: "cursor", path: appPath };
|
|
280
1331
|
}
|
|
281
1332
|
}
|
|
@@ -319,17 +1370,17 @@ function getToolDisplayName(tool) {
|
|
|
319
1370
|
return names[tool];
|
|
320
1371
|
}
|
|
321
1372
|
function getAllTools() {
|
|
322
|
-
return
|
|
1373
|
+
return VALID_TOOLS;
|
|
323
1374
|
}
|
|
324
1375
|
|
|
325
1376
|
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as
|
|
327
|
-
import { join as
|
|
1377
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
1378
|
+
import { join as join5 } from "path";
|
|
328
1379
|
function readManifest(configRoot) {
|
|
329
1380
|
const manifestPath = getManifestPath(configRoot);
|
|
330
|
-
if (
|
|
1381
|
+
if (existsSync5(manifestPath)) {
|
|
331
1382
|
try {
|
|
332
|
-
const content =
|
|
1383
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
333
1384
|
return JSON.parse(content);
|
|
334
1385
|
} catch {
|
|
335
1386
|
}
|
|
@@ -340,18 +1391,18 @@ function readAllManifests(configRoot) {
|
|
|
340
1391
|
const manifests = [];
|
|
341
1392
|
const plugins = getAllPlugins();
|
|
342
1393
|
const standardPath = getManifestPath(configRoot);
|
|
343
|
-
if (
|
|
1394
|
+
if (existsSync5(standardPath)) {
|
|
344
1395
|
try {
|
|
345
|
-
const content =
|
|
1396
|
+
const content = readFileSync3(standardPath, "utf-8");
|
|
346
1397
|
manifests.push(JSON.parse(content));
|
|
347
1398
|
} catch {
|
|
348
1399
|
}
|
|
349
1400
|
}
|
|
350
1401
|
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath =
|
|
352
|
-
if (
|
|
1402
|
+
const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
|
|
1403
|
+
if (existsSync5(pluginManifestPath)) {
|
|
353
1404
|
try {
|
|
354
|
-
const content =
|
|
1405
|
+
const content = readFileSync3(pluginManifestPath, "utf-8");
|
|
355
1406
|
manifests.push(JSON.parse(content));
|
|
356
1407
|
} catch {
|
|
357
1408
|
}
|
|
@@ -362,7 +1413,7 @@ function readAllManifests(configRoot) {
|
|
|
362
1413
|
function scanInstalled() {
|
|
363
1414
|
const plugins = getAllPlugins();
|
|
364
1415
|
const installed = [];
|
|
365
|
-
const tools =
|
|
1416
|
+
const tools = VALID_TOOLS;
|
|
366
1417
|
const levels = ["project", "global"];
|
|
367
1418
|
for (const tool of tools) {
|
|
368
1419
|
for (const level of levels) {
|
|
@@ -371,6 +1422,9 @@ function scanInstalled() {
|
|
|
371
1422
|
for (const manifest of manifests) {
|
|
372
1423
|
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
1424
|
if (!plugin) continue;
|
|
1425
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
374
1428
|
const alreadyAdded = installed.some(
|
|
375
1429
|
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
1430
|
);
|
|
@@ -387,278 +1441,77 @@ function scanInstalled() {
|
|
|
387
1441
|
});
|
|
388
1442
|
}
|
|
389
1443
|
}
|
|
390
|
-
}
|
|
391
|
-
return installed;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// src/utils/i18n.ts
|
|
395
|
-
var zh_CN = {
|
|
396
|
-
wizard_title: "CANNBot Install Helper",
|
|
397
|
-
wizard_detect: "\u6B63\u5728\u68C0\u6D4B\u5DF2\u5B89\u88C5\u7684 AI \u7F16\u7A0B\u5DE5\u5177...",
|
|
398
|
-
wizard_select_tool: "\u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
399
|
-
wizard_select_level: "\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
|
|
400
|
-
wizard_select_plugins: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
401
|
-
wizard_confirm: "\u786E\u8BA4\u5B89\u88C5",
|
|
402
|
-
wizard_detected_tool: "\u68C0\u6D4B\u5230",
|
|
403
|
-
wizard_confirm_tool: "\u4F7F\u7528",
|
|
404
|
-
wizard_last_used: "\u4E0A\u6B21\u4F7F\u7528",
|
|
405
|
-
wizard_already_installed: "\u5DF2\u5B89\u88C5",
|
|
406
|
-
install_cloning: "\u6B63\u5728\u514B\u9686 cannbot-skills \u4ED3\u5E93...",
|
|
407
|
-
install_repo_ready: "\u4ED3\u5E93\u5C31\u7EEA",
|
|
408
|
-
install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
409
|
-
install_done: "\u5B89\u88C5\u5B8C\u6210",
|
|
410
|
-
install_success: "\u6210\u529F",
|
|
411
|
-
install_failed: "\u5931\u8D25",
|
|
412
|
-
install_already_installed: "\u5DF2\u5B89\u88C5",
|
|
413
|
-
install_reinstall_confirm: "\u662F\u5426\u91CD\u65B0\u5B89\u88C5\uFF08\u8986\u76D6\uFF09",
|
|
414
|
-
install_skip: "\u8DF3\u8FC7",
|
|
415
|
-
install_enhanced_next_steps: "\u4E0B\u4E00\u6B65",
|
|
416
|
-
install_enhanced_launch: "\u542F\u52A8",
|
|
417
|
-
install_enhanced_try: "\u8BD5\u8BD5",
|
|
418
|
-
install_enhanced_more: "\u66F4\u591A",
|
|
419
|
-
install_enhanced_check: "\u68C0\u67E5",
|
|
420
|
-
install_enhanced_docs: "\u6587\u6863",
|
|
421
|
-
update_title: "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 Skills",
|
|
422
|
-
update_updating: "\u6B63\u5728\u66F4\u65B0",
|
|
423
|
-
update_done: "\u66F4\u65B0\u5B8C\u6210",
|
|
424
|
-
update_no_plugins: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
425
|
-
doctor_title: "CANNBot Doctor",
|
|
426
|
-
doctor_tools: "AI \u5DE5\u5177",
|
|
427
|
-
doctor_plugins: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
428
|
-
doctor_links: "\u94FE\u63A5\u5B8C\u6574\u6027",
|
|
429
|
-
doctor_config: "\u914D\u7F6E\u6587\u4EF6",
|
|
430
|
-
doctor_result: "\u7ED3\u679C",
|
|
431
|
-
doctor_fix_title: "\u81EA\u52A8\u4FEE\u590D",
|
|
432
|
-
doctor_fix_cleaning: "\u6E05\u7406\u5931\u6548\u94FE\u63A5",
|
|
433
|
-
doctor_fix_rebuilding: "\u91CD\u5EFA\u76EE\u5F55",
|
|
434
|
-
doctor_fix_done: "\u4FEE\u590D\u5B8C\u6210",
|
|
435
|
-
info_title: "\u63D2\u4EF6\u8BE6\u60C5",
|
|
436
|
-
info_description: "\u63CF\u8FF0",
|
|
437
|
-
info_skills: "Skills",
|
|
438
|
-
info_agents: "Agents",
|
|
439
|
-
info_status: "\u72B6\u6001",
|
|
440
|
-
info_quickstart: "\u5FEB\u901F\u5F00\u59CB",
|
|
441
|
-
info_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
442
|
-
lang_title: "\u8BED\u8A00\u8BBE\u7F6E",
|
|
443
|
-
lang_current: "\u5F53\u524D\u8BED\u8A00",
|
|
444
|
-
lang_set: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A",
|
|
445
|
-
lang_invalid: "\u65E0\u6548\u7684\u8BED\u8A00\uFF0C\u652F\u6301: zh_CN, en_US",
|
|
446
|
-
list_title: "\u53EF\u7528\u573A\u666F",
|
|
447
|
-
list_id: "\u5E8F\u53F7",
|
|
448
|
-
list_name: "\u573A\u666F\u540D",
|
|
449
|
-
list_status: "\u72B6\u6001",
|
|
450
|
-
list_skills: "Skills",
|
|
451
|
-
list_agents: "Agents",
|
|
452
|
-
status_installed: "\u5DF2\u88C5",
|
|
453
|
-
status_not_installed: "\u672A\u88C5",
|
|
454
|
-
error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
455
|
-
error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
456
|
-
error_repo_not_found: "\u4ED3\u5E93\u672A\u627E\u5230",
|
|
457
|
-
quick_start: "\u542F\u52A8\u547D\u4EE4",
|
|
458
|
-
quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
|
|
459
|
-
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
460
|
-
skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
|
|
461
|
-
skill_already_installed: "\u5DF2\u5B89\u88C5",
|
|
462
|
-
skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
463
|
-
skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
|
|
464
|
-
skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
|
|
465
|
-
skill_list_title: "\u53EF\u7528 Skills",
|
|
466
|
-
wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
467
|
-
wizard_mode_plugin: "\u5B89\u88C5 Plugin",
|
|
468
|
-
wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
|
|
469
|
-
wizard_mode_skill: "\u5B89\u88C5 Skill",
|
|
470
|
-
wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
|
|
471
|
-
wizard_back: "\u8FD4\u56DE",
|
|
472
|
-
wizard_cancel: "\u9000\u51FA",
|
|
473
|
-
wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
474
|
-
wizard_back_to_reselect: "\u8FD4\u56DE",
|
|
475
|
-
status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
476
|
-
status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
477
|
-
status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
|
|
478
|
-
status_install_time: "\u5B89\u88C5\u65F6\u95F4",
|
|
479
|
-
install_installed_to: "\u5B89\u88C5\u5230",
|
|
480
|
-
install_skills_count: "Skills",
|
|
481
|
-
install_agents_count: "Agents"
|
|
482
|
-
};
|
|
483
|
-
var en_US = {
|
|
484
|
-
wizard_title: "CANNBot Skills Setup Wizard",
|
|
485
|
-
wizard_detect: "Detecting installed AI coding tools...",
|
|
486
|
-
wizard_select_tool: "Select AI coding tool",
|
|
487
|
-
wizard_select_level: "Select install level",
|
|
488
|
-
wizard_select_plugins: "Select scenarios to install",
|
|
489
|
-
wizard_confirm: "Confirm installation",
|
|
490
|
-
wizard_detected_tool: "Detected",
|
|
491
|
-
wizard_confirm_tool: "Confirm using",
|
|
492
|
-
wizard_last_used: "last used",
|
|
493
|
-
wizard_already_installed: "installed",
|
|
494
|
-
install_cloning: "Cloning cannbot-skills repository...",
|
|
495
|
-
install_repo_ready: "Repository ready",
|
|
496
|
-
install_progress: "Installing",
|
|
497
|
-
install_done: "Installation complete",
|
|
498
|
-
install_success: "success",
|
|
499
|
-
install_failed: "failed",
|
|
500
|
-
install_already_installed: "already installed",
|
|
501
|
-
install_reinstall_confirm: "Reinstall (overwrite)",
|
|
502
|
-
install_skip: "skipped",
|
|
503
|
-
install_enhanced_next_steps: "Next steps",
|
|
504
|
-
install_enhanced_launch: "Launch",
|
|
505
|
-
install_enhanced_try: "Try",
|
|
506
|
-
install_enhanced_more: "More",
|
|
507
|
-
install_enhanced_check: "Check",
|
|
508
|
-
install_enhanced_docs: "Docs",
|
|
509
|
-
update_title: "Update installed Skills",
|
|
510
|
-
update_updating: "Updating",
|
|
511
|
-
update_done: "Update complete",
|
|
512
|
-
update_no_plugins: "No installed plugins",
|
|
513
|
-
doctor_title: "CANNBot Doctor",
|
|
514
|
-
doctor_tools: "AI Tools",
|
|
515
|
-
doctor_plugins: "Installed Plugins",
|
|
516
|
-
doctor_links: "Link Integrity",
|
|
517
|
-
doctor_config: "Configuration",
|
|
518
|
-
doctor_result: "Result",
|
|
519
|
-
doctor_fix_title: "Auto-fix",
|
|
520
|
-
doctor_fix_cleaning: "Cleaning broken links",
|
|
521
|
-
doctor_fix_rebuilding: "Rebuilding directories",
|
|
522
|
-
doctor_fix_done: "Fix complete",
|
|
523
|
-
info_title: "Plugin Details",
|
|
524
|
-
info_description: "Description",
|
|
525
|
-
info_skills: "Skills",
|
|
526
|
-
info_agents: "Agents",
|
|
527
|
-
info_status: "Status",
|
|
528
|
-
info_quickstart: "Quick Start",
|
|
529
|
-
info_not_found: "Plugin not found",
|
|
530
|
-
lang_title: "Language Settings",
|
|
531
|
-
lang_current: "Current language",
|
|
532
|
-
lang_set: "Language set to",
|
|
533
|
-
lang_invalid: "Invalid language, supported: zh_CN, en_US",
|
|
534
|
-
list_title: "Available Scenarios",
|
|
535
|
-
list_id: "#",
|
|
536
|
-
list_name: "Name",
|
|
537
|
-
list_status: "Status",
|
|
538
|
-
list_skills: "Skills",
|
|
539
|
-
list_agents: "Agents",
|
|
540
|
-
status_installed: "installed",
|
|
541
|
-
status_not_installed: "not installed",
|
|
542
|
-
error_tool_not_found: "No AI coding tool detected",
|
|
543
|
-
error_plugin_not_found: "Plugin not found",
|
|
544
|
-
error_repo_not_found: "Repository not found",
|
|
545
|
-
quick_start: "Launch command",
|
|
546
|
-
quick_start_hint: "Try: help me develop an Abs operator",
|
|
547
|
-
skill_select_category: "Select Skill category",
|
|
548
|
-
skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
|
|
549
|
-
skill_already_installed: "installed",
|
|
550
|
-
skill_install_progress: "Installing",
|
|
551
|
-
skill_install_done: "Skill installation complete",
|
|
552
|
-
skill_uninstall_done: "Skill uninstallation complete",
|
|
553
|
-
skill_list_title: "Available Skills",
|
|
554
|
-
wizard_select_mode: "Select installation mode",
|
|
555
|
-
wizard_mode_plugin: "Install Plugin",
|
|
556
|
-
wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
|
|
557
|
-
wizard_mode_skill: "Install Skill",
|
|
558
|
-
wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
|
|
559
|
-
wizard_back: "Go back",
|
|
560
|
-
wizard_cancel: "Cancel installation",
|
|
561
|
-
wizard_no_selection: "No items selected, please choose an action",
|
|
562
|
-
wizard_back_to_reselect: "Go back",
|
|
563
|
-
status_title: "Installed Plugins",
|
|
564
|
-
status_none: "No installed plugins",
|
|
565
|
-
status_hint: "Run install-helper init to start",
|
|
566
|
-
status_install_time: "Installed at",
|
|
567
|
-
install_installed_to: "Installed to",
|
|
568
|
-
install_skills_count: "Skills",
|
|
569
|
-
install_agents_count: "Agents"
|
|
570
|
-
};
|
|
571
|
-
var currentLang = "zh_CN";
|
|
572
|
-
function setLanguage(lang) {
|
|
573
|
-
currentLang = lang;
|
|
574
|
-
}
|
|
575
|
-
function t(key) {
|
|
576
|
-
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// src/utils/logger.ts
|
|
580
|
-
import chalk from "chalk";
|
|
581
|
-
import ora from "ora";
|
|
582
|
-
var logger = {
|
|
583
|
-
success: (msg) => console.log(chalk.green("\u2713") + " " + msg),
|
|
584
|
-
error: (msg) => console.log(chalk.red("\u2717") + " " + msg),
|
|
585
|
-
warn: (msg) => console.log(chalk.yellow("\u26A0") + " " + msg),
|
|
586
|
-
info: (msg) => console.log(chalk.cyan("\u2192") + " " + msg),
|
|
587
|
-
step: (msg) => console.log(chalk.dim(msg)),
|
|
588
|
-
blank: () => console.log()
|
|
589
|
-
};
|
|
590
|
-
function createSpinner(text) {
|
|
591
|
-
return ora({ text, color: "cyan" });
|
|
592
|
-
}
|
|
593
|
-
function printBanner(subtitle) {
|
|
594
|
-
console.log(chalk.cyan(`
|
|
595
|
-
____ _ _ _ _ _ ____ _
|
|
596
|
-
/ ___| / \\ | \\ | | \\ | | __ ) ___ | |_
|
|
597
|
-
| | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
|
|
598
|
-
| |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
|
|
599
|
-
\\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
|
|
600
|
-
`));
|
|
601
|
-
if (subtitle) {
|
|
602
|
-
console.log(chalk.bold(` ${subtitle}`));
|
|
603
|
-
console.log();
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
function getDisplayWidth(str) {
|
|
607
|
-
let width = 0;
|
|
608
|
-
for (const ch of str) {
|
|
609
|
-
const code = ch.codePointAt(0);
|
|
610
|
-
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) {
|
|
611
|
-
width += 2;
|
|
612
|
-
} else {
|
|
613
|
-
width += 1;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
return width;
|
|
617
|
-
}
|
|
618
|
-
function printBoxTitle(title, width = 65) {
|
|
619
|
-
const border = "\u2550".repeat(width);
|
|
620
|
-
const displayWidth = getDisplayWidth(title);
|
|
621
|
-
const totalPadding = width - displayWidth;
|
|
622
|
-
const leftPadding = Math.floor(totalPadding / 2);
|
|
623
|
-
const rightPadding = totalPadding - leftPadding;
|
|
624
|
-
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
625
|
-
console.log();
|
|
626
|
-
console.log(chalk.cyan.bold(` \u2554${border}\u2557`));
|
|
627
|
-
console.log(chalk.cyan.bold(` \u2551`) + chalk.cyan.bold(paddedTitle) + chalk.cyan.bold(`\u2551`));
|
|
628
|
-
console.log(chalk.cyan.bold(` \u255A${border}\u255D`));
|
|
629
|
-
console.log();
|
|
1444
|
+
}
|
|
1445
|
+
return installed;
|
|
630
1446
|
}
|
|
631
|
-
function
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
1447
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
1448
|
+
const skillsDir = join5(configRoot, "skills");
|
|
1449
|
+
const agentsDir = join5(configRoot, "agents");
|
|
1450
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
1451
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
1452
|
+
const skillPath = join5(skillsDir, skillId);
|
|
1453
|
+
return existsSync5(skillPath);
|
|
1454
|
+
});
|
|
1455
|
+
if (!hasAnySkill) return false;
|
|
1456
|
+
}
|
|
1457
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
1458
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
1459
|
+
const agentPath = join5(agentsDir, agentId);
|
|
1460
|
+
return existsSync5(agentPath);
|
|
1461
|
+
});
|
|
1462
|
+
if (!hasAnyAgent) return false;
|
|
1463
|
+
}
|
|
1464
|
+
let agentsMdPath;
|
|
1465
|
+
if (tool === "opencode" && level === "project") {
|
|
1466
|
+
agentsMdPath = join5(configRoot, "..", "AGENTS.md");
|
|
635
1467
|
} else {
|
|
636
|
-
|
|
637
|
-
`);
|
|
1468
|
+
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
638
1469
|
}
|
|
1470
|
+
if (!existsSync5(agentsMdPath)) return false;
|
|
1471
|
+
return true;
|
|
639
1472
|
}
|
|
640
1473
|
|
|
641
1474
|
// src/utils/config.ts
|
|
642
|
-
import { existsSync as
|
|
643
|
-
import { join as
|
|
644
|
-
import { homedir as homedir3 } from "os";
|
|
1475
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
1476
|
+
import { join as join6 } from "path";
|
|
645
1477
|
import { parse, stringify } from "yaml";
|
|
1478
|
+
|
|
1479
|
+
// src/utils/fs.ts
|
|
1480
|
+
import { writeFileSync, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
1481
|
+
import { dirname as dirname3 } from "path";
|
|
1482
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
1483
|
+
const dir = dirname3(filePath);
|
|
1484
|
+
if (!existsSync6(dir)) {
|
|
1485
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
1486
|
+
}
|
|
1487
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1488
|
+
writeFileSync(tmpPath, data, encoding);
|
|
1489
|
+
if (existsSync6(filePath)) {
|
|
1490
|
+
try {
|
|
1491
|
+
unlinkSync(filePath);
|
|
1492
|
+
} catch {
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
renameSync(tmpPath, filePath);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// src/utils/config.ts
|
|
646
1499
|
function getConfigDir() {
|
|
647
|
-
return
|
|
1500
|
+
return getCannbotConfigDir();
|
|
648
1501
|
}
|
|
649
1502
|
function getConfigPath() {
|
|
650
|
-
return
|
|
1503
|
+
return join6(getConfigDir(), "config.yaml");
|
|
651
1504
|
}
|
|
652
1505
|
function readConfig() {
|
|
653
1506
|
const configPath = getConfigPath();
|
|
654
|
-
if (!
|
|
1507
|
+
if (!existsSync7(configPath)) {
|
|
655
1508
|
return {
|
|
656
1509
|
language: "zh_CN",
|
|
657
1510
|
installedPlugins: []
|
|
658
1511
|
};
|
|
659
1512
|
}
|
|
660
1513
|
try {
|
|
661
|
-
const content =
|
|
1514
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
662
1515
|
const parsed = parse(content);
|
|
663
1516
|
return {
|
|
664
1517
|
language: parsed.language || "zh_CN",
|
|
@@ -668,6 +1521,7 @@ function readConfig() {
|
|
|
668
1521
|
installedPlugins: parsed.installedPlugins || []
|
|
669
1522
|
};
|
|
670
1523
|
} catch {
|
|
1524
|
+
logger.warn(t("config_corrupted"));
|
|
671
1525
|
return {
|
|
672
1526
|
language: "zh_CN",
|
|
673
1527
|
installedPlugins: []
|
|
@@ -676,12 +1530,12 @@ function readConfig() {
|
|
|
676
1530
|
}
|
|
677
1531
|
function writeConfig(config) {
|
|
678
1532
|
const configDir = getConfigDir();
|
|
679
|
-
if (!
|
|
1533
|
+
if (!existsSync7(configDir)) {
|
|
680
1534
|
mkdirSync(configDir, { recursive: true });
|
|
681
1535
|
}
|
|
682
1536
|
const configPath = getConfigPath();
|
|
683
1537
|
const content = stringify(config);
|
|
684
|
-
|
|
1538
|
+
atomicWriteFileSync(configPath, content);
|
|
685
1539
|
}
|
|
686
1540
|
function updateConfig(updates) {
|
|
687
1541
|
const config = readConfig();
|
|
@@ -704,22 +1558,11 @@ function removeInstalledPlugin(pluginId) {
|
|
|
704
1558
|
writeConfig(config);
|
|
705
1559
|
}
|
|
706
1560
|
|
|
707
|
-
// src/
|
|
708
|
-
var selectTheme = {
|
|
709
|
-
helpMode: "never"
|
|
710
|
-
};
|
|
711
|
-
var checkboxTheme = {
|
|
712
|
-
icon: {
|
|
713
|
-
checked: "\x1B[32m\u2611\x1B[22m",
|
|
714
|
-
unchecked: "\u2610",
|
|
715
|
-
cursor: "\u276F"
|
|
716
|
-
},
|
|
717
|
-
helpMode: "never"
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
// src/ui/wizard.ts
|
|
1561
|
+
// src/utils/constants.ts
|
|
721
1562
|
var BACK = "__back__";
|
|
722
1563
|
var CANCEL = "__cancel__";
|
|
1564
|
+
|
|
1565
|
+
// src/ui/wizard.ts
|
|
723
1566
|
async function selectToolWithDetection() {
|
|
724
1567
|
const config = readConfig();
|
|
725
1568
|
logger.blank();
|
|
@@ -789,7 +1632,7 @@ async function runWizard() {
|
|
|
789
1632
|
step = 2;
|
|
790
1633
|
break;
|
|
791
1634
|
}
|
|
792
|
-
if (result === CANCEL
|
|
1635
|
+
if (result === CANCEL) {
|
|
793
1636
|
return { language: config.language || "zh_CN", tool: selectedTool, level, plugins: [], confirmed: false };
|
|
794
1637
|
}
|
|
795
1638
|
updateConfig({ lastTool: selectedTool, lastLevel: level });
|
|
@@ -799,11 +1642,11 @@ async function runWizard() {
|
|
|
799
1642
|
}
|
|
800
1643
|
}
|
|
801
1644
|
async function stepTool(detectedTools, lastTool) {
|
|
802
|
-
printBoxTitle("
|
|
1645
|
+
printBoxTitle(t("wizard_step_1_title"));
|
|
803
1646
|
if (detectedTools.length === 0) {
|
|
804
1647
|
logger.error(t("error_tool_not_found"));
|
|
805
1648
|
const choices2 = [
|
|
806
|
-
...
|
|
1649
|
+
...VALID_TOOLS.map((tool) => ({
|
|
807
1650
|
name: `> ${getToolDisplayName(tool)}`,
|
|
808
1651
|
value: tool
|
|
809
1652
|
})),
|
|
@@ -874,10 +1717,10 @@ async function stepToolManual(lastTool) {
|
|
|
874
1717
|
return result;
|
|
875
1718
|
}
|
|
876
1719
|
async function stepLevel(lastLevel) {
|
|
877
|
-
printBoxTitle("
|
|
1720
|
+
printBoxTitle(t("wizard_step_2_title"));
|
|
878
1721
|
const choices = [
|
|
879
|
-
{ name:
|
|
880
|
-
{ name:
|
|
1722
|
+
{ name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
|
|
1723
|
+
{ name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
|
|
881
1724
|
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
882
1725
|
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
883
1726
|
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
@@ -895,7 +1738,7 @@ async function stepLevel(lastLevel) {
|
|
|
895
1738
|
return result;
|
|
896
1739
|
}
|
|
897
1740
|
async function stepPlugins(tool, level) {
|
|
898
|
-
printBoxTitle("
|
|
1741
|
+
printBoxTitle(t("wizard_step_3_title"));
|
|
899
1742
|
const plugins = getAllPlugins();
|
|
900
1743
|
const configRoot = getConfigRoot(tool, level);
|
|
901
1744
|
const manifests = readAllManifests(configRoot);
|
|
@@ -904,9 +1747,10 @@ async function stepPlugins(tool, level) {
|
|
|
904
1747
|
const isInstalled = installedSet.has(p.id);
|
|
905
1748
|
const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
|
|
906
1749
|
return {
|
|
907
|
-
name: `${p.displayName}${suffix}
|
|
1750
|
+
name: `${chalk3.cyan(p.displayName)}${suffix}`,
|
|
908
1751
|
value: p.id,
|
|
909
|
-
checked: false
|
|
1752
|
+
checked: false,
|
|
1753
|
+
description: p.description
|
|
910
1754
|
};
|
|
911
1755
|
});
|
|
912
1756
|
showOperationHints(true);
|
|
@@ -936,17 +1780,17 @@ async function stepPlugins(tool, level) {
|
|
|
936
1780
|
return pluginIds;
|
|
937
1781
|
}
|
|
938
1782
|
async function stepConfirm(tool, level, plugins) {
|
|
939
|
-
printBoxTitle("
|
|
1783
|
+
printBoxTitle(t("wizard_step_4_title"));
|
|
940
1784
|
const allPlugins = getAllPlugins();
|
|
941
1785
|
const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
|
|
942
1786
|
const displayPath = getConfigRoot(tool, level);
|
|
943
1787
|
const toolName = getToolDisplayName(tool);
|
|
944
1788
|
const levelText = level === "project" ? "project" : "global";
|
|
945
|
-
logger.info(
|
|
946
|
-
logger.info(
|
|
1789
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk3.bold(String(plugins.length))).replace("{tool}", chalk3.green(toolName)).replace("{level}", chalk3.cyan(levelText)));
|
|
1790
|
+
logger.info(`${t("wizard_install_path")}: ${chalk3.cyan(displayPath)}`);
|
|
947
1791
|
for (const plugin of selectedPlugins) {
|
|
948
1792
|
if (plugin) {
|
|
949
|
-
logger.step(` \u2022 ${
|
|
1793
|
+
logger.step(` \u2022 ${chalk3.bold(plugin.displayName)}`);
|
|
950
1794
|
}
|
|
951
1795
|
}
|
|
952
1796
|
logger.blank();
|
|
@@ -964,14 +1808,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
964
1808
|
}
|
|
965
1809
|
|
|
966
1810
|
// src/core/repository.ts
|
|
967
|
-
import { existsSync as
|
|
968
|
-
import { join as
|
|
1811
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1812
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
1813
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
969
1814
|
import { execa as execa2 } from "execa";
|
|
1815
|
+
import { parse as parseYaml3 } from "yaml";
|
|
970
1816
|
|
|
971
1817
|
// src/core/scanner.ts
|
|
972
|
-
import { existsSync as
|
|
973
|
-
import { join as
|
|
974
|
-
import {
|
|
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";
|
|
1820
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1821
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1822
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
1823
|
+
var __dirname3 = dirname4(__filename3);
|
|
975
1824
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
976
1825
|
"node_modules",
|
|
977
1826
|
".git",
|
|
@@ -992,46 +1841,70 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
992
1841
|
"docs",
|
|
993
1842
|
"scripts"
|
|
994
1843
|
]);
|
|
995
|
-
|
|
996
|
-
"
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
]
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1844
|
+
function loadScanConfig() {
|
|
1845
|
+
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
1846
|
+
try {
|
|
1847
|
+
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
1848
|
+
const ttlHours = config.scanCacheTtlHours || 24;
|
|
1849
|
+
return {
|
|
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
|
|
1853
|
+
};
|
|
1854
|
+
} catch {
|
|
1855
|
+
return {
|
|
1856
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1857
|
+
pluginDirs: ["plugins-official", "plugins-community"],
|
|
1858
|
+
cacheTtlMs: 24 * 60 * 60 * 1e3
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
var scanConfig = loadScanConfig();
|
|
1863
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
1864
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1006
1865
|
function scanSkills(repoPath) {
|
|
1007
1866
|
const skills = [];
|
|
1008
1867
|
const seen = /* @__PURE__ */ new Set();
|
|
1009
1868
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1010
|
-
const fullPath =
|
|
1011
|
-
if (!
|
|
1869
|
+
const fullPath = join7(repoPath, dir);
|
|
1870
|
+
if (!existsSync8(fullPath)) continue;
|
|
1012
1871
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1013
1872
|
}
|
|
1014
1873
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1015
|
-
const pluginParent =
|
|
1016
|
-
if (!
|
|
1874
|
+
const pluginParent = join7(repoPath, pluginDir);
|
|
1875
|
+
if (!existsSync8(pluginParent)) continue;
|
|
1017
1876
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1018
|
-
const pluginPath =
|
|
1877
|
+
const pluginPath = join7(pluginParent, plugin);
|
|
1019
1878
|
if (!isDirectory(pluginPath)) continue;
|
|
1020
|
-
const skillsDir =
|
|
1021
|
-
if (
|
|
1879
|
+
const skillsDir = join7(pluginPath, "skills");
|
|
1880
|
+
if (existsSync8(skillsDir)) {
|
|
1022
1881
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1023
1882
|
}
|
|
1024
|
-
const skillDir =
|
|
1025
|
-
if (
|
|
1883
|
+
const skillDir = join7(pluginPath, "skill");
|
|
1884
|
+
if (existsSync8(skillDir)) {
|
|
1026
1885
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1027
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
|
+
}
|
|
1028
1901
|
}
|
|
1029
1902
|
}
|
|
1030
1903
|
return skills;
|
|
1031
1904
|
}
|
|
1032
1905
|
function parseFrontmatter(filePath) {
|
|
1033
1906
|
try {
|
|
1034
|
-
const content =
|
|
1907
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1035
1908
|
const lines = content.split("\n");
|
|
1036
1909
|
if (lines.length < 3) return null;
|
|
1037
1910
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1044,11 +1917,15 @@ function parseFrontmatter(filePath) {
|
|
|
1044
1917
|
}
|
|
1045
1918
|
if (endIndex === -1) return null;
|
|
1046
1919
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1047
|
-
const parsed =
|
|
1920
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1048
1921
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1049
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
|
+
}
|
|
1050
1927
|
return {
|
|
1051
|
-
name:
|
|
1928
|
+
name: trimmedName,
|
|
1052
1929
|
description: (parsed.description || "").trim()
|
|
1053
1930
|
};
|
|
1054
1931
|
} catch {
|
|
@@ -1057,17 +1934,17 @@ function parseFrontmatter(filePath) {
|
|
|
1057
1934
|
}
|
|
1058
1935
|
function getCurrentCommit(repoPath) {
|
|
1059
1936
|
try {
|
|
1060
|
-
const headFile =
|
|
1061
|
-
if (!
|
|
1062
|
-
const headContent =
|
|
1937
|
+
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1938
|
+
if (!existsSync8(headFile)) return "unknown";
|
|
1939
|
+
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1063
1940
|
if (headContent.startsWith("ref: ")) {
|
|
1064
|
-
const refPath =
|
|
1065
|
-
if (
|
|
1066
|
-
return
|
|
1941
|
+
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1942
|
+
if (existsSync8(refPath)) {
|
|
1943
|
+
return readFileSync5(refPath, "utf-8").trim();
|
|
1067
1944
|
}
|
|
1068
|
-
const packedRefsPath =
|
|
1069
|
-
if (
|
|
1070
|
-
const packedRefs =
|
|
1945
|
+
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1946
|
+
if (existsSync8(packedRefsPath)) {
|
|
1947
|
+
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1071
1948
|
const refName = headContent.slice(5);
|
|
1072
1949
|
const lines = packedRefs.split("\n");
|
|
1073
1950
|
for (const line of lines) {
|
|
@@ -1085,12 +1962,12 @@ function getCurrentCommit(repoPath) {
|
|
|
1085
1962
|
function readScanCache() {
|
|
1086
1963
|
try {
|
|
1087
1964
|
const cachePath = getCachePath();
|
|
1088
|
-
if (!
|
|
1089
|
-
const content =
|
|
1965
|
+
if (!existsSync8(cachePath)) return null;
|
|
1966
|
+
const content = readFileSync5(cachePath, "utf-8");
|
|
1090
1967
|
const cache = JSON.parse(content);
|
|
1091
1968
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1092
1969
|
const age = Date.now() - cache.timestamp;
|
|
1093
|
-
if (age >
|
|
1970
|
+
if (age > scanConfig.cacheTtlMs) return null;
|
|
1094
1971
|
return cache;
|
|
1095
1972
|
} catch {
|
|
1096
1973
|
return null;
|
|
@@ -1099,39 +1976,24 @@ function readScanCache() {
|
|
|
1099
1976
|
function writeScanCache(cache) {
|
|
1100
1977
|
try {
|
|
1101
1978
|
const cachePath = getCachePath();
|
|
1102
|
-
const cacheDir =
|
|
1103
|
-
if (!
|
|
1979
|
+
const cacheDir = dirname4(cachePath);
|
|
1980
|
+
if (!existsSync8(cacheDir)) {
|
|
1104
1981
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1105
1982
|
}
|
|
1106
|
-
|
|
1983
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1107
1984
|
} catch {
|
|
1108
1985
|
}
|
|
1109
1986
|
}
|
|
1110
1987
|
function getCachePath() {
|
|
1111
|
-
|
|
1112
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
1988
|
+
return join7(getCannbotConfigDir(), "scan-cache.json");
|
|
1113
1989
|
}
|
|
1114
1990
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1115
|
-
const selfSkillMd = join5(dirPath, "SKILL.md");
|
|
1116
|
-
if (existsSync5(selfSkillMd)) {
|
|
1117
|
-
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1118
|
-
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1119
|
-
seen.add(frontmatter.name);
|
|
1120
|
-
skills.push({
|
|
1121
|
-
id: frontmatter.name,
|
|
1122
|
-
description: frontmatter.description,
|
|
1123
|
-
source: sourcePrefix,
|
|
1124
|
-
filePath: selfSkillMd
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
1991
|
for (const entry of safeReaddir(dirPath)) {
|
|
1130
|
-
const fullPath =
|
|
1992
|
+
const fullPath = join7(dirPath, entry);
|
|
1131
1993
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1132
1994
|
if (!isDirectory(fullPath)) continue;
|
|
1133
|
-
const skillMd =
|
|
1134
|
-
if (
|
|
1995
|
+
const skillMd = join7(fullPath, "SKILL.md");
|
|
1996
|
+
if (existsSync8(skillMd)) {
|
|
1135
1997
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1136
1998
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1137
1999
|
seen.add(frontmatter.name);
|
|
@@ -1144,38 +2006,32 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1144
2006
|
}
|
|
1145
2007
|
continue;
|
|
1146
2008
|
}
|
|
1147
|
-
scanDirectory(fullPath, repoPath, sourcePrefix, skills, seen);
|
|
2009
|
+
scanDirectory(fullPath, repoPath, sourcePrefix + "/" + entry, skills, seen);
|
|
1148
2010
|
}
|
|
1149
2011
|
}
|
|
1150
2012
|
function safeReaddir(dir) {
|
|
1151
2013
|
try {
|
|
1152
|
-
return
|
|
2014
|
+
return readdirSync2(dir);
|
|
1153
2015
|
} catch {
|
|
1154
2016
|
return [];
|
|
1155
2017
|
}
|
|
1156
2018
|
}
|
|
1157
|
-
function isDirectory(path) {
|
|
1158
|
-
try {
|
|
1159
|
-
return statSync(path).isDirectory();
|
|
1160
|
-
} catch {
|
|
1161
|
-
return false;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
2019
|
|
|
1165
2020
|
// src/core/skill-registry.ts
|
|
1166
2021
|
var CATEGORY_DEFS = [
|
|
1167
|
-
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
|
|
1168
|
-
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
|
|
1169
|
-
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
|
|
1170
|
-
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
|
|
1171
|
-
{ id: "ascendc", name: "AscendC \u5F00\u53D1" },
|
|
1172
|
-
{ id: "pypto", name: "PyPTO \u5F00\u53D1" },
|
|
1173
|
-
{ id: "tilelang", name: "TileLang \u5F00\u53D1" },
|
|
1174
|
-
{ id: "triton", name: "Triton \u5F00\u53D1" },
|
|
1175
|
-
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
|
|
1176
|
-
{ id: "
|
|
1177
|
-
{ id: "
|
|
1178
|
-
{ 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" }
|
|
1179
2035
|
];
|
|
1180
2036
|
var dynamicSkills = [];
|
|
1181
2037
|
var initialized = false;
|
|
@@ -1183,7 +2039,8 @@ function initFromScan(scanned) {
|
|
|
1183
2039
|
dynamicSkills = scanned.map((s) => ({
|
|
1184
2040
|
id: s.id,
|
|
1185
2041
|
description: s.description,
|
|
1186
|
-
source: s.source
|
|
2042
|
+
source: s.source,
|
|
2043
|
+
filePath: s.filePath
|
|
1187
2044
|
}));
|
|
1188
2045
|
initialized = true;
|
|
1189
2046
|
}
|
|
@@ -1259,9 +2116,7 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1259
2116
|
{ id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
|
|
1260
2117
|
{ id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
|
|
1261
2118
|
{ id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
|
|
1262
|
-
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1263
|
-
{ id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
|
|
1264
|
-
{ 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" }
|
|
1265
2120
|
]
|
|
1266
2121
|
},
|
|
1267
2122
|
{
|
|
@@ -1318,7 +2173,9 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1318
2173
|
{ id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
|
|
1319
2174
|
{ id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
|
|
1320
2175
|
{ id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
|
|
1321
|
-
{ 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" }
|
|
1322
2179
|
]
|
|
1323
2180
|
},
|
|
1324
2181
|
{
|
|
@@ -1334,6 +2191,13 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1334
2191
|
{ id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
|
|
1335
2192
|
]
|
|
1336
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
|
+
},
|
|
1337
2201
|
{
|
|
1338
2202
|
id: "platform",
|
|
1339
2203
|
name: "\u5E73\u53F0\u5DE5\u5177",
|
|
@@ -1377,9 +2241,10 @@ function getAllSkills() {
|
|
|
1377
2241
|
}
|
|
1378
2242
|
function getAllCategories() {
|
|
1379
2243
|
const skills = getActiveSkills();
|
|
2244
|
+
const isEn = t("list_name") === "Name";
|
|
1380
2245
|
return CATEGORY_DEFS.map((def) => ({
|
|
1381
2246
|
id: def.id,
|
|
1382
|
-
name: def.name,
|
|
2247
|
+
name: isEn ? def.nameEn : def.name,
|
|
1383
2248
|
skills: skills.filter((s) => {
|
|
1384
2249
|
const catId = CATEGORY_MAP[s.id];
|
|
1385
2250
|
if (catId) return catId === def.id;
|
|
@@ -1389,11 +2254,27 @@ function getAllCategories() {
|
|
|
1389
2254
|
}
|
|
1390
2255
|
|
|
1391
2256
|
// src/core/repository.ts
|
|
1392
|
-
var
|
|
2257
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
2258
|
+
var __dirname4 = dirname5(__filename4);
|
|
2259
|
+
function loadRepoUrl() {
|
|
2260
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
2261
|
+
return process.env.CANNBOT_REPO_URL;
|
|
2262
|
+
}
|
|
2263
|
+
const configPath = join8(__dirname4, "config", "repository.yaml");
|
|
2264
|
+
try {
|
|
2265
|
+
const config = parseYaml3(readFileSync6(configPath, "utf-8"));
|
|
2266
|
+
return config.repository?.url || "https://gitcode.com/yanhf/cann_skills.git";
|
|
2267
|
+
} catch {
|
|
2268
|
+
return "https://gitcode.com/yanhf/cann_skills.git";
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
var REPO_URL = loadRepoUrl();
|
|
1393
2272
|
var RepositoryManager = class {
|
|
1394
2273
|
repoPath;
|
|
2274
|
+
customPath;
|
|
1395
2275
|
constructor(customPath) {
|
|
1396
2276
|
this.repoPath = customPath;
|
|
2277
|
+
this.customPath = customPath;
|
|
1397
2278
|
}
|
|
1398
2279
|
async ensureRepo() {
|
|
1399
2280
|
if (this.repoPath && this.isValidRepo(this.repoPath)) {
|
|
@@ -1414,10 +2295,16 @@ var RepositoryManager = class {
|
|
|
1414
2295
|
this.repoPath = cachedPath;
|
|
1415
2296
|
return cachedPath;
|
|
1416
2297
|
}
|
|
1417
|
-
|
|
2298
|
+
const clonedPath = await this.cloneRepo();
|
|
2299
|
+
return clonedPath;
|
|
1418
2300
|
}
|
|
1419
2301
|
async ensureRepoAndScan() {
|
|
1420
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);
|
|
1421
2308
|
const cache = readScanCache();
|
|
1422
2309
|
if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
|
|
1423
2310
|
initFromScan(cache.skills);
|
|
@@ -1437,7 +2324,9 @@ var RepositoryManager = class {
|
|
|
1437
2324
|
try {
|
|
1438
2325
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1439
2326
|
} catch {
|
|
2327
|
+
logger.warn(t("repo_pull_failed"));
|
|
1440
2328
|
}
|
|
2329
|
+
mergeDynamicPlugins(repoPath);
|
|
1441
2330
|
}
|
|
1442
2331
|
getRepoPath() {
|
|
1443
2332
|
if (!this.repoPath) {
|
|
@@ -1446,18 +2335,18 @@ var RepositoryManager = class {
|
|
|
1446
2335
|
return this.repoPath;
|
|
1447
2336
|
}
|
|
1448
2337
|
isValidRepo(path) {
|
|
1449
|
-
if (!
|
|
1450
|
-
const gitDir =
|
|
1451
|
-
const pluginsDir =
|
|
1452
|
-
return
|
|
2338
|
+
if (!existsSync9(path)) return false;
|
|
2339
|
+
const gitDir = join8(path, ".git");
|
|
2340
|
+
const pluginsDir = join8(path, "plugins-official");
|
|
2341
|
+
return existsSync9(gitDir) && existsSync9(pluginsDir);
|
|
1453
2342
|
}
|
|
1454
2343
|
async cloneRepo() {
|
|
1455
2344
|
const configDir = getCannbotConfigDir();
|
|
1456
|
-
if (!
|
|
2345
|
+
if (!existsSync9(configDir)) {
|
|
1457
2346
|
mkdirSync3(configDir, { recursive: true });
|
|
1458
2347
|
}
|
|
1459
2348
|
const targetPath = getCannbotRepoPath();
|
|
1460
|
-
if (
|
|
2349
|
+
if (existsSync9(targetPath)) {
|
|
1461
2350
|
if (this.isValidRepo(targetPath)) {
|
|
1462
2351
|
this.repoPath = targetPath;
|
|
1463
2352
|
return targetPath;
|
|
@@ -1483,182 +2372,598 @@ function createRepositoryManager(customPath) {
|
|
|
1483
2372
|
}
|
|
1484
2373
|
|
|
1485
2374
|
// src/core/installer.ts
|
|
1486
|
-
import { existsSync as
|
|
1487
|
-
import { join as
|
|
1488
|
-
import { execa as
|
|
2375
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2376
|
+
import { join as join12, basename as basename2 } from "path";
|
|
2377
|
+
import { execa as execa4, execaSync } from "execa";
|
|
1489
2378
|
|
|
1490
2379
|
// src/core/record.ts
|
|
1491
|
-
import { existsSync as
|
|
1492
|
-
import { join as
|
|
2380
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
2381
|
+
import { join as join9 } from "path";
|
|
1493
2382
|
function getInstallsDir() {
|
|
1494
|
-
return
|
|
2383
|
+
return join9(getCannbotConfigDir(), "installs");
|
|
1495
2384
|
}
|
|
1496
2385
|
function getRecordPath(pluginId) {
|
|
1497
|
-
return
|
|
2386
|
+
return join9(getInstallsDir(), `${pluginId}.json`);
|
|
1498
2387
|
}
|
|
1499
2388
|
function readRecord(pluginId) {
|
|
1500
2389
|
const recordPath = getRecordPath(pluginId);
|
|
1501
|
-
if (!
|
|
2390
|
+
if (!existsSync10(recordPath)) {
|
|
1502
2391
|
return null;
|
|
1503
2392
|
}
|
|
1504
2393
|
try {
|
|
1505
|
-
const content =
|
|
2394
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1506
2395
|
return JSON.parse(content);
|
|
1507
2396
|
} catch {
|
|
2397
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1508
2398
|
return null;
|
|
1509
2399
|
}
|
|
1510
2400
|
}
|
|
1511
2401
|
function writeRecord(record) {
|
|
1512
2402
|
const installsDir = getInstallsDir();
|
|
1513
|
-
if (!
|
|
2403
|
+
if (!existsSync10(installsDir)) {
|
|
1514
2404
|
mkdirSync4(installsDir, { recursive: true });
|
|
1515
2405
|
}
|
|
1516
2406
|
const recordPath = getRecordPath(record.pluginId);
|
|
1517
|
-
|
|
2407
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1518
2408
|
}
|
|
1519
2409
|
function deleteRecord(pluginId) {
|
|
1520
2410
|
const recordPath = getRecordPath(pluginId);
|
|
1521
|
-
if (
|
|
1522
|
-
|
|
2411
|
+
if (existsSync10(recordPath)) {
|
|
2412
|
+
unlinkSync2(recordPath);
|
|
1523
2413
|
}
|
|
1524
2414
|
}
|
|
1525
|
-
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
2415
|
+
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames) {
|
|
1526
2416
|
const files = [];
|
|
1527
2417
|
const directories = [];
|
|
1528
2418
|
if (manifest) {
|
|
1529
|
-
const skillsDir =
|
|
1530
|
-
if (
|
|
2419
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2420
|
+
if (existsSync10(skillsDir)) {
|
|
1531
2421
|
directories.push(skillsDir);
|
|
1532
2422
|
for (const skillName of manifest.installed_skills || []) {
|
|
1533
|
-
const skillPath =
|
|
1534
|
-
if (
|
|
2423
|
+
const skillPath = join9(skillsDir, skillName);
|
|
2424
|
+
if (existsSync10(skillPath) || isSymlink(skillPath)) {
|
|
1535
2425
|
files.push(skillPath);
|
|
1536
2426
|
}
|
|
1537
2427
|
}
|
|
1538
2428
|
}
|
|
1539
|
-
const agentsDir =
|
|
1540
|
-
if (
|
|
2429
|
+
const agentsDir = join9(configRoot, "agents");
|
|
2430
|
+
if (existsSync10(agentsDir)) {
|
|
1541
2431
|
directories.push(agentsDir);
|
|
1542
2432
|
for (const agentName of manifest.installed_agents || []) {
|
|
1543
|
-
const agentPath =
|
|
1544
|
-
const agentPathMd =
|
|
1545
|
-
if (
|
|
2433
|
+
const agentPath = join9(agentsDir, agentName);
|
|
2434
|
+
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
2435
|
+
if (existsSync10(agentPath) || isSymlink(agentPath)) {
|
|
1546
2436
|
files.push(agentPath);
|
|
1547
|
-
} else if (
|
|
2437
|
+
} else if (existsSync10(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1548
2438
|
files.push(agentPathMd);
|
|
1549
2439
|
}
|
|
1550
2440
|
}
|
|
1551
2441
|
}
|
|
1552
2442
|
}
|
|
1553
|
-
const workflowsLink =
|
|
2443
|
+
const workflowsLink = join9(configRoot, "workflows");
|
|
1554
2444
|
if (isSymlink(workflowsLink)) {
|
|
1555
2445
|
files.push(workflowsLink);
|
|
1556
2446
|
}
|
|
1557
|
-
const manifestPath =
|
|
1558
|
-
if (
|
|
2447
|
+
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
2448
|
+
if (existsSync10(manifestPath)) {
|
|
1559
2449
|
files.push(manifestPath);
|
|
1560
2450
|
}
|
|
1561
|
-
const pluginManifestPath =
|
|
1562
|
-
if (
|
|
2451
|
+
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
2452
|
+
if (existsSync10(pluginManifestPath)) {
|
|
1563
2453
|
files.push(pluginManifestPath);
|
|
1564
2454
|
}
|
|
1565
2455
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1566
|
-
const configFilePath = level === "project" ?
|
|
1567
|
-
if (
|
|
2456
|
+
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
2457
|
+
if (existsSync10(configFilePath)) {
|
|
1568
2458
|
files.push(configFilePath);
|
|
1569
2459
|
}
|
|
1570
|
-
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
2460
|
+
const repoLinks = externalRepoNames && externalRepoNames.length > 0 ? externalRepoNames : ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1571
2461
|
for (const repoName of repoLinks) {
|
|
1572
|
-
const repoLinkPath =
|
|
2462
|
+
const repoLinkPath = join9(installPath, repoName);
|
|
1573
2463
|
if (isSymlink(repoLinkPath)) {
|
|
1574
2464
|
files.push(repoLinkPath);
|
|
1575
2465
|
}
|
|
1576
|
-
const repoLinkInConfig =
|
|
2466
|
+
const repoLinkInConfig = join9(configRoot, repoName);
|
|
1577
2467
|
if (isSymlink(repoLinkInConfig)) {
|
|
1578
2468
|
files.push(repoLinkInConfig);
|
|
1579
2469
|
}
|
|
1580
2470
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
2471
|
+
return {
|
|
2472
|
+
pluginId,
|
|
2473
|
+
displayName,
|
|
2474
|
+
tool,
|
|
2475
|
+
level,
|
|
2476
|
+
installPath,
|
|
2477
|
+
configRoot,
|
|
2478
|
+
installTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2479
|
+
files,
|
|
2480
|
+
directories
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
function getSkillRecordPath() {
|
|
2484
|
+
return join9(getInstallsDir(), "skills.json");
|
|
2485
|
+
}
|
|
2486
|
+
function readSkillRecord() {
|
|
2487
|
+
const recordPath = getSkillRecordPath();
|
|
2488
|
+
if (!existsSync10(recordPath)) {
|
|
2489
|
+
return {};
|
|
2490
|
+
}
|
|
2491
|
+
try {
|
|
2492
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
2493
|
+
return JSON.parse(content);
|
|
2494
|
+
} catch {
|
|
2495
|
+
logger.warn(t("skill_record_corrupted"));
|
|
2496
|
+
return {};
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
function writeSkillRecord(record) {
|
|
2500
|
+
const installsDir = getInstallsDir();
|
|
2501
|
+
if (!existsSync10(installsDir)) {
|
|
2502
|
+
mkdirSync4(installsDir, { recursive: true });
|
|
2503
|
+
}
|
|
2504
|
+
const recordPath = getSkillRecordPath();
|
|
2505
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
2506
|
+
}
|
|
2507
|
+
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
2508
|
+
const record = readSkillRecord();
|
|
2509
|
+
if (!record[tool]) record[tool] = {};
|
|
2510
|
+
if (!record[tool][level]) record[tool][level] = {};
|
|
2511
|
+
if (!record[tool][level][installPath]) {
|
|
2512
|
+
record[tool][level][installPath] = { skills: [], installTime: "" };
|
|
2513
|
+
}
|
|
2514
|
+
const entry = record[tool][level][installPath];
|
|
2515
|
+
for (const id of skillIds) {
|
|
2516
|
+
if (!entry.skills.includes(id)) {
|
|
2517
|
+
entry.skills.push(id);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
entry.installTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
2521
|
+
writeSkillRecord(record);
|
|
2522
|
+
}
|
|
2523
|
+
function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
2524
|
+
const record = readSkillRecord();
|
|
2525
|
+
if (!record[tool]?.[level]?.[installPath]) return;
|
|
2526
|
+
const entry = record[tool][level][installPath];
|
|
2527
|
+
entry.skills = entry.skills.filter((id) => !skillIds.includes(id));
|
|
2528
|
+
if (entry.skills.length === 0) {
|
|
2529
|
+
delete record[tool][level][installPath];
|
|
2530
|
+
if (Object.keys(record[tool][level]).length === 0) {
|
|
2531
|
+
delete record[tool][level];
|
|
2532
|
+
if (Object.keys(record[tool]).length === 0) {
|
|
2533
|
+
delete record[tool];
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
writeSkillRecord(record);
|
|
2538
|
+
}
|
|
2539
|
+
function getInstalledSkills(tool, level, installPath) {
|
|
2540
|
+
const record = readSkillRecord();
|
|
2541
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
2542
|
+
const configRoot = getConfigRoot(tool, level);
|
|
2543
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2544
|
+
return recordedSkills.filter((skillId) => {
|
|
2545
|
+
const skillPath = join9(skillsDir, skillId);
|
|
2546
|
+
return existsSync10(skillPath) || isSymlink(skillPath);
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
// src/core/backup.ts
|
|
2551
|
+
import { existsSync as existsSync11, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2552
|
+
import { join as join10 } from "path";
|
|
2553
|
+
function detectCurrentPlugin(configRoot, tool) {
|
|
2554
|
+
const manifest = readManifest(configRoot);
|
|
2555
|
+
if (manifest?.team) {
|
|
2556
|
+
const plugin = findPlugin(manifest.team);
|
|
2557
|
+
if (plugin) {
|
|
2558
|
+
return {
|
|
2559
|
+
pluginId: plugin.id,
|
|
2560
|
+
pluginName: plugin.displayName
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2565
|
+
if (!existsSync11(agentsFile)) {
|
|
2566
|
+
return null;
|
|
2567
|
+
}
|
|
2568
|
+
const allRecords = readAllRecords();
|
|
2569
|
+
for (const record of allRecords) {
|
|
2570
|
+
if (record.configRoot === configRoot && record.tool === tool) {
|
|
2571
|
+
const plugin = findPlugin(record.pluginId);
|
|
2572
|
+
if (plugin) {
|
|
2573
|
+
return {
|
|
2574
|
+
pluginId: plugin.id,
|
|
2575
|
+
pluginName: plugin.displayName
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
return null;
|
|
2581
|
+
}
|
|
2582
|
+
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
2583
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2584
|
+
if (!existsSync11(agentsFile)) {
|
|
2585
|
+
return null;
|
|
2586
|
+
}
|
|
2587
|
+
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
2588
|
+
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
2589
|
+
const backupPath = join10(configRoot, backupFileName);
|
|
2590
|
+
try {
|
|
2591
|
+
copyFileSync(agentsFile, backupPath);
|
|
2592
|
+
return {
|
|
2593
|
+
filePath: backupPath,
|
|
2594
|
+
pluginId: fromPluginId,
|
|
2595
|
+
pluginName: fromPluginName,
|
|
2596
|
+
backupTime: timestamp
|
|
2597
|
+
};
|
|
2598
|
+
} catch {
|
|
2599
|
+
return null;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
function findBackups(configRoot) {
|
|
2603
|
+
if (!existsSync11(configRoot)) {
|
|
2604
|
+
return [];
|
|
2605
|
+
}
|
|
2606
|
+
const files = readdirSync3(configRoot);
|
|
2607
|
+
const backups = [];
|
|
2608
|
+
for (const file of files) {
|
|
2609
|
+
if (file.includes(".cannbot-backup.")) {
|
|
2610
|
+
const parts = file.split(".cannbot-backup.");
|
|
2611
|
+
if (parts.length === 2) {
|
|
2612
|
+
const pluginIdAndTime = parts[1];
|
|
2613
|
+
const lastDotIndex = pluginIdAndTime.lastIndexOf(".");
|
|
2614
|
+
if (lastDotIndex > 0) {
|
|
2615
|
+
const pluginId = pluginIdAndTime.substring(0, lastDotIndex);
|
|
2616
|
+
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
2617
|
+
const plugin = findPlugin(pluginId);
|
|
2618
|
+
backups.push({
|
|
2619
|
+
filePath: join10(configRoot, file),
|
|
2620
|
+
pluginId,
|
|
2621
|
+
pluginName: plugin?.displayName || pluginId,
|
|
2622
|
+
backupTime: timestamp
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
return backups.sort((a, b) => b.backupTime.localeCompare(a.backupTime));
|
|
2629
|
+
}
|
|
2630
|
+
function restoreBackup(backupPath, configRoot, tool) {
|
|
2631
|
+
try {
|
|
2632
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2633
|
+
copyFileSync(backupPath, agentsFile);
|
|
2634
|
+
return true;
|
|
2635
|
+
} catch {
|
|
2636
|
+
return false;
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
function deleteBackup(backupPath) {
|
|
2640
|
+
try {
|
|
2641
|
+
if (existsSync11(backupPath)) {
|
|
2642
|
+
unlinkSync3(backupPath);
|
|
2643
|
+
return true;
|
|
2644
|
+
}
|
|
2645
|
+
return false;
|
|
2646
|
+
} catch {
|
|
2647
|
+
return false;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
function formatTimestamp(date) {
|
|
2651
|
+
const year = date.getFullYear();
|
|
2652
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
2653
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
2654
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
2655
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
2656
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
2657
|
+
return `${year}${month}${day}-${hours}${minutes}${seconds}`;
|
|
2658
|
+
}
|
|
2659
|
+
function readAllRecords() {
|
|
2660
|
+
const records = [];
|
|
2661
|
+
const plugins = getAllPlugins();
|
|
2662
|
+
for (const plugin of plugins) {
|
|
2663
|
+
const record = readRecord(plugin.id);
|
|
2664
|
+
if (record) {
|
|
2665
|
+
records.push(record);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return records;
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
// src/ui/backup-prompts.ts
|
|
2672
|
+
import { select as select2 } from "@inquirer/prompts";
|
|
2673
|
+
import chalk4 from "chalk";
|
|
2674
|
+
async function showOverwriteWarning(currentPluginName, newPluginName) {
|
|
2675
|
+
console.log();
|
|
2676
|
+
console.log(chalk4.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
|
|
2677
|
+
console.log();
|
|
2678
|
+
console.log(` ${t("backup_current")}: ${chalk4.cyan(currentPluginName)}`);
|
|
2679
|
+
console.log(` ${t("backup_installing")}: ${chalk4.cyan(newPluginName)}`);
|
|
2680
|
+
console.log();
|
|
2681
|
+
console.log(` ${t("backup_overwrite_warning")}`);
|
|
2682
|
+
console.log(` ${t("backup_auto_create")}`);
|
|
2683
|
+
console.log();
|
|
2684
|
+
const choice = await select2({
|
|
2685
|
+
message: t("backup_select_action"),
|
|
2686
|
+
choices: [
|
|
2687
|
+
{ name: t("backup_overwrite"), value: "overwrite" },
|
|
2688
|
+
{ name: t("backup_cancel"), value: "cancel" }
|
|
2689
|
+
]
|
|
2690
|
+
});
|
|
2691
|
+
return choice;
|
|
2692
|
+
}
|
|
2693
|
+
async function showRestorePrompt(backups) {
|
|
2694
|
+
console.log();
|
|
2695
|
+
console.log(chalk4.cyan(` ${t("backup_restore_prompt")}`));
|
|
2696
|
+
console.log();
|
|
2697
|
+
const choices = backups.map((b) => ({
|
|
2698
|
+
name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
|
|
2699
|
+
value: b.filePath
|
|
2700
|
+
}));
|
|
2701
|
+
choices.push({ name: t("backup_only_uninstall"), value: "none" });
|
|
2702
|
+
const choice = await select2({
|
|
2703
|
+
message: t("backup_select_action"),
|
|
2704
|
+
choices
|
|
2705
|
+
});
|
|
2706
|
+
return choice;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
// src/core/plugin-installer.ts
|
|
2710
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, realpathSync, copyFileSync as copyFileSync2, cpSync, writeFileSync as writeFileSync3, rmSync } from "fs";
|
|
2711
|
+
import { join as join11, basename } from "path";
|
|
2712
|
+
import { execa as execa3 } from "execa";
|
|
2713
|
+
function installLink(source, target) {
|
|
2714
|
+
try {
|
|
2715
|
+
const resolved = realpathSync(source);
|
|
2716
|
+
if (existsSync12(target) || isSymlink(target)) {
|
|
2717
|
+
unlinkSync4(target);
|
|
2718
|
+
}
|
|
2719
|
+
try {
|
|
2720
|
+
symlinkSync(resolved, target);
|
|
2721
|
+
return "symlink";
|
|
2722
|
+
} catch (e) {
|
|
2723
|
+
if (e.code === "EPERM") {
|
|
2724
|
+
cpSync(resolved, target, { recursive: true });
|
|
2725
|
+
return "copy";
|
|
2726
|
+
}
|
|
2727
|
+
throw e;
|
|
2728
|
+
}
|
|
2729
|
+
} catch {
|
|
2730
|
+
return "failed";
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
function installConfigFile(pluginDir, configFile, tool, level, configRoot, installPath, configRootConfigLink) {
|
|
2734
|
+
const sourcePath = join11(pluginDir, configFile);
|
|
2735
|
+
if (!existsSync12(sourcePath)) return;
|
|
2736
|
+
const agentsFileName = getAgentsFileName(tool);
|
|
2737
|
+
const isProjectLevel = level === "project";
|
|
2738
|
+
let primaryTarget;
|
|
2739
|
+
if (isProjectLevel) {
|
|
2740
|
+
primaryTarget = join11(installPath, agentsFileName);
|
|
2741
|
+
} else {
|
|
2742
|
+
primaryTarget = join11(configRoot, agentsFileName);
|
|
2743
|
+
}
|
|
2744
|
+
try {
|
|
2745
|
+
const resolvedSource = realpathSync(sourcePath);
|
|
2746
|
+
if (existsSync12(primaryTarget) || isSymlink(primaryTarget)) {
|
|
2747
|
+
unlinkSync4(primaryTarget);
|
|
2748
|
+
}
|
|
2749
|
+
try {
|
|
2750
|
+
symlinkSync(resolvedSource, primaryTarget);
|
|
2751
|
+
} catch (e) {
|
|
2752
|
+
if (e.code === "EPERM") {
|
|
2753
|
+
copyFileSync2(sourcePath, primaryTarget);
|
|
2754
|
+
} else {
|
|
2755
|
+
throw e;
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
} catch (e) {
|
|
2759
|
+
logger.warn(` Failed to install config file: ${e.message}`);
|
|
2760
|
+
}
|
|
2761
|
+
if (isProjectLevel && primaryTarget !== join11(configRoot, agentsFileName)) {
|
|
2762
|
+
if (configRootConfigLink === false) return;
|
|
2763
|
+
const configRootTarget = join11(configRoot, agentsFileName);
|
|
2764
|
+
if (existsSync12(configRootTarget) || isSymlink(configRootTarget)) {
|
|
2765
|
+
unlinkSync4(configRootTarget);
|
|
2766
|
+
}
|
|
2767
|
+
try {
|
|
2768
|
+
symlinkSync(realpathSync(sourcePath), configRootTarget);
|
|
2769
|
+
} catch (e) {
|
|
2770
|
+
if (e.code === "EPERM") {
|
|
2771
|
+
copyFileSync2(sourcePath, configRootTarget);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
async function installExternalRepos(repos, repoPath, configRoot, installPath, level) {
|
|
2777
|
+
if (!repos || repos.length === 0) return;
|
|
2778
|
+
for (const repo of repos) {
|
|
2779
|
+
const targetDir = join11(repoPath, repo.dir);
|
|
2780
|
+
if (!existsSync12(targetDir)) {
|
|
2781
|
+
const args = ["clone"];
|
|
2782
|
+
if (repo.depth) {
|
|
2783
|
+
args.push("--depth", String(repo.depth));
|
|
2784
|
+
}
|
|
2785
|
+
if (repo.recursive) {
|
|
2786
|
+
args.push("--recursive");
|
|
2787
|
+
}
|
|
2788
|
+
args.push(repo.url, targetDir);
|
|
2789
|
+
try {
|
|
2790
|
+
await execa3("git", args, { timeout: 12e4 });
|
|
2791
|
+
} catch {
|
|
2792
|
+
logger.warn(` git clone failed: ${repo.url}, clone manually: git clone ${repo.url} ${repo.dir}`);
|
|
2793
|
+
try {
|
|
2794
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
2795
|
+
} catch {
|
|
2796
|
+
}
|
|
2797
|
+
continue;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
const linkName = basename(repo.dir);
|
|
2801
|
+
const resolved = realpathSync(targetDir);
|
|
2802
|
+
const shouldConfigRootLink = repo.configRootLink !== false && (level === "global" || repo.configRootLink === true);
|
|
2803
|
+
if (shouldConfigRootLink) {
|
|
2804
|
+
const configRootLink = join11(configRoot, linkName);
|
|
2805
|
+
try {
|
|
2806
|
+
if (existsSync12(configRootLink) || isSymlink(configRootLink)) {
|
|
2807
|
+
unlinkSync4(configRootLink);
|
|
2808
|
+
}
|
|
2809
|
+
try {
|
|
2810
|
+
symlinkSync(resolved, configRootLink);
|
|
2811
|
+
} catch (e) {
|
|
2812
|
+
if (e.code === "EPERM") {
|
|
2813
|
+
cpSync(resolved, configRootLink, { recursive: true });
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
} catch {
|
|
2817
|
+
logger.warn(` Failed to link ${linkName} to config directory`);
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
if (level === "project") {
|
|
2821
|
+
const projectLink = join11(installPath, linkName);
|
|
2822
|
+
try {
|
|
2823
|
+
if (existsSync12(projectLink) || isSymlink(projectLink)) {
|
|
2824
|
+
unlinkSync4(projectLink);
|
|
2825
|
+
}
|
|
2826
|
+
try {
|
|
2827
|
+
symlinkSync(resolved, projectLink);
|
|
2828
|
+
} catch (e) {
|
|
2829
|
+
if (e.code === "EPERM") {
|
|
2830
|
+
cpSync(resolved, projectLink, { recursive: true });
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
} catch {
|
|
2834
|
+
logger.warn(` Failed to link ${linkName} to project directory`);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
1593
2838
|
}
|
|
1594
|
-
function
|
|
2839
|
+
function installWorkflows(pluginDir, configRoot) {
|
|
2840
|
+
const workflowsDir = join11(pluginDir, "workflows");
|
|
2841
|
+
if (!existsSync12(workflowsDir)) return;
|
|
2842
|
+
const targetPath = join11(configRoot, "workflows");
|
|
1595
2843
|
try {
|
|
1596
|
-
|
|
2844
|
+
const resolved = realpathSync(workflowsDir);
|
|
2845
|
+
if (existsSync12(targetPath) || isSymlink(targetPath)) {
|
|
2846
|
+
unlinkSync4(targetPath);
|
|
2847
|
+
}
|
|
2848
|
+
try {
|
|
2849
|
+
symlinkSync(resolved, targetPath);
|
|
2850
|
+
} catch (e) {
|
|
2851
|
+
if (e.code === "EPERM") {
|
|
2852
|
+
cpSync(resolved, targetPath, { recursive: true });
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
1597
2855
|
} catch {
|
|
1598
|
-
|
|
2856
|
+
logger.warn(` Failed to link workflows to config directory`);
|
|
1599
2857
|
}
|
|
1600
2858
|
}
|
|
1601
|
-
function
|
|
1602
|
-
|
|
2859
|
+
function writeCannbotManifest(configRoot, pluginId, version, level, tool, skills, agents) {
|
|
2860
|
+
const manifest = {
|
|
2861
|
+
brand: "CANNBot",
|
|
2862
|
+
version,
|
|
2863
|
+
team: pluginId,
|
|
2864
|
+
level,
|
|
2865
|
+
tool,
|
|
2866
|
+
installed_skills: skills,
|
|
2867
|
+
installed_agents: agents,
|
|
2868
|
+
brand_dir: configRoot,
|
|
2869
|
+
install_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2870
|
+
};
|
|
2871
|
+
const manifestPath = join11(configRoot, "cannbot-manifest.json");
|
|
2872
|
+
writeFileSync3(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
2873
|
+
return manifest;
|
|
1603
2874
|
}
|
|
1604
|
-
function
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
}
|
|
2875
|
+
async function installViaManifest(plugin, repoPath, tool, level, installPath) {
|
|
2876
|
+
const cwd = installPath || process.cwd();
|
|
2877
|
+
const configRoot = getConfigRoot(tool, level, installPath);
|
|
2878
|
+
const pluginDir = join11(repoPath, plugin.dir);
|
|
1609
2879
|
try {
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
2880
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2881
|
+
const agentsDir = join11(configRoot, "agents");
|
|
2882
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
2883
|
+
mkdirSync5(agentsDir, { recursive: true });
|
|
2884
|
+
const installedSkills = [];
|
|
2885
|
+
const installedAgents = [];
|
|
2886
|
+
for (const source of plugin.installSkills || []) {
|
|
2887
|
+
const sourceDir = join11(repoPath, source.dir);
|
|
2888
|
+
if (!existsSync12(sourceDir)) {
|
|
2889
|
+
logger.warn(` Skill source not found: ${source.dir}`);
|
|
2890
|
+
continue;
|
|
2891
|
+
}
|
|
2892
|
+
for (const skillEntry of source.skills) {
|
|
2893
|
+
const skillName = typeof skillEntry === "string" ? skillEntry : skillEntry.name;
|
|
2894
|
+
const skillAlias = typeof skillEntry === "string" ? skillName : skillEntry.as || skillName;
|
|
2895
|
+
const skillPath = join11(sourceDir, skillName);
|
|
2896
|
+
if (!existsSync12(skillPath)) {
|
|
2897
|
+
logger.warn(` Skill not found: ${skillName} in ${source.dir}`);
|
|
2898
|
+
continue;
|
|
2899
|
+
}
|
|
2900
|
+
const targetPath = join11(skillsDir, skillAlias);
|
|
2901
|
+
const result = installLink(skillPath, targetPath);
|
|
2902
|
+
if (result !== "failed") {
|
|
2903
|
+
installedSkills.push(skillAlias);
|
|
2904
|
+
} else {
|
|
2905
|
+
logger.warn(` Failed to install skill: ${skillAlias}`);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
1635
2908
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
if (Object.keys(record[tool]).length === 0) {
|
|
1650
|
-
delete record[tool];
|
|
2909
|
+
for (const agentName of plugin.installAgents || []) {
|
|
2910
|
+
const agentFile = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
|
|
2911
|
+
const agentPath = join11(pluginDir, "agents", agentFile);
|
|
2912
|
+
if (!existsSync12(agentPath)) {
|
|
2913
|
+
logger.warn(` Agent not found: ${agentFile}`);
|
|
2914
|
+
continue;
|
|
2915
|
+
}
|
|
2916
|
+
const targetPath = join11(agentsDir, agentFile);
|
|
2917
|
+
const result = installLink(agentPath, targetPath);
|
|
2918
|
+
if (result !== "failed") {
|
|
2919
|
+
installedAgents.push(agentFile);
|
|
2920
|
+
} else {
|
|
2921
|
+
logger.warn(` Failed to install agent: ${agentFile}`);
|
|
1651
2922
|
}
|
|
1652
2923
|
}
|
|
2924
|
+
installConfigFile(pluginDir, plugin.configFile || "AGENTS.md", tool, level, configRoot, cwd, plugin.configRootConfigLink);
|
|
2925
|
+
installWorkflows(pluginDir, configRoot);
|
|
2926
|
+
await installExternalRepos(plugin.externalRepos, repoPath, configRoot, cwd, level);
|
|
2927
|
+
const cannbotManifest = writeCannbotManifest(
|
|
2928
|
+
configRoot,
|
|
2929
|
+
plugin.id,
|
|
2930
|
+
plugin.version || "1.0.0",
|
|
2931
|
+
level,
|
|
2932
|
+
tool,
|
|
2933
|
+
installedSkills,
|
|
2934
|
+
installedAgents
|
|
2935
|
+
);
|
|
2936
|
+
return {
|
|
2937
|
+
success: true,
|
|
2938
|
+
skillsCount: installedSkills.length,
|
|
2939
|
+
agentsCount: installedAgents.length,
|
|
2940
|
+
errors: [],
|
|
2941
|
+
manifest: cannbotManifest
|
|
2942
|
+
};
|
|
2943
|
+
} catch (error) {
|
|
2944
|
+
return {
|
|
2945
|
+
success: false,
|
|
2946
|
+
skillsCount: 0,
|
|
2947
|
+
agentsCount: 0,
|
|
2948
|
+
errors: [error instanceof Error ? error.message : t("error_unknown")],
|
|
2949
|
+
manifest: null
|
|
2950
|
+
};
|
|
1653
2951
|
}
|
|
1654
|
-
writeSkillRecord(record);
|
|
1655
|
-
}
|
|
1656
|
-
function getInstalledSkills(tool, level, installPath) {
|
|
1657
|
-
const record = readSkillRecord();
|
|
1658
|
-
return record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1659
2952
|
}
|
|
1660
2953
|
|
|
1661
2954
|
// src/core/installer.ts
|
|
2955
|
+
function findShell() {
|
|
2956
|
+
const candidates = process.platform === "win32" ? ["bash", "sh"] : ["bash", "sh"];
|
|
2957
|
+
for (const cmd of candidates) {
|
|
2958
|
+
try {
|
|
2959
|
+
const detectCmd = process.platform === "win32" ? "where" : "which";
|
|
2960
|
+
execaSync(detectCmd, [cmd], { timeout: 3e3 });
|
|
2961
|
+
return cmd;
|
|
2962
|
+
} catch {
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
return null;
|
|
2966
|
+
}
|
|
1662
2967
|
async function installPlugin(opts) {
|
|
1663
2968
|
const plugin = getPluginById(opts.pluginId);
|
|
1664
2969
|
if (!plugin) {
|
|
@@ -1667,18 +2972,115 @@ async function installPlugin(opts) {
|
|
|
1667
2972
|
pluginId: opts.pluginId,
|
|
1668
2973
|
skillsCount: 0,
|
|
1669
2974
|
agentsCount: 0,
|
|
1670
|
-
errors: [
|
|
2975
|
+
errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
|
|
1671
2976
|
warnings: []
|
|
1672
2977
|
};
|
|
1673
2978
|
}
|
|
1674
|
-
const
|
|
1675
|
-
|
|
2979
|
+
const pluginDir = join12(opts.repoPath, plugin.dir);
|
|
2980
|
+
const cwd = opts.installPath || process.cwd();
|
|
2981
|
+
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
2982
|
+
const agentsFile = join12(configRoot, getAgentsFileName(opts.tool));
|
|
2983
|
+
let backupInfo = null;
|
|
2984
|
+
if (existsSync13(agentsFile)) {
|
|
2985
|
+
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
2986
|
+
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
2987
|
+
let choice = "overwrite";
|
|
2988
|
+
if (!opts.yes) {
|
|
2989
|
+
choice = await showOverwriteWarning(
|
|
2990
|
+
currentPlugin.pluginName,
|
|
2991
|
+
plugin.displayName
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
if (choice === "cancel") {
|
|
2995
|
+
logger.info(t("backup_cancel"));
|
|
2996
|
+
return {
|
|
2997
|
+
success: false,
|
|
2998
|
+
pluginId: opts.pluginId,
|
|
2999
|
+
skillsCount: 0,
|
|
3000
|
+
agentsCount: 0,
|
|
3001
|
+
errors: [t("backup_cancel")],
|
|
3002
|
+
warnings: []
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
backupInfo = createBackup(
|
|
3006
|
+
configRoot,
|
|
3007
|
+
opts.tool,
|
|
3008
|
+
currentPlugin.pluginId,
|
|
3009
|
+
currentPlugin.pluginName
|
|
3010
|
+
);
|
|
3011
|
+
if (!backupInfo) {
|
|
3012
|
+
logger.error(t("backup_failed"));
|
|
3013
|
+
return {
|
|
3014
|
+
success: false,
|
|
3015
|
+
pluginId: opts.pluginId,
|
|
3016
|
+
skillsCount: 0,
|
|
3017
|
+
agentsCount: 0,
|
|
3018
|
+
errors: [t("backup_failed")],
|
|
3019
|
+
warnings: []
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
if (plugin.installSkills && plugin.installSkills.length > 0) {
|
|
3026
|
+
const result = await installViaManifest(
|
|
3027
|
+
plugin,
|
|
3028
|
+
opts.repoPath,
|
|
3029
|
+
opts.tool,
|
|
3030
|
+
opts.level,
|
|
3031
|
+
opts.installPath
|
|
3032
|
+
);
|
|
3033
|
+
if (result.success) {
|
|
3034
|
+
try {
|
|
3035
|
+
const record = scanInstalledFiles(
|
|
3036
|
+
opts.pluginId,
|
|
3037
|
+
plugin.displayName,
|
|
3038
|
+
opts.tool,
|
|
3039
|
+
opts.level,
|
|
3040
|
+
cwd,
|
|
3041
|
+
configRoot,
|
|
3042
|
+
result.manifest,
|
|
3043
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
3044
|
+
);
|
|
3045
|
+
if (backupInfo) {
|
|
3046
|
+
record.backup = {
|
|
3047
|
+
filePath: backupInfo.filePath,
|
|
3048
|
+
fromPluginId: backupInfo.pluginId,
|
|
3049
|
+
fromPluginName: backupInfo.pluginName,
|
|
3050
|
+
backupTime: backupInfo.backupTime
|
|
3051
|
+
};
|
|
3052
|
+
}
|
|
3053
|
+
writeRecord(record);
|
|
3054
|
+
} catch {
|
|
3055
|
+
logger.warn(t("record_write_failed"));
|
|
3056
|
+
}
|
|
3057
|
+
return {
|
|
3058
|
+
success: true,
|
|
3059
|
+
pluginId: opts.pluginId,
|
|
3060
|
+
skillsCount: result.skillsCount,
|
|
3061
|
+
agentsCount: result.agentsCount,
|
|
3062
|
+
errors: result.errors,
|
|
3063
|
+
warnings: []
|
|
3064
|
+
};
|
|
3065
|
+
} else {
|
|
3066
|
+
return {
|
|
3067
|
+
success: false,
|
|
3068
|
+
pluginId: opts.pluginId,
|
|
3069
|
+
skillsCount: 0,
|
|
3070
|
+
agentsCount: 0,
|
|
3071
|
+
errors: result.errors,
|
|
3072
|
+
warnings: []
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
const scriptPath = join12(pluginDir, plugin.script);
|
|
3077
|
+
if (!existsSync13(scriptPath)) {
|
|
1676
3078
|
return {
|
|
1677
3079
|
success: false,
|
|
1678
3080
|
pluginId: opts.pluginId,
|
|
1679
3081
|
skillsCount: 0,
|
|
1680
3082
|
agentsCount: 0,
|
|
1681
|
-
errors: [
|
|
3083
|
+
errors: [t("error_script_not_found").replace("{path}", scriptPath)],
|
|
1682
3084
|
warnings: []
|
|
1683
3085
|
};
|
|
1684
3086
|
}
|
|
@@ -1686,14 +3088,25 @@ async function installPlugin(opts) {
|
|
|
1686
3088
|
if (opts.installPath) {
|
|
1687
3089
|
args.push(opts.installPath);
|
|
1688
3090
|
}
|
|
1689
|
-
const cwd = opts.installPath || process.cwd();
|
|
1690
3091
|
try {
|
|
1691
|
-
|
|
3092
|
+
const shell = findShell();
|
|
3093
|
+
if (!shell) {
|
|
3094
|
+
return {
|
|
3095
|
+
success: false,
|
|
3096
|
+
pluginId: opts.pluginId,
|
|
3097
|
+
skillsCount: 0,
|
|
3098
|
+
agentsCount: 0,
|
|
3099
|
+
errors: [t("error_no_shell")],
|
|
3100
|
+
warnings: []
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
const execOptions = {
|
|
1692
3104
|
cwd,
|
|
1693
3105
|
timeout: 3e5,
|
|
1694
|
-
stdio: "pipe"
|
|
1695
|
-
|
|
1696
|
-
|
|
3106
|
+
stdio: "pipe",
|
|
3107
|
+
input: opts.yes ? "y\n" : void 0
|
|
3108
|
+
};
|
|
3109
|
+
await execa4(shell, [scriptPath, ...args], execOptions);
|
|
1697
3110
|
const manifest = readManifest(configRoot);
|
|
1698
3111
|
let skillsCount = 0;
|
|
1699
3112
|
let agentsCount = 0;
|
|
@@ -1712,10 +3125,20 @@ async function installPlugin(opts) {
|
|
|
1712
3125
|
opts.level,
|
|
1713
3126
|
cwd,
|
|
1714
3127
|
configRoot,
|
|
1715
|
-
manifest
|
|
3128
|
+
manifest,
|
|
3129
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
1716
3130
|
);
|
|
3131
|
+
if (backupInfo) {
|
|
3132
|
+
record.backup = {
|
|
3133
|
+
filePath: backupInfo.filePath,
|
|
3134
|
+
fromPluginId: backupInfo.pluginId,
|
|
3135
|
+
fromPluginName: backupInfo.pluginName,
|
|
3136
|
+
backupTime: backupInfo.backupTime
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
1717
3139
|
writeRecord(record);
|
|
1718
3140
|
} catch {
|
|
3141
|
+
logger.warn(t("record_write_failed"));
|
|
1719
3142
|
}
|
|
1720
3143
|
return {
|
|
1721
3144
|
success: true,
|
|
@@ -1726,7 +3149,7 @@ async function installPlugin(opts) {
|
|
|
1726
3149
|
warnings: []
|
|
1727
3150
|
};
|
|
1728
3151
|
} catch (error) {
|
|
1729
|
-
const errorMessage = error instanceof Error ? error.message : "
|
|
3152
|
+
const errorMessage = error instanceof Error ? error.message : t("error_unknown");
|
|
1730
3153
|
return {
|
|
1731
3154
|
success: false,
|
|
1732
3155
|
pluginId: opts.pluginId,
|
|
@@ -1739,41 +3162,46 @@ async function installPlugin(opts) {
|
|
|
1739
3162
|
}
|
|
1740
3163
|
|
|
1741
3164
|
// src/core/skill-installer.ts
|
|
1742
|
-
import { existsSync as
|
|
1743
|
-
import { join as
|
|
1744
|
-
import { select as
|
|
1745
|
-
import
|
|
3165
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, unlinkSync as unlinkSync5, readdirSync as readdirSync4, rmdirSync, realpathSync as realpathSync2 } from "fs";
|
|
3166
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
3167
|
+
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
3168
|
+
import chalk5 from "chalk";
|
|
1746
3169
|
import Table from "cli-table3";
|
|
1747
3170
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
1748
3171
|
const configRoot = getConfigRoot(tool, level);
|
|
1749
|
-
const skillsDir =
|
|
3172
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1750
3173
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1751
|
-
if (!
|
|
1752
|
-
|
|
3174
|
+
if (!existsSync14(skillsDir)) {
|
|
3175
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
1753
3176
|
}
|
|
1754
3177
|
const results = [];
|
|
1755
3178
|
const installedIds = [];
|
|
1756
3179
|
for (const skillId of skillIds) {
|
|
1757
3180
|
const skill = findSkill(skillId);
|
|
1758
3181
|
if (!skill) {
|
|
1759
|
-
results.push({ skillId, success: false, error: "
|
|
3182
|
+
results.push({ skillId, success: false, error: t("error_skill_not_found") });
|
|
1760
3183
|
continue;
|
|
1761
3184
|
}
|
|
1762
|
-
const sourcePath =
|
|
1763
|
-
const targetPath =
|
|
1764
|
-
if (!
|
|
1765
|
-
results.push({ skillId, success: false, error:
|
|
3185
|
+
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
3186
|
+
const targetPath = join13(skillsDir, skillId);
|
|
3187
|
+
if (!existsSync14(sourcePath)) {
|
|
3188
|
+
results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
|
|
1766
3189
|
continue;
|
|
1767
3190
|
}
|
|
1768
3191
|
try {
|
|
1769
|
-
|
|
1770
|
-
|
|
3192
|
+
const resolvedSource = realpathSync2(sourcePath);
|
|
3193
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3194
|
+
unlinkSync5(targetPath);
|
|
1771
3195
|
}
|
|
1772
|
-
|
|
3196
|
+
symlinkSync2(resolvedSource, targetPath);
|
|
1773
3197
|
results.push({ skillId, success: true });
|
|
1774
3198
|
installedIds.push(skillId);
|
|
1775
3199
|
} catch (error) {
|
|
1776
|
-
|
|
3200
|
+
let msg = error instanceof Error ? error.message : t("error_unknown");
|
|
3201
|
+
if (process.platform === "win32" && msg.includes("EPERM")) {
|
|
3202
|
+
msg = t("error_symlink_windows");
|
|
3203
|
+
}
|
|
3204
|
+
results.push({ skillId, success: false, error: msg });
|
|
1777
3205
|
}
|
|
1778
3206
|
}
|
|
1779
3207
|
if (installedIds.length > 0) {
|
|
@@ -1783,106 +3211,118 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1783
3211
|
}
|
|
1784
3212
|
async function uninstallSkills(skillIds, tool, level) {
|
|
1785
3213
|
const configRoot = getConfigRoot(tool, level);
|
|
1786
|
-
const skillsDir =
|
|
3214
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1787
3215
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1788
3216
|
const results = [];
|
|
1789
3217
|
const removedIds = [];
|
|
1790
3218
|
for (const skillId of skillIds) {
|
|
1791
|
-
const targetPath =
|
|
3219
|
+
const targetPath = join13(skillsDir, skillId);
|
|
1792
3220
|
try {
|
|
1793
|
-
if (
|
|
1794
|
-
|
|
3221
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3222
|
+
unlinkSync5(targetPath);
|
|
1795
3223
|
results.push({ skillId, success: true });
|
|
1796
3224
|
removedIds.push(skillId);
|
|
1797
|
-
logger.step(`
|
|
3225
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
1798
3226
|
} else {
|
|
1799
|
-
results.push({ skillId, success: false, error: "
|
|
1800
|
-
logger.warn(`${skillId}
|
|
3227
|
+
results.push({ skillId, success: false, error: t("error_not_installed") });
|
|
3228
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
1801
3229
|
}
|
|
1802
3230
|
} catch (error) {
|
|
1803
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
3231
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
1804
3232
|
}
|
|
1805
3233
|
}
|
|
1806
3234
|
if (removedIds.length > 0) {
|
|
1807
3235
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
1808
3236
|
}
|
|
1809
|
-
if (
|
|
3237
|
+
if (existsSync14(skillsDir)) {
|
|
1810
3238
|
try {
|
|
1811
|
-
const entries =
|
|
3239
|
+
const entries = readdirSync4(skillsDir);
|
|
1812
3240
|
if (entries.length === 0) {
|
|
1813
3241
|
rmdirSync(skillsDir);
|
|
1814
|
-
logger.step(`
|
|
1815
|
-
}
|
|
1816
|
-
} catch {
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
if (existsSync9(configRoot)) {
|
|
1820
|
-
try {
|
|
1821
|
-
const entries = readdirSync3(configRoot);
|
|
1822
|
-
if (entries.length === 0) {
|
|
1823
|
-
rmdirSync(configRoot);
|
|
1824
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
3242
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
1825
3243
|
}
|
|
1826
3244
|
} catch {
|
|
1827
3245
|
}
|
|
1828
3246
|
}
|
|
1829
3247
|
return results;
|
|
1830
3248
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
3249
|
+
var SELECT_ALL = "__select_all__";
|
|
3250
|
+
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
3251
|
+
var CONTINUE_SELECT = "__continue_select__";
|
|
3252
|
+
async function interactiveSkillSelect(tool, level) {
|
|
1834
3253
|
const categories = getAllCategories();
|
|
3254
|
+
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
3255
|
+
let accumulated = [];
|
|
1835
3256
|
let step = 0;
|
|
1836
3257
|
let selectedCategoryId = "";
|
|
1837
3258
|
while (true) {
|
|
1838
3259
|
switch (step) {
|
|
1839
3260
|
case 0: {
|
|
1840
|
-
|
|
3261
|
+
const titleSuffix = accumulated.length > 0 ? `${t("wizard_step_3_skill_title")}\uFF08${t("skill_count_format").replace("{count}", String(accumulated.length))}\uFF09` : t("wizard_step_3_skill_title");
|
|
3262
|
+
printBoxTitle(titleSuffix);
|
|
1841
3263
|
const categoryChoices = [
|
|
3264
|
+
{
|
|
3265
|
+
name: `>> ${t("skill_select_all").replace("{count}", String(allSkillIds.length))}`,
|
|
3266
|
+
value: SELECT_ALL
|
|
3267
|
+
},
|
|
3268
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1842
3269
|
...categories.map((cat) => ({
|
|
1843
3270
|
name: `> ${cat.name} (${cat.skills.length} skills)`,
|
|
1844
3271
|
value: cat.id
|
|
1845
3272
|
})),
|
|
1846
3273
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1847
|
-
{ name:
|
|
1848
|
-
{ name: "
|
|
3274
|
+
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
3275
|
+
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
3276
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
1849
3277
|
];
|
|
1850
3278
|
showOperationHints();
|
|
1851
|
-
selectedCategoryId = await
|
|
3279
|
+
selectedCategoryId = await select3({
|
|
1852
3280
|
message: t("skill_select_category"),
|
|
1853
3281
|
choices: categoryChoices,
|
|
1854
3282
|
loop: false,
|
|
1855
3283
|
theme: selectTheme,
|
|
1856
3284
|
pageSize: 15
|
|
1857
3285
|
});
|
|
1858
|
-
if (selectedCategoryId ===
|
|
1859
|
-
if (selectedCategoryId ===
|
|
3286
|
+
if (selectedCategoryId === BACK) return "back";
|
|
3287
|
+
if (selectedCategoryId === CANCEL) return "cancel";
|
|
3288
|
+
if (selectedCategoryId === SELECT_ALL) {
|
|
3289
|
+
return allSkillIds;
|
|
3290
|
+
}
|
|
3291
|
+
if (selectedCategoryId === "install") {
|
|
3292
|
+
return accumulated;
|
|
3293
|
+
}
|
|
1860
3294
|
step = 1;
|
|
1861
3295
|
break;
|
|
1862
3296
|
}
|
|
1863
3297
|
case 1: {
|
|
1864
3298
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
1865
3299
|
if (!category) return [];
|
|
1866
|
-
printBoxTitle(
|
|
1867
|
-
const
|
|
1868
|
-
const
|
|
1869
|
-
const installedSkills = [];
|
|
1870
|
-
for (const tk of tools) {
|
|
1871
|
-
for (const l of levels) {
|
|
1872
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
1873
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
3300
|
+
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
3301
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
3302
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
1876
3303
|
const installedSet = new Set(installedSkills);
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
name:
|
|
1882
|
-
value:
|
|
1883
|
-
checked:
|
|
1884
|
-
|
|
1885
|
-
|
|
3304
|
+
const categorySkillIds = new Set(category.skills.map((s) => s.id));
|
|
3305
|
+
const accumulatedInCategory = new Set(accumulated.filter((id) => categorySkillIds.has(id)));
|
|
3306
|
+
const skillChoices = [
|
|
3307
|
+
{
|
|
3308
|
+
name: `>> ${t("skill_select_all_category")}`,
|
|
3309
|
+
value: SELECT_ALL_CATEGORY,
|
|
3310
|
+
checked: accumulatedInCategory.size === category.skills.length,
|
|
3311
|
+
description: t("skill_select_all_category")
|
|
3312
|
+
},
|
|
3313
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
3314
|
+
...category.skills.map((skill) => {
|
|
3315
|
+
const isInstalled = installedSet.has(skill.id);
|
|
3316
|
+
const isAccumulated = accumulatedInCategory.has(skill.id);
|
|
3317
|
+
const suffix = isInstalled ? ` [${t("skill_already_installed")}]` : isAccumulated ? " [+]" : "";
|
|
3318
|
+
return {
|
|
3319
|
+
name: `${chalk5.cyan(skill.id)}${suffix}`,
|
|
3320
|
+
value: skill.id,
|
|
3321
|
+
checked: isAccumulated,
|
|
3322
|
+
description: skill.description
|
|
3323
|
+
};
|
|
3324
|
+
})
|
|
3325
|
+
];
|
|
1886
3326
|
showOperationHints(true);
|
|
1887
3327
|
const selectedSkills = await checkbox2({
|
|
1888
3328
|
message: t("skill_select_items"),
|
|
@@ -1892,11 +3332,21 @@ async function interactiveSkillSelect() {
|
|
|
1892
3332
|
theme: checkboxTheme,
|
|
1893
3333
|
pageSize: 15
|
|
1894
3334
|
});
|
|
1895
|
-
|
|
3335
|
+
let skillIds;
|
|
3336
|
+
if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
|
|
3337
|
+
skillIds = category.skills.map((s) => s.id);
|
|
3338
|
+
} else {
|
|
3339
|
+
skillIds = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
|
|
3340
|
+
}
|
|
1896
3341
|
if (skillIds.length === 0) {
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
3342
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
3343
|
+
const action2 = await select3({
|
|
3344
|
+
message: accumulated.length > 0 ? t("skill_continue_or_install").replace("{count}", String(accumulated.length)) : t("wizard_no_selection"),
|
|
3345
|
+
choices: accumulated.length > 0 ? [
|
|
3346
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
3347
|
+
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
3348
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
3349
|
+
] : [
|
|
1900
3350
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1901
3351
|
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
1902
3352
|
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
@@ -1904,13 +3354,38 @@ async function interactiveSkillSelect() {
|
|
|
1904
3354
|
loop: false,
|
|
1905
3355
|
theme: selectTheme
|
|
1906
3356
|
});
|
|
1907
|
-
if (
|
|
3357
|
+
if (action2 === "install") return accumulated;
|
|
3358
|
+
if (action2 === "back") {
|
|
1908
3359
|
step = 0;
|
|
1909
3360
|
break;
|
|
1910
3361
|
}
|
|
1911
3362
|
return "cancel";
|
|
1912
3363
|
}
|
|
1913
|
-
|
|
3364
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
3365
|
+
for (const id of skillIds) {
|
|
3366
|
+
if (!accumulated.includes(id)) {
|
|
3367
|
+
accumulated.push(id);
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
const action = await select3({
|
|
3371
|
+
message: t("skill_continue_or_install").replace("{count}", String(accumulated.length)),
|
|
3372
|
+
choices: [
|
|
3373
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
3374
|
+
{ name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
|
|
3375
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
3376
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
3377
|
+
],
|
|
3378
|
+
loop: false,
|
|
3379
|
+
theme: selectTheme
|
|
3380
|
+
});
|
|
3381
|
+
if (action === "install") {
|
|
3382
|
+
return accumulated;
|
|
3383
|
+
}
|
|
3384
|
+
if (action === CONTINUE_SELECT) {
|
|
3385
|
+
step = 0;
|
|
3386
|
+
break;
|
|
3387
|
+
}
|
|
3388
|
+
return "cancel";
|
|
1914
3389
|
}
|
|
1915
3390
|
}
|
|
1916
3391
|
}
|
|
@@ -1918,11 +3393,11 @@ async function interactiveSkillSelect() {
|
|
|
1918
3393
|
function listAllSkills() {
|
|
1919
3394
|
const categories = getAllCategories();
|
|
1920
3395
|
console.log();
|
|
1921
|
-
console.log(
|
|
1922
|
-
console.log(
|
|
3396
|
+
console.log(chalk5.bold(` ${t("skill_list_title")}`));
|
|
3397
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
1923
3398
|
for (const category of categories) {
|
|
1924
3399
|
console.log();
|
|
1925
|
-
console.log(
|
|
3400
|
+
console.log(chalk5.bold(` ${category.name}`) + chalk5.dim(` (${category.skills.length} skills)`));
|
|
1926
3401
|
const table = new Table({
|
|
1927
3402
|
style: { head: [], border: [] },
|
|
1928
3403
|
colWidths: [35, 45],
|
|
@@ -1930,51 +3405,54 @@ function listAllSkills() {
|
|
|
1930
3405
|
});
|
|
1931
3406
|
for (const skill of category.skills) {
|
|
1932
3407
|
table.push([
|
|
1933
|
-
|
|
3408
|
+
chalk5.cyan(skill.id),
|
|
1934
3409
|
skill.description
|
|
1935
3410
|
]);
|
|
1936
3411
|
}
|
|
1937
3412
|
console.log(table.toString());
|
|
1938
3413
|
}
|
|
1939
3414
|
console.log();
|
|
1940
|
-
console.log(
|
|
3415
|
+
console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
1941
3416
|
console.log();
|
|
1942
3417
|
}
|
|
1943
|
-
function
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
3418
|
+
function resolveSkillSourcePath(skill, repoPath) {
|
|
3419
|
+
if (skill.filePath) {
|
|
3420
|
+
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join13(repoPath, skill.filePath);
|
|
3421
|
+
if (existsSync14(absPath)) {
|
|
3422
|
+
return dirname6(absPath);
|
|
3423
|
+
}
|
|
1948
3424
|
}
|
|
3425
|
+
return join13(repoPath, skill.source, skill.id);
|
|
1949
3426
|
}
|
|
1950
3427
|
|
|
1951
3428
|
// src/ui/display.ts
|
|
1952
|
-
import
|
|
3429
|
+
import chalk6 from "chalk";
|
|
1953
3430
|
import Table2 from "cli-table3";
|
|
1954
3431
|
function printPluginList(plugins, installed) {
|
|
1955
3432
|
const table = new Table2({
|
|
1956
3433
|
head: [
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
chalk4.cyan(t("list_agents"))
|
|
3434
|
+
chalk6.cyan(t("list_id")),
|
|
3435
|
+
chalk6.cyan(t("list_name")),
|
|
3436
|
+
chalk6.cyan(t("list_status")),
|
|
3437
|
+
chalk6.cyan(t("list_description"))
|
|
1962
3438
|
],
|
|
1963
|
-
style: { head: [], border: [] }
|
|
3439
|
+
style: { head: [], border: [] },
|
|
3440
|
+
colWidths: [6, 32, 8, 50],
|
|
3441
|
+
wordWrap: true
|
|
1964
3442
|
});
|
|
1965
3443
|
plugins.forEach((plugin, index) => {
|
|
1966
3444
|
const isInstalled = installed.has(plugin.id);
|
|
1967
|
-
const status = isInstalled ?
|
|
3445
|
+
const status = isInstalled ? chalk6.green(`\u2713 ${t("status_installed")}`) : chalk6.dim(`\u2014`);
|
|
1968
3446
|
table.push([
|
|
1969
3447
|
String(index + 1),
|
|
1970
3448
|
plugin.displayName,
|
|
1971
3449
|
status,
|
|
1972
|
-
|
|
1973
|
-
String(plugin.agents)
|
|
3450
|
+
plugin.description || chalk6.dim("\u2014")
|
|
1974
3451
|
]);
|
|
1975
3452
|
});
|
|
1976
3453
|
console.log();
|
|
1977
|
-
|
|
3454
|
+
const countSuffix = t("count_suffix");
|
|
3455
|
+
console.log(chalk6.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
1978
3456
|
console.log(table.toString());
|
|
1979
3457
|
console.log();
|
|
1980
3458
|
}
|
|
@@ -1983,7 +3461,7 @@ function printInstallSummary(results) {
|
|
|
1983
3461
|
const totalCount = results.length;
|
|
1984
3462
|
console.log();
|
|
1985
3463
|
console.log(
|
|
1986
|
-
|
|
3464
|
+
chalk6.bold(
|
|
1987
3465
|
` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
|
|
1988
3466
|
)
|
|
1989
3467
|
);
|
|
@@ -1991,10 +3469,10 @@ function printInstallSummary(results) {
|
|
|
1991
3469
|
for (const result of results) {
|
|
1992
3470
|
if (result.success) {
|
|
1993
3471
|
console.log(
|
|
1994
|
-
|
|
3472
|
+
chalk6.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
|
|
1995
3473
|
);
|
|
1996
3474
|
} else {
|
|
1997
|
-
console.log(
|
|
3475
|
+
console.log(chalk6.red(" \u2717") + ` ${result.displayName}`);
|
|
1998
3476
|
}
|
|
1999
3477
|
}
|
|
2000
3478
|
console.log();
|
|
@@ -2007,22 +3485,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2007
3485
|
return;
|
|
2008
3486
|
}
|
|
2009
3487
|
const toolName = getToolDisplayName(tool);
|
|
3488
|
+
const countSuffix = t("count_suffix");
|
|
2010
3489
|
const lines = [
|
|
2011
|
-
|
|
3490
|
+
chalk6.bold(` ${t("install_done")}!`),
|
|
2012
3491
|
"",
|
|
2013
|
-
` ${
|
|
2014
|
-
` ${
|
|
3492
|
+
` ${chalk6.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
3493
|
+
` ${chalk6.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk6.dim("|")} ${chalk6.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2015
3494
|
"",
|
|
2016
|
-
|
|
2017
|
-
` ${
|
|
2018
|
-
` ${
|
|
2019
|
-
` ${
|
|
2020
|
-
` ${
|
|
3495
|
+
chalk6.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
3496
|
+
` ${chalk6.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk6.green(toolName.toLowerCase())}`,
|
|
3497
|
+
` ${chalk6.cyan("2.")} ${t("install_enhanced_try")}: ${chalk6.green(t("try_prompt"))}`,
|
|
3498
|
+
` ${chalk6.cyan("3.")} ${t("install_enhanced_more")}: ${chalk6.green("install-helper list")}`,
|
|
3499
|
+
` ${chalk6.cyan("4.")} ${t("install_enhanced_check")}: ${chalk6.green("install-helper doctor")}`,
|
|
2021
3500
|
""
|
|
2022
3501
|
];
|
|
2023
3502
|
if (successResults.length === 1) {
|
|
3503
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
3504
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2024
3505
|
lines.push(
|
|
2025
|
-
` ${
|
|
3506
|
+
` ${chalk6.dim(t("install_enhanced_docs"))}: ${chalk6.dim(docsPath)}`
|
|
2026
3507
|
);
|
|
2027
3508
|
}
|
|
2028
3509
|
console.log();
|
|
@@ -2033,34 +3514,32 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2033
3514
|
}
|
|
2034
3515
|
|
|
2035
3516
|
// src/commands/init.ts
|
|
2036
|
-
import { existsSync as
|
|
2037
|
-
import { join as
|
|
2038
|
-
var BACK2 = "__back__";
|
|
2039
|
-
var CANCEL2 = "__cancel__";
|
|
3517
|
+
import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
|
|
3518
|
+
import { join as join14 } from "path";
|
|
2040
3519
|
async function initCommand() {
|
|
2041
3520
|
const cwd = process.cwd();
|
|
2042
|
-
if (
|
|
3521
|
+
if (existsSync15(join14(cwd, "package.json")) && (() => {
|
|
2043
3522
|
try {
|
|
2044
|
-
const pkg = JSON.parse(
|
|
3523
|
+
const pkg = JSON.parse(readFileSync8(join14(cwd, "package.json"), "utf-8"));
|
|
2045
3524
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2046
3525
|
} catch {
|
|
2047
3526
|
return false;
|
|
2048
3527
|
}
|
|
2049
3528
|
})()) {
|
|
2050
|
-
logger.warn("
|
|
2051
|
-
logger.info("
|
|
2052
|
-
logger.info("
|
|
3529
|
+
logger.warn(t("init_pkg_dir_warn"));
|
|
3530
|
+
logger.info(t("init_pkg_dir_hint"));
|
|
3531
|
+
logger.info(t("init_pkg_dir_example"));
|
|
2053
3532
|
return;
|
|
2054
3533
|
}
|
|
2055
3534
|
printBanner(t("wizard_title"));
|
|
2056
|
-
const spinner = createSpinner("
|
|
3535
|
+
const spinner = createSpinner(t("loading_skills_list"));
|
|
2057
3536
|
spinner.start();
|
|
2058
3537
|
try {
|
|
2059
3538
|
const repoManager = createRepositoryManager();
|
|
2060
3539
|
await repoManager.ensureRepoAndScan();
|
|
2061
|
-
spinner.succeed("
|
|
3540
|
+
spinner.succeed(t("loading_skills_list_complete"));
|
|
2062
3541
|
} catch {
|
|
2063
|
-
spinner.warn("
|
|
3542
|
+
spinner.warn(t("loading_skills_list_failed"));
|
|
2064
3543
|
}
|
|
2065
3544
|
let step = 0;
|
|
2066
3545
|
let mode = "plugin";
|
|
@@ -2094,8 +3573,7 @@ async function initCommand() {
|
|
|
2094
3573
|
}
|
|
2095
3574
|
}
|
|
2096
3575
|
async function selectMode() {
|
|
2097
|
-
printBoxTitle("
|
|
2098
|
-
const CANCEL3 = "__cancel__";
|
|
3576
|
+
printBoxTitle(t("wizard_select_mode_title"));
|
|
2099
3577
|
const choices = [
|
|
2100
3578
|
{
|
|
2101
3579
|
name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
|
|
@@ -2106,30 +3584,30 @@ async function selectMode() {
|
|
|
2106
3584
|
value: "skill"
|
|
2107
3585
|
},
|
|
2108
3586
|
new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2109
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3587
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2110
3588
|
];
|
|
2111
3589
|
showOperationHints();
|
|
2112
|
-
const result = await
|
|
3590
|
+
const result = await select4({
|
|
2113
3591
|
message: t("wizard_select_mode"),
|
|
2114
3592
|
choices,
|
|
2115
3593
|
loop: false,
|
|
2116
3594
|
theme: selectTheme
|
|
2117
3595
|
});
|
|
2118
|
-
if (result ===
|
|
3596
|
+
if (result === CANCEL) return null;
|
|
2119
3597
|
return result;
|
|
2120
3598
|
}
|
|
2121
3599
|
async function pluginInstallFlow() {
|
|
2122
3600
|
const answers = await runWizard();
|
|
2123
3601
|
if (!answers.confirmed) {
|
|
2124
3602
|
if (answers.back) return "back";
|
|
2125
|
-
logger.info("
|
|
3603
|
+
logger.info(t("init_cancelled"));
|
|
2126
3604
|
return "done";
|
|
2127
3605
|
}
|
|
2128
3606
|
const repoManager = createRepositoryManager();
|
|
2129
|
-
const spinner = createSpinner("
|
|
3607
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2130
3608
|
spinner.start();
|
|
2131
3609
|
const repoPath = await repoManager.ensureRepo();
|
|
2132
|
-
spinner.succeed("
|
|
3610
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2133
3611
|
const allPlugins = getAllPlugins();
|
|
2134
3612
|
const selectedPlugins = answers.plugins.map(
|
|
2135
3613
|
(id) => allPlugins.find((p) => p.id === id)
|
|
@@ -2183,7 +3661,7 @@ async function skillInstallFlow() {
|
|
|
2183
3661
|
const result = await selectToolWithDetection();
|
|
2184
3662
|
if (result === "back") return "back";
|
|
2185
3663
|
if (result === "cancel") {
|
|
2186
|
-
logger.info("
|
|
3664
|
+
logger.info(t("init_cancelled"));
|
|
2187
3665
|
return "done";
|
|
2188
3666
|
}
|
|
2189
3667
|
tool = result;
|
|
@@ -2192,12 +3670,12 @@ async function skillInstallFlow() {
|
|
|
2192
3670
|
}
|
|
2193
3671
|
case 1: {
|
|
2194
3672
|
const result = await stepLevel();
|
|
2195
|
-
if (result ===
|
|
3673
|
+
if (result === BACK) {
|
|
2196
3674
|
step = 0;
|
|
2197
3675
|
break;
|
|
2198
3676
|
}
|
|
2199
|
-
if (result ===
|
|
2200
|
-
logger.info("
|
|
3677
|
+
if (result === CANCEL) {
|
|
3678
|
+
logger.info(t("init_cancelled"));
|
|
2201
3679
|
return "done";
|
|
2202
3680
|
}
|
|
2203
3681
|
level = result;
|
|
@@ -2205,25 +3683,25 @@ async function skillInstallFlow() {
|
|
|
2205
3683
|
break;
|
|
2206
3684
|
}
|
|
2207
3685
|
case 2: {
|
|
2208
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
3686
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2209
3687
|
if (selectedSkills === "back") {
|
|
2210
3688
|
step = 1;
|
|
2211
3689
|
break;
|
|
2212
3690
|
}
|
|
2213
3691
|
if (selectedSkills === "cancel") {
|
|
2214
|
-
logger.info("
|
|
3692
|
+
logger.info(t("init_cancelled"));
|
|
2215
3693
|
return "done";
|
|
2216
3694
|
}
|
|
2217
3695
|
if (selectedSkills.length === 0) {
|
|
2218
|
-
logger.info("
|
|
3696
|
+
logger.info(t("install_no_skill_selected"));
|
|
2219
3697
|
return "done";
|
|
2220
3698
|
}
|
|
2221
3699
|
const repoManager = createRepositoryManager();
|
|
2222
|
-
const spinner = createSpinner("
|
|
3700
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2223
3701
|
spinner.start();
|
|
2224
3702
|
const repoPath = await repoManager.ensureRepo();
|
|
2225
|
-
spinner.succeed("
|
|
2226
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
3703
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3704
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk7.bold(String(selectedSkills.length)))}...`);
|
|
2227
3705
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2228
3706
|
let successCount = 0;
|
|
2229
3707
|
let failCount = 0;
|
|
@@ -2241,10 +3719,10 @@ async function skillInstallFlow() {
|
|
|
2241
3719
|
}
|
|
2242
3720
|
const configRoot = getConfigRoot(tool, level);
|
|
2243
3721
|
logger.blank();
|
|
2244
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
3722
|
+
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)))}`);
|
|
2245
3723
|
logger.blank();
|
|
2246
|
-
logger.info(
|
|
2247
|
-
logger.info(
|
|
3724
|
+
logger.info(`${t("install_to")}: ${chalk7.cyan(configRoot + "/skills/")}`);
|
|
3725
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
|
|
2248
3726
|
logger.blank();
|
|
2249
3727
|
return "done";
|
|
2250
3728
|
}
|
|
@@ -2266,32 +3744,31 @@ async function listCommand() {
|
|
|
2266
3744
|
}
|
|
2267
3745
|
|
|
2268
3746
|
// src/commands/doctor.ts
|
|
2269
|
-
import
|
|
2270
|
-
import { existsSync as
|
|
2271
|
-
import { join as
|
|
3747
|
+
import chalk8 from "chalk";
|
|
3748
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
3749
|
+
import { join as join15 } from "path";
|
|
2272
3750
|
async function doctorCommand(options = {}) {
|
|
2273
3751
|
console.log();
|
|
2274
|
-
console.log(
|
|
2275
|
-
console.log(
|
|
2276
|
-
let errors = 0;
|
|
3752
|
+
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
3753
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2277
3754
|
let warnings = 0;
|
|
2278
3755
|
let fixes = 0;
|
|
2279
3756
|
console.log();
|
|
2280
|
-
console.log(
|
|
3757
|
+
console.log(chalk8.bold(` ${t("doctor_tools")}`));
|
|
2281
3758
|
const detectedTools = await detectTools();
|
|
2282
3759
|
const allTools = getAllTools();
|
|
2283
3760
|
for (const tool of allTools) {
|
|
2284
3761
|
const detected = detectedTools.find((d) => d.name === tool);
|
|
2285
3762
|
if (detected) {
|
|
2286
3763
|
console.log(
|
|
2287
|
-
|
|
3764
|
+
chalk8.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2288
3765
|
);
|
|
2289
3766
|
} else {
|
|
2290
|
-
console.log(
|
|
3767
|
+
console.log(chalk8.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2291
3768
|
}
|
|
2292
3769
|
}
|
|
2293
3770
|
console.log();
|
|
2294
|
-
console.log(
|
|
3771
|
+
console.log(chalk8.bold(` ${t("doctor_plugins")}`));
|
|
2295
3772
|
const plugins = getAllPlugins();
|
|
2296
3773
|
const installed = scanInstalled();
|
|
2297
3774
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
@@ -2299,80 +3776,80 @@ async function doctorCommand(options = {}) {
|
|
|
2299
3776
|
const inst = installedMap.get(plugin.id);
|
|
2300
3777
|
if (inst) {
|
|
2301
3778
|
console.log(
|
|
2302
|
-
|
|
3779
|
+
chalk8.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2303
3780
|
);
|
|
2304
3781
|
} else {
|
|
2305
|
-
console.log(
|
|
3782
|
+
console.log(chalk8.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2306
3783
|
}
|
|
2307
3784
|
}
|
|
2308
3785
|
console.log();
|
|
2309
|
-
console.log(
|
|
3786
|
+
console.log(chalk8.bold(` ${t("doctor_links")}`));
|
|
2310
3787
|
const primaryTool = detectedTools[0]?.name || "opencode";
|
|
2311
3788
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2312
3789
|
const skillsDir = getSkillsDir(configRoot);
|
|
2313
3790
|
const agentsDir = getAgentsDir(configRoot);
|
|
2314
|
-
if (
|
|
3791
|
+
if (existsSync16(skillsDir)) {
|
|
2315
3792
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2316
3793
|
if (brokenLinks === 0) {
|
|
2317
|
-
const count =
|
|
2318
|
-
console.log(
|
|
3794
|
+
const count = readdirSync5(skillsDir).length;
|
|
3795
|
+
console.log(chalk8.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2319
3796
|
} else {
|
|
2320
|
-
console.log(
|
|
3797
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2321
3798
|
warnings++;
|
|
2322
3799
|
if (options.fix) {
|
|
2323
3800
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2324
3801
|
fixes += fixed;
|
|
2325
|
-
console.log(
|
|
3802
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2326
3803
|
}
|
|
2327
3804
|
}
|
|
2328
3805
|
} else {
|
|
2329
|
-
console.log(
|
|
3806
|
+
console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2330
3807
|
if (options.fix) {
|
|
2331
|
-
|
|
3808
|
+
mkdirSync7(skillsDir, { recursive: true });
|
|
2332
3809
|
fixes++;
|
|
2333
|
-
console.log(
|
|
3810
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2334
3811
|
}
|
|
2335
3812
|
}
|
|
2336
|
-
if (
|
|
3813
|
+
if (existsSync16(agentsDir)) {
|
|
2337
3814
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2338
3815
|
if (brokenLinks === 0) {
|
|
2339
|
-
const count =
|
|
2340
|
-
console.log(
|
|
3816
|
+
const count = readdirSync5(agentsDir).length;
|
|
3817
|
+
console.log(chalk8.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2341
3818
|
} else {
|
|
2342
|
-
console.log(
|
|
3819
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2343
3820
|
warnings++;
|
|
2344
3821
|
if (options.fix) {
|
|
2345
3822
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2346
3823
|
fixes += fixed;
|
|
2347
|
-
console.log(
|
|
3824
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2348
3825
|
}
|
|
2349
3826
|
}
|
|
2350
3827
|
} else {
|
|
2351
|
-
console.log(
|
|
3828
|
+
console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2352
3829
|
if (options.fix) {
|
|
2353
|
-
|
|
3830
|
+
mkdirSync7(agentsDir, { recursive: true });
|
|
2354
3831
|
fixes++;
|
|
2355
|
-
console.log(
|
|
3832
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2356
3833
|
}
|
|
2357
3834
|
}
|
|
2358
3835
|
console.log();
|
|
2359
|
-
console.log(
|
|
3836
|
+
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2360
3837
|
const configFile = getConfigFileName(primaryTool);
|
|
2361
|
-
const configPath =
|
|
2362
|
-
if (
|
|
2363
|
-
console.log(
|
|
3838
|
+
const configPath = join15(process.cwd(), configFile);
|
|
3839
|
+
if (existsSync16(configPath)) {
|
|
3840
|
+
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2364
3841
|
} else {
|
|
2365
|
-
console.log(
|
|
3842
|
+
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2366
3843
|
}
|
|
2367
3844
|
console.log();
|
|
2368
|
-
console.log(
|
|
3845
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2369
3846
|
if (options.fix && fixes > 0) {
|
|
2370
3847
|
console.log(
|
|
2371
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3848
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
|
|
2372
3849
|
);
|
|
2373
3850
|
} else {
|
|
2374
3851
|
console.log(
|
|
2375
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3852
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
|
|
2376
3853
|
);
|
|
2377
3854
|
}
|
|
2378
3855
|
console.log();
|
|
@@ -2380,13 +3857,13 @@ async function doctorCommand(options = {}) {
|
|
|
2380
3857
|
function checkBrokenLinks(dir) {
|
|
2381
3858
|
let broken = 0;
|
|
2382
3859
|
try {
|
|
2383
|
-
const entries =
|
|
3860
|
+
const entries = readdirSync5(dir);
|
|
2384
3861
|
for (const entry of entries) {
|
|
2385
|
-
const fullPath =
|
|
3862
|
+
const fullPath = join15(dir, entry);
|
|
2386
3863
|
try {
|
|
2387
|
-
const stats =
|
|
3864
|
+
const stats = lstatSync2(fullPath);
|
|
2388
3865
|
if (stats.isSymbolicLink()) {
|
|
2389
|
-
if (!
|
|
3866
|
+
if (!existsSync16(fullPath)) {
|
|
2390
3867
|
broken++;
|
|
2391
3868
|
}
|
|
2392
3869
|
}
|
|
@@ -2401,14 +3878,14 @@ function checkBrokenLinks(dir) {
|
|
|
2401
3878
|
function fixBrokenLinks(dir) {
|
|
2402
3879
|
let fixed = 0;
|
|
2403
3880
|
try {
|
|
2404
|
-
const entries =
|
|
3881
|
+
const entries = readdirSync5(dir);
|
|
2405
3882
|
for (const entry of entries) {
|
|
2406
|
-
const fullPath =
|
|
3883
|
+
const fullPath = join15(dir, entry);
|
|
2407
3884
|
try {
|
|
2408
|
-
const stats =
|
|
3885
|
+
const stats = lstatSync2(fullPath);
|
|
2409
3886
|
if (stats.isSymbolicLink()) {
|
|
2410
|
-
if (!
|
|
2411
|
-
|
|
3887
|
+
if (!existsSync16(fullPath)) {
|
|
3888
|
+
unlinkSync6(fullPath);
|
|
2412
3889
|
fixed++;
|
|
2413
3890
|
}
|
|
2414
3891
|
}
|
|
@@ -2421,36 +3898,35 @@ function fixBrokenLinks(dir) {
|
|
|
2421
3898
|
}
|
|
2422
3899
|
|
|
2423
3900
|
// src/commands/status.ts
|
|
2424
|
-
import
|
|
3901
|
+
import chalk9 from "chalk";
|
|
2425
3902
|
async function statusCommand() {
|
|
2426
|
-
const plugins = getAllPlugins();
|
|
2427
3903
|
const installed = scanInstalled();
|
|
2428
3904
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2429
3905
|
console.log();
|
|
2430
|
-
console.log(
|
|
3906
|
+
console.log(chalk9.bold(` ${t("status_title")}`));
|
|
2431
3907
|
console.log();
|
|
2432
3908
|
if (installed.length === 0) {
|
|
2433
|
-
console.log(
|
|
2434
|
-
console.log(
|
|
3909
|
+
console.log(chalk9.dim(` ${t("status_none")}`));
|
|
3910
|
+
console.log(chalk9.dim(` ${t("status_hint")}`));
|
|
2435
3911
|
console.log();
|
|
2436
3912
|
return;
|
|
2437
3913
|
}
|
|
2438
3914
|
for (const inst of installed) {
|
|
2439
3915
|
console.log(
|
|
2440
|
-
|
|
3916
|
+
chalk9.green(" \u2713") + ` ${inst.displayName}` + chalk9.dim(` (${inst.tool}, ${inst.level})`)
|
|
2441
3917
|
);
|
|
2442
3918
|
console.log(
|
|
2443
|
-
|
|
3919
|
+
chalk9.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
|
|
2444
3920
|
);
|
|
2445
|
-
console.log(
|
|
2446
|
-
console.log(
|
|
3921
|
+
console.log(chalk9.dim(` ${inst.configRoot}`));
|
|
3922
|
+
console.log(chalk9.dim(` ${t("status_install_time")}: ${inst.installTime}`));
|
|
2447
3923
|
console.log();
|
|
2448
3924
|
}
|
|
2449
3925
|
}
|
|
2450
3926
|
|
|
2451
3927
|
// src/commands/install.ts
|
|
2452
3928
|
import { confirm } from "@inquirer/prompts";
|
|
2453
|
-
import
|
|
3929
|
+
import chalk10 from "chalk";
|
|
2454
3930
|
async function installCommand(names, options) {
|
|
2455
3931
|
if (options.list) {
|
|
2456
3932
|
try {
|
|
@@ -2461,79 +3937,150 @@ async function installCommand(names, options) {
|
|
|
2461
3937
|
listAllSkills();
|
|
2462
3938
|
return;
|
|
2463
3939
|
}
|
|
3940
|
+
if (options.all) {
|
|
3941
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
3942
|
+
scanSpinner2.start();
|
|
3943
|
+
try {
|
|
3944
|
+
const scanRepoManager = createRepositoryManager();
|
|
3945
|
+
await scanRepoManager.ensureRepoAndScan();
|
|
3946
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
3947
|
+
} catch {
|
|
3948
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
3949
|
+
}
|
|
3950
|
+
const allSkills = getAllSkills();
|
|
3951
|
+
const skillIds = allSkills.map((s) => s.id);
|
|
3952
|
+
if (skillIds.length === 0) {
|
|
3953
|
+
logger.error(t("install_no_skill_selected"));
|
|
3954
|
+
return;
|
|
3955
|
+
}
|
|
3956
|
+
let tool2;
|
|
3957
|
+
if (options.tool) {
|
|
3958
|
+
tool2 = validateTool(options.tool);
|
|
3959
|
+
} else {
|
|
3960
|
+
const detected = await selectToolWithDetection();
|
|
3961
|
+
if (detected === "back" || detected === "cancel") {
|
|
3962
|
+
logger.info(t("init_cancelled"));
|
|
3963
|
+
return;
|
|
3964
|
+
}
|
|
3965
|
+
tool2 = detected;
|
|
3966
|
+
}
|
|
3967
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
3968
|
+
const repoManager = createRepositoryManager();
|
|
3969
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
3970
|
+
spinner.start();
|
|
3971
|
+
const repoPath = await repoManager.ensureRepo();
|
|
3972
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3973
|
+
logger.info(`${t("skill_all_install_progress").replace("{count}", chalk10.bold(String(skillIds.length)))}`);
|
|
3974
|
+
const results = await installSkills(skillIds, tool2, level2, repoPath);
|
|
3975
|
+
let successCount = 0;
|
|
3976
|
+
let failCount = 0;
|
|
3977
|
+
const total = results.length;
|
|
3978
|
+
for (let i = 0; i < results.length; i++) {
|
|
3979
|
+
const result = results[i];
|
|
3980
|
+
const progress = `[${i + 1}/${total}]`;
|
|
3981
|
+
if (result.success) {
|
|
3982
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
3983
|
+
successCount++;
|
|
3984
|
+
} else {
|
|
3985
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
3986
|
+
failCount++;
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
const configRoot = getConfigRoot(tool2, level2);
|
|
3990
|
+
logger.blank();
|
|
3991
|
+
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)))}`);
|
|
3992
|
+
logger.blank();
|
|
3993
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
3994
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
3995
|
+
logger.blank();
|
|
3996
|
+
return;
|
|
3997
|
+
}
|
|
2464
3998
|
if (names.length === 0) {
|
|
2465
|
-
const scanSpinner2 = createSpinner("
|
|
3999
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2466
4000
|
scanSpinner2.start();
|
|
2467
4001
|
try {
|
|
2468
4002
|
const scanRepoManager = createRepositoryManager();
|
|
2469
4003
|
await scanRepoManager.ensureRepoAndScan();
|
|
2470
|
-
scanSpinner2.succeed("
|
|
4004
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2471
4005
|
} catch {
|
|
2472
|
-
scanSpinner2.warn("
|
|
4006
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2473
4007
|
}
|
|
2474
4008
|
let tool2;
|
|
2475
4009
|
if (options.tool) {
|
|
2476
|
-
tool2 = options.tool;
|
|
4010
|
+
tool2 = validateTool(options.tool);
|
|
2477
4011
|
} else {
|
|
2478
4012
|
const detected = await selectToolWithDetection();
|
|
2479
|
-
if (detected === "back")
|
|
4013
|
+
if (detected === "back" || detected === "cancel") {
|
|
4014
|
+
logger.info(t("init_cancelled"));
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
2480
4017
|
tool2 = detected;
|
|
2481
4018
|
}
|
|
4019
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2482
4020
|
let step = 1;
|
|
2483
4021
|
while (true) {
|
|
2484
4022
|
switch (step) {
|
|
2485
4023
|
case 1: {
|
|
2486
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
4024
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2487
4025
|
if (selectedSkills === "back") {
|
|
2488
4026
|
const redetected = await selectToolWithDetection();
|
|
2489
|
-
if (redetected === "back")
|
|
4027
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
4028
|
+
logger.info(t("init_cancelled"));
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
2490
4031
|
tool2 = redetected;
|
|
2491
4032
|
break;
|
|
2492
4033
|
}
|
|
4034
|
+
if (selectedSkills === "cancel") {
|
|
4035
|
+
logger.info(t("init_cancelled"));
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
2493
4038
|
if (selectedSkills.length === 0) {
|
|
2494
|
-
logger.info("
|
|
4039
|
+
logger.info(t("install_no_skill_selected"));
|
|
2495
4040
|
return;
|
|
2496
4041
|
}
|
|
2497
4042
|
names = selectedSkills;
|
|
2498
4043
|
const repoManager = createRepositoryManager();
|
|
2499
|
-
const spinner = createSpinner(t("
|
|
4044
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2500
4045
|
spinner.start();
|
|
2501
4046
|
const repoPath = await repoManager.ensureRepo();
|
|
2502
|
-
spinner.succeed(t("
|
|
2503
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2504
|
-
const level2 = options.level || "project";
|
|
4047
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
4048
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(names.length)))}...`);
|
|
2505
4049
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2506
4050
|
let successCount = 0;
|
|
2507
4051
|
let failCount = 0;
|
|
2508
|
-
|
|
4052
|
+
const total = results.length;
|
|
4053
|
+
for (let i = 0; i < results.length; i++) {
|
|
4054
|
+
const result = results[i];
|
|
4055
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2509
4056
|
if (result.success) {
|
|
2510
|
-
logger.success(`${result.skillId}`);
|
|
4057
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2511
4058
|
successCount++;
|
|
2512
4059
|
} else {
|
|
2513
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
4060
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2514
4061
|
failCount++;
|
|
2515
4062
|
}
|
|
2516
4063
|
}
|
|
2517
4064
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2518
4065
|
logger.blank();
|
|
2519
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
4066
|
+
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)))}`);
|
|
2520
4067
|
logger.blank();
|
|
2521
|
-
logger.info(
|
|
2522
|
-
logger.info(
|
|
4068
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
4069
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2523
4070
|
logger.blank();
|
|
2524
4071
|
return;
|
|
2525
4072
|
}
|
|
2526
4073
|
}
|
|
2527
4074
|
}
|
|
2528
4075
|
}
|
|
2529
|
-
const scanSpinner = createSpinner("
|
|
4076
|
+
const scanSpinner = createSpinner(t("loading_skills_list"));
|
|
2530
4077
|
scanSpinner.start();
|
|
2531
4078
|
try {
|
|
2532
4079
|
const scanRepoManager = createRepositoryManager();
|
|
2533
4080
|
await scanRepoManager.ensureRepoAndScan();
|
|
2534
|
-
scanSpinner.succeed("
|
|
4081
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2535
4082
|
} catch {
|
|
2536
|
-
scanSpinner.warn("
|
|
4083
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2537
4084
|
}
|
|
2538
4085
|
const plugins = [];
|
|
2539
4086
|
const skills = [];
|
|
@@ -2550,22 +4097,25 @@ async function installCommand(names, options) {
|
|
|
2550
4097
|
}
|
|
2551
4098
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2552
4099
|
const allPlugins = getAllPlugins();
|
|
2553
|
-
logger.info("
|
|
4100
|
+
logger.info(t("available_plugins") + ":");
|
|
2554
4101
|
for (const p of allPlugins) {
|
|
2555
4102
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2556
4103
|
}
|
|
2557
|
-
logger.info(
|
|
4104
|
+
logger.info(`${t("available_skills")}: ${chalk10.cyan("install-helper install --list")}`);
|
|
2558
4105
|
return;
|
|
2559
4106
|
}
|
|
2560
4107
|
let tool;
|
|
2561
4108
|
if (options.tool) {
|
|
2562
|
-
tool = options.tool;
|
|
4109
|
+
tool = validateTool(options.tool);
|
|
2563
4110
|
} else {
|
|
2564
4111
|
const detected = await selectToolWithDetection();
|
|
2565
|
-
if (detected === "back")
|
|
4112
|
+
if (detected === "back" || detected === "cancel") {
|
|
4113
|
+
logger.info(t("init_cancelled"));
|
|
4114
|
+
return;
|
|
4115
|
+
}
|
|
2566
4116
|
tool = detected;
|
|
2567
4117
|
}
|
|
2568
|
-
const level = options.level
|
|
4118
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2569
4119
|
if (plugins.length > 0) {
|
|
2570
4120
|
const configRoot = getConfigRoot(tool, level);
|
|
2571
4121
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2593,16 +4143,18 @@ async function installCommand(names, options) {
|
|
|
2593
4143
|
}
|
|
2594
4144
|
if (pluginsToInstall.length > 0) {
|
|
2595
4145
|
const repoManager = createRepositoryManager();
|
|
2596
|
-
const spinner = createSpinner(t("
|
|
4146
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2597
4147
|
spinner.start();
|
|
2598
4148
|
const repoPath = await repoManager.ensureRepo();
|
|
2599
|
-
spinner.succeed(t("
|
|
4149
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2600
4150
|
const allPlugins = getAllPlugins();
|
|
2601
4151
|
const results = await installPlugins(
|
|
2602
4152
|
pluginsToInstall,
|
|
2603
4153
|
tool,
|
|
2604
4154
|
level,
|
|
2605
|
-
repoPath
|
|
4155
|
+
repoPath,
|
|
4156
|
+
void 0,
|
|
4157
|
+
options.yes
|
|
2606
4158
|
);
|
|
2607
4159
|
const summary = results.map((result) => {
|
|
2608
4160
|
const plugin = allPlugins.find((p) => p.id === result.pluginId);
|
|
@@ -2620,32 +4172,35 @@ async function installCommand(names, options) {
|
|
|
2620
4172
|
}
|
|
2621
4173
|
if (skills.length > 0) {
|
|
2622
4174
|
const repoManager = createRepositoryManager();
|
|
2623
|
-
const spinner = createSpinner(t("
|
|
4175
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2624
4176
|
spinner.start();
|
|
2625
4177
|
const repoPath = await repoManager.ensureRepo();
|
|
2626
|
-
spinner.succeed(t("
|
|
2627
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
4178
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
4179
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(skills.length)))}...`);
|
|
2628
4180
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2629
4181
|
let successCount = 0;
|
|
2630
4182
|
let failCount = 0;
|
|
2631
|
-
|
|
4183
|
+
const total = results.length;
|
|
4184
|
+
for (let i = 0; i < results.length; i++) {
|
|
4185
|
+
const result = results[i];
|
|
4186
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2632
4187
|
if (result.success) {
|
|
2633
|
-
logger.success(`${result.skillId}`);
|
|
4188
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2634
4189
|
successCount++;
|
|
2635
4190
|
} else {
|
|
2636
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
4191
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2637
4192
|
failCount++;
|
|
2638
4193
|
}
|
|
2639
4194
|
}
|
|
2640
4195
|
logger.blank();
|
|
2641
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
4196
|
+
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)))}`);
|
|
2642
4197
|
logger.blank();
|
|
2643
|
-
logger.info(
|
|
2644
|
-
logger.info(
|
|
4198
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
4199
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
2645
4200
|
logger.blank();
|
|
2646
4201
|
}
|
|
2647
4202
|
}
|
|
2648
|
-
async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
4203
|
+
async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes) {
|
|
2649
4204
|
const results = [];
|
|
2650
4205
|
const total = pluginIds.length;
|
|
2651
4206
|
for (let i = 0; i < pluginIds.length; i++) {
|
|
@@ -2657,7 +4212,9 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2657
4212
|
pluginId,
|
|
2658
4213
|
tool,
|
|
2659
4214
|
level,
|
|
2660
|
-
repoPath
|
|
4215
|
+
repoPath,
|
|
4216
|
+
installPath,
|
|
4217
|
+
yes
|
|
2661
4218
|
});
|
|
2662
4219
|
if (result.success) {
|
|
2663
4220
|
spinner.succeed(
|
|
@@ -2673,11 +4230,12 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2673
4230
|
}
|
|
2674
4231
|
|
|
2675
4232
|
// src/commands/uninstall.ts
|
|
2676
|
-
import { existsSync as
|
|
2677
|
-
import
|
|
4233
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync7, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
|
|
4234
|
+
import { resolve, basename as basename3, sep } from "path";
|
|
4235
|
+
import chalk11 from "chalk";
|
|
2678
4236
|
async function uninstallCommand(names, options) {
|
|
2679
|
-
const tool = options.tool
|
|
2680
|
-
const level = options.level
|
|
4237
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
4238
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2681
4239
|
const plugins = [];
|
|
2682
4240
|
const skills = [];
|
|
2683
4241
|
for (const name of names) {
|
|
@@ -2691,40 +4249,50 @@ async function uninstallCommand(names, options) {
|
|
|
2691
4249
|
skills.push(skill.id);
|
|
2692
4250
|
continue;
|
|
2693
4251
|
}
|
|
2694
|
-
logger.error(
|
|
4252
|
+
logger.error(`${t("uninstall_not_found")}: ${name}`);
|
|
2695
4253
|
}
|
|
2696
4254
|
for (const pluginId of plugins) {
|
|
2697
4255
|
const plugin = findPlugin(pluginId);
|
|
2698
4256
|
if (!plugin) continue;
|
|
2699
4257
|
const record = readRecord(plugin.id);
|
|
2700
4258
|
if (!record) {
|
|
2701
|
-
logger.warn(`${plugin.displayName}
|
|
2702
|
-
logger.info(
|
|
4259
|
+
logger.warn(`${plugin.displayName} ${t("uninstall_no_record")}`);
|
|
4260
|
+
logger.info(`${t("uninstall_no_record_hint")}`);
|
|
2703
4261
|
continue;
|
|
2704
4262
|
}
|
|
2705
|
-
logger.info(
|
|
4263
|
+
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
4264
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
4265
|
+
const allowedBases = [resolve(configRoot), resolve(record.installPath)];
|
|
4266
|
+
const isSafePath = (p) => {
|
|
4267
|
+
const resolved = resolve(p);
|
|
4268
|
+
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + sep));
|
|
4269
|
+
};
|
|
2706
4270
|
let removedFiles = 0;
|
|
2707
4271
|
let removedDirs = 0;
|
|
2708
4272
|
for (const filePath of record.files) {
|
|
4273
|
+
if (!isSafePath(filePath)) continue;
|
|
2709
4274
|
try {
|
|
2710
|
-
if (
|
|
2711
|
-
|
|
2712
|
-
const name = filePath
|
|
2713
|
-
logger.step(`
|
|
4275
|
+
if (existsSync17(filePath) || isSymlink(filePath)) {
|
|
4276
|
+
unlinkSync7(filePath);
|
|
4277
|
+
const name = basename3(filePath);
|
|
4278
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2714
4279
|
removedFiles++;
|
|
2715
4280
|
}
|
|
2716
4281
|
} catch {
|
|
2717
4282
|
}
|
|
2718
4283
|
}
|
|
2719
|
-
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
4284
|
+
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
2720
4285
|
for (const dirPath of sortedDirs) {
|
|
2721
4286
|
try {
|
|
2722
|
-
if (
|
|
2723
|
-
|
|
4287
|
+
if (resolve(dirPath) === resolve(configRoot)) {
|
|
4288
|
+
continue;
|
|
4289
|
+
}
|
|
4290
|
+
if (existsSync17(dirPath)) {
|
|
4291
|
+
const entries = readdirSync6(dirPath);
|
|
2724
4292
|
if (entries.length === 0) {
|
|
2725
4293
|
rmdirSync2(dirPath);
|
|
2726
|
-
const name = dirPath
|
|
2727
|
-
logger.step(`
|
|
4294
|
+
const name = basename3(dirPath);
|
|
4295
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
2728
4296
|
removedDirs++;
|
|
2729
4297
|
}
|
|
2730
4298
|
}
|
|
@@ -2733,15 +4301,32 @@ async function uninstallCommand(names, options) {
|
|
|
2733
4301
|
}
|
|
2734
4302
|
deleteRecord(plugin.id);
|
|
2735
4303
|
removeInstalledPlugin(plugin.id);
|
|
4304
|
+
const backups = findBackups(configRoot);
|
|
4305
|
+
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
4306
|
+
if (otherBackups.length > 0) {
|
|
4307
|
+
const choice = await showRestorePrompt(otherBackups);
|
|
4308
|
+
if (choice !== "none") {
|
|
4309
|
+
const restored = restoreBackup(choice, configRoot, record.tool);
|
|
4310
|
+
if (restored) {
|
|
4311
|
+
const backupInfo = otherBackups.find((b) => b.filePath === choice);
|
|
4312
|
+
logger.success(`${t("backup_restore_success")}: ${backupInfo?.pluginName || ""}`);
|
|
4313
|
+
deleteBackup(choice);
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
const currentBackups = backups.filter((b) => b.pluginId === pluginId);
|
|
4318
|
+
for (const backup of currentBackups) {
|
|
4319
|
+
deleteBackup(backup.filePath);
|
|
4320
|
+
}
|
|
2736
4321
|
const totalRemoved = removedFiles + removedDirs;
|
|
2737
4322
|
if (totalRemoved > 0) {
|
|
2738
|
-
logger.success(
|
|
4323
|
+
logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
|
|
2739
4324
|
} else {
|
|
2740
|
-
logger.warn(`${plugin.displayName}
|
|
4325
|
+
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
2741
4326
|
}
|
|
2742
4327
|
}
|
|
2743
4328
|
if (skills.length > 0) {
|
|
2744
|
-
logger.info(
|
|
4329
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
2745
4330
|
const results = await uninstallSkills(skills, tool, level);
|
|
2746
4331
|
let successCount = 0;
|
|
2747
4332
|
let failCount = 0;
|
|
@@ -2753,30 +4338,26 @@ async function uninstallCommand(names, options) {
|
|
|
2753
4338
|
}
|
|
2754
4339
|
}
|
|
2755
4340
|
logger.blank();
|
|
2756
|
-
logger.success(`${t("skill_uninstall_done")}: ${
|
|
4341
|
+
logger.success(`${t("skill_uninstall_done")}: ${chalk11.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk11.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk11.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2757
4342
|
logger.blank();
|
|
2758
4343
|
}
|
|
2759
4344
|
}
|
|
2760
|
-
function isSymlink3(path) {
|
|
2761
|
-
try {
|
|
2762
|
-
return lstatSync4(path).isSymbolicLink();
|
|
2763
|
-
} catch {
|
|
2764
|
-
return false;
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
4345
|
|
|
2768
4346
|
// src/commands/update.ts
|
|
2769
|
-
import
|
|
4347
|
+
import chalk12 from "chalk";
|
|
2770
4348
|
async function updateCommand(pluginNames, options) {
|
|
2771
4349
|
let tool;
|
|
2772
4350
|
if (options.tool) {
|
|
2773
|
-
tool = options.tool;
|
|
4351
|
+
tool = validateTool(options.tool);
|
|
2774
4352
|
} else {
|
|
2775
4353
|
const detected = await selectToolWithDetection();
|
|
2776
|
-
if (detected === "back")
|
|
4354
|
+
if (detected === "back" || detected === "cancel") {
|
|
4355
|
+
logger.info(t("init_cancelled"));
|
|
4356
|
+
return;
|
|
4357
|
+
}
|
|
2777
4358
|
tool = detected;
|
|
2778
4359
|
}
|
|
2779
|
-
const level = options.level
|
|
4360
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2780
4361
|
let pluginsToUpdate = [];
|
|
2781
4362
|
if (pluginNames.length === 0) {
|
|
2782
4363
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -2784,7 +4365,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2784
4365
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
2785
4366
|
if (pluginsToUpdate.length === 0) {
|
|
2786
4367
|
logger.info(t("update_no_plugins"));
|
|
2787
|
-
logger.info(
|
|
4368
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk12.cyan("install-helper install <plugin>")));
|
|
2788
4369
|
return;
|
|
2789
4370
|
}
|
|
2790
4371
|
} else {
|
|
@@ -2800,9 +4381,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2800
4381
|
const repoManager = createRepositoryManager();
|
|
2801
4382
|
const updateSpinner = createSpinner(t("update_updating") + "...");
|
|
2802
4383
|
updateSpinner.start();
|
|
2803
|
-
await repoManager.
|
|
2804
|
-
const repoPath = repoManager.getRepoPath();
|
|
2805
|
-
await repoManager.ensureRepoAndScan();
|
|
4384
|
+
const repoPath = await repoManager.ensureRepoAndScan();
|
|
2806
4385
|
updateSpinner.succeed(t("install_repo_ready"));
|
|
2807
4386
|
const allPlugins = getAllPlugins();
|
|
2808
4387
|
const results = [];
|
|
@@ -2846,42 +4425,43 @@ async function updateCommand(pluginNames, options) {
|
|
|
2846
4425
|
}
|
|
2847
4426
|
|
|
2848
4427
|
// src/commands/info.ts
|
|
2849
|
-
import
|
|
2850
|
-
import { existsSync as
|
|
2851
|
-
import { join as
|
|
4428
|
+
import chalk13 from "chalk";
|
|
4429
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
|
|
4430
|
+
import { join as join16 } from "path";
|
|
2852
4431
|
async function infoCommand(pluginName) {
|
|
2853
4432
|
const plugin = findPlugin(pluginName);
|
|
2854
4433
|
if (!plugin) {
|
|
2855
|
-
console.log(
|
|
4434
|
+
console.log(chalk13.red(` ${t("info_not_found")}: ${pluginName}`));
|
|
2856
4435
|
return;
|
|
2857
4436
|
}
|
|
2858
4437
|
const installed = scanInstalled();
|
|
2859
|
-
const
|
|
2860
|
-
const
|
|
4438
|
+
const inst = installed.find((p) => p.id === plugin.id);
|
|
4439
|
+
const isInstalled = !!inst;
|
|
4440
|
+
const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
|
|
2861
4441
|
console.log();
|
|
2862
|
-
console.log(
|
|
2863
|
-
console.log(
|
|
4442
|
+
console.log(chalk13.bold(` ${plugin.displayName}`));
|
|
4443
|
+
console.log(chalk13.dim(" " + "\u2500".repeat(40)));
|
|
2864
4444
|
console.log();
|
|
2865
|
-
console.log(` ${
|
|
2866
|
-
console.log(` ${
|
|
2867
|
-
console.log(` ${
|
|
2868
|
-
console.log(` ${
|
|
2869
|
-
console.log(` ${
|
|
2870
|
-
console.log(` ${
|
|
4445
|
+
console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
4446
|
+
console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
|
|
4447
|
+
console.log(` ${chalk13.dim(t("info_status") + ":")} ${statusText}`);
|
|
4448
|
+
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${inst ? inst.skillsCount : plugin.skills}`);
|
|
4449
|
+
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${inst ? inst.agentsCount : plugin.agents}`);
|
|
4450
|
+
console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
2871
4451
|
console.log();
|
|
2872
4452
|
try {
|
|
2873
4453
|
const repoManager = createRepositoryManager();
|
|
2874
4454
|
const repoPath = await repoManager.ensureRepo();
|
|
2875
|
-
const quickstartPath =
|
|
2876
|
-
if (
|
|
2877
|
-
console.log(` ${
|
|
4455
|
+
const quickstartPath = join16(repoPath, plugin.dir, "quickstart.md");
|
|
4456
|
+
if (existsSync18(quickstartPath)) {
|
|
4457
|
+
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
2878
4458
|
}
|
|
2879
|
-
const agentsDir =
|
|
2880
|
-
if (
|
|
2881
|
-
const agentFiles =
|
|
4459
|
+
const agentsDir = join16(repoPath, plugin.dir, "agents");
|
|
4460
|
+
if (existsSync18(agentsDir)) {
|
|
4461
|
+
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
2882
4462
|
if (agentFiles.length > 0) {
|
|
2883
4463
|
console.log();
|
|
2884
|
-
console.log(` ${
|
|
4464
|
+
console.log(` ${chalk13.bold("Agents:")}`);
|
|
2885
4465
|
for (const agent of agentFiles) {
|
|
2886
4466
|
console.log(` \u2022 ${agent.replace(".md", "")}`);
|
|
2887
4467
|
}
|
|
@@ -2893,49 +4473,80 @@ async function infoCommand(pluginName) {
|
|
|
2893
4473
|
}
|
|
2894
4474
|
|
|
2895
4475
|
// src/commands/lang.ts
|
|
2896
|
-
import
|
|
4476
|
+
import chalk14 from "chalk";
|
|
2897
4477
|
async function langCommand(action, value) {
|
|
2898
4478
|
const config = readConfig();
|
|
2899
4479
|
if (!action || action === "show") {
|
|
2900
4480
|
console.log();
|
|
2901
|
-
console.log(
|
|
2902
|
-
console.log(
|
|
4481
|
+
console.log(chalk14.bold(` ${t("lang_title")}`));
|
|
4482
|
+
console.log(chalk14.dim(" " + "\u2500".repeat(40)));
|
|
2903
4483
|
console.log();
|
|
2904
|
-
console.log(` ${
|
|
4484
|
+
console.log(` ${chalk14.dim(t("lang_current") + ":")} ${config.language}`);
|
|
2905
4485
|
console.log();
|
|
2906
|
-
console.log(
|
|
2907
|
-
console.log(
|
|
4486
|
+
console.log(chalk14.dim(` ${t("lang_supported")}`));
|
|
4487
|
+
console.log(chalk14.dim(` ${t("lang_usage")}`));
|
|
2908
4488
|
console.log();
|
|
2909
4489
|
return;
|
|
2910
4490
|
}
|
|
2911
4491
|
if (action === "set") {
|
|
2912
4492
|
if (!value) {
|
|
2913
|
-
console.log(
|
|
4493
|
+
console.log(chalk14.red(` ${t("lang_specify")}`));
|
|
2914
4494
|
return;
|
|
2915
4495
|
}
|
|
2916
4496
|
if (value !== "zh_CN" && value !== "en_US") {
|
|
2917
|
-
console.log(
|
|
4497
|
+
console.log(chalk14.red(` ${t("lang_invalid")}`));
|
|
2918
4498
|
return;
|
|
2919
4499
|
}
|
|
2920
4500
|
updateConfig({ language: value });
|
|
2921
4501
|
setLanguage(value);
|
|
2922
4502
|
console.log();
|
|
2923
|
-
console.log(
|
|
4503
|
+
console.log(chalk14.green(` \u2713 ${t("lang_set")} ${value}`));
|
|
2924
4504
|
console.log();
|
|
2925
4505
|
return;
|
|
2926
4506
|
}
|
|
2927
|
-
console.log(
|
|
2928
|
-
console.log(
|
|
4507
|
+
console.log(chalk14.red(` ${t("lang_unknown_action")}: ${action}`));
|
|
4508
|
+
console.log(chalk14.dim(` ${t("lang_supported_actions")}`));
|
|
2929
4509
|
}
|
|
2930
4510
|
|
|
2931
4511
|
// src/cli.ts
|
|
4512
|
+
import { readFileSync as readFileSync9, existsSync as existsSync19 } from "fs";
|
|
4513
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
4514
|
+
import { dirname as dirname7, join as join17 } from "path";
|
|
4515
|
+
function readVersion() {
|
|
4516
|
+
try {
|
|
4517
|
+
if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
|
|
4518
|
+
return INSTALL_HELPER_VERSION;
|
|
4519
|
+
}
|
|
4520
|
+
} catch {
|
|
4521
|
+
}
|
|
4522
|
+
try {
|
|
4523
|
+
const __filename5 = fileURLToPath5(import.meta.url);
|
|
4524
|
+
const __dirname5 = dirname7(__filename5);
|
|
4525
|
+
const candidates = [
|
|
4526
|
+
join17(__dirname5, "..", "package.json"),
|
|
4527
|
+
join17(__dirname5, "package.json"),
|
|
4528
|
+
join17(process.cwd(), "package.json")
|
|
4529
|
+
];
|
|
4530
|
+
for (const pkgPath of candidates) {
|
|
4531
|
+
if (existsSync19(pkgPath)) {
|
|
4532
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
4533
|
+
if (pkg.version && (pkg.name === "@cannbot-ai/install-helper" || pkg.name?.startsWith("@cannbot-ai/install-helper-"))) {
|
|
4534
|
+
return pkg.version;
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
return "0.0.0";
|
|
4539
|
+
} catch {
|
|
4540
|
+
return "0.0.0";
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
2932
4543
|
function createCLI() {
|
|
2933
4544
|
const config = readConfig();
|
|
2934
4545
|
if (config.language) {
|
|
2935
4546
|
setLanguage(config.language);
|
|
2936
4547
|
}
|
|
2937
4548
|
const program2 = new Command();
|
|
2938
|
-
program2.name("install-helper").description("Install Helper - Interactive installer for CANN operator development skills").version(
|
|
4549
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
|
|
2939
4550
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
2940
4551
|
await initCommand();
|
|
2941
4552
|
});
|
|
@@ -2948,7 +4559,7 @@ function createCLI() {
|
|
|
2948
4559
|
program2.command("status").description("Show installed plugins").action(async () => {
|
|
2949
4560
|
await statusCommand();
|
|
2950
4561
|
});
|
|
2951
|
-
program2.command("install [names...]").description("Install plugins or skills (auto-detects type)").option("-t, --tool <tool>", "AI tool (opencode, claude, trae, cursor, copilot)").option("-l, --level <level>", "Install level (project, global)", "project").option("-y, --yes", "Skip confirmation prompts").option("--list", "List all available skills by category").action(async (names, options) => {
|
|
4562
|
+
program2.command("install [names...]").description("Install plugins or skills (auto-detects type)").option("-t, --tool <tool>", "AI tool (opencode, claude, trae, cursor, copilot)").option("-l, --level <level>", "Install level (project, global)", "project").option("-y, --yes", "Skip confirmation prompts").option("-a, --all", "Install ALL available skills").option("--list", "List all available skills by category").action(async (names, options) => {
|
|
2952
4563
|
await installCommand(names, options);
|
|
2953
4564
|
});
|
|
2954
4565
|
program2.command("uninstall <names...>").description("Uninstall plugins or skills (auto-detects type)").option("-t, --tool <tool>", "AI tool (opencode, claude, trae, cursor, copilot)").option("-l, --level <level>", "Install level (project, global)", "project").action(async (names, options) => {
|
|
@@ -2970,13 +4581,28 @@ function createCLI() {
|
|
|
2970
4581
|
}
|
|
2971
4582
|
|
|
2972
4583
|
// src/index.ts
|
|
2973
|
-
|
|
2974
|
-
if (error
|
|
2975
|
-
|
|
4584
|
+
function isExitError(error) {
|
|
4585
|
+
if (!error) return false;
|
|
4586
|
+
return error.name === "ExitPromptError" || error.message?.includes("force closed") || error.message?.includes("SIGINT") || error.constructor?.name === "ExitPromptError";
|
|
4587
|
+
}
|
|
4588
|
+
function handleExit(error) {
|
|
4589
|
+
if (isExitError(error)) {
|
|
4590
|
+
console.log("\n" + t("init_cancelled"));
|
|
2976
4591
|
process.exit(0);
|
|
2977
4592
|
}
|
|
4593
|
+
}
|
|
4594
|
+
process.on("SIGINT", () => {
|
|
4595
|
+
console.log("\n" + t("init_cancelled"));
|
|
4596
|
+
process.exit(130);
|
|
4597
|
+
});
|
|
4598
|
+
process.on("uncaughtException", (error) => {
|
|
4599
|
+
handleExit(error);
|
|
2978
4600
|
throw error;
|
|
2979
4601
|
});
|
|
4602
|
+
process.on("unhandledRejection", (error) => {
|
|
4603
|
+
handleExit(error);
|
|
4604
|
+
process.exit(1);
|
|
4605
|
+
});
|
|
2980
4606
|
var program = createCLI();
|
|
2981
4607
|
program.parse();
|
|
2982
4608
|
//# sourceMappingURL=index.js.map
|