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