@cannbot-ai/install-helper 0.0.2-beta.4 → 0.0.2-beta.41
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 +122 -31
- package/dist/config/repository.yaml +28 -0
- package/dist/index.js +2108 -935
- package/dist/index.js.map +1 -1
- package/dist/locales/en_US.json +180 -0
- package/dist/locales/zh_CN.json +180 -0
- package/dist/plugins.d/_defaults.yml +4 -0
- package/dist/plugins.d/catlass-op-generator.yml +7 -0
- package/dist/plugins.d/model-infer-optimize.yml +7 -0
- package/dist/plugins.d/ops-code-reviewer.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke.yml +7 -0
- package/dist/plugins.d/ops-registry-invoke.yml +7 -0
- package/dist/plugins.d/pypto-op-orchestrator.yml +7 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +7 -0
- package/dist/plugins.d/torch-compile.yml +7 -0
- package/dist/plugins.d/triton-op-generator.yml +7 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -4,116 +4,802 @@
|
|
|
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_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",
|
|
92
|
+
quick_start: "\u542F\u52A8\u547D\u4EE4",
|
|
93
|
+
quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
|
|
94
|
+
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
95
|
+
skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
|
|
96
|
+
skill_already_installed: "\u5DF2\u5B89\u88C5",
|
|
97
|
+
skill_select_all: "\u5168\u9009\u6240\u6709 Skills\uFF08\u5171 {count} \u4E2A\uFF09",
|
|
98
|
+
skill_select_all_category: "\u5168\u9009\u5F53\u524D\u7C7B\u522B",
|
|
99
|
+
skill_continue_or_install: "\u5DF2\u9009 {count} \u4E2A Skills\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
100
|
+
skill_continue_select: "\u7EE7\u7EED\u9009\u62E9\u5176\u4ED6\u7C7B\u522B",
|
|
101
|
+
skill_confirm_install_count: "\u786E\u8BA4\u5B89\u88C5\uFF08\u5DF2\u9009 {count} \u4E2A Skills\uFF09",
|
|
102
|
+
skill_all_install_progress: "\u6B63\u5728\u5B89\u88C5\u5168\u90E8 {count} \u4E2A Skills...",
|
|
103
|
+
skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
104
|
+
skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
|
|
105
|
+
skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
|
|
106
|
+
skill_list_title: "\u53EF\u7528 Skills",
|
|
107
|
+
wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
108
|
+
wizard_mode_plugin: "\u5B89\u88C5 Plugin",
|
|
109
|
+
wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
|
|
110
|
+
wizard_mode_skill: "\u5B89\u88C5 Skill",
|
|
111
|
+
wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
|
|
112
|
+
wizard_back: "\u8FD4\u56DE",
|
|
113
|
+
wizard_cancel: "\u9000\u51FA",
|
|
114
|
+
wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
115
|
+
wizard_back_to_reselect: "\u8FD4\u56DE",
|
|
116
|
+
status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
117
|
+
status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
118
|
+
status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
|
|
119
|
+
status_install_time: "\u5B89\u88C5\u65F6\u95F4",
|
|
120
|
+
install_installed_to: "\u5B89\u88C5\u5230",
|
|
121
|
+
install_skills_count: "Skills",
|
|
122
|
+
install_agents_count: "Agents",
|
|
123
|
+
lang_supported: "\u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US",
|
|
124
|
+
lang_usage: "\u7528\u6CD5: install-helper lang set <language>",
|
|
125
|
+
lang_specify: "\u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN",
|
|
126
|
+
lang_unknown_action: "\u672A\u77E5\u64CD\u4F5C",
|
|
127
|
+
lang_supported_actions: "\u652F\u6301\u7684\u64CD\u4F5C: show, set",
|
|
128
|
+
info_aliases: "\u522B\u540D",
|
|
129
|
+
uninstall_not_found: "\u672A\u627E\u5230",
|
|
130
|
+
uninstall_no_record: "\u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
|
|
131
|
+
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",
|
|
132
|
+
uninstall_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
133
|
+
uninstall_removed: "\u5DF2\u5378\u8F7D",
|
|
134
|
+
uninstall_summary_format: "{name}\uFF08\u79FB\u9664 {files} \u4E2A\u6587\u4EF6\uFF0C{dirs} \u4E2A\u76EE\u5F55\uFF09",
|
|
135
|
+
uninstall_files_gone: "\u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55",
|
|
136
|
+
uninstall_skills_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
137
|
+
init_pkg_dir_warn: "\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55",
|
|
138
|
+
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",
|
|
139
|
+
init_pkg_dir_example: "\u793A\u4F8B\uFF1Acd ~/my-project && install-helper",
|
|
140
|
+
init_cancelled: "\u5DF2\u53D6\u6D88\u5B89\u88C5",
|
|
141
|
+
backup_detected: "\u68C0\u6D4B\u5230\u5F53\u524D\u76EE\u5F55\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
142
|
+
backup_current: "\u5F53\u524D\u5B89\u88C5",
|
|
143
|
+
backup_installing: "\u5373\u5C06\u5B89\u88C5",
|
|
144
|
+
backup_overwrite_warning: "\u7EE7\u7EED\u5B89\u88C5\u5C06\u8986\u76D6\u73B0\u6709\u7684 AGENTS.md \u914D\u7F6E\u6587\u4EF6\u3002",
|
|
145
|
+
backup_auto_create: "\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u521B\u5EFA\u5907\u4EFD\uFF0C\u4EE5\u4FBF\u540E\u7EED\u6062\u590D\u3002",
|
|
146
|
+
backup_overwrite: "\u8986\u76D6\u5B89\u88C5\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09",
|
|
147
|
+
backup_cancel: "\u53D6\u6D88\u5B89\u88C5",
|
|
148
|
+
backup_restore_prompt: "\u68C0\u6D4B\u5230\u5907\u4EFD\u914D\u7F6E\uFF0C\u662F\u5426\u6062\u590D\uFF1F",
|
|
149
|
+
backup_restore: "\u6062\u590D",
|
|
150
|
+
backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
|
|
151
|
+
backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
152
|
+
backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
|
|
153
|
+
backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
|
|
154
|
+
backup_failed: "\u5907\u4EFD\u521B\u5EFA\u5931\u8D25\uFF0C\u5B89\u88C5\u5DF2\u4E2D\u6B62\u4EE5\u4FDD\u62A4\u73B0\u6709\u914D\u7F6E",
|
|
155
|
+
loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
|
|
156
|
+
loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
|
|
157
|
+
loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
|
|
158
|
+
loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
|
|
159
|
+
loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
|
|
160
|
+
repo_pull_failed: "\u65E0\u6CD5\u62C9\u53D6\u6700\u65B0\u5185\u5BB9\uFF0C\u4F7F\u7528\u7F13\u5B58\u6570\u636E",
|
|
161
|
+
config_corrupted: "\u914D\u7F6E\u6587\u4EF6\u5DF2\u635F\u574F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E",
|
|
162
|
+
record_corrupted: "\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F\uFF1A{file}",
|
|
163
|
+
skill_record_corrupted: "Skill \u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
|
|
164
|
+
plugin_yaml_error: "\u63D2\u4EF6\u914D\u7F6E\u89E3\u6790\u5931\u8D25\uFF1A{file}",
|
|
165
|
+
record_write_failed: "\u5B89\u88C5\u8BB0\u5F55\u5199\u5165\u5931\u8D25\uFF0C\u5378\u8F7D\u65F6\u53EF\u80FD\u65E0\u6CD5\u81EA\u52A8\u6E05\u7406",
|
|
166
|
+
install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
|
|
167
|
+
install_to: "\u5B89\u88C5\u5230",
|
|
168
|
+
start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
|
|
169
|
+
wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
170
|
+
wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
|
|
171
|
+
wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
172
|
+
wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
|
|
173
|
+
wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
|
|
174
|
+
wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
|
|
175
|
+
install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
|
|
176
|
+
install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
|
|
177
|
+
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
178
|
+
skill_count_format: "{count} \u4E2A Skills",
|
|
179
|
+
result_success_format: "{count} \u6210\u529F",
|
|
180
|
+
result_failed_format: "{count} \u5931\u8D25",
|
|
181
|
+
uninstall_remove_label: "\u79FB\u9664",
|
|
182
|
+
uninstall_remove_file_format: "\u79FB\u9664 {count} \u4E2A\u6587\u4EF6",
|
|
183
|
+
uninstall_remove_dir_format: "{count} \u4E2A\u76EE\u5F55",
|
|
184
|
+
uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
|
|
185
|
+
uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
|
|
186
|
+
available_plugins: "\u53EF\u7528\u63D2\u4EF6",
|
|
187
|
+
available_skills: "\u53EF\u7528 Skills",
|
|
188
|
+
skill_total_count: "\u5171 {count} \u4E2A Skills",
|
|
189
|
+
wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
|
|
190
|
+
wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
|
|
191
|
+
update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
|
|
192
|
+
hint_select_move: "\u79FB\u52A8",
|
|
193
|
+
hint_select_space: "\u7A7A\u683C",
|
|
194
|
+
hint_select_check: "\u52FE\u9009",
|
|
195
|
+
hint_select_confirm: "\u786E\u8BA4",
|
|
196
|
+
hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
|
|
197
|
+
doctor_not_installed: "\u672A\u5B89\u88C5",
|
|
198
|
+
doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
|
|
199
|
+
doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
200
|
+
doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
201
|
+
doctor_not_exist: "\u4E0D\u5B58\u5728",
|
|
202
|
+
doctor_created: "\u5DF2\u521B\u5EFA {path}",
|
|
203
|
+
doctor_config_exists: "\u5B58\u5728",
|
|
204
|
+
doctor_config_not_exist: "\u4E0D\u5B58\u5728",
|
|
205
|
+
doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {fixes} \u9879\u5DF2\u4FEE\u590D",
|
|
206
|
+
doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A",
|
|
207
|
+
count_suffix: "\u4E2A",
|
|
208
|
+
try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/locales/en_US.json
|
|
212
|
+
var en_US_default = {
|
|
213
|
+
wizard_title: "CANNBot Install Helper",
|
|
214
|
+
wizard_detect: "Detecting installed AI coding tools...",
|
|
215
|
+
wizard_select_tool: "Select AI coding tool",
|
|
216
|
+
wizard_select_level: "Select install level",
|
|
217
|
+
wizard_select_plugins: "Select scenarios to install",
|
|
218
|
+
wizard_confirm: "Confirm installation",
|
|
219
|
+
wizard_detected_tool: "Detected",
|
|
220
|
+
wizard_confirm_tool: "Confirm using",
|
|
221
|
+
wizard_last_used: "last used",
|
|
222
|
+
wizard_already_installed: "installed",
|
|
223
|
+
install_repo_ready: "Repository ready",
|
|
224
|
+
install_progress: "Installing",
|
|
225
|
+
install_done: "Installation complete",
|
|
226
|
+
install_success: "success",
|
|
227
|
+
install_already_installed: "already installed",
|
|
228
|
+
install_reinstall_confirm: "Reinstall (overwrite)",
|
|
229
|
+
install_skip: "skipped",
|
|
230
|
+
install_enhanced_next_steps: "Next steps",
|
|
231
|
+
install_enhanced_launch: "Launch",
|
|
232
|
+
install_enhanced_try: "Try",
|
|
233
|
+
install_enhanced_more: "More",
|
|
234
|
+
install_enhanced_check: "Check",
|
|
235
|
+
install_enhanced_docs: "Docs",
|
|
236
|
+
update_updating: "Updating",
|
|
237
|
+
update_done: "Update complete",
|
|
238
|
+
update_no_plugins: "No installed plugins",
|
|
239
|
+
doctor_title: "CANNBot Doctor",
|
|
240
|
+
doctor_tools: "AI Tools",
|
|
241
|
+
doctor_plugins: "Installed Plugins",
|
|
242
|
+
doctor_links: "Link Integrity",
|
|
243
|
+
doctor_config: "Configuration",
|
|
244
|
+
doctor_result: "Result",
|
|
245
|
+
doctor_fix_cleaning: "Cleaning broken links",
|
|
246
|
+
doctor_fix_rebuilding: "Rebuilding directories",
|
|
247
|
+
info_description: "Description",
|
|
248
|
+
info_skills: "Skills",
|
|
249
|
+
info_agents: "Agents",
|
|
250
|
+
info_status: "Status",
|
|
251
|
+
info_quickstart: "Quick Start",
|
|
252
|
+
info_not_found: "Plugin not found",
|
|
253
|
+
lang_title: "Language Settings",
|
|
254
|
+
lang_current: "Current language",
|
|
255
|
+
lang_set: "Language set to",
|
|
256
|
+
lang_invalid: "Invalid language, supported: zh_CN, en_US",
|
|
257
|
+
list_title: "Available Scenarios",
|
|
258
|
+
list_id: "#",
|
|
259
|
+
list_name: "Name",
|
|
260
|
+
list_status: "Status",
|
|
261
|
+
list_description: "Description",
|
|
262
|
+
status_installed: "installed",
|
|
263
|
+
status_not_installed: "not installed",
|
|
264
|
+
error_tool_not_found: "No AI coding tool detected",
|
|
265
|
+
error_plugin_not_found: "Plugin not found",
|
|
266
|
+
error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
|
|
267
|
+
error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
|
|
268
|
+
error_skill_not_found: "Skill not found in registry",
|
|
269
|
+
error_source_not_found: "Source not found: {path}",
|
|
270
|
+
error_script_not_found: "Script not found: {path}",
|
|
271
|
+
error_unknown: "Unknown error",
|
|
272
|
+
error_not_installed: "Not installed",
|
|
273
|
+
error_symlink_windows: "Failed to create symlink, please run as administrator or enable Windows Developer Mode",
|
|
274
|
+
quick_start: "Launch command",
|
|
275
|
+
quick_start_hint: "Try: help me develop an Abs operator",
|
|
276
|
+
skill_select_category: "Select Skill category",
|
|
277
|
+
skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
|
|
278
|
+
skill_already_installed: "installed",
|
|
279
|
+
skill_select_all: "Select ALL Skills ({count} total)",
|
|
280
|
+
skill_select_all_category: "Select all in this category",
|
|
281
|
+
skill_continue_or_install: "{count} Skills selected, choose an action",
|
|
282
|
+
skill_continue_select: "Continue selecting from other categories",
|
|
283
|
+
skill_confirm_install_count: "Install now ({count} Skills selected)",
|
|
284
|
+
skill_all_install_progress: "Installing all {count} Skills...",
|
|
285
|
+
skill_install_progress: "Installing",
|
|
286
|
+
skill_install_done: "Skill installation complete",
|
|
287
|
+
skill_uninstall_done: "Skill uninstallation complete",
|
|
288
|
+
skill_list_title: "Available Skills",
|
|
289
|
+
wizard_select_mode: "Select installation mode",
|
|
290
|
+
wizard_mode_plugin: "Install Plugin",
|
|
291
|
+
wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
|
|
292
|
+
wizard_mode_skill: "Install Skill",
|
|
293
|
+
wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
|
|
294
|
+
wizard_back: "Go back",
|
|
295
|
+
wizard_cancel: "Cancel installation",
|
|
296
|
+
wizard_no_selection: "No items selected, please choose an action",
|
|
297
|
+
wizard_back_to_reselect: "Go back",
|
|
298
|
+
status_title: "Installed Plugins",
|
|
299
|
+
status_none: "No installed plugins",
|
|
300
|
+
status_hint: "Run install-helper init to start",
|
|
301
|
+
status_install_time: "Installed at",
|
|
302
|
+
install_installed_to: "Installed to",
|
|
303
|
+
install_skills_count: "Skills",
|
|
304
|
+
install_agents_count: "Agents",
|
|
305
|
+
lang_supported: "Supported languages: zh_CN, en_US",
|
|
306
|
+
lang_usage: "Usage: install-helper lang set <language>",
|
|
307
|
+
lang_specify: "Please specify language: install-helper lang set zh_CN",
|
|
308
|
+
lang_unknown_action: "Unknown action",
|
|
309
|
+
lang_supported_actions: "Supported actions: show, set",
|
|
310
|
+
info_aliases: "Aliases",
|
|
311
|
+
uninstall_not_found: "Not found",
|
|
312
|
+
uninstall_no_record: "No install record found, may not be installed or record is corrupted",
|
|
313
|
+
uninstall_no_record_hint: "For manual cleanup, delete related files in .opencode/ or .claude/ directory",
|
|
314
|
+
uninstall_in_progress: "Uninstalling",
|
|
315
|
+
uninstall_removed: "Uninstalled",
|
|
316
|
+
uninstall_summary_format: "{name} (removed {files} files, {dirs} directories)",
|
|
317
|
+
uninstall_files_gone: " files no longer exist, install record cleaned",
|
|
318
|
+
uninstall_skills_in_progress: "Uninstalling",
|
|
319
|
+
init_pkg_dir_warn: "Current directory appears to be the install-helper package directory",
|
|
320
|
+
init_pkg_dir_hint: "Please run this command in your project directory, not in the install-helper package directory",
|
|
321
|
+
init_pkg_dir_example: "Example: cd ~/my-project && install-helper",
|
|
322
|
+
init_cancelled: "Installation cancelled",
|
|
323
|
+
backup_detected: "Plugin already installed in current directory",
|
|
324
|
+
backup_current: "Currently installed",
|
|
325
|
+
backup_installing: "About to install",
|
|
326
|
+
backup_overwrite_warning: "Continuing will overwrite existing AGENTS.md configuration.",
|
|
327
|
+
backup_auto_create: "System will automatically create backup for future restoration.",
|
|
328
|
+
backup_overwrite: "Overwrite (auto backup)",
|
|
329
|
+
backup_cancel: "Cancel installation",
|
|
330
|
+
backup_restore_prompt: "Backup configuration detected, restore?",
|
|
331
|
+
backup_restore: "Restore",
|
|
332
|
+
backup_only_uninstall: "Uninstall only (no restore)",
|
|
333
|
+
backup_select_action: "Please select an action",
|
|
334
|
+
backup_restore_success: "Configuration restored",
|
|
335
|
+
backup_created: "Backup created",
|
|
336
|
+
backup_failed: "Backup creation failed, installation aborted to protect existing config",
|
|
337
|
+
loading_skills_list: "Loading Skills list...",
|
|
338
|
+
loading_skills_list_complete: "Skills list loaded",
|
|
339
|
+
loading_plugin_data: "Loading plugin data...",
|
|
340
|
+
loading_plugin_data_complete: "Plugin data loaded",
|
|
341
|
+
loading_skills_list_failed: "Failed to load Skills list, using built-in data",
|
|
342
|
+
repo_pull_failed: "Failed to pull latest updates, using cached data",
|
|
343
|
+
config_corrupted: "Config file corrupted, using defaults",
|
|
344
|
+
record_corrupted: "Install record corrupted: {file}",
|
|
345
|
+
skill_record_corrupted: "Skill install record corrupted",
|
|
346
|
+
plugin_yaml_error: "Plugin config parse error: {file}",
|
|
347
|
+
record_write_failed: "Install record write failed, uninstall may not work properly",
|
|
348
|
+
install_no_skill_selected: "No Skill selected",
|
|
349
|
+
install_to: "Install to",
|
|
350
|
+
start_to_use: "Start {tool} to use",
|
|
351
|
+
wizard_step_1_title: "[1/4] Select AI coding tool",
|
|
352
|
+
wizard_step_2_title: "[2/4] Select installation location",
|
|
353
|
+
wizard_step_3_title: "[3/4] Select plugins to install",
|
|
354
|
+
wizard_step_4_title: "[4/4] Confirm installation",
|
|
355
|
+
wizard_step_3_skill_title: "[3/4] Select Skill category",
|
|
356
|
+
wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
|
|
357
|
+
install_level_project: "Install to current directory (recommended, current project only)",
|
|
358
|
+
install_level_global: "Install globally (all projects)",
|
|
359
|
+
wizard_select_mode_title: "Select installation type",
|
|
360
|
+
skill_count_format: "{count} Skills",
|
|
361
|
+
result_success_format: "{count} succeeded",
|
|
362
|
+
result_failed_format: "{count} failed",
|
|
363
|
+
uninstall_remove_label: "Removed",
|
|
364
|
+
uninstall_remove_file_format: "Removed {count} files",
|
|
365
|
+
uninstall_remove_dir_format: "{count} directories",
|
|
366
|
+
uninstall_clean_empty_dir: "Cleaned empty directory",
|
|
367
|
+
uninstall_skill_not_installed: "Not installed",
|
|
368
|
+
available_plugins: "Available plugins",
|
|
369
|
+
available_skills: "Available Skills",
|
|
370
|
+
skill_total_count: "Total {count} Skills",
|
|
371
|
+
wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
|
|
372
|
+
wizard_install_path: "Install path",
|
|
373
|
+
update_install_hint: "Use {cmd} to install plugins",
|
|
374
|
+
hint_select_move: "Navigate",
|
|
375
|
+
hint_select_space: "Space",
|
|
376
|
+
hint_select_check: "Toggle",
|
|
377
|
+
hint_select_confirm: "Confirm",
|
|
378
|
+
hint_checkbox_suffix: "Press enter without selection to go back",
|
|
379
|
+
doctor_not_installed: "Not installed",
|
|
380
|
+
doctor_links_valid: "{count} symlinks all valid",
|
|
381
|
+
doctor_broken_links: "{count} broken links",
|
|
382
|
+
doctor_fixed: "Cleaned {count} broken links",
|
|
383
|
+
doctor_not_exist: "Does not exist",
|
|
384
|
+
doctor_created: "Created {path}",
|
|
385
|
+
doctor_config_exists: "Exists",
|
|
386
|
+
doctor_config_not_exist: "Does not exist",
|
|
387
|
+
doctor_result_with_fix: "{warnings} warnings, {fixes} fixed",
|
|
388
|
+
doctor_result_without_fix: "{warnings} warnings",
|
|
389
|
+
count_suffix: "",
|
|
390
|
+
try_prompt: "help me develop an Abs operator"
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// src/utils/i18n.ts
|
|
394
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
395
|
+
var __dirname = dirname(__filename);
|
|
396
|
+
var EMBEDDED_LOCALES = {
|
|
397
|
+
zh_CN: zh_CN_default,
|
|
398
|
+
en_US: en_US_default
|
|
399
|
+
};
|
|
400
|
+
function loadLocale(locale) {
|
|
401
|
+
const embedded = EMBEDDED_LOCALES[locale];
|
|
402
|
+
if (embedded) return embedded;
|
|
403
|
+
const localePath = join(__dirname, "locales", `${locale}.json`);
|
|
404
|
+
if (existsSync(localePath)) {
|
|
405
|
+
try {
|
|
406
|
+
const content = readFileSync(localePath, "utf-8");
|
|
407
|
+
return JSON.parse(content);
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const fallbackPath = join(__dirname, "locales", "zh_CN.json");
|
|
412
|
+
if (existsSync(fallbackPath)) {
|
|
413
|
+
try {
|
|
414
|
+
const content = readFileSync(fallbackPath, "utf-8");
|
|
415
|
+
return JSON.parse(content);
|
|
416
|
+
} catch {
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return EMBEDDED_LOCALES.zh_CN || {};
|
|
420
|
+
}
|
|
421
|
+
var currentLang = "zh_CN";
|
|
422
|
+
var messages = loadLocale("zh_CN");
|
|
423
|
+
function setLanguage(lang) {
|
|
424
|
+
currentLang = lang;
|
|
425
|
+
messages = loadLocale(lang);
|
|
426
|
+
}
|
|
427
|
+
function t(key) {
|
|
428
|
+
return messages[key] !== void 0 ? messages[key] : key;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/ui/theme.ts
|
|
432
|
+
import chalk from "chalk";
|
|
433
|
+
var selectTheme = {
|
|
434
|
+
helpMode: "never"
|
|
435
|
+
};
|
|
436
|
+
function isCJK(code) {
|
|
437
|
+
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;
|
|
438
|
+
}
|
|
439
|
+
function isEmoji(code) {
|
|
440
|
+
return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
|
|
441
|
+
}
|
|
442
|
+
function getDisplayWidth(str) {
|
|
443
|
+
let width = 0;
|
|
444
|
+
for (const ch of str) {
|
|
445
|
+
const code = ch.codePointAt(0);
|
|
446
|
+
if (isCJK(code) || isEmoji(code)) {
|
|
447
|
+
width += 2;
|
|
448
|
+
} else {
|
|
449
|
+
width += 1;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return width;
|
|
453
|
+
}
|
|
454
|
+
function wrapDescription(text) {
|
|
455
|
+
const prefix = " \u{1F4A1} ";
|
|
456
|
+
const indent = " ";
|
|
457
|
+
const cols = process.stdout.columns || 120;
|
|
458
|
+
const maxLen = Math.max(20, cols - getDisplayWidth(prefix) - 2);
|
|
459
|
+
const tokens = [];
|
|
460
|
+
let i = 0;
|
|
461
|
+
while (i < text.length) {
|
|
462
|
+
const code = text[i].codePointAt(0);
|
|
463
|
+
if (isCJK(code)) {
|
|
464
|
+
tokens.push({ text: text[i], width: 2 });
|
|
465
|
+
i++;
|
|
466
|
+
} else {
|
|
467
|
+
let j = i;
|
|
468
|
+
while (j < text.length) {
|
|
469
|
+
const c = text[j].codePointAt(0);
|
|
470
|
+
if (isCJK(c)) break;
|
|
471
|
+
j++;
|
|
472
|
+
}
|
|
473
|
+
const word = text.slice(i, j);
|
|
474
|
+
tokens.push({ text: word, width: getDisplayWidth(word) });
|
|
475
|
+
i = j;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const lines = [];
|
|
479
|
+
let line = "";
|
|
480
|
+
let lineWidth = 0;
|
|
481
|
+
for (const token of tokens) {
|
|
482
|
+
if (lineWidth + token.width > maxLen && line) {
|
|
483
|
+
lines.push(line.trimEnd());
|
|
484
|
+
line = token.text;
|
|
485
|
+
lineWidth = token.width;
|
|
486
|
+
} else {
|
|
487
|
+
line += token.text;
|
|
488
|
+
lineWidth += token.width;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (line) lines.push(line.trimEnd());
|
|
492
|
+
return lines.map((l, i2) => chalk.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
|
|
493
|
+
}
|
|
494
|
+
var checkboxTheme = {
|
|
495
|
+
icon: {
|
|
496
|
+
checked: "\x1B[32m\u2611\x1B[22m",
|
|
497
|
+
unchecked: "\u2610",
|
|
498
|
+
cursor: "\u276F"
|
|
499
|
+
},
|
|
500
|
+
helpMode: "never",
|
|
501
|
+
style: {
|
|
502
|
+
description: (text) => wrapDescription(text)
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// src/utils/logger.ts
|
|
507
|
+
var logger = {
|
|
508
|
+
success: (msg) => console.log(chalk2.green("\u2713") + " " + msg),
|
|
509
|
+
error: (msg) => console.log(chalk2.red("\u2717") + " " + msg),
|
|
510
|
+
warn: (msg) => console.log(chalk2.yellow("\u26A0") + " " + msg),
|
|
511
|
+
info: (msg) => console.log(chalk2.cyan("\u2192") + " " + msg),
|
|
512
|
+
step: (msg) => console.log(chalk2.dim(msg)),
|
|
513
|
+
blank: () => console.log()
|
|
514
|
+
};
|
|
515
|
+
function createSpinner(text) {
|
|
516
|
+
return ora({ text, color: "cyan" });
|
|
517
|
+
}
|
|
518
|
+
function printBanner(subtitle) {
|
|
519
|
+
console.log(chalk2.cyan(`
|
|
520
|
+
____ _ _ _ _ _ ____ _
|
|
521
|
+
/ ___| / \\ | \\ | | \\ | | __ ) ___ | |_
|
|
522
|
+
| | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
|
|
523
|
+
| |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
|
|
524
|
+
\\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
|
|
525
|
+
`));
|
|
526
|
+
if (subtitle) {
|
|
527
|
+
console.log(chalk2.bold(` ${subtitle}`));
|
|
528
|
+
console.log();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function printBoxTitle(title, width = 65) {
|
|
532
|
+
const border = "\u2550".repeat(width);
|
|
533
|
+
const displayWidth = getDisplayWidth(title);
|
|
534
|
+
const totalPadding = width - displayWidth;
|
|
535
|
+
const leftPadding = Math.floor(totalPadding / 2);
|
|
536
|
+
const rightPadding = totalPadding - leftPadding;
|
|
537
|
+
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
538
|
+
console.log();
|
|
539
|
+
console.log(chalk2.cyan.bold(` \u2554${border}\u2557`));
|
|
540
|
+
console.log(chalk2.cyan.bold(` \u2551`) + chalk2.cyan.bold(paddedTitle) + chalk2.cyan.bold(`\u2551`));
|
|
541
|
+
console.log(chalk2.cyan.bold(` \u255A${border}\u255D`));
|
|
542
|
+
console.log();
|
|
543
|
+
}
|
|
544
|
+
function showOperationHints(isCheckbox = false) {
|
|
545
|
+
if (isCheckbox) {
|
|
546
|
+
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
|
|
547
|
+
`);
|
|
548
|
+
} else {
|
|
549
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
550
|
+
`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/utils/fs-helpers.ts
|
|
555
|
+
import { lstatSync, statSync } from "fs";
|
|
556
|
+
function isSymlink(path) {
|
|
557
|
+
try {
|
|
558
|
+
return lstatSync(path).isSymbolicLink();
|
|
559
|
+
} catch {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
function isDirectory(path) {
|
|
564
|
+
try {
|
|
565
|
+
return statSync(path).isDirectory();
|
|
566
|
+
} catch {
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// src/embedded-plugins.json
|
|
572
|
+
var embedded_plugins_default = [
|
|
16
573
|
{
|
|
17
574
|
id: "ops-direct-invoke",
|
|
18
575
|
dir: "plugins-official/ops-direct-invoke",
|
|
19
576
|
displayName: "AscendC Kernel \u76F4\u8C03",
|
|
20
577
|
script: "init.sh",
|
|
21
|
-
aliases: [
|
|
578
|
+
aliases: [
|
|
579
|
+
"ops-direct",
|
|
580
|
+
"ascendc-direct",
|
|
581
|
+
"direct",
|
|
582
|
+
"kernel"
|
|
583
|
+
],
|
|
22
584
|
skills: 16,
|
|
23
585
|
agents: 3,
|
|
24
|
-
description: "Ascend C
|
|
586
|
+
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"
|
|
25
587
|
},
|
|
26
588
|
{
|
|
27
589
|
id: "ops-direct-invoke-flash",
|
|
28
590
|
dir: "plugins-official/ops-direct-invoke-flash",
|
|
29
|
-
displayName: "AscendC Kernel \
|
|
591
|
+
displayName: "AscendC Kernel \u76F4\u8C03\uFF08\u5FEB\u901F\u7248\uFF09",
|
|
30
592
|
script: "init.sh",
|
|
31
|
-
aliases: [
|
|
593
|
+
aliases: [
|
|
594
|
+
"flash",
|
|
595
|
+
"kernel-flash"
|
|
596
|
+
],
|
|
32
597
|
skills: 1,
|
|
33
598
|
agents: 1,
|
|
34
|
-
description: "Ascend
|
|
599
|
+
description: "\u4ECE\u6570\u5B66\u516C\u5F0F\u6216\u4EE3\u7801\u7247\u6BB5\u5FEB\u901F\u6784\u5EFA\u5E76\u9A8C\u8BC1 Ascend NPU \u6838\u51FD\u6570"
|
|
35
600
|
},
|
|
36
601
|
{
|
|
37
602
|
id: "ops-registry-invoke",
|
|
38
603
|
dir: "plugins-official/ops-registry-invoke",
|
|
39
604
|
displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
|
|
40
605
|
script: "init.sh",
|
|
41
|
-
aliases: [
|
|
606
|
+
aliases: [
|
|
607
|
+
"ops-registry",
|
|
608
|
+
"ascendc-registry",
|
|
609
|
+
"registry"
|
|
610
|
+
],
|
|
42
611
|
skills: 12,
|
|
43
612
|
agents: 4,
|
|
44
|
-
description: "
|
|
613
|
+
description: "\u751F\u4EA7\u7EA7 ACLNN \u6CE8\u518C\u7B97\u5B50\u5168\u6D41\u7A0B\uFF1A\u4EE3\u7801\u2192UT/ST\u2192\u4E0A\u5E93"
|
|
45
614
|
},
|
|
46
615
|
{
|
|
47
|
-
id: "
|
|
48
|
-
dir: "plugins-official/
|
|
49
|
-
displayName: "
|
|
50
|
-
script: "init.sh",
|
|
51
|
-
aliases: ["pypto", "pytorch"],
|
|
52
|
-
skills: 8,
|
|
53
|
-
agents: 3,
|
|
54
|
-
description: "PyPTO \u7B97\u5B50\u7AEF\u5230\u7AEF\u5F00\u53D1\u7F16\u6392"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: "triton-op-generator",
|
|
58
|
-
dir: "plugins-official/triton-op-generator",
|
|
59
|
-
displayName: "Triton \u7B97\u5B50\u751F\u6210",
|
|
60
|
-
script: "install.sh",
|
|
61
|
-
aliases: ["triton"],
|
|
62
|
-
skills: 6,
|
|
63
|
-
agents: 0,
|
|
64
|
-
description: "Triton-Ascend \u7B97\u5B50\u4EE3\u7801\u751F\u6210\u4E0E\u4F18\u5316"
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
id: "tilelang-op-orchestrator",
|
|
68
|
-
dir: "plugins-official/tilelang-op-orchestrator",
|
|
69
|
-
displayName: "TileLang \u7B97\u5B50",
|
|
616
|
+
id: "catlass-op-generator",
|
|
617
|
+
dir: "plugins-official/catlass-op-generator",
|
|
618
|
+
displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
|
|
70
619
|
script: "init.sh",
|
|
71
|
-
aliases: [
|
|
72
|
-
|
|
620
|
+
aliases: [
|
|
621
|
+
"catlass"
|
|
622
|
+
],
|
|
623
|
+
skills: 10,
|
|
73
624
|
agents: 3,
|
|
74
|
-
description: "
|
|
625
|
+
description: "Catlass \u6A21\u677F\u62FC\u88C5 GEMM/Matmul/Epilogue \u7B97\u5B50\u5F00\u53D1"
|
|
75
626
|
},
|
|
76
627
|
{
|
|
77
628
|
id: "model-infer-optimize",
|
|
78
629
|
dir: "plugins-official/model-infer-optimize",
|
|
79
630
|
displayName: "NPU \u63A8\u7406\u4F18\u5316",
|
|
80
631
|
script: "init.sh",
|
|
81
|
-
aliases: [
|
|
632
|
+
aliases: [
|
|
633
|
+
"model-infer",
|
|
634
|
+
"infer",
|
|
635
|
+
"inference"
|
|
636
|
+
],
|
|
82
637
|
skills: 11,
|
|
83
638
|
agents: 3,
|
|
84
|
-
description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316"
|
|
639
|
+
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"
|
|
85
640
|
},
|
|
86
641
|
{
|
|
87
|
-
id: "
|
|
88
|
-
dir: "plugins-official/
|
|
89
|
-
displayName: "
|
|
642
|
+
id: "pypto-op-orchestrator",
|
|
643
|
+
dir: "plugins-official/pypto-op-orchestrator",
|
|
644
|
+
displayName: "PyPTO \u7B97\u5B50\u5F00\u53D1",
|
|
90
645
|
script: "init.sh",
|
|
91
|
-
aliases: [
|
|
92
|
-
|
|
646
|
+
aliases: [
|
|
647
|
+
"pypto",
|
|
648
|
+
"pytorch"
|
|
649
|
+
],
|
|
650
|
+
skills: 8,
|
|
93
651
|
agents: 3,
|
|
94
|
-
description: "
|
|
652
|
+
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"
|
|
95
653
|
},
|
|
96
654
|
{
|
|
97
|
-
id: "
|
|
98
|
-
dir: "plugins-official/
|
|
99
|
-
displayName: "\
|
|
655
|
+
id: "tilelang-op-orchestrator",
|
|
656
|
+
dir: "plugins-official/tilelang-op-orchestrator",
|
|
657
|
+
displayName: "TileLang \u7B97\u5B50\u5F00\u53D1",
|
|
100
658
|
script: "init.sh",
|
|
101
|
-
aliases: [
|
|
102
|
-
|
|
659
|
+
aliases: [
|
|
660
|
+
"tilelang"
|
|
661
|
+
],
|
|
662
|
+
skills: 9,
|
|
663
|
+
agents: 3,
|
|
664
|
+
description: "TileLang Python DSL + @tilelang.jit \u7B97\u5B50\u5F00\u53D1\uFF08\u652F\u6301 Developer/Expert \u53CC\u6A21\u5F0F\uFF09"
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
id: "triton-op-generator",
|
|
668
|
+
dir: "plugins-official/triton-op-generator",
|
|
669
|
+
displayName: "Triton \u7B97\u5B50\u5F00\u53D1",
|
|
670
|
+
script: "init.sh",
|
|
671
|
+
aliases: [
|
|
672
|
+
"triton"
|
|
673
|
+
],
|
|
674
|
+
skills: 6,
|
|
103
675
|
agents: 1,
|
|
104
|
-
description: "\u4EE3\u7801\
|
|
676
|
+
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"
|
|
105
677
|
},
|
|
106
678
|
{
|
|
107
679
|
id: "torch-compile",
|
|
108
680
|
dir: "plugins-official/torch-compile",
|
|
109
681
|
displayName: "torch.compile \u56FE\u6A21\u5F0F",
|
|
110
682
|
script: "init.sh",
|
|
111
|
-
aliases: [
|
|
683
|
+
aliases: [
|
|
684
|
+
"torch",
|
|
685
|
+
"compile",
|
|
686
|
+
"graph"
|
|
687
|
+
],
|
|
112
688
|
skills: 6,
|
|
113
689
|
agents: 1,
|
|
114
|
-
description: "
|
|
690
|
+
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"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
id: "ops-code-reviewer",
|
|
694
|
+
dir: "plugins-official/ops-code-reviewer",
|
|
695
|
+
displayName: "\u4EE3\u7801\u68C0\u89C6",
|
|
696
|
+
script: "init.sh",
|
|
697
|
+
aliases: [
|
|
698
|
+
"code-review",
|
|
699
|
+
"reviewer",
|
|
700
|
+
"review"
|
|
701
|
+
],
|
|
702
|
+
skills: 5,
|
|
703
|
+
agents: 1,
|
|
704
|
+
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"
|
|
705
|
+
}
|
|
706
|
+
];
|
|
707
|
+
|
|
708
|
+
// src/core/registry.ts
|
|
709
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
710
|
+
var __dirname2 = dirname2(__filename2);
|
|
711
|
+
function findPluginsDir() {
|
|
712
|
+
const candidates = [
|
|
713
|
+
join2(__dirname2, "plugins.d"),
|
|
714
|
+
join2(__dirname2, "..", "plugins.d"),
|
|
715
|
+
join2(__dirname2, "..", "..", "plugins.d")
|
|
716
|
+
];
|
|
717
|
+
for (const dir of candidates) {
|
|
718
|
+
if (existsSync2(dir)) return dir;
|
|
719
|
+
}
|
|
720
|
+
return candidates[0];
|
|
721
|
+
}
|
|
722
|
+
function loadPluginsFromYaml() {
|
|
723
|
+
const pluginsDir = findPluginsDir();
|
|
724
|
+
const defaultsPath = join2(pluginsDir, "_defaults.yml");
|
|
725
|
+
let defaults = {};
|
|
726
|
+
try {
|
|
727
|
+
defaults = parseYaml(readFileSync2(defaultsPath, "utf-8")) || {};
|
|
728
|
+
} catch {
|
|
729
|
+
}
|
|
730
|
+
const plugins = [];
|
|
731
|
+
try {
|
|
732
|
+
for (const file of readdirSync(pluginsDir)) {
|
|
733
|
+
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
734
|
+
try {
|
|
735
|
+
const content = parseYaml(readFileSync2(join2(pluginsDir, file), "utf-8"));
|
|
736
|
+
if (!content || !content.id) continue;
|
|
737
|
+
plugins.push({
|
|
738
|
+
id: content.id,
|
|
739
|
+
dir: content.dir,
|
|
740
|
+
displayName: content.displayName || content.id,
|
|
741
|
+
script: content.script || defaults.script || "init.sh",
|
|
742
|
+
aliases: content.aliases || [],
|
|
743
|
+
skills: content.skills ?? defaults.skills ?? 0,
|
|
744
|
+
agents: content.agents ?? defaults.agents ?? 0,
|
|
745
|
+
description: content.description || ""
|
|
746
|
+
});
|
|
747
|
+
} catch {
|
|
748
|
+
logger.warn(t("plugin_yaml_error").replace("{file}", file));
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
} catch {
|
|
752
|
+
}
|
|
753
|
+
if (plugins.length === 0) {
|
|
754
|
+
return [...embedded_plugins_default];
|
|
755
|
+
}
|
|
756
|
+
plugins.sort(
|
|
757
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
758
|
+
);
|
|
759
|
+
return plugins;
|
|
760
|
+
}
|
|
761
|
+
var PLUGIN_REGISTRY = loadPluginsFromYaml();
|
|
762
|
+
var PLUGIN_DIRS = ["plugins-official", "plugins-community"];
|
|
763
|
+
function mergeDynamicPlugins(repoPath) {
|
|
764
|
+
if (!existsSync2(repoPath)) return;
|
|
765
|
+
const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
|
|
766
|
+
const dynamic = [];
|
|
767
|
+
for (const pluginDir of PLUGIN_DIRS) {
|
|
768
|
+
const parent = join2(repoPath, pluginDir);
|
|
769
|
+
if (!existsSync2(parent)) continue;
|
|
770
|
+
let entries = [];
|
|
771
|
+
try {
|
|
772
|
+
entries = readdirSync(parent);
|
|
773
|
+
} catch {
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
for (const entry of entries) {
|
|
777
|
+
const pluginPath = join2(parent, entry);
|
|
778
|
+
if (!isDirectory(pluginPath)) continue;
|
|
779
|
+
if (entry === "install-helper") continue;
|
|
780
|
+
const initSh = join2(pluginPath, "init.sh");
|
|
781
|
+
if (!existsSync2(initSh)) continue;
|
|
782
|
+
const pluginId = entry;
|
|
783
|
+
if (existingIds.has(pluginId)) continue;
|
|
784
|
+
dynamic.push({
|
|
785
|
+
id: pluginId,
|
|
786
|
+
dir: `${pluginDir}/${entry}`,
|
|
787
|
+
displayName: entry,
|
|
788
|
+
script: "init.sh",
|
|
789
|
+
aliases: [],
|
|
790
|
+
skills: 0,
|
|
791
|
+
agents: 0,
|
|
792
|
+
description: ""
|
|
793
|
+
});
|
|
794
|
+
existingIds.add(pluginId);
|
|
795
|
+
}
|
|
115
796
|
}
|
|
116
|
-
|
|
797
|
+
if (dynamic.length > 0) {
|
|
798
|
+
PLUGIN_REGISTRY = [...PLUGIN_REGISTRY, ...dynamic].sort(
|
|
799
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
117
803
|
function findPlugin(query) {
|
|
118
804
|
const normalized = query.toLowerCase().trim();
|
|
119
805
|
const exactMatch = PLUGIN_REGISTRY.find(
|
|
@@ -137,19 +823,37 @@ function getPluginById(id) {
|
|
|
137
823
|
|
|
138
824
|
// src/core/detector.ts
|
|
139
825
|
import { execa } from "execa";
|
|
140
|
-
import { existsSync as
|
|
826
|
+
import { existsSync as existsSync4 } from "fs";
|
|
141
827
|
import { homedir as homedir2 } from "os";
|
|
142
|
-
import { join as
|
|
828
|
+
import { join as join4 } from "path";
|
|
143
829
|
|
|
144
830
|
// src/utils/paths.ts
|
|
145
831
|
import { homedir } from "os";
|
|
146
|
-
import { join } from "path";
|
|
147
|
-
import { existsSync } from "fs";
|
|
832
|
+
import { join as join3 } from "path";
|
|
833
|
+
import { existsSync as existsSync3 } from "fs";
|
|
834
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
835
|
+
var VALID_LEVELS = ["project", "global"];
|
|
836
|
+
function validateTool(tool) {
|
|
837
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
838
|
+
throw new Error(
|
|
839
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
return tool;
|
|
843
|
+
}
|
|
844
|
+
function validateLevel(level) {
|
|
845
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
846
|
+
throw new Error(
|
|
847
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
return level;
|
|
851
|
+
}
|
|
148
852
|
function detectTraeVariant() {
|
|
149
853
|
const home = homedir();
|
|
150
|
-
if (
|
|
151
|
-
if (
|
|
152
|
-
if (
|
|
854
|
+
if (existsSync3(join3(home, ".trae-cn"))) return "ide";
|
|
855
|
+
if (existsSync3(join3(home, ".marscode"))) return "plugin";
|
|
856
|
+
if (existsSync3(join3(home, ".traecli"))) return "cli";
|
|
153
857
|
return "unknown";
|
|
154
858
|
}
|
|
155
859
|
function getConfigRoot(tool, level, base) {
|
|
@@ -157,66 +861,77 @@ function getConfigRoot(tool, level, base) {
|
|
|
157
861
|
if (level === "global") {
|
|
158
862
|
switch (tool) {
|
|
159
863
|
case "opencode":
|
|
160
|
-
return
|
|
864
|
+
return join3(home, ".config", "opencode");
|
|
161
865
|
case "claude":
|
|
162
|
-
return
|
|
866
|
+
return join3(home, ".claude");
|
|
163
867
|
case "trae": {
|
|
164
868
|
const variant = detectTraeVariant();
|
|
165
869
|
switch (variant) {
|
|
166
870
|
case "plugin":
|
|
167
|
-
return
|
|
871
|
+
return join3(home, ".marscode");
|
|
168
872
|
case "cli":
|
|
169
|
-
return
|
|
873
|
+
return join3(home, ".traecli");
|
|
170
874
|
default:
|
|
171
|
-
return
|
|
875
|
+
return join3(home, ".trae-cn");
|
|
172
876
|
}
|
|
173
877
|
}
|
|
174
878
|
case "cursor":
|
|
175
|
-
return
|
|
879
|
+
return join3(home, ".cursor");
|
|
176
880
|
case "copilot":
|
|
177
|
-
return
|
|
881
|
+
return join3(home, ".copilot");
|
|
882
|
+
default:
|
|
883
|
+
throw new Error(
|
|
884
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
885
|
+
);
|
|
178
886
|
}
|
|
179
887
|
}
|
|
180
888
|
const baseDir = base || process.cwd();
|
|
181
889
|
switch (tool) {
|
|
182
890
|
case "opencode":
|
|
183
|
-
return
|
|
891
|
+
return join3(baseDir, ".opencode");
|
|
184
892
|
case "claude":
|
|
185
|
-
return
|
|
893
|
+
return join3(baseDir, ".claude");
|
|
186
894
|
case "trae": {
|
|
187
895
|
const variant = detectTraeVariant();
|
|
188
896
|
switch (variant) {
|
|
189
897
|
case "plugin":
|
|
190
|
-
return
|
|
898
|
+
return join3(baseDir, ".marscode");
|
|
191
899
|
case "cli":
|
|
192
|
-
return
|
|
900
|
+
return join3(baseDir, ".traecli");
|
|
193
901
|
default:
|
|
194
|
-
return
|
|
902
|
+
return join3(baseDir, ".trae");
|
|
195
903
|
}
|
|
196
904
|
}
|
|
197
905
|
case "cursor":
|
|
198
|
-
return
|
|
906
|
+
return join3(baseDir, ".cursor");
|
|
199
907
|
case "copilot":
|
|
200
|
-
return
|
|
908
|
+
return join3(baseDir, ".github");
|
|
909
|
+
default:
|
|
910
|
+
throw new Error(
|
|
911
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
912
|
+
);
|
|
201
913
|
}
|
|
202
914
|
}
|
|
203
915
|
function getConfigFileName(tool) {
|
|
204
916
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
917
|
}
|
|
918
|
+
function getAgentsFileName(tool) {
|
|
919
|
+
return getConfigFileName(tool);
|
|
920
|
+
}
|
|
206
921
|
function getSkillsDir(configRoot) {
|
|
207
|
-
return
|
|
922
|
+
return join3(configRoot, "skills");
|
|
208
923
|
}
|
|
209
924
|
function getAgentsDir(configRoot) {
|
|
210
|
-
return
|
|
925
|
+
return join3(configRoot, "agents");
|
|
211
926
|
}
|
|
212
927
|
function getManifestPath(configRoot) {
|
|
213
|
-
return
|
|
928
|
+
return join3(configRoot, "cannbot-manifest.json");
|
|
214
929
|
}
|
|
215
930
|
function getCannbotConfigDir() {
|
|
216
|
-
return
|
|
931
|
+
return join3(homedir(), ".cannbot");
|
|
217
932
|
}
|
|
218
933
|
function getCannbotRepoPath() {
|
|
219
|
-
return
|
|
934
|
+
return join3(getCannbotConfigDir(), "repo");
|
|
220
935
|
}
|
|
221
936
|
|
|
222
937
|
// src/core/detector.ts
|
|
@@ -232,8 +947,9 @@ async function getCommandVersion(cmd, args = ["--version"]) {
|
|
|
232
947
|
}
|
|
233
948
|
async function getCommandPath(cmd) {
|
|
234
949
|
try {
|
|
235
|
-
const
|
|
236
|
-
|
|
950
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
951
|
+
const result = await execa(whichCmd, [cmd], { timeout: 5e3 });
|
|
952
|
+
return result.stdout.trim().split("\n")[0];
|
|
237
953
|
} catch {
|
|
238
954
|
return void 0;
|
|
239
955
|
}
|
|
@@ -254,12 +970,12 @@ async function detectTrae() {
|
|
|
254
970
|
const home = homedir2();
|
|
255
971
|
const variant = detectTraeVariant();
|
|
256
972
|
let path;
|
|
257
|
-
if (variant === "ide" &&
|
|
258
|
-
path =
|
|
259
|
-
} else if (variant === "plugin" &&
|
|
260
|
-
path =
|
|
261
|
-
} else if (variant === "cli" &&
|
|
262
|
-
path =
|
|
973
|
+
if (variant === "ide" && existsSync4(join4(home, ".trae-cn"))) {
|
|
974
|
+
path = join4(home, ".trae-cn");
|
|
975
|
+
} else if (variant === "plugin" && existsSync4(join4(home, ".marscode"))) {
|
|
976
|
+
path = join4(home, ".marscode");
|
|
977
|
+
} else if (variant === "cli" && existsSync4(join4(home, ".traecli"))) {
|
|
978
|
+
path = join4(home, ".traecli");
|
|
263
979
|
} else if (variant === "unknown") {
|
|
264
980
|
const cmdPath = await getCommandPath("trae");
|
|
265
981
|
if (cmdPath) path = cmdPath;
|
|
@@ -275,7 +991,7 @@ async function detectCursor() {
|
|
|
275
991
|
}
|
|
276
992
|
if (process.platform === "darwin") {
|
|
277
993
|
const appPath = "/Applications/Cursor.app";
|
|
278
|
-
if (
|
|
994
|
+
if (existsSync4(appPath)) {
|
|
279
995
|
return { name: "cursor", path: appPath };
|
|
280
996
|
}
|
|
281
997
|
}
|
|
@@ -319,346 +1035,148 @@ function getToolDisplayName(tool) {
|
|
|
319
1035
|
return names[tool];
|
|
320
1036
|
}
|
|
321
1037
|
function getAllTools() {
|
|
322
|
-
return
|
|
1038
|
+
return VALID_TOOLS;
|
|
323
1039
|
}
|
|
324
1040
|
|
|
325
1041
|
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as
|
|
327
|
-
import { join as
|
|
1042
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
1043
|
+
import { join as join5 } from "path";
|
|
328
1044
|
function readManifest(configRoot) {
|
|
329
1045
|
const manifestPath = getManifestPath(configRoot);
|
|
330
|
-
if (
|
|
331
|
-
try {
|
|
332
|
-
const content =
|
|
333
|
-
return JSON.parse(content);
|
|
334
|
-
} catch {
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
function readAllManifests(configRoot) {
|
|
340
|
-
const manifests = [];
|
|
341
|
-
const plugins = getAllPlugins();
|
|
342
|
-
const standardPath = getManifestPath(configRoot);
|
|
343
|
-
if (existsSync3(standardPath)) {
|
|
344
|
-
try {
|
|
345
|
-
const content = readFileSync(standardPath, "utf-8");
|
|
346
|
-
manifests.push(JSON.parse(content));
|
|
347
|
-
} catch {
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
|
|
352
|
-
if (existsSync3(pluginManifestPath)) {
|
|
353
|
-
try {
|
|
354
|
-
const content = readFileSync(pluginManifestPath, "utf-8");
|
|
355
|
-
manifests.push(JSON.parse(content));
|
|
356
|
-
} catch {
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return manifests;
|
|
361
|
-
}
|
|
362
|
-
function scanInstalled() {
|
|
363
|
-
const plugins = getAllPlugins();
|
|
364
|
-
const installed = [];
|
|
365
|
-
const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
366
|
-
const levels = ["project", "global"];
|
|
367
|
-
for (const tool of tools) {
|
|
368
|
-
for (const level of levels) {
|
|
369
|
-
const configRoot = getConfigRoot(tool, level);
|
|
370
|
-
const manifests = readAllManifests(configRoot);
|
|
371
|
-
for (const manifest of manifests) {
|
|
372
|
-
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
|
-
if (!plugin) continue;
|
|
374
|
-
const alreadyAdded = installed.some(
|
|
375
|
-
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
|
-
);
|
|
377
|
-
if (alreadyAdded) continue;
|
|
378
|
-
installed.push({
|
|
379
|
-
id: plugin.id,
|
|
380
|
-
displayName: plugin.displayName,
|
|
381
|
-
tool,
|
|
382
|
-
level,
|
|
383
|
-
skillsCount: manifest.installed_skills?.length || 0,
|
|
384
|
-
agentsCount: manifest.installed_agents?.length || 0,
|
|
385
|
-
installTime: manifest.install_time,
|
|
386
|
-
configRoot
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}
|
|
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();
|
|
1046
|
+
if (existsSync5(manifestPath)) {
|
|
1047
|
+
try {
|
|
1048
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
1049
|
+
return JSON.parse(content);
|
|
1050
|
+
} catch {
|
|
1051
|
+
}
|
|
604
1052
|
}
|
|
1053
|
+
return null;
|
|
605
1054
|
}
|
|
606
|
-
function
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1055
|
+
function readAllManifests(configRoot) {
|
|
1056
|
+
const manifests = [];
|
|
1057
|
+
const plugins = getAllPlugins();
|
|
1058
|
+
const standardPath = getManifestPath(configRoot);
|
|
1059
|
+
if (existsSync5(standardPath)) {
|
|
1060
|
+
try {
|
|
1061
|
+
const content = readFileSync3(standardPath, "utf-8");
|
|
1062
|
+
manifests.push(JSON.parse(content));
|
|
1063
|
+
} catch {
|
|
614
1064
|
}
|
|
615
1065
|
}
|
|
616
|
-
|
|
1066
|
+
for (const plugin of plugins) {
|
|
1067
|
+
const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
|
|
1068
|
+
if (existsSync5(pluginManifestPath)) {
|
|
1069
|
+
try {
|
|
1070
|
+
const content = readFileSync3(pluginManifestPath, "utf-8");
|
|
1071
|
+
manifests.push(JSON.parse(content));
|
|
1072
|
+
} catch {
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return manifests;
|
|
617
1077
|
}
|
|
618
|
-
function
|
|
619
|
-
const
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1078
|
+
function scanInstalled() {
|
|
1079
|
+
const plugins = getAllPlugins();
|
|
1080
|
+
const installed = [];
|
|
1081
|
+
const tools = VALID_TOOLS;
|
|
1082
|
+
const levels = ["project", "global"];
|
|
1083
|
+
for (const tool of tools) {
|
|
1084
|
+
for (const level of levels) {
|
|
1085
|
+
const configRoot = getConfigRoot(tool, level);
|
|
1086
|
+
const manifests = readAllManifests(configRoot);
|
|
1087
|
+
for (const manifest of manifests) {
|
|
1088
|
+
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
1089
|
+
if (!plugin) continue;
|
|
1090
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
const alreadyAdded = installed.some(
|
|
1094
|
+
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
1095
|
+
);
|
|
1096
|
+
if (alreadyAdded) continue;
|
|
1097
|
+
installed.push({
|
|
1098
|
+
id: plugin.id,
|
|
1099
|
+
displayName: plugin.displayName,
|
|
1100
|
+
tool,
|
|
1101
|
+
level,
|
|
1102
|
+
skillsCount: manifest.installed_skills?.length || 0,
|
|
1103
|
+
agentsCount: manifest.installed_agents?.length || 0,
|
|
1104
|
+
installTime: manifest.install_time,
|
|
1105
|
+
configRoot
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return installed;
|
|
630
1111
|
}
|
|
631
|
-
function
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
1112
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
1113
|
+
const skillsDir = join5(configRoot, "skills");
|
|
1114
|
+
const agentsDir = join5(configRoot, "agents");
|
|
1115
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
1116
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
1117
|
+
const skillPath = join5(skillsDir, skillId);
|
|
1118
|
+
return existsSync5(skillPath);
|
|
1119
|
+
});
|
|
1120
|
+
if (!hasAnySkill) return false;
|
|
1121
|
+
}
|
|
1122
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
1123
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
1124
|
+
const agentPath = join5(agentsDir, agentId);
|
|
1125
|
+
return existsSync5(agentPath);
|
|
1126
|
+
});
|
|
1127
|
+
if (!hasAnyAgent) return false;
|
|
1128
|
+
}
|
|
1129
|
+
let agentsMdPath;
|
|
1130
|
+
if (tool === "opencode" && level === "project") {
|
|
1131
|
+
agentsMdPath = join5(configRoot, "..", "AGENTS.md");
|
|
635
1132
|
} else {
|
|
636
|
-
|
|
637
|
-
`);
|
|
1133
|
+
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
638
1134
|
}
|
|
1135
|
+
if (!existsSync5(agentsMdPath)) return false;
|
|
1136
|
+
return true;
|
|
639
1137
|
}
|
|
640
1138
|
|
|
641
1139
|
// src/utils/config.ts
|
|
642
|
-
import { existsSync as
|
|
643
|
-
import { join as
|
|
644
|
-
import { homedir as homedir3 } from "os";
|
|
1140
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
1141
|
+
import { join as join6 } from "path";
|
|
645
1142
|
import { parse, stringify } from "yaml";
|
|
1143
|
+
|
|
1144
|
+
// src/utils/fs.ts
|
|
1145
|
+
import { writeFileSync, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
1146
|
+
import { dirname as dirname3 } from "path";
|
|
1147
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
1148
|
+
const dir = dirname3(filePath);
|
|
1149
|
+
if (!existsSync6(dir)) {
|
|
1150
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
1151
|
+
}
|
|
1152
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1153
|
+
writeFileSync(tmpPath, data, encoding);
|
|
1154
|
+
if (existsSync6(filePath)) {
|
|
1155
|
+
try {
|
|
1156
|
+
unlinkSync(filePath);
|
|
1157
|
+
} catch {
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
renameSync(tmpPath, filePath);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// src/utils/config.ts
|
|
646
1164
|
function getConfigDir() {
|
|
647
|
-
return
|
|
1165
|
+
return getCannbotConfigDir();
|
|
648
1166
|
}
|
|
649
1167
|
function getConfigPath() {
|
|
650
|
-
return
|
|
1168
|
+
return join6(getConfigDir(), "config.yaml");
|
|
651
1169
|
}
|
|
652
1170
|
function readConfig() {
|
|
653
1171
|
const configPath = getConfigPath();
|
|
654
|
-
if (!
|
|
1172
|
+
if (!existsSync7(configPath)) {
|
|
655
1173
|
return {
|
|
656
1174
|
language: "zh_CN",
|
|
657
1175
|
installedPlugins: []
|
|
658
1176
|
};
|
|
659
1177
|
}
|
|
660
1178
|
try {
|
|
661
|
-
const content =
|
|
1179
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
662
1180
|
const parsed = parse(content);
|
|
663
1181
|
return {
|
|
664
1182
|
language: parsed.language || "zh_CN",
|
|
@@ -668,6 +1186,7 @@ function readConfig() {
|
|
|
668
1186
|
installedPlugins: parsed.installedPlugins || []
|
|
669
1187
|
};
|
|
670
1188
|
} catch {
|
|
1189
|
+
logger.warn(t("config_corrupted"));
|
|
671
1190
|
return {
|
|
672
1191
|
language: "zh_CN",
|
|
673
1192
|
installedPlugins: []
|
|
@@ -676,12 +1195,12 @@ function readConfig() {
|
|
|
676
1195
|
}
|
|
677
1196
|
function writeConfig(config) {
|
|
678
1197
|
const configDir = getConfigDir();
|
|
679
|
-
if (!
|
|
1198
|
+
if (!existsSync7(configDir)) {
|
|
680
1199
|
mkdirSync(configDir, { recursive: true });
|
|
681
1200
|
}
|
|
682
1201
|
const configPath = getConfigPath();
|
|
683
1202
|
const content = stringify(config);
|
|
684
|
-
|
|
1203
|
+
atomicWriteFileSync(configPath, content);
|
|
685
1204
|
}
|
|
686
1205
|
function updateConfig(updates) {
|
|
687
1206
|
const config = readConfig();
|
|
@@ -704,22 +1223,11 @@ function removeInstalledPlugin(pluginId) {
|
|
|
704
1223
|
writeConfig(config);
|
|
705
1224
|
}
|
|
706
1225
|
|
|
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
|
|
1226
|
+
// src/utils/constants.ts
|
|
721
1227
|
var BACK = "__back__";
|
|
722
1228
|
var CANCEL = "__cancel__";
|
|
1229
|
+
|
|
1230
|
+
// src/ui/wizard.ts
|
|
723
1231
|
async function selectToolWithDetection() {
|
|
724
1232
|
const config = readConfig();
|
|
725
1233
|
logger.blank();
|
|
@@ -789,7 +1297,7 @@ async function runWizard() {
|
|
|
789
1297
|
step = 2;
|
|
790
1298
|
break;
|
|
791
1299
|
}
|
|
792
|
-
if (result === CANCEL
|
|
1300
|
+
if (result === CANCEL) {
|
|
793
1301
|
return { language: config.language || "zh_CN", tool: selectedTool, level, plugins: [], confirmed: false };
|
|
794
1302
|
}
|
|
795
1303
|
updateConfig({ lastTool: selectedTool, lastLevel: level });
|
|
@@ -799,11 +1307,11 @@ async function runWizard() {
|
|
|
799
1307
|
}
|
|
800
1308
|
}
|
|
801
1309
|
async function stepTool(detectedTools, lastTool) {
|
|
802
|
-
printBoxTitle("
|
|
1310
|
+
printBoxTitle(t("wizard_step_1_title"));
|
|
803
1311
|
if (detectedTools.length === 0) {
|
|
804
1312
|
logger.error(t("error_tool_not_found"));
|
|
805
1313
|
const choices2 = [
|
|
806
|
-
...
|
|
1314
|
+
...VALID_TOOLS.map((tool) => ({
|
|
807
1315
|
name: `> ${getToolDisplayName(tool)}`,
|
|
808
1316
|
value: tool
|
|
809
1317
|
})),
|
|
@@ -874,10 +1382,10 @@ async function stepToolManual(lastTool) {
|
|
|
874
1382
|
return result;
|
|
875
1383
|
}
|
|
876
1384
|
async function stepLevel(lastLevel) {
|
|
877
|
-
printBoxTitle("
|
|
1385
|
+
printBoxTitle(t("wizard_step_2_title"));
|
|
878
1386
|
const choices = [
|
|
879
|
-
{ name:
|
|
880
|
-
{ name:
|
|
1387
|
+
{ name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
|
|
1388
|
+
{ name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
|
|
881
1389
|
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
882
1390
|
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
883
1391
|
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
@@ -895,7 +1403,7 @@ async function stepLevel(lastLevel) {
|
|
|
895
1403
|
return result;
|
|
896
1404
|
}
|
|
897
1405
|
async function stepPlugins(tool, level) {
|
|
898
|
-
printBoxTitle("
|
|
1406
|
+
printBoxTitle(t("wizard_step_3_title"));
|
|
899
1407
|
const plugins = getAllPlugins();
|
|
900
1408
|
const configRoot = getConfigRoot(tool, level);
|
|
901
1409
|
const manifests = readAllManifests(configRoot);
|
|
@@ -904,14 +1412,12 @@ async function stepPlugins(tool, level) {
|
|
|
904
1412
|
const isInstalled = installedSet.has(p.id);
|
|
905
1413
|
const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
|
|
906
1414
|
return {
|
|
907
|
-
name: `${p.displayName}${suffix}
|
|
1415
|
+
name: `${chalk3.cyan(p.displayName)}${suffix}`,
|
|
908
1416
|
value: p.id,
|
|
909
|
-
checked: false
|
|
1417
|
+
checked: false,
|
|
1418
|
+
description: p.description
|
|
910
1419
|
};
|
|
911
1420
|
});
|
|
912
|
-
choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
913
|
-
choices.push({ name: "<- " + t("wizard_back"), value: BACK, checked: false });
|
|
914
|
-
choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL, checked: false });
|
|
915
1421
|
showOperationHints(true);
|
|
916
1422
|
const selected = await checkbox({
|
|
917
1423
|
message: t("wizard_select_plugins"),
|
|
@@ -921,9 +1427,7 @@ async function stepPlugins(tool, level) {
|
|
|
921
1427
|
theme: checkboxTheme,
|
|
922
1428
|
pageSize: 15
|
|
923
1429
|
});
|
|
924
|
-
|
|
925
|
-
if (selected.includes(CANCEL)) return CANCEL;
|
|
926
|
-
const pluginIds = selected.filter((v) => v !== BACK && v !== CANCEL);
|
|
1430
|
+
const pluginIds = selected;
|
|
927
1431
|
if (pluginIds.length === 0) {
|
|
928
1432
|
const action = await select({
|
|
929
1433
|
message: t("wizard_no_selection"),
|
|
@@ -941,17 +1445,17 @@ async function stepPlugins(tool, level) {
|
|
|
941
1445
|
return pluginIds;
|
|
942
1446
|
}
|
|
943
1447
|
async function stepConfirm(tool, level, plugins) {
|
|
944
|
-
printBoxTitle("
|
|
1448
|
+
printBoxTitle(t("wizard_step_4_title"));
|
|
945
1449
|
const allPlugins = getAllPlugins();
|
|
946
1450
|
const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
|
|
947
1451
|
const displayPath = getConfigRoot(tool, level);
|
|
948
1452
|
const toolName = getToolDisplayName(tool);
|
|
949
1453
|
const levelText = level === "project" ? "project" : "global";
|
|
950
|
-
logger.info(
|
|
951
|
-
logger.info(
|
|
1454
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk3.bold(String(plugins.length))).replace("{tool}", chalk3.green(toolName)).replace("{level}", chalk3.cyan(levelText)));
|
|
1455
|
+
logger.info(`${t("wizard_install_path")}: ${chalk3.cyan(displayPath)}`);
|
|
952
1456
|
for (const plugin of selectedPlugins) {
|
|
953
1457
|
if (plugin) {
|
|
954
|
-
logger.step(` \u2022 ${
|
|
1458
|
+
logger.step(` \u2022 ${chalk3.bold(plugin.displayName)}`);
|
|
955
1459
|
}
|
|
956
1460
|
}
|
|
957
1461
|
logger.blank();
|
|
@@ -969,14 +1473,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
969
1473
|
}
|
|
970
1474
|
|
|
971
1475
|
// src/core/repository.ts
|
|
972
|
-
import { existsSync as
|
|
973
|
-
import { join as
|
|
1476
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1477
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
1478
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
974
1479
|
import { execa as execa2 } from "execa";
|
|
1480
|
+
import { parse as parseYaml3 } from "yaml";
|
|
975
1481
|
|
|
976
1482
|
// src/core/scanner.ts
|
|
977
|
-
import { existsSync as
|
|
978
|
-
import { join as
|
|
979
|
-
import {
|
|
1483
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
1484
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
1485
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1486
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1487
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
1488
|
+
var __dirname3 = dirname4(__filename3);
|
|
980
1489
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
981
1490
|
"node_modules",
|
|
982
1491
|
".git",
|
|
@@ -997,46 +1506,70 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
997
1506
|
"docs",
|
|
998
1507
|
"scripts"
|
|
999
1508
|
]);
|
|
1000
|
-
|
|
1001
|
-
"
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
]
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1509
|
+
function loadScanConfig() {
|
|
1510
|
+
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
1511
|
+
try {
|
|
1512
|
+
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
1513
|
+
const ttlHours = config.scanCacheTtlHours || 24;
|
|
1514
|
+
return {
|
|
1515
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1516
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"],
|
|
1517
|
+
cacheTtlMs: ttlHours * 60 * 60 * 1e3
|
|
1518
|
+
};
|
|
1519
|
+
} catch {
|
|
1520
|
+
return {
|
|
1521
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1522
|
+
pluginDirs: ["plugins-official", "plugins-community"],
|
|
1523
|
+
cacheTtlMs: 24 * 60 * 60 * 1e3
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
var scanConfig = loadScanConfig();
|
|
1528
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
1529
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1011
1530
|
function scanSkills(repoPath) {
|
|
1012
1531
|
const skills = [];
|
|
1013
1532
|
const seen = /* @__PURE__ */ new Set();
|
|
1014
1533
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1015
|
-
const fullPath =
|
|
1016
|
-
if (!
|
|
1534
|
+
const fullPath = join7(repoPath, dir);
|
|
1535
|
+
if (!existsSync8(fullPath)) continue;
|
|
1017
1536
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1018
1537
|
}
|
|
1019
1538
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1020
|
-
const pluginParent =
|
|
1021
|
-
if (!
|
|
1539
|
+
const pluginParent = join7(repoPath, pluginDir);
|
|
1540
|
+
if (!existsSync8(pluginParent)) continue;
|
|
1022
1541
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1023
|
-
const pluginPath =
|
|
1542
|
+
const pluginPath = join7(pluginParent, plugin);
|
|
1024
1543
|
if (!isDirectory(pluginPath)) continue;
|
|
1025
|
-
const skillsDir =
|
|
1026
|
-
if (
|
|
1544
|
+
const skillsDir = join7(pluginPath, "skills");
|
|
1545
|
+
if (existsSync8(skillsDir)) {
|
|
1027
1546
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1028
1547
|
}
|
|
1029
|
-
const skillDir =
|
|
1030
|
-
if (
|
|
1548
|
+
const skillDir = join7(pluginPath, "skill");
|
|
1549
|
+
if (existsSync8(skillDir)) {
|
|
1031
1550
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1032
1551
|
}
|
|
1552
|
+
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
1553
|
+
const initSh = join7(pluginPath, "init.sh");
|
|
1554
|
+
if (existsSync8(rootSkillMd) && !existsSync8(initSh)) {
|
|
1555
|
+
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
1556
|
+
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1557
|
+
seen.add(frontmatter.name);
|
|
1558
|
+
skills.push({
|
|
1559
|
+
id: frontmatter.name,
|
|
1560
|
+
description: frontmatter.description,
|
|
1561
|
+
source: `${pluginDir}/${plugin}`,
|
|
1562
|
+
filePath: rootSkillMd
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1033
1566
|
}
|
|
1034
1567
|
}
|
|
1035
1568
|
return skills;
|
|
1036
1569
|
}
|
|
1037
1570
|
function parseFrontmatter(filePath) {
|
|
1038
1571
|
try {
|
|
1039
|
-
const content =
|
|
1572
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1040
1573
|
const lines = content.split("\n");
|
|
1041
1574
|
if (lines.length < 3) return null;
|
|
1042
1575
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1049,11 +1582,15 @@ function parseFrontmatter(filePath) {
|
|
|
1049
1582
|
}
|
|
1050
1583
|
if (endIndex === -1) return null;
|
|
1051
1584
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1052
|
-
const parsed =
|
|
1585
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1053
1586
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1054
1587
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1588
|
+
const trimmedName = parsed.name.trim();
|
|
1589
|
+
if (!trimmedName || /[\/\\]|\.\./.test(trimmedName) || !/^[a-zA-Z0-9._-]+$/.test(trimmedName)) {
|
|
1590
|
+
return null;
|
|
1591
|
+
}
|
|
1055
1592
|
return {
|
|
1056
|
-
name:
|
|
1593
|
+
name: trimmedName,
|
|
1057
1594
|
description: (parsed.description || "").trim()
|
|
1058
1595
|
};
|
|
1059
1596
|
} catch {
|
|
@@ -1062,17 +1599,17 @@ function parseFrontmatter(filePath) {
|
|
|
1062
1599
|
}
|
|
1063
1600
|
function getCurrentCommit(repoPath) {
|
|
1064
1601
|
try {
|
|
1065
|
-
const headFile =
|
|
1066
|
-
if (!
|
|
1067
|
-
const headContent =
|
|
1602
|
+
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1603
|
+
if (!existsSync8(headFile)) return "unknown";
|
|
1604
|
+
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1068
1605
|
if (headContent.startsWith("ref: ")) {
|
|
1069
|
-
const refPath =
|
|
1070
|
-
if (
|
|
1071
|
-
return
|
|
1606
|
+
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1607
|
+
if (existsSync8(refPath)) {
|
|
1608
|
+
return readFileSync5(refPath, "utf-8").trim();
|
|
1072
1609
|
}
|
|
1073
|
-
const packedRefsPath =
|
|
1074
|
-
if (
|
|
1075
|
-
const packedRefs =
|
|
1610
|
+
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1611
|
+
if (existsSync8(packedRefsPath)) {
|
|
1612
|
+
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1076
1613
|
const refName = headContent.slice(5);
|
|
1077
1614
|
const lines = packedRefs.split("\n");
|
|
1078
1615
|
for (const line of lines) {
|
|
@@ -1090,12 +1627,12 @@ function getCurrentCommit(repoPath) {
|
|
|
1090
1627
|
function readScanCache() {
|
|
1091
1628
|
try {
|
|
1092
1629
|
const cachePath = getCachePath();
|
|
1093
|
-
if (!
|
|
1094
|
-
const content =
|
|
1630
|
+
if (!existsSync8(cachePath)) return null;
|
|
1631
|
+
const content = readFileSync5(cachePath, "utf-8");
|
|
1095
1632
|
const cache = JSON.parse(content);
|
|
1096
1633
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1097
1634
|
const age = Date.now() - cache.timestamp;
|
|
1098
|
-
if (age >
|
|
1635
|
+
if (age > scanConfig.cacheTtlMs) return null;
|
|
1099
1636
|
return cache;
|
|
1100
1637
|
} catch {
|
|
1101
1638
|
return null;
|
|
@@ -1104,39 +1641,24 @@ function readScanCache() {
|
|
|
1104
1641
|
function writeScanCache(cache) {
|
|
1105
1642
|
try {
|
|
1106
1643
|
const cachePath = getCachePath();
|
|
1107
|
-
const cacheDir =
|
|
1108
|
-
if (!
|
|
1644
|
+
const cacheDir = dirname4(cachePath);
|
|
1645
|
+
if (!existsSync8(cacheDir)) {
|
|
1109
1646
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1110
1647
|
}
|
|
1111
|
-
|
|
1648
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1112
1649
|
} catch {
|
|
1113
1650
|
}
|
|
1114
1651
|
}
|
|
1115
1652
|
function getCachePath() {
|
|
1116
|
-
|
|
1117
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
1653
|
+
return join7(getCannbotConfigDir(), "scan-cache.json");
|
|
1118
1654
|
}
|
|
1119
1655
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1120
|
-
const selfSkillMd = join5(dirPath, "SKILL.md");
|
|
1121
|
-
if (existsSync5(selfSkillMd)) {
|
|
1122
|
-
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1123
|
-
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1124
|
-
seen.add(frontmatter.name);
|
|
1125
|
-
skills.push({
|
|
1126
|
-
id: frontmatter.name,
|
|
1127
|
-
description: frontmatter.description,
|
|
1128
|
-
source: sourcePrefix,
|
|
1129
|
-
filePath: selfSkillMd
|
|
1130
|
-
});
|
|
1131
|
-
}
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
1656
|
for (const entry of safeReaddir(dirPath)) {
|
|
1135
|
-
const fullPath =
|
|
1657
|
+
const fullPath = join7(dirPath, entry);
|
|
1136
1658
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1137
1659
|
if (!isDirectory(fullPath)) continue;
|
|
1138
|
-
const skillMd =
|
|
1139
|
-
if (
|
|
1660
|
+
const skillMd = join7(fullPath, "SKILL.md");
|
|
1661
|
+
if (existsSync8(skillMd)) {
|
|
1140
1662
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1141
1663
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1142
1664
|
seen.add(frontmatter.name);
|
|
@@ -1149,38 +1671,32 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1149
1671
|
}
|
|
1150
1672
|
continue;
|
|
1151
1673
|
}
|
|
1152
|
-
scanDirectory(fullPath, repoPath, sourcePrefix, skills, seen);
|
|
1674
|
+
scanDirectory(fullPath, repoPath, sourcePrefix + "/" + entry, skills, seen);
|
|
1153
1675
|
}
|
|
1154
1676
|
}
|
|
1155
1677
|
function safeReaddir(dir) {
|
|
1156
1678
|
try {
|
|
1157
|
-
return
|
|
1679
|
+
return readdirSync2(dir);
|
|
1158
1680
|
} catch {
|
|
1159
1681
|
return [];
|
|
1160
1682
|
}
|
|
1161
1683
|
}
|
|
1162
|
-
function isDirectory(path) {
|
|
1163
|
-
try {
|
|
1164
|
-
return statSync(path).isDirectory();
|
|
1165
|
-
} catch {
|
|
1166
|
-
return false;
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
1684
|
|
|
1170
1685
|
// src/core/skill-registry.ts
|
|
1171
1686
|
var CATEGORY_DEFS = [
|
|
1172
|
-
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
|
|
1173
|
-
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
|
|
1174
|
-
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
|
|
1175
|
-
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
|
|
1176
|
-
{ id: "ascendc", name: "AscendC \u5F00\u53D1" },
|
|
1177
|
-
{ id: "pypto", name: "PyPTO \u5F00\u53D1" },
|
|
1178
|
-
{ id: "tilelang", name: "TileLang \u5F00\u53D1" },
|
|
1179
|
-
{ id: "triton", name: "Triton \u5F00\u53D1" },
|
|
1180
|
-
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
|
|
1181
|
-
{ id: "
|
|
1182
|
-
{ id: "
|
|
1183
|
-
{ id: "
|
|
1687
|
+
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
|
|
1688
|
+
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
|
|
1689
|
+
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
|
|
1690
|
+
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
|
|
1691
|
+
{ id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
|
|
1692
|
+
{ id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
|
|
1693
|
+
{ id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
|
|
1694
|
+
{ id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
|
|
1695
|
+
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
|
|
1696
|
+
{ id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
|
|
1697
|
+
{ id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
|
|
1698
|
+
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
|
|
1699
|
+
{ id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
|
|
1184
1700
|
];
|
|
1185
1701
|
var dynamicSkills = [];
|
|
1186
1702
|
var initialized = false;
|
|
@@ -1188,7 +1704,8 @@ function initFromScan(scanned) {
|
|
|
1188
1704
|
dynamicSkills = scanned.map((s) => ({
|
|
1189
1705
|
id: s.id,
|
|
1190
1706
|
description: s.description,
|
|
1191
|
-
source: s.source
|
|
1707
|
+
source: s.source,
|
|
1708
|
+
filePath: s.filePath
|
|
1192
1709
|
}));
|
|
1193
1710
|
initialized = true;
|
|
1194
1711
|
}
|
|
@@ -1264,9 +1781,7 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1264
1781
|
{ id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
|
|
1265
1782
|
{ id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
|
|
1266
1783
|
{ id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
|
|
1267
|
-
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1268
|
-
{ id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
|
|
1269
|
-
{ id: "ops-easyasc-dsl", description: "EasyASC DSL \u7B97\u5B50\u5F00\u53D1", source: "plugins-community/ops-easyasc-dsl/skill" }
|
|
1784
|
+
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1270
1785
|
]
|
|
1271
1786
|
},
|
|
1272
1787
|
{
|
|
@@ -1323,7 +1838,9 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1323
1838
|
{ id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
|
|
1324
1839
|
{ id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
|
|
1325
1840
|
{ id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
|
|
1326
|
-
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" }
|
|
1841
|
+
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
|
|
1842
|
+
{ id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
|
|
1843
|
+
{ 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" }
|
|
1327
1844
|
]
|
|
1328
1845
|
},
|
|
1329
1846
|
{
|
|
@@ -1339,6 +1856,13 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1339
1856
|
{ id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
|
|
1340
1857
|
]
|
|
1341
1858
|
},
|
|
1859
|
+
{
|
|
1860
|
+
id: "runtime",
|
|
1861
|
+
name: "Runtime \u8FC1\u79FB",
|
|
1862
|
+
skills: [
|
|
1863
|
+
{ id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
|
|
1864
|
+
]
|
|
1865
|
+
},
|
|
1342
1866
|
{
|
|
1343
1867
|
id: "platform",
|
|
1344
1868
|
name: "\u5E73\u53F0\u5DE5\u5177",
|
|
@@ -1382,9 +1906,10 @@ function getAllSkills() {
|
|
|
1382
1906
|
}
|
|
1383
1907
|
function getAllCategories() {
|
|
1384
1908
|
const skills = getActiveSkills();
|
|
1909
|
+
const isEn = t("list_name") === "Name";
|
|
1385
1910
|
return CATEGORY_DEFS.map((def) => ({
|
|
1386
1911
|
id: def.id,
|
|
1387
|
-
name: def.name,
|
|
1912
|
+
name: isEn ? def.nameEn : def.name,
|
|
1388
1913
|
skills: skills.filter((s) => {
|
|
1389
1914
|
const catId = CATEGORY_MAP[s.id];
|
|
1390
1915
|
if (catId) return catId === def.id;
|
|
@@ -1394,7 +1919,21 @@ function getAllCategories() {
|
|
|
1394
1919
|
}
|
|
1395
1920
|
|
|
1396
1921
|
// src/core/repository.ts
|
|
1397
|
-
var
|
|
1922
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1923
|
+
var __dirname4 = dirname5(__filename4);
|
|
1924
|
+
function loadRepoUrl() {
|
|
1925
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
1926
|
+
return process.env.CANNBOT_REPO_URL;
|
|
1927
|
+
}
|
|
1928
|
+
const configPath = join8(__dirname4, "config", "repository.yaml");
|
|
1929
|
+
try {
|
|
1930
|
+
const config = parseYaml3(readFileSync6(configPath, "utf-8"));
|
|
1931
|
+
return config.repository?.url || "https://gitcode.com/cann/cannbot-skills.git";
|
|
1932
|
+
} catch {
|
|
1933
|
+
return "https://gitcode.com/cann/cannbot-skills.git";
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
var REPO_URL = loadRepoUrl();
|
|
1398
1937
|
var RepositoryManager = class {
|
|
1399
1938
|
repoPath;
|
|
1400
1939
|
constructor(customPath) {
|
|
@@ -1419,10 +1958,13 @@ var RepositoryManager = class {
|
|
|
1419
1958
|
this.repoPath = cachedPath;
|
|
1420
1959
|
return cachedPath;
|
|
1421
1960
|
}
|
|
1422
|
-
|
|
1961
|
+
const clonedPath = await this.cloneRepo();
|
|
1962
|
+
return clonedPath;
|
|
1423
1963
|
}
|
|
1424
1964
|
async ensureRepoAndScan() {
|
|
1425
1965
|
const repoPath = await this.ensureRepo();
|
|
1966
|
+
await this.updateRepo();
|
|
1967
|
+
mergeDynamicPlugins(repoPath);
|
|
1426
1968
|
const cache = readScanCache();
|
|
1427
1969
|
if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
|
|
1428
1970
|
initFromScan(cache.skills);
|
|
@@ -1442,7 +1984,9 @@ var RepositoryManager = class {
|
|
|
1442
1984
|
try {
|
|
1443
1985
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1444
1986
|
} catch {
|
|
1987
|
+
logger.warn(t("repo_pull_failed"));
|
|
1445
1988
|
}
|
|
1989
|
+
mergeDynamicPlugins(repoPath);
|
|
1446
1990
|
}
|
|
1447
1991
|
getRepoPath() {
|
|
1448
1992
|
if (!this.repoPath) {
|
|
@@ -1451,18 +1995,18 @@ var RepositoryManager = class {
|
|
|
1451
1995
|
return this.repoPath;
|
|
1452
1996
|
}
|
|
1453
1997
|
isValidRepo(path) {
|
|
1454
|
-
if (!
|
|
1455
|
-
const gitDir =
|
|
1456
|
-
const pluginsDir =
|
|
1457
|
-
return
|
|
1998
|
+
if (!existsSync9(path)) return false;
|
|
1999
|
+
const gitDir = join8(path, ".git");
|
|
2000
|
+
const pluginsDir = join8(path, "plugins-official");
|
|
2001
|
+
return existsSync9(gitDir) && existsSync9(pluginsDir);
|
|
1458
2002
|
}
|
|
1459
2003
|
async cloneRepo() {
|
|
1460
2004
|
const configDir = getCannbotConfigDir();
|
|
1461
|
-
if (!
|
|
2005
|
+
if (!existsSync9(configDir)) {
|
|
1462
2006
|
mkdirSync3(configDir, { recursive: true });
|
|
1463
2007
|
}
|
|
1464
2008
|
const targetPath = getCannbotRepoPath();
|
|
1465
|
-
if (
|
|
2009
|
+
if (existsSync9(targetPath)) {
|
|
1466
2010
|
if (this.isValidRepo(targetPath)) {
|
|
1467
2011
|
this.repoPath = targetPath;
|
|
1468
2012
|
return targetPath;
|
|
@@ -1488,102 +2032,102 @@ function createRepositoryManager(customPath) {
|
|
|
1488
2032
|
}
|
|
1489
2033
|
|
|
1490
2034
|
// src/core/installer.ts
|
|
1491
|
-
import { existsSync as
|
|
1492
|
-
import { join as
|
|
1493
|
-
import { execa as
|
|
2035
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2036
|
+
import { join as join12 } from "path";
|
|
2037
|
+
import { execa as execa4 } from "execa";
|
|
1494
2038
|
|
|
1495
2039
|
// src/core/record.ts
|
|
1496
|
-
import { existsSync as
|
|
1497
|
-
import { join as
|
|
2040
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
2041
|
+
import { join as join9 } from "path";
|
|
1498
2042
|
function getInstallsDir() {
|
|
1499
|
-
return
|
|
2043
|
+
return join9(getCannbotConfigDir(), "installs");
|
|
1500
2044
|
}
|
|
1501
2045
|
function getRecordPath(pluginId) {
|
|
1502
|
-
return
|
|
2046
|
+
return join9(getInstallsDir(), `${pluginId}.json`);
|
|
1503
2047
|
}
|
|
1504
2048
|
function readRecord(pluginId) {
|
|
1505
2049
|
const recordPath = getRecordPath(pluginId);
|
|
1506
|
-
if (!
|
|
2050
|
+
if (!existsSync10(recordPath)) {
|
|
1507
2051
|
return null;
|
|
1508
2052
|
}
|
|
1509
2053
|
try {
|
|
1510
|
-
const content =
|
|
2054
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1511
2055
|
return JSON.parse(content);
|
|
1512
2056
|
} catch {
|
|
2057
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1513
2058
|
return null;
|
|
1514
2059
|
}
|
|
1515
2060
|
}
|
|
1516
2061
|
function writeRecord(record) {
|
|
1517
2062
|
const installsDir = getInstallsDir();
|
|
1518
|
-
if (!
|
|
2063
|
+
if (!existsSync10(installsDir)) {
|
|
1519
2064
|
mkdirSync4(installsDir, { recursive: true });
|
|
1520
2065
|
}
|
|
1521
2066
|
const recordPath = getRecordPath(record.pluginId);
|
|
1522
|
-
|
|
2067
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1523
2068
|
}
|
|
1524
2069
|
function deleteRecord(pluginId) {
|
|
1525
2070
|
const recordPath = getRecordPath(pluginId);
|
|
1526
|
-
if (
|
|
1527
|
-
|
|
2071
|
+
if (existsSync10(recordPath)) {
|
|
2072
|
+
unlinkSync2(recordPath);
|
|
1528
2073
|
}
|
|
1529
2074
|
}
|
|
1530
2075
|
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
1531
2076
|
const files = [];
|
|
1532
2077
|
const directories = [];
|
|
1533
2078
|
if (manifest) {
|
|
1534
|
-
const skillsDir =
|
|
1535
|
-
if (
|
|
2079
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2080
|
+
if (existsSync10(skillsDir)) {
|
|
1536
2081
|
directories.push(skillsDir);
|
|
1537
2082
|
for (const skillName of manifest.installed_skills || []) {
|
|
1538
|
-
const skillPath =
|
|
1539
|
-
if (
|
|
2083
|
+
const skillPath = join9(skillsDir, skillName);
|
|
2084
|
+
if (existsSync10(skillPath) || isSymlink(skillPath)) {
|
|
1540
2085
|
files.push(skillPath);
|
|
1541
2086
|
}
|
|
1542
2087
|
}
|
|
1543
2088
|
}
|
|
1544
|
-
const agentsDir =
|
|
1545
|
-
if (
|
|
2089
|
+
const agentsDir = join9(configRoot, "agents");
|
|
2090
|
+
if (existsSync10(agentsDir)) {
|
|
1546
2091
|
directories.push(agentsDir);
|
|
1547
2092
|
for (const agentName of manifest.installed_agents || []) {
|
|
1548
|
-
const agentPath =
|
|
1549
|
-
const agentPathMd =
|
|
1550
|
-
if (
|
|
2093
|
+
const agentPath = join9(agentsDir, agentName);
|
|
2094
|
+
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
2095
|
+
if (existsSync10(agentPath) || isSymlink(agentPath)) {
|
|
1551
2096
|
files.push(agentPath);
|
|
1552
|
-
} else if (
|
|
2097
|
+
} else if (existsSync10(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1553
2098
|
files.push(agentPathMd);
|
|
1554
2099
|
}
|
|
1555
2100
|
}
|
|
1556
2101
|
}
|
|
1557
2102
|
}
|
|
1558
|
-
const workflowsLink =
|
|
2103
|
+
const workflowsLink = join9(configRoot, "workflows");
|
|
1559
2104
|
if (isSymlink(workflowsLink)) {
|
|
1560
2105
|
files.push(workflowsLink);
|
|
1561
2106
|
}
|
|
1562
|
-
const manifestPath =
|
|
1563
|
-
if (
|
|
2107
|
+
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
2108
|
+
if (existsSync10(manifestPath)) {
|
|
1564
2109
|
files.push(manifestPath);
|
|
1565
2110
|
}
|
|
1566
|
-
const pluginManifestPath =
|
|
1567
|
-
if (
|
|
2111
|
+
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
2112
|
+
if (existsSync10(pluginManifestPath)) {
|
|
1568
2113
|
files.push(pluginManifestPath);
|
|
1569
2114
|
}
|
|
1570
2115
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1571
|
-
const configFilePath = level === "project" ?
|
|
1572
|
-
if (
|
|
2116
|
+
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
2117
|
+
if (existsSync10(configFilePath)) {
|
|
1573
2118
|
files.push(configFilePath);
|
|
1574
2119
|
}
|
|
1575
2120
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1576
2121
|
for (const repoName of repoLinks) {
|
|
1577
|
-
const repoLinkPath =
|
|
2122
|
+
const repoLinkPath = join9(installPath, repoName);
|
|
1578
2123
|
if (isSymlink(repoLinkPath)) {
|
|
1579
2124
|
files.push(repoLinkPath);
|
|
1580
2125
|
}
|
|
1581
|
-
const repoLinkInConfig =
|
|
2126
|
+
const repoLinkInConfig = join9(configRoot, repoName);
|
|
1582
2127
|
if (isSymlink(repoLinkInConfig)) {
|
|
1583
2128
|
files.push(repoLinkInConfig);
|
|
1584
2129
|
}
|
|
1585
2130
|
}
|
|
1586
|
-
directories.push(configRoot);
|
|
1587
2131
|
return {
|
|
1588
2132
|
pluginId,
|
|
1589
2133
|
displayName,
|
|
@@ -1596,71 +2140,418 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1596
2140
|
directories
|
|
1597
2141
|
};
|
|
1598
2142
|
}
|
|
1599
|
-
function
|
|
2143
|
+
function getSkillRecordPath() {
|
|
2144
|
+
return join9(getInstallsDir(), "skills.json");
|
|
2145
|
+
}
|
|
2146
|
+
function readSkillRecord() {
|
|
2147
|
+
const recordPath = getSkillRecordPath();
|
|
2148
|
+
if (!existsSync10(recordPath)) {
|
|
2149
|
+
return {};
|
|
2150
|
+
}
|
|
2151
|
+
try {
|
|
2152
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
2153
|
+
return JSON.parse(content);
|
|
2154
|
+
} catch {
|
|
2155
|
+
logger.warn(t("skill_record_corrupted"));
|
|
2156
|
+
return {};
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
function writeSkillRecord(record) {
|
|
2160
|
+
const installsDir = getInstallsDir();
|
|
2161
|
+
if (!existsSync10(installsDir)) {
|
|
2162
|
+
mkdirSync4(installsDir, { recursive: true });
|
|
2163
|
+
}
|
|
2164
|
+
const recordPath = getSkillRecordPath();
|
|
2165
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
2166
|
+
}
|
|
2167
|
+
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
2168
|
+
const record = readSkillRecord();
|
|
2169
|
+
if (!record[tool]) record[tool] = {};
|
|
2170
|
+
if (!record[tool][level]) record[tool][level] = {};
|
|
2171
|
+
if (!record[tool][level][installPath]) {
|
|
2172
|
+
record[tool][level][installPath] = { skills: [], installTime: "" };
|
|
2173
|
+
}
|
|
2174
|
+
const entry = record[tool][level][installPath];
|
|
2175
|
+
for (const id of skillIds) {
|
|
2176
|
+
if (!entry.skills.includes(id)) {
|
|
2177
|
+
entry.skills.push(id);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
entry.installTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
2181
|
+
writeSkillRecord(record);
|
|
2182
|
+
}
|
|
2183
|
+
function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
2184
|
+
const record = readSkillRecord();
|
|
2185
|
+
if (!record[tool]?.[level]?.[installPath]) return;
|
|
2186
|
+
const entry = record[tool][level][installPath];
|
|
2187
|
+
entry.skills = entry.skills.filter((id) => !skillIds.includes(id));
|
|
2188
|
+
if (entry.skills.length === 0) {
|
|
2189
|
+
delete record[tool][level][installPath];
|
|
2190
|
+
if (Object.keys(record[tool][level]).length === 0) {
|
|
2191
|
+
delete record[tool][level];
|
|
2192
|
+
if (Object.keys(record[tool]).length === 0) {
|
|
2193
|
+
delete record[tool];
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
writeSkillRecord(record);
|
|
2198
|
+
}
|
|
2199
|
+
function getInstalledSkills(tool, level, installPath) {
|
|
2200
|
+
const record = readSkillRecord();
|
|
2201
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
2202
|
+
const configRoot = getConfigRoot(tool, level);
|
|
2203
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2204
|
+
return recordedSkills.filter((skillId) => {
|
|
2205
|
+
const skillPath = join9(skillsDir, skillId);
|
|
2206
|
+
return existsSync10(skillPath) || isSymlink(skillPath);
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
// src/core/backup.ts
|
|
2211
|
+
import { existsSync as existsSync11, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2212
|
+
import { join as join10 } from "path";
|
|
2213
|
+
function detectCurrentPlugin(configRoot, tool) {
|
|
2214
|
+
const manifest = readManifest(configRoot);
|
|
2215
|
+
if (manifest?.team) {
|
|
2216
|
+
const plugin = findPlugin(manifest.team);
|
|
2217
|
+
if (plugin) {
|
|
2218
|
+
return {
|
|
2219
|
+
pluginId: plugin.id,
|
|
2220
|
+
pluginName: plugin.displayName
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2225
|
+
if (!existsSync11(agentsFile)) {
|
|
2226
|
+
return null;
|
|
2227
|
+
}
|
|
2228
|
+
const allRecords = readAllRecords();
|
|
2229
|
+
for (const record of allRecords) {
|
|
2230
|
+
if (record.configRoot === configRoot && record.tool === tool) {
|
|
2231
|
+
const plugin = findPlugin(record.pluginId);
|
|
2232
|
+
if (plugin) {
|
|
2233
|
+
return {
|
|
2234
|
+
pluginId: plugin.id,
|
|
2235
|
+
pluginName: plugin.displayName
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
return null;
|
|
2241
|
+
}
|
|
2242
|
+
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
2243
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2244
|
+
if (!existsSync11(agentsFile)) {
|
|
2245
|
+
return null;
|
|
2246
|
+
}
|
|
2247
|
+
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
2248
|
+
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
2249
|
+
const backupPath = join10(configRoot, backupFileName);
|
|
2250
|
+
try {
|
|
2251
|
+
copyFileSync(agentsFile, backupPath);
|
|
2252
|
+
return {
|
|
2253
|
+
filePath: backupPath,
|
|
2254
|
+
pluginId: fromPluginId,
|
|
2255
|
+
pluginName: fromPluginName,
|
|
2256
|
+
backupTime: timestamp
|
|
2257
|
+
};
|
|
2258
|
+
} catch {
|
|
2259
|
+
return null;
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
function findBackups(configRoot) {
|
|
2263
|
+
if (!existsSync11(configRoot)) {
|
|
2264
|
+
return [];
|
|
2265
|
+
}
|
|
2266
|
+
const files = readdirSync3(configRoot);
|
|
2267
|
+
const backups = [];
|
|
2268
|
+
for (const file of files) {
|
|
2269
|
+
if (file.includes(".cannbot-backup.")) {
|
|
2270
|
+
const parts = file.split(".cannbot-backup.");
|
|
2271
|
+
if (parts.length === 2) {
|
|
2272
|
+
const pluginIdAndTime = parts[1];
|
|
2273
|
+
const lastDotIndex = pluginIdAndTime.lastIndexOf(".");
|
|
2274
|
+
if (lastDotIndex > 0) {
|
|
2275
|
+
const pluginId = pluginIdAndTime.substring(0, lastDotIndex);
|
|
2276
|
+
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
2277
|
+
const plugin = findPlugin(pluginId);
|
|
2278
|
+
backups.push({
|
|
2279
|
+
filePath: join10(configRoot, file),
|
|
2280
|
+
pluginId,
|
|
2281
|
+
pluginName: plugin?.displayName || pluginId,
|
|
2282
|
+
backupTime: timestamp
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
return backups.sort((a, b) => b.backupTime.localeCompare(a.backupTime));
|
|
2289
|
+
}
|
|
2290
|
+
function restoreBackup(backupPath, configRoot, tool) {
|
|
2291
|
+
try {
|
|
2292
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2293
|
+
copyFileSync(backupPath, agentsFile);
|
|
2294
|
+
return true;
|
|
2295
|
+
} catch {
|
|
2296
|
+
return false;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
function deleteBackup(backupPath) {
|
|
2300
|
+
try {
|
|
2301
|
+
if (existsSync11(backupPath)) {
|
|
2302
|
+
unlinkSync3(backupPath);
|
|
2303
|
+
return true;
|
|
2304
|
+
}
|
|
2305
|
+
return false;
|
|
2306
|
+
} catch {
|
|
2307
|
+
return false;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
function formatTimestamp(date) {
|
|
2311
|
+
const year = date.getFullYear();
|
|
2312
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
2313
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
2314
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
2315
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
2316
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
2317
|
+
return `${year}${month}${day}-${hours}${minutes}${seconds}`;
|
|
2318
|
+
}
|
|
2319
|
+
function readAllRecords() {
|
|
2320
|
+
const records = [];
|
|
2321
|
+
const plugins = getAllPlugins();
|
|
2322
|
+
for (const plugin of plugins) {
|
|
2323
|
+
const record = readRecord(plugin.id);
|
|
2324
|
+
if (record) {
|
|
2325
|
+
records.push(record);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
return records;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// src/ui/backup-prompts.ts
|
|
2332
|
+
import { select as select2 } from "@inquirer/prompts";
|
|
2333
|
+
import chalk4 from "chalk";
|
|
2334
|
+
async function showOverwriteWarning(currentPluginName, newPluginName) {
|
|
2335
|
+
console.log();
|
|
2336
|
+
console.log(chalk4.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
|
|
2337
|
+
console.log();
|
|
2338
|
+
console.log(` ${t("backup_current")}: ${chalk4.cyan(currentPluginName)}`);
|
|
2339
|
+
console.log(` ${t("backup_installing")}: ${chalk4.cyan(newPluginName)}`);
|
|
2340
|
+
console.log();
|
|
2341
|
+
console.log(` ${t("backup_overwrite_warning")}`);
|
|
2342
|
+
console.log(` ${t("backup_auto_create")}`);
|
|
2343
|
+
console.log();
|
|
2344
|
+
const choice = await select2({
|
|
2345
|
+
message: t("backup_select_action"),
|
|
2346
|
+
choices: [
|
|
2347
|
+
{ name: t("backup_overwrite"), value: "overwrite" },
|
|
2348
|
+
{ name: t("backup_cancel"), value: "cancel" }
|
|
2349
|
+
]
|
|
2350
|
+
});
|
|
2351
|
+
return choice;
|
|
2352
|
+
}
|
|
2353
|
+
async function showRestorePrompt(backups) {
|
|
2354
|
+
console.log();
|
|
2355
|
+
console.log(chalk4.cyan(` ${t("backup_restore_prompt")}`));
|
|
2356
|
+
console.log();
|
|
2357
|
+
const choices = backups.map((b) => ({
|
|
2358
|
+
name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
|
|
2359
|
+
value: b.filePath
|
|
2360
|
+
}));
|
|
2361
|
+
choices.push({ name: t("backup_only_uninstall"), value: "none" });
|
|
2362
|
+
const choice = await select2({
|
|
2363
|
+
message: t("backup_select_action"),
|
|
2364
|
+
choices
|
|
2365
|
+
});
|
|
2366
|
+
return choice;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
// src/core/plugin-installer.ts
|
|
2370
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, realpathSync, copyFileSync as copyFileSync2, cpSync, writeFileSync as writeFileSync3, readFileSync as readFileSync8 } from "fs";
|
|
2371
|
+
import { join as join11 } from "path";
|
|
2372
|
+
import { execa as execa3 } from "execa";
|
|
2373
|
+
function readPluginManifest(pluginDir) {
|
|
2374
|
+
const manifestPath = join11(pluginDir, "plugin-manifest.json");
|
|
2375
|
+
if (!existsSync12(manifestPath)) return null;
|
|
1600
2376
|
try {
|
|
1601
|
-
|
|
2377
|
+
const content = JSON.parse(readFileSync8(manifestPath, "utf-8"));
|
|
2378
|
+
if (!content || !content.id) return null;
|
|
2379
|
+
return content;
|
|
1602
2380
|
} catch {
|
|
1603
|
-
return
|
|
2381
|
+
return null;
|
|
1604
2382
|
}
|
|
1605
2383
|
}
|
|
1606
|
-
function
|
|
1607
|
-
return join7(getInstallsDir(), "skills.json");
|
|
1608
|
-
}
|
|
1609
|
-
function readSkillRecord() {
|
|
1610
|
-
const recordPath = getSkillRecordPath();
|
|
1611
|
-
if (!existsSync7(recordPath)) {
|
|
1612
|
-
return {};
|
|
1613
|
-
}
|
|
2384
|
+
function installLink(source, target) {
|
|
1614
2385
|
try {
|
|
1615
|
-
const
|
|
1616
|
-
|
|
2386
|
+
const resolved = realpathSync(source);
|
|
2387
|
+
if (existsSync12(target) || isSymlink(target)) {
|
|
2388
|
+
unlinkSync4(target);
|
|
2389
|
+
}
|
|
2390
|
+
try {
|
|
2391
|
+
symlinkSync(resolved, target);
|
|
2392
|
+
return "symlink";
|
|
2393
|
+
} catch (e) {
|
|
2394
|
+
if (e.code === "EPERM") {
|
|
2395
|
+
cpSync(resolved, target, { recursive: true });
|
|
2396
|
+
return "copy";
|
|
2397
|
+
}
|
|
2398
|
+
throw e;
|
|
2399
|
+
}
|
|
1617
2400
|
} catch {
|
|
1618
|
-
return
|
|
2401
|
+
return "failed";
|
|
1619
2402
|
}
|
|
1620
2403
|
}
|
|
1621
|
-
function
|
|
1622
|
-
const
|
|
1623
|
-
if (!
|
|
1624
|
-
|
|
2404
|
+
function installConfigFile(pluginDir, configFile, tool, level, configRoot, installPath) {
|
|
2405
|
+
const sourcePath = join11(pluginDir, configFile);
|
|
2406
|
+
if (!existsSync12(sourcePath)) return;
|
|
2407
|
+
const agentsFileName = getAgentsFileName(tool);
|
|
2408
|
+
const isProjectLevel = level === "project";
|
|
2409
|
+
let primaryTarget;
|
|
2410
|
+
if (isProjectLevel) {
|
|
2411
|
+
primaryTarget = join11(installPath, agentsFileName);
|
|
2412
|
+
} else {
|
|
2413
|
+
primaryTarget = join11(configRoot, agentsFileName);
|
|
1625
2414
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
}
|
|
1629
|
-
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
1630
|
-
const record = readSkillRecord();
|
|
1631
|
-
if (!record[tool]) record[tool] = {};
|
|
1632
|
-
if (!record[tool][level]) record[tool][level] = {};
|
|
1633
|
-
if (!record[tool][level][installPath]) {
|
|
1634
|
-
record[tool][level][installPath] = { skills: [], installTime: "" };
|
|
2415
|
+
if (existsSync12(primaryTarget) || isSymlink(primaryTarget)) {
|
|
2416
|
+
unlinkSync4(primaryTarget);
|
|
1635
2417
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
2418
|
+
try {
|
|
2419
|
+
symlinkSync(realpathSync(sourcePath), primaryTarget);
|
|
2420
|
+
} catch (e) {
|
|
2421
|
+
if (e.code === "EPERM") {
|
|
2422
|
+
copyFileSync2(sourcePath, primaryTarget);
|
|
2423
|
+
} else {
|
|
2424
|
+
throw e;
|
|
1640
2425
|
}
|
|
1641
2426
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
if (Object.keys(record[tool][level]).length === 0) {
|
|
1653
|
-
delete record[tool][level];
|
|
1654
|
-
if (Object.keys(record[tool]).length === 0) {
|
|
1655
|
-
delete record[tool];
|
|
2427
|
+
if (isProjectLevel && primaryTarget !== join11(configRoot, agentsFileName)) {
|
|
2428
|
+
const configRootTarget = join11(configRoot, agentsFileName);
|
|
2429
|
+
if (existsSync12(configRootTarget) || isSymlink(configRootTarget)) {
|
|
2430
|
+
unlinkSync4(configRootTarget);
|
|
2431
|
+
}
|
|
2432
|
+
try {
|
|
2433
|
+
symlinkSync(realpathSync(sourcePath), configRootTarget);
|
|
2434
|
+
} catch (e) {
|
|
2435
|
+
if (e.code === "EPERM") {
|
|
2436
|
+
copyFileSync2(sourcePath, configRootTarget);
|
|
1656
2437
|
}
|
|
1657
2438
|
}
|
|
1658
2439
|
}
|
|
1659
|
-
writeSkillRecord(record);
|
|
1660
2440
|
}
|
|
1661
|
-
function
|
|
1662
|
-
|
|
1663
|
-
|
|
2441
|
+
async function installExternalRepos(repos, repoPath) {
|
|
2442
|
+
if (!repos || repos.length === 0) return;
|
|
2443
|
+
for (const repo of repos) {
|
|
2444
|
+
const targetDir = join11(repoPath, repo.dir);
|
|
2445
|
+
if (existsSync12(targetDir)) {
|
|
2446
|
+
logger.info(` ${repo.dir} already exists, skipping clone`);
|
|
2447
|
+
continue;
|
|
2448
|
+
}
|
|
2449
|
+
const args = ["clone"];
|
|
2450
|
+
if (repo.depth) {
|
|
2451
|
+
args.push("--depth", String(repo.depth));
|
|
2452
|
+
}
|
|
2453
|
+
if (repo.recursive) {
|
|
2454
|
+
args.push("--recursive");
|
|
2455
|
+
}
|
|
2456
|
+
args.push(repo.url, targetDir);
|
|
2457
|
+
try {
|
|
2458
|
+
await execa3("git", args, { timeout: 12e4 });
|
|
2459
|
+
} catch {
|
|
2460
|
+
logger.warn(` git clone failed: ${repo.url}, clone manually: git clone ${repo.url} ${repo.dir}`);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
function writeCannbotManifest(configRoot, pluginId, version, level, tool, skills, agents) {
|
|
2465
|
+
const manifest = {
|
|
2466
|
+
brand: "CANNBot",
|
|
2467
|
+
version,
|
|
2468
|
+
team: pluginId,
|
|
2469
|
+
level,
|
|
2470
|
+
tool,
|
|
2471
|
+
installed_skills: skills,
|
|
2472
|
+
installed_agents: agents,
|
|
2473
|
+
brand_dir: configRoot,
|
|
2474
|
+
install_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2475
|
+
};
|
|
2476
|
+
const manifestPath = join11(configRoot, "cannbot-manifest.json");
|
|
2477
|
+
writeFileSync3(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
2478
|
+
return manifest;
|
|
2479
|
+
}
|
|
2480
|
+
async function installViaManifest(manifest, pluginDir, repoPath, tool, level, installPath) {
|
|
2481
|
+
const cwd = installPath || process.cwd();
|
|
2482
|
+
const configRoot = getConfigRoot(tool, level, installPath);
|
|
2483
|
+
try {
|
|
2484
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2485
|
+
const agentsDir = join11(configRoot, "agents");
|
|
2486
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
2487
|
+
mkdirSync5(agentsDir, { recursive: true });
|
|
2488
|
+
const installedSkills = [];
|
|
2489
|
+
const installedAgents = [];
|
|
2490
|
+
for (const source of manifest.skills) {
|
|
2491
|
+
const sourceDir = join11(repoPath, source.dir);
|
|
2492
|
+
if (!existsSync12(sourceDir)) {
|
|
2493
|
+
logger.warn(` Skill source not found: ${source.dir}`);
|
|
2494
|
+
continue;
|
|
2495
|
+
}
|
|
2496
|
+
for (const skillEntry of source.skills) {
|
|
2497
|
+
const skillName = typeof skillEntry === "string" ? skillEntry : skillEntry.name;
|
|
2498
|
+
const skillAlias = typeof skillEntry === "string" ? skillName : skillEntry.as || skillName;
|
|
2499
|
+
const skillPath = join11(sourceDir, skillName);
|
|
2500
|
+
if (!existsSync12(skillPath)) {
|
|
2501
|
+
logger.warn(` Skill not found: ${skillName} in ${source.dir}`);
|
|
2502
|
+
continue;
|
|
2503
|
+
}
|
|
2504
|
+
const targetPath = join11(skillsDir, skillAlias);
|
|
2505
|
+
const result = installLink(skillPath, targetPath);
|
|
2506
|
+
if (result !== "failed") {
|
|
2507
|
+
installedSkills.push(skillAlias);
|
|
2508
|
+
} else {
|
|
2509
|
+
logger.warn(` Failed to install skill: ${skillAlias}`);
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
for (const agentName of manifest.agents) {
|
|
2514
|
+
const agentFile = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
|
|
2515
|
+
const agentPath = join11(pluginDir, "agents", agentFile);
|
|
2516
|
+
if (!existsSync12(agentPath)) {
|
|
2517
|
+
logger.warn(` Agent not found: ${agentFile}`);
|
|
2518
|
+
continue;
|
|
2519
|
+
}
|
|
2520
|
+
const targetPath = join11(agentsDir, agentFile);
|
|
2521
|
+
const result = installLink(agentPath, targetPath);
|
|
2522
|
+
if (result !== "failed") {
|
|
2523
|
+
installedAgents.push(agentFile);
|
|
2524
|
+
} else {
|
|
2525
|
+
logger.warn(` Failed to install agent: ${agentFile}`);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
installConfigFile(pluginDir, manifest.configFile, tool, level, configRoot, cwd);
|
|
2529
|
+
await installExternalRepos(manifest.externalRepos, repoPath);
|
|
2530
|
+
const cannbotManifest = writeCannbotManifest(
|
|
2531
|
+
configRoot,
|
|
2532
|
+
manifest.id,
|
|
2533
|
+
manifest.version || "1.0.0",
|
|
2534
|
+
level,
|
|
2535
|
+
tool,
|
|
2536
|
+
installedSkills,
|
|
2537
|
+
installedAgents
|
|
2538
|
+
);
|
|
2539
|
+
return {
|
|
2540
|
+
success: true,
|
|
2541
|
+
skillsCount: installedSkills.length,
|
|
2542
|
+
agentsCount: installedAgents.length,
|
|
2543
|
+
errors: [],
|
|
2544
|
+
manifest: cannbotManifest
|
|
2545
|
+
};
|
|
2546
|
+
} catch (error) {
|
|
2547
|
+
return {
|
|
2548
|
+
success: false,
|
|
2549
|
+
skillsCount: 0,
|
|
2550
|
+
agentsCount: 0,
|
|
2551
|
+
errors: [error instanceof Error ? error.message : t("error_unknown")],
|
|
2552
|
+
manifest: null
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
1664
2555
|
}
|
|
1665
2556
|
|
|
1666
2557
|
// src/core/installer.ts
|
|
@@ -1672,18 +2563,116 @@ async function installPlugin(opts) {
|
|
|
1672
2563
|
pluginId: opts.pluginId,
|
|
1673
2564
|
skillsCount: 0,
|
|
1674
2565
|
agentsCount: 0,
|
|
1675
|
-
errors: [
|
|
2566
|
+
errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
|
|
1676
2567
|
warnings: []
|
|
1677
2568
|
};
|
|
1678
2569
|
}
|
|
1679
|
-
const
|
|
1680
|
-
|
|
2570
|
+
const pluginDir = join12(opts.repoPath, plugin.dir);
|
|
2571
|
+
const cwd = opts.installPath || process.cwd();
|
|
2572
|
+
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
2573
|
+
const agentsFile = join12(configRoot, getAgentsFileName(opts.tool));
|
|
2574
|
+
let backupInfo = null;
|
|
2575
|
+
if (existsSync13(agentsFile)) {
|
|
2576
|
+
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
2577
|
+
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
2578
|
+
let choice = "overwrite";
|
|
2579
|
+
if (!opts.yes) {
|
|
2580
|
+
choice = await showOverwriteWarning(
|
|
2581
|
+
currentPlugin.pluginName,
|
|
2582
|
+
plugin.displayName
|
|
2583
|
+
);
|
|
2584
|
+
}
|
|
2585
|
+
if (choice === "cancel") {
|
|
2586
|
+
logger.info(t("backup_cancel"));
|
|
2587
|
+
return {
|
|
2588
|
+
success: false,
|
|
2589
|
+
pluginId: opts.pluginId,
|
|
2590
|
+
skillsCount: 0,
|
|
2591
|
+
agentsCount: 0,
|
|
2592
|
+
errors: [t("backup_cancel")],
|
|
2593
|
+
warnings: []
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
backupInfo = createBackup(
|
|
2597
|
+
configRoot,
|
|
2598
|
+
opts.tool,
|
|
2599
|
+
currentPlugin.pluginId,
|
|
2600
|
+
currentPlugin.pluginName
|
|
2601
|
+
);
|
|
2602
|
+
if (!backupInfo) {
|
|
2603
|
+
logger.error(t("backup_failed"));
|
|
2604
|
+
return {
|
|
2605
|
+
success: false,
|
|
2606
|
+
pluginId: opts.pluginId,
|
|
2607
|
+
skillsCount: 0,
|
|
2608
|
+
agentsCount: 0,
|
|
2609
|
+
errors: [t("backup_failed")],
|
|
2610
|
+
warnings: []
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
const pluginManifest = readPluginManifest(pluginDir);
|
|
2617
|
+
if (pluginManifest) {
|
|
2618
|
+
const result = await installViaManifest(
|
|
2619
|
+
pluginManifest,
|
|
2620
|
+
pluginDir,
|
|
2621
|
+
opts.repoPath,
|
|
2622
|
+
opts.tool,
|
|
2623
|
+
opts.level,
|
|
2624
|
+
opts.installPath
|
|
2625
|
+
);
|
|
2626
|
+
if (result.success) {
|
|
2627
|
+
try {
|
|
2628
|
+
const record = scanInstalledFiles(
|
|
2629
|
+
opts.pluginId,
|
|
2630
|
+
plugin.displayName,
|
|
2631
|
+
opts.tool,
|
|
2632
|
+
opts.level,
|
|
2633
|
+
cwd,
|
|
2634
|
+
configRoot,
|
|
2635
|
+
result.manifest
|
|
2636
|
+
);
|
|
2637
|
+
if (backupInfo) {
|
|
2638
|
+
record.backup = {
|
|
2639
|
+
filePath: backupInfo.filePath,
|
|
2640
|
+
fromPluginId: backupInfo.pluginId,
|
|
2641
|
+
fromPluginName: backupInfo.pluginName,
|
|
2642
|
+
backupTime: backupInfo.backupTime
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
writeRecord(record);
|
|
2646
|
+
} catch {
|
|
2647
|
+
logger.warn(t("record_write_failed"));
|
|
2648
|
+
}
|
|
2649
|
+
return {
|
|
2650
|
+
success: true,
|
|
2651
|
+
pluginId: opts.pluginId,
|
|
2652
|
+
skillsCount: result.skillsCount,
|
|
2653
|
+
agentsCount: result.agentsCount,
|
|
2654
|
+
errors: result.errors,
|
|
2655
|
+
warnings: []
|
|
2656
|
+
};
|
|
2657
|
+
} else {
|
|
2658
|
+
return {
|
|
2659
|
+
success: false,
|
|
2660
|
+
pluginId: opts.pluginId,
|
|
2661
|
+
skillsCount: 0,
|
|
2662
|
+
agentsCount: 0,
|
|
2663
|
+
errors: result.errors,
|
|
2664
|
+
warnings: []
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
const scriptPath = join12(pluginDir, plugin.script);
|
|
2669
|
+
if (!existsSync13(scriptPath)) {
|
|
1681
2670
|
return {
|
|
1682
2671
|
success: false,
|
|
1683
2672
|
pluginId: opts.pluginId,
|
|
1684
2673
|
skillsCount: 0,
|
|
1685
2674
|
agentsCount: 0,
|
|
1686
|
-
errors: [
|
|
2675
|
+
errors: [t("error_script_not_found").replace("{path}", scriptPath)],
|
|
1687
2676
|
warnings: []
|
|
1688
2677
|
};
|
|
1689
2678
|
}
|
|
@@ -1691,14 +2680,15 @@ async function installPlugin(opts) {
|
|
|
1691
2680
|
if (opts.installPath) {
|
|
1692
2681
|
args.push(opts.installPath);
|
|
1693
2682
|
}
|
|
1694
|
-
const cwd = opts.installPath || process.cwd();
|
|
1695
2683
|
try {
|
|
1696
|
-
|
|
2684
|
+
const shell = process.platform === "win32" ? "sh" : "bash";
|
|
2685
|
+
const execOptions = {
|
|
1697
2686
|
cwd,
|
|
1698
2687
|
timeout: 3e5,
|
|
1699
|
-
stdio: "pipe"
|
|
1700
|
-
|
|
1701
|
-
|
|
2688
|
+
stdio: "pipe",
|
|
2689
|
+
input: opts.yes ? "y\n" : void 0
|
|
2690
|
+
};
|
|
2691
|
+
await execa4(shell, [scriptPath, ...args], execOptions);
|
|
1702
2692
|
const manifest = readManifest(configRoot);
|
|
1703
2693
|
let skillsCount = 0;
|
|
1704
2694
|
let agentsCount = 0;
|
|
@@ -1719,8 +2709,17 @@ async function installPlugin(opts) {
|
|
|
1719
2709
|
configRoot,
|
|
1720
2710
|
manifest
|
|
1721
2711
|
);
|
|
2712
|
+
if (backupInfo) {
|
|
2713
|
+
record.backup = {
|
|
2714
|
+
filePath: backupInfo.filePath,
|
|
2715
|
+
fromPluginId: backupInfo.pluginId,
|
|
2716
|
+
fromPluginName: backupInfo.pluginName,
|
|
2717
|
+
backupTime: backupInfo.backupTime
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
1722
2720
|
writeRecord(record);
|
|
1723
2721
|
} catch {
|
|
2722
|
+
logger.warn(t("record_write_failed"));
|
|
1724
2723
|
}
|
|
1725
2724
|
return {
|
|
1726
2725
|
success: true,
|
|
@@ -1731,7 +2730,7 @@ async function installPlugin(opts) {
|
|
|
1731
2730
|
warnings: []
|
|
1732
2731
|
};
|
|
1733
2732
|
} catch (error) {
|
|
1734
|
-
const errorMessage = error instanceof Error ? error.message : "
|
|
2733
|
+
const errorMessage = error instanceof Error ? error.message : t("error_unknown");
|
|
1735
2734
|
return {
|
|
1736
2735
|
success: false,
|
|
1737
2736
|
pluginId: opts.pluginId,
|
|
@@ -1744,41 +2743,46 @@ async function installPlugin(opts) {
|
|
|
1744
2743
|
}
|
|
1745
2744
|
|
|
1746
2745
|
// src/core/skill-installer.ts
|
|
1747
|
-
import { existsSync as
|
|
1748
|
-
import { join as
|
|
1749
|
-
import { select as
|
|
1750
|
-
import
|
|
2746
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, unlinkSync as unlinkSync5, readdirSync as readdirSync4, rmdirSync, realpathSync as realpathSync2 } from "fs";
|
|
2747
|
+
import { join as join13, dirname as dirname7 } from "path";
|
|
2748
|
+
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
2749
|
+
import chalk5 from "chalk";
|
|
1751
2750
|
import Table from "cli-table3";
|
|
1752
2751
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
1753
2752
|
const configRoot = getConfigRoot(tool, level);
|
|
1754
|
-
const skillsDir =
|
|
2753
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1755
2754
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1756
|
-
if (!
|
|
1757
|
-
|
|
2755
|
+
if (!existsSync14(skillsDir)) {
|
|
2756
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
1758
2757
|
}
|
|
1759
2758
|
const results = [];
|
|
1760
2759
|
const installedIds = [];
|
|
1761
2760
|
for (const skillId of skillIds) {
|
|
1762
2761
|
const skill = findSkill(skillId);
|
|
1763
2762
|
if (!skill) {
|
|
1764
|
-
results.push({ skillId, success: false, error: "
|
|
2763
|
+
results.push({ skillId, success: false, error: t("error_skill_not_found") });
|
|
1765
2764
|
continue;
|
|
1766
2765
|
}
|
|
1767
|
-
const sourcePath =
|
|
1768
|
-
const targetPath =
|
|
1769
|
-
if (!
|
|
1770
|
-
results.push({ skillId, success: false, error:
|
|
2766
|
+
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
2767
|
+
const targetPath = join13(skillsDir, skillId);
|
|
2768
|
+
if (!existsSync14(sourcePath)) {
|
|
2769
|
+
results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
|
|
1771
2770
|
continue;
|
|
1772
2771
|
}
|
|
1773
2772
|
try {
|
|
1774
|
-
|
|
1775
|
-
|
|
2773
|
+
const resolvedSource = realpathSync2(sourcePath);
|
|
2774
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
2775
|
+
unlinkSync5(targetPath);
|
|
1776
2776
|
}
|
|
1777
|
-
|
|
2777
|
+
symlinkSync2(resolvedSource, targetPath);
|
|
1778
2778
|
results.push({ skillId, success: true });
|
|
1779
2779
|
installedIds.push(skillId);
|
|
1780
2780
|
} catch (error) {
|
|
1781
|
-
|
|
2781
|
+
let msg = error instanceof Error ? error.message : t("error_unknown");
|
|
2782
|
+
if (process.platform === "win32" && msg.includes("EPERM")) {
|
|
2783
|
+
msg = t("error_symlink_windows");
|
|
2784
|
+
}
|
|
2785
|
+
results.push({ skillId, success: false, error: msg });
|
|
1782
2786
|
}
|
|
1783
2787
|
}
|
|
1784
2788
|
if (installedIds.length > 0) {
|
|
@@ -1788,109 +2792,118 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1788
2792
|
}
|
|
1789
2793
|
async function uninstallSkills(skillIds, tool, level) {
|
|
1790
2794
|
const configRoot = getConfigRoot(tool, level);
|
|
1791
|
-
const skillsDir =
|
|
2795
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1792
2796
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1793
2797
|
const results = [];
|
|
1794
2798
|
const removedIds = [];
|
|
1795
2799
|
for (const skillId of skillIds) {
|
|
1796
|
-
const targetPath =
|
|
2800
|
+
const targetPath = join13(skillsDir, skillId);
|
|
1797
2801
|
try {
|
|
1798
|
-
if (
|
|
1799
|
-
|
|
2802
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
2803
|
+
unlinkSync5(targetPath);
|
|
1800
2804
|
results.push({ skillId, success: true });
|
|
1801
2805
|
removedIds.push(skillId);
|
|
1802
|
-
logger.step(`
|
|
2806
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
1803
2807
|
} else {
|
|
1804
|
-
results.push({ skillId, success: false, error: "
|
|
1805
|
-
logger.warn(`${skillId}
|
|
2808
|
+
results.push({ skillId, success: false, error: t("error_not_installed") });
|
|
2809
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
1806
2810
|
}
|
|
1807
2811
|
} catch (error) {
|
|
1808
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
2812
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
1809
2813
|
}
|
|
1810
2814
|
}
|
|
1811
2815
|
if (removedIds.length > 0) {
|
|
1812
2816
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
1813
2817
|
}
|
|
1814
|
-
if (
|
|
2818
|
+
if (existsSync14(skillsDir)) {
|
|
1815
2819
|
try {
|
|
1816
|
-
const entries =
|
|
2820
|
+
const entries = readdirSync4(skillsDir);
|
|
1817
2821
|
if (entries.length === 0) {
|
|
1818
2822
|
rmdirSync(skillsDir);
|
|
1819
|
-
logger.step(`
|
|
1820
|
-
}
|
|
1821
|
-
} catch {
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
if (existsSync9(configRoot)) {
|
|
1825
|
-
try {
|
|
1826
|
-
const entries = readdirSync3(configRoot);
|
|
1827
|
-
if (entries.length === 0) {
|
|
1828
|
-
rmdirSync(configRoot);
|
|
1829
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
2823
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
1830
2824
|
}
|
|
1831
2825
|
} catch {
|
|
1832
2826
|
}
|
|
1833
2827
|
}
|
|
1834
2828
|
return results;
|
|
1835
2829
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
2830
|
+
var SELECT_ALL = "__select_all__";
|
|
2831
|
+
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
2832
|
+
var CONTINUE_SELECT = "__continue_select__";
|
|
2833
|
+
async function interactiveSkillSelect(tool, level) {
|
|
1839
2834
|
const categories = getAllCategories();
|
|
2835
|
+
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
2836
|
+
let accumulated = [];
|
|
1840
2837
|
let step = 0;
|
|
1841
2838
|
let selectedCategoryId = "";
|
|
1842
2839
|
while (true) {
|
|
1843
2840
|
switch (step) {
|
|
1844
2841
|
case 0: {
|
|
1845
|
-
|
|
2842
|
+
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");
|
|
2843
|
+
printBoxTitle(titleSuffix);
|
|
1846
2844
|
const categoryChoices = [
|
|
2845
|
+
{
|
|
2846
|
+
name: `>> ${t("skill_select_all").replace("{count}", String(allSkillIds.length))}`,
|
|
2847
|
+
value: SELECT_ALL
|
|
2848
|
+
},
|
|
2849
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1847
2850
|
...categories.map((cat) => ({
|
|
1848
2851
|
name: `> ${cat.name} (${cat.skills.length} skills)`,
|
|
1849
2852
|
value: cat.id
|
|
1850
2853
|
})),
|
|
1851
2854
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1852
|
-
{ name:
|
|
1853
|
-
{ name: "
|
|
2855
|
+
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
2856
|
+
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
2857
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
1854
2858
|
];
|
|
1855
2859
|
showOperationHints();
|
|
1856
|
-
selectedCategoryId = await
|
|
2860
|
+
selectedCategoryId = await select3({
|
|
1857
2861
|
message: t("skill_select_category"),
|
|
1858
2862
|
choices: categoryChoices,
|
|
1859
2863
|
loop: false,
|
|
1860
2864
|
theme: selectTheme,
|
|
1861
2865
|
pageSize: 15
|
|
1862
2866
|
});
|
|
1863
|
-
if (selectedCategoryId ===
|
|
1864
|
-
if (selectedCategoryId ===
|
|
2867
|
+
if (selectedCategoryId === BACK) return "back";
|
|
2868
|
+
if (selectedCategoryId === CANCEL) return "cancel";
|
|
2869
|
+
if (selectedCategoryId === SELECT_ALL) {
|
|
2870
|
+
return allSkillIds;
|
|
2871
|
+
}
|
|
2872
|
+
if (selectedCategoryId === "install") {
|
|
2873
|
+
return accumulated;
|
|
2874
|
+
}
|
|
1865
2875
|
step = 1;
|
|
1866
2876
|
break;
|
|
1867
2877
|
}
|
|
1868
2878
|
case 1: {
|
|
1869
2879
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
1870
2880
|
if (!category) return [];
|
|
1871
|
-
printBoxTitle(
|
|
1872
|
-
const
|
|
1873
|
-
const
|
|
1874
|
-
const installedSkills = [];
|
|
1875
|
-
for (const tk of tools) {
|
|
1876
|
-
for (const l of levels) {
|
|
1877
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
1878
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
2881
|
+
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
2882
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
2883
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
1881
2884
|
const installedSet = new Set(installedSkills);
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
name:
|
|
1887
|
-
value:
|
|
1888
|
-
checked:
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2885
|
+
const categorySkillIds = new Set(category.skills.map((s) => s.id));
|
|
2886
|
+
const accumulatedInCategory = new Set(accumulated.filter((id) => categorySkillIds.has(id)));
|
|
2887
|
+
const skillChoices = [
|
|
2888
|
+
{
|
|
2889
|
+
name: `>> ${t("skill_select_all_category")}`,
|
|
2890
|
+
value: SELECT_ALL_CATEGORY,
|
|
2891
|
+
checked: accumulatedInCategory.size === category.skills.length,
|
|
2892
|
+
description: t("skill_select_all_category")
|
|
2893
|
+
},
|
|
2894
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2895
|
+
...category.skills.map((skill) => {
|
|
2896
|
+
const isInstalled = installedSet.has(skill.id);
|
|
2897
|
+
const isAccumulated = accumulatedInCategory.has(skill.id);
|
|
2898
|
+
const suffix = isInstalled ? ` [${t("skill_already_installed")}]` : isAccumulated ? " [+]" : "";
|
|
2899
|
+
return {
|
|
2900
|
+
name: `${chalk5.cyan(skill.id)}${suffix}`,
|
|
2901
|
+
value: skill.id,
|
|
2902
|
+
checked: isAccumulated,
|
|
2903
|
+
description: skill.description
|
|
2904
|
+
};
|
|
2905
|
+
})
|
|
2906
|
+
];
|
|
1894
2907
|
showOperationHints(true);
|
|
1895
2908
|
const selectedSkills = await checkbox2({
|
|
1896
2909
|
message: t("skill_select_items"),
|
|
@@ -1900,16 +2913,21 @@ async function interactiveSkillSelect() {
|
|
|
1900
2913
|
theme: checkboxTheme,
|
|
1901
2914
|
pageSize: 15
|
|
1902
2915
|
});
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
2916
|
+
let skillIds;
|
|
2917
|
+
if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
|
|
2918
|
+
skillIds = category.skills.map((s) => s.id);
|
|
2919
|
+
} else {
|
|
2920
|
+
skillIds = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
|
|
1906
2921
|
}
|
|
1907
|
-
if (selectedSkills.includes(CANCEL3)) return "cancel";
|
|
1908
|
-
const skillIds = selectedSkills.filter((v) => v !== BACK3 && v !== CANCEL3);
|
|
1909
2922
|
if (skillIds.length === 0) {
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2923
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
2924
|
+
const action2 = await select3({
|
|
2925
|
+
message: accumulated.length > 0 ? t("skill_continue_or_install").replace("{count}", String(accumulated.length)) : t("wizard_no_selection"),
|
|
2926
|
+
choices: accumulated.length > 0 ? [
|
|
2927
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
2928
|
+
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
2929
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
2930
|
+
] : [
|
|
1913
2931
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1914
2932
|
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
1915
2933
|
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
@@ -1917,13 +2935,38 @@ async function interactiveSkillSelect() {
|
|
|
1917
2935
|
loop: false,
|
|
1918
2936
|
theme: selectTheme
|
|
1919
2937
|
});
|
|
1920
|
-
if (
|
|
2938
|
+
if (action2 === "install") return accumulated;
|
|
2939
|
+
if (action2 === "back") {
|
|
1921
2940
|
step = 0;
|
|
1922
2941
|
break;
|
|
1923
2942
|
}
|
|
1924
2943
|
return "cancel";
|
|
1925
2944
|
}
|
|
1926
|
-
|
|
2945
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
2946
|
+
for (const id of skillIds) {
|
|
2947
|
+
if (!accumulated.includes(id)) {
|
|
2948
|
+
accumulated.push(id);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
const action = await select3({
|
|
2952
|
+
message: t("skill_continue_or_install").replace("{count}", String(accumulated.length)),
|
|
2953
|
+
choices: [
|
|
2954
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
2955
|
+
{ name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
|
|
2956
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2957
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
2958
|
+
],
|
|
2959
|
+
loop: false,
|
|
2960
|
+
theme: selectTheme
|
|
2961
|
+
});
|
|
2962
|
+
if (action === "install") {
|
|
2963
|
+
return accumulated;
|
|
2964
|
+
}
|
|
2965
|
+
if (action === CONTINUE_SELECT) {
|
|
2966
|
+
step = 0;
|
|
2967
|
+
break;
|
|
2968
|
+
}
|
|
2969
|
+
return "cancel";
|
|
1927
2970
|
}
|
|
1928
2971
|
}
|
|
1929
2972
|
}
|
|
@@ -1931,11 +2974,11 @@ async function interactiveSkillSelect() {
|
|
|
1931
2974
|
function listAllSkills() {
|
|
1932
2975
|
const categories = getAllCategories();
|
|
1933
2976
|
console.log();
|
|
1934
|
-
console.log(
|
|
1935
|
-
console.log(
|
|
2977
|
+
console.log(chalk5.bold(` ${t("skill_list_title")}`));
|
|
2978
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
1936
2979
|
for (const category of categories) {
|
|
1937
2980
|
console.log();
|
|
1938
|
-
console.log(
|
|
2981
|
+
console.log(chalk5.bold(` ${category.name}`) + chalk5.dim(` (${category.skills.length} skills)`));
|
|
1939
2982
|
const table = new Table({
|
|
1940
2983
|
style: { head: [], border: [] },
|
|
1941
2984
|
colWidths: [35, 45],
|
|
@@ -1943,51 +2986,54 @@ function listAllSkills() {
|
|
|
1943
2986
|
});
|
|
1944
2987
|
for (const skill of category.skills) {
|
|
1945
2988
|
table.push([
|
|
1946
|
-
|
|
2989
|
+
chalk5.cyan(skill.id),
|
|
1947
2990
|
skill.description
|
|
1948
2991
|
]);
|
|
1949
2992
|
}
|
|
1950
2993
|
console.log(table.toString());
|
|
1951
2994
|
}
|
|
1952
2995
|
console.log();
|
|
1953
|
-
console.log(
|
|
2996
|
+
console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
1954
2997
|
console.log();
|
|
1955
2998
|
}
|
|
1956
|
-
function
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2999
|
+
function resolveSkillSourcePath(skill, repoPath) {
|
|
3000
|
+
if (skill.filePath) {
|
|
3001
|
+
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join13(repoPath, skill.filePath);
|
|
3002
|
+
if (existsSync14(absPath)) {
|
|
3003
|
+
return dirname7(absPath);
|
|
3004
|
+
}
|
|
1961
3005
|
}
|
|
3006
|
+
return join13(repoPath, skill.source, skill.id);
|
|
1962
3007
|
}
|
|
1963
3008
|
|
|
1964
3009
|
// src/ui/display.ts
|
|
1965
|
-
import
|
|
3010
|
+
import chalk6 from "chalk";
|
|
1966
3011
|
import Table2 from "cli-table3";
|
|
1967
3012
|
function printPluginList(plugins, installed) {
|
|
1968
3013
|
const table = new Table2({
|
|
1969
3014
|
head: [
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
chalk4.cyan(t("list_agents"))
|
|
3015
|
+
chalk6.cyan(t("list_id")),
|
|
3016
|
+
chalk6.cyan(t("list_name")),
|
|
3017
|
+
chalk6.cyan(t("list_status")),
|
|
3018
|
+
chalk6.cyan(t("list_description"))
|
|
1975
3019
|
],
|
|
1976
|
-
style: { head: [], border: [] }
|
|
3020
|
+
style: { head: [], border: [] },
|
|
3021
|
+
colWidths: [6, 32, 8, 50],
|
|
3022
|
+
wordWrap: true
|
|
1977
3023
|
});
|
|
1978
3024
|
plugins.forEach((plugin, index) => {
|
|
1979
3025
|
const isInstalled = installed.has(plugin.id);
|
|
1980
|
-
const status = isInstalled ?
|
|
3026
|
+
const status = isInstalled ? chalk6.green(`\u2713 ${t("status_installed")}`) : chalk6.dim(`\u2014`);
|
|
1981
3027
|
table.push([
|
|
1982
3028
|
String(index + 1),
|
|
1983
3029
|
plugin.displayName,
|
|
1984
3030
|
status,
|
|
1985
|
-
|
|
1986
|
-
String(plugin.agents)
|
|
3031
|
+
plugin.description || chalk6.dim("\u2014")
|
|
1987
3032
|
]);
|
|
1988
3033
|
});
|
|
1989
3034
|
console.log();
|
|
1990
|
-
|
|
3035
|
+
const countSuffix = t("count_suffix");
|
|
3036
|
+
console.log(chalk6.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
1991
3037
|
console.log(table.toString());
|
|
1992
3038
|
console.log();
|
|
1993
3039
|
}
|
|
@@ -1996,7 +3042,7 @@ function printInstallSummary(results) {
|
|
|
1996
3042
|
const totalCount = results.length;
|
|
1997
3043
|
console.log();
|
|
1998
3044
|
console.log(
|
|
1999
|
-
|
|
3045
|
+
chalk6.bold(
|
|
2000
3046
|
` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
|
|
2001
3047
|
)
|
|
2002
3048
|
);
|
|
@@ -2004,10 +3050,10 @@ function printInstallSummary(results) {
|
|
|
2004
3050
|
for (const result of results) {
|
|
2005
3051
|
if (result.success) {
|
|
2006
3052
|
console.log(
|
|
2007
|
-
|
|
3053
|
+
chalk6.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
|
|
2008
3054
|
);
|
|
2009
3055
|
} else {
|
|
2010
|
-
console.log(
|
|
3056
|
+
console.log(chalk6.red(" \u2717") + ` ${result.displayName}`);
|
|
2011
3057
|
}
|
|
2012
3058
|
}
|
|
2013
3059
|
console.log();
|
|
@@ -2020,22 +3066,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2020
3066
|
return;
|
|
2021
3067
|
}
|
|
2022
3068
|
const toolName = getToolDisplayName(tool);
|
|
3069
|
+
const countSuffix = t("count_suffix");
|
|
2023
3070
|
const lines = [
|
|
2024
|
-
|
|
3071
|
+
chalk6.bold(` ${t("install_done")}!`),
|
|
2025
3072
|
"",
|
|
2026
|
-
` ${
|
|
2027
|
-
` ${
|
|
3073
|
+
` ${chalk6.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
3074
|
+
` ${chalk6.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk6.dim("|")} ${chalk6.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2028
3075
|
"",
|
|
2029
|
-
|
|
2030
|
-
` ${
|
|
2031
|
-
` ${
|
|
2032
|
-
` ${
|
|
2033
|
-
` ${
|
|
3076
|
+
chalk6.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
3077
|
+
` ${chalk6.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk6.green(toolName.toLowerCase())}`,
|
|
3078
|
+
` ${chalk6.cyan("2.")} ${t("install_enhanced_try")}: ${chalk6.green(t("try_prompt"))}`,
|
|
3079
|
+
` ${chalk6.cyan("3.")} ${t("install_enhanced_more")}: ${chalk6.green("install-helper list")}`,
|
|
3080
|
+
` ${chalk6.cyan("4.")} ${t("install_enhanced_check")}: ${chalk6.green("install-helper doctor")}`,
|
|
2034
3081
|
""
|
|
2035
3082
|
];
|
|
2036
3083
|
if (successResults.length === 1) {
|
|
3084
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
3085
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2037
3086
|
lines.push(
|
|
2038
|
-
` ${
|
|
3087
|
+
` ${chalk6.dim(t("install_enhanced_docs"))}: ${chalk6.dim(docsPath)}`
|
|
2039
3088
|
);
|
|
2040
3089
|
}
|
|
2041
3090
|
console.log();
|
|
@@ -2046,34 +3095,32 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2046
3095
|
}
|
|
2047
3096
|
|
|
2048
3097
|
// src/commands/init.ts
|
|
2049
|
-
import { existsSync as
|
|
2050
|
-
import { join as
|
|
2051
|
-
var BACK2 = "__back__";
|
|
2052
|
-
var CANCEL2 = "__cancel__";
|
|
3098
|
+
import { existsSync as existsSync15, readFileSync as readFileSync9 } from "fs";
|
|
3099
|
+
import { join as join14 } from "path";
|
|
2053
3100
|
async function initCommand() {
|
|
2054
3101
|
const cwd = process.cwd();
|
|
2055
|
-
if (
|
|
3102
|
+
if (existsSync15(join14(cwd, "package.json")) && (() => {
|
|
2056
3103
|
try {
|
|
2057
|
-
const pkg = JSON.parse(
|
|
3104
|
+
const pkg = JSON.parse(readFileSync9(join14(cwd, "package.json"), "utf-8"));
|
|
2058
3105
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2059
3106
|
} catch {
|
|
2060
3107
|
return false;
|
|
2061
3108
|
}
|
|
2062
3109
|
})()) {
|
|
2063
|
-
logger.warn("
|
|
2064
|
-
logger.info("
|
|
2065
|
-
logger.info("
|
|
3110
|
+
logger.warn(t("init_pkg_dir_warn"));
|
|
3111
|
+
logger.info(t("init_pkg_dir_hint"));
|
|
3112
|
+
logger.info(t("init_pkg_dir_example"));
|
|
2066
3113
|
return;
|
|
2067
3114
|
}
|
|
2068
3115
|
printBanner(t("wizard_title"));
|
|
2069
|
-
const spinner = createSpinner("
|
|
3116
|
+
const spinner = createSpinner(t("loading_skills_list"));
|
|
2070
3117
|
spinner.start();
|
|
2071
3118
|
try {
|
|
2072
3119
|
const repoManager = createRepositoryManager();
|
|
2073
3120
|
await repoManager.ensureRepoAndScan();
|
|
2074
|
-
spinner.succeed("
|
|
3121
|
+
spinner.succeed(t("loading_skills_list_complete"));
|
|
2075
3122
|
} catch {
|
|
2076
|
-
spinner.warn("
|
|
3123
|
+
spinner.warn(t("loading_skills_list_failed"));
|
|
2077
3124
|
}
|
|
2078
3125
|
let step = 0;
|
|
2079
3126
|
let mode = "plugin";
|
|
@@ -2107,8 +3154,7 @@ async function initCommand() {
|
|
|
2107
3154
|
}
|
|
2108
3155
|
}
|
|
2109
3156
|
async function selectMode() {
|
|
2110
|
-
printBoxTitle("
|
|
2111
|
-
const CANCEL3 = "__cancel__";
|
|
3157
|
+
printBoxTitle(t("wizard_select_mode_title"));
|
|
2112
3158
|
const choices = [
|
|
2113
3159
|
{
|
|
2114
3160
|
name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
|
|
@@ -2119,30 +3165,30 @@ async function selectMode() {
|
|
|
2119
3165
|
value: "skill"
|
|
2120
3166
|
},
|
|
2121
3167
|
new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2122
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3168
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2123
3169
|
];
|
|
2124
3170
|
showOperationHints();
|
|
2125
|
-
const result = await
|
|
3171
|
+
const result = await select4({
|
|
2126
3172
|
message: t("wizard_select_mode"),
|
|
2127
3173
|
choices,
|
|
2128
3174
|
loop: false,
|
|
2129
3175
|
theme: selectTheme
|
|
2130
3176
|
});
|
|
2131
|
-
if (result ===
|
|
3177
|
+
if (result === CANCEL) return null;
|
|
2132
3178
|
return result;
|
|
2133
3179
|
}
|
|
2134
3180
|
async function pluginInstallFlow() {
|
|
2135
3181
|
const answers = await runWizard();
|
|
2136
3182
|
if (!answers.confirmed) {
|
|
2137
3183
|
if (answers.back) return "back";
|
|
2138
|
-
logger.info("
|
|
3184
|
+
logger.info(t("init_cancelled"));
|
|
2139
3185
|
return "done";
|
|
2140
3186
|
}
|
|
2141
3187
|
const repoManager = createRepositoryManager();
|
|
2142
|
-
const spinner = createSpinner("
|
|
3188
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2143
3189
|
spinner.start();
|
|
2144
3190
|
const repoPath = await repoManager.ensureRepo();
|
|
2145
|
-
spinner.succeed("
|
|
3191
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2146
3192
|
const allPlugins = getAllPlugins();
|
|
2147
3193
|
const selectedPlugins = answers.plugins.map(
|
|
2148
3194
|
(id) => allPlugins.find((p) => p.id === id)
|
|
@@ -2196,7 +3242,7 @@ async function skillInstallFlow() {
|
|
|
2196
3242
|
const result = await selectToolWithDetection();
|
|
2197
3243
|
if (result === "back") return "back";
|
|
2198
3244
|
if (result === "cancel") {
|
|
2199
|
-
logger.info("
|
|
3245
|
+
logger.info(t("init_cancelled"));
|
|
2200
3246
|
return "done";
|
|
2201
3247
|
}
|
|
2202
3248
|
tool = result;
|
|
@@ -2205,12 +3251,12 @@ async function skillInstallFlow() {
|
|
|
2205
3251
|
}
|
|
2206
3252
|
case 1: {
|
|
2207
3253
|
const result = await stepLevel();
|
|
2208
|
-
if (result ===
|
|
3254
|
+
if (result === BACK) {
|
|
2209
3255
|
step = 0;
|
|
2210
3256
|
break;
|
|
2211
3257
|
}
|
|
2212
|
-
if (result ===
|
|
2213
|
-
logger.info("
|
|
3258
|
+
if (result === CANCEL) {
|
|
3259
|
+
logger.info(t("init_cancelled"));
|
|
2214
3260
|
return "done";
|
|
2215
3261
|
}
|
|
2216
3262
|
level = result;
|
|
@@ -2218,25 +3264,25 @@ async function skillInstallFlow() {
|
|
|
2218
3264
|
break;
|
|
2219
3265
|
}
|
|
2220
3266
|
case 2: {
|
|
2221
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
3267
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2222
3268
|
if (selectedSkills === "back") {
|
|
2223
3269
|
step = 1;
|
|
2224
3270
|
break;
|
|
2225
3271
|
}
|
|
2226
3272
|
if (selectedSkills === "cancel") {
|
|
2227
|
-
logger.info("
|
|
3273
|
+
logger.info(t("init_cancelled"));
|
|
2228
3274
|
return "done";
|
|
2229
3275
|
}
|
|
2230
3276
|
if (selectedSkills.length === 0) {
|
|
2231
|
-
logger.info("
|
|
3277
|
+
logger.info(t("install_no_skill_selected"));
|
|
2232
3278
|
return "done";
|
|
2233
3279
|
}
|
|
2234
3280
|
const repoManager = createRepositoryManager();
|
|
2235
|
-
const spinner = createSpinner("
|
|
3281
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2236
3282
|
spinner.start();
|
|
2237
3283
|
const repoPath = await repoManager.ensureRepo();
|
|
2238
|
-
spinner.succeed("
|
|
2239
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
3284
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3285
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk7.bold(String(selectedSkills.length)))}...`);
|
|
2240
3286
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2241
3287
|
let successCount = 0;
|
|
2242
3288
|
let failCount = 0;
|
|
@@ -2254,10 +3300,10 @@ async function skillInstallFlow() {
|
|
|
2254
3300
|
}
|
|
2255
3301
|
const configRoot = getConfigRoot(tool, level);
|
|
2256
3302
|
logger.blank();
|
|
2257
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
3303
|
+
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)))}`);
|
|
2258
3304
|
logger.blank();
|
|
2259
|
-
logger.info(
|
|
2260
|
-
logger.info(
|
|
3305
|
+
logger.info(`${t("install_to")}: ${chalk7.cyan(configRoot + "/skills/")}`);
|
|
3306
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
|
|
2261
3307
|
logger.blank();
|
|
2262
3308
|
return "done";
|
|
2263
3309
|
}
|
|
@@ -2279,32 +3325,31 @@ async function listCommand() {
|
|
|
2279
3325
|
}
|
|
2280
3326
|
|
|
2281
3327
|
// src/commands/doctor.ts
|
|
2282
|
-
import
|
|
2283
|
-
import { existsSync as
|
|
2284
|
-
import { join as
|
|
3328
|
+
import chalk8 from "chalk";
|
|
3329
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
3330
|
+
import { join as join15 } from "path";
|
|
2285
3331
|
async function doctorCommand(options = {}) {
|
|
2286
3332
|
console.log();
|
|
2287
|
-
console.log(
|
|
2288
|
-
console.log(
|
|
2289
|
-
let errors = 0;
|
|
3333
|
+
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
3334
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2290
3335
|
let warnings = 0;
|
|
2291
3336
|
let fixes = 0;
|
|
2292
3337
|
console.log();
|
|
2293
|
-
console.log(
|
|
3338
|
+
console.log(chalk8.bold(` ${t("doctor_tools")}`));
|
|
2294
3339
|
const detectedTools = await detectTools();
|
|
2295
3340
|
const allTools = getAllTools();
|
|
2296
3341
|
for (const tool of allTools) {
|
|
2297
3342
|
const detected = detectedTools.find((d) => d.name === tool);
|
|
2298
3343
|
if (detected) {
|
|
2299
3344
|
console.log(
|
|
2300
|
-
|
|
3345
|
+
chalk8.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2301
3346
|
);
|
|
2302
3347
|
} else {
|
|
2303
|
-
console.log(
|
|
3348
|
+
console.log(chalk8.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2304
3349
|
}
|
|
2305
3350
|
}
|
|
2306
3351
|
console.log();
|
|
2307
|
-
console.log(
|
|
3352
|
+
console.log(chalk8.bold(` ${t("doctor_plugins")}`));
|
|
2308
3353
|
const plugins = getAllPlugins();
|
|
2309
3354
|
const installed = scanInstalled();
|
|
2310
3355
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
@@ -2312,80 +3357,80 @@ async function doctorCommand(options = {}) {
|
|
|
2312
3357
|
const inst = installedMap.get(plugin.id);
|
|
2313
3358
|
if (inst) {
|
|
2314
3359
|
console.log(
|
|
2315
|
-
|
|
3360
|
+
chalk8.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2316
3361
|
);
|
|
2317
3362
|
} else {
|
|
2318
|
-
console.log(
|
|
3363
|
+
console.log(chalk8.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2319
3364
|
}
|
|
2320
3365
|
}
|
|
2321
3366
|
console.log();
|
|
2322
|
-
console.log(
|
|
3367
|
+
console.log(chalk8.bold(` ${t("doctor_links")}`));
|
|
2323
3368
|
const primaryTool = detectedTools[0]?.name || "opencode";
|
|
2324
3369
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2325
3370
|
const skillsDir = getSkillsDir(configRoot);
|
|
2326
3371
|
const agentsDir = getAgentsDir(configRoot);
|
|
2327
|
-
if (
|
|
3372
|
+
if (existsSync16(skillsDir)) {
|
|
2328
3373
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2329
3374
|
if (brokenLinks === 0) {
|
|
2330
|
-
const count =
|
|
2331
|
-
console.log(
|
|
3375
|
+
const count = readdirSync5(skillsDir).length;
|
|
3376
|
+
console.log(chalk8.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2332
3377
|
} else {
|
|
2333
|
-
console.log(
|
|
3378
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2334
3379
|
warnings++;
|
|
2335
3380
|
if (options.fix) {
|
|
2336
3381
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2337
3382
|
fixes += fixed;
|
|
2338
|
-
console.log(
|
|
3383
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2339
3384
|
}
|
|
2340
3385
|
}
|
|
2341
3386
|
} else {
|
|
2342
|
-
console.log(
|
|
3387
|
+
console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2343
3388
|
if (options.fix) {
|
|
2344
|
-
|
|
3389
|
+
mkdirSync7(skillsDir, { recursive: true });
|
|
2345
3390
|
fixes++;
|
|
2346
|
-
console.log(
|
|
3391
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2347
3392
|
}
|
|
2348
3393
|
}
|
|
2349
|
-
if (
|
|
3394
|
+
if (existsSync16(agentsDir)) {
|
|
2350
3395
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2351
3396
|
if (brokenLinks === 0) {
|
|
2352
|
-
const count =
|
|
2353
|
-
console.log(
|
|
3397
|
+
const count = readdirSync5(agentsDir).length;
|
|
3398
|
+
console.log(chalk8.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2354
3399
|
} else {
|
|
2355
|
-
console.log(
|
|
3400
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2356
3401
|
warnings++;
|
|
2357
3402
|
if (options.fix) {
|
|
2358
3403
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2359
3404
|
fixes += fixed;
|
|
2360
|
-
console.log(
|
|
3405
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2361
3406
|
}
|
|
2362
3407
|
}
|
|
2363
3408
|
} else {
|
|
2364
|
-
console.log(
|
|
3409
|
+
console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2365
3410
|
if (options.fix) {
|
|
2366
|
-
|
|
3411
|
+
mkdirSync7(agentsDir, { recursive: true });
|
|
2367
3412
|
fixes++;
|
|
2368
|
-
console.log(
|
|
3413
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2369
3414
|
}
|
|
2370
3415
|
}
|
|
2371
3416
|
console.log();
|
|
2372
|
-
console.log(
|
|
3417
|
+
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2373
3418
|
const configFile = getConfigFileName(primaryTool);
|
|
2374
|
-
const configPath =
|
|
2375
|
-
if (
|
|
2376
|
-
console.log(
|
|
3419
|
+
const configPath = join15(process.cwd(), configFile);
|
|
3420
|
+
if (existsSync16(configPath)) {
|
|
3421
|
+
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2377
3422
|
} else {
|
|
2378
|
-
console.log(
|
|
3423
|
+
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2379
3424
|
}
|
|
2380
3425
|
console.log();
|
|
2381
|
-
console.log(
|
|
3426
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2382
3427
|
if (options.fix && fixes > 0) {
|
|
2383
3428
|
console.log(
|
|
2384
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3429
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
|
|
2385
3430
|
);
|
|
2386
3431
|
} else {
|
|
2387
3432
|
console.log(
|
|
2388
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3433
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
|
|
2389
3434
|
);
|
|
2390
3435
|
}
|
|
2391
3436
|
console.log();
|
|
@@ -2393,13 +3438,13 @@ async function doctorCommand(options = {}) {
|
|
|
2393
3438
|
function checkBrokenLinks(dir) {
|
|
2394
3439
|
let broken = 0;
|
|
2395
3440
|
try {
|
|
2396
|
-
const entries =
|
|
3441
|
+
const entries = readdirSync5(dir);
|
|
2397
3442
|
for (const entry of entries) {
|
|
2398
|
-
const fullPath =
|
|
3443
|
+
const fullPath = join15(dir, entry);
|
|
2399
3444
|
try {
|
|
2400
|
-
const stats =
|
|
3445
|
+
const stats = lstatSync2(fullPath);
|
|
2401
3446
|
if (stats.isSymbolicLink()) {
|
|
2402
|
-
if (!
|
|
3447
|
+
if (!existsSync16(fullPath)) {
|
|
2403
3448
|
broken++;
|
|
2404
3449
|
}
|
|
2405
3450
|
}
|
|
@@ -2414,14 +3459,14 @@ function checkBrokenLinks(dir) {
|
|
|
2414
3459
|
function fixBrokenLinks(dir) {
|
|
2415
3460
|
let fixed = 0;
|
|
2416
3461
|
try {
|
|
2417
|
-
const entries =
|
|
3462
|
+
const entries = readdirSync5(dir);
|
|
2418
3463
|
for (const entry of entries) {
|
|
2419
|
-
const fullPath =
|
|
3464
|
+
const fullPath = join15(dir, entry);
|
|
2420
3465
|
try {
|
|
2421
|
-
const stats =
|
|
3466
|
+
const stats = lstatSync2(fullPath);
|
|
2422
3467
|
if (stats.isSymbolicLink()) {
|
|
2423
|
-
if (!
|
|
2424
|
-
|
|
3468
|
+
if (!existsSync16(fullPath)) {
|
|
3469
|
+
unlinkSync6(fullPath);
|
|
2425
3470
|
fixed++;
|
|
2426
3471
|
}
|
|
2427
3472
|
}
|
|
@@ -2434,36 +3479,35 @@ function fixBrokenLinks(dir) {
|
|
|
2434
3479
|
}
|
|
2435
3480
|
|
|
2436
3481
|
// src/commands/status.ts
|
|
2437
|
-
import
|
|
3482
|
+
import chalk9 from "chalk";
|
|
2438
3483
|
async function statusCommand() {
|
|
2439
|
-
const plugins = getAllPlugins();
|
|
2440
3484
|
const installed = scanInstalled();
|
|
2441
3485
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2442
3486
|
console.log();
|
|
2443
|
-
console.log(
|
|
3487
|
+
console.log(chalk9.bold(` ${t("status_title")}`));
|
|
2444
3488
|
console.log();
|
|
2445
3489
|
if (installed.length === 0) {
|
|
2446
|
-
console.log(
|
|
2447
|
-
console.log(
|
|
3490
|
+
console.log(chalk9.dim(` ${t("status_none")}`));
|
|
3491
|
+
console.log(chalk9.dim(` ${t("status_hint")}`));
|
|
2448
3492
|
console.log();
|
|
2449
3493
|
return;
|
|
2450
3494
|
}
|
|
2451
3495
|
for (const inst of installed) {
|
|
2452
3496
|
console.log(
|
|
2453
|
-
|
|
3497
|
+
chalk9.green(" \u2713") + ` ${inst.displayName}` + chalk9.dim(` (${inst.tool}, ${inst.level})`)
|
|
2454
3498
|
);
|
|
2455
3499
|
console.log(
|
|
2456
|
-
|
|
3500
|
+
chalk9.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
|
|
2457
3501
|
);
|
|
2458
|
-
console.log(
|
|
2459
|
-
console.log(
|
|
3502
|
+
console.log(chalk9.dim(` ${inst.configRoot}`));
|
|
3503
|
+
console.log(chalk9.dim(` ${t("status_install_time")}: ${inst.installTime}`));
|
|
2460
3504
|
console.log();
|
|
2461
3505
|
}
|
|
2462
3506
|
}
|
|
2463
3507
|
|
|
2464
3508
|
// src/commands/install.ts
|
|
2465
3509
|
import { confirm } from "@inquirer/prompts";
|
|
2466
|
-
import
|
|
3510
|
+
import chalk10 from "chalk";
|
|
2467
3511
|
async function installCommand(names, options) {
|
|
2468
3512
|
if (options.list) {
|
|
2469
3513
|
try {
|
|
@@ -2474,79 +3518,150 @@ async function installCommand(names, options) {
|
|
|
2474
3518
|
listAllSkills();
|
|
2475
3519
|
return;
|
|
2476
3520
|
}
|
|
3521
|
+
if (options.all) {
|
|
3522
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
3523
|
+
scanSpinner2.start();
|
|
3524
|
+
try {
|
|
3525
|
+
const scanRepoManager = createRepositoryManager();
|
|
3526
|
+
await scanRepoManager.ensureRepoAndScan();
|
|
3527
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
3528
|
+
} catch {
|
|
3529
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
3530
|
+
}
|
|
3531
|
+
const allSkills = getAllSkills();
|
|
3532
|
+
const skillIds = allSkills.map((s) => s.id);
|
|
3533
|
+
if (skillIds.length === 0) {
|
|
3534
|
+
logger.error(t("install_no_skill_selected"));
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
let tool2;
|
|
3538
|
+
if (options.tool) {
|
|
3539
|
+
tool2 = validateTool(options.tool);
|
|
3540
|
+
} else {
|
|
3541
|
+
const detected = await selectToolWithDetection();
|
|
3542
|
+
if (detected === "back" || detected === "cancel") {
|
|
3543
|
+
logger.info(t("init_cancelled"));
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
tool2 = detected;
|
|
3547
|
+
}
|
|
3548
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
3549
|
+
const repoManager = createRepositoryManager();
|
|
3550
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
3551
|
+
spinner.start();
|
|
3552
|
+
const repoPath = await repoManager.ensureRepo();
|
|
3553
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3554
|
+
logger.info(`${t("skill_all_install_progress").replace("{count}", chalk10.bold(String(skillIds.length)))}`);
|
|
3555
|
+
const results = await installSkills(skillIds, tool2, level2, repoPath);
|
|
3556
|
+
let successCount = 0;
|
|
3557
|
+
let failCount = 0;
|
|
3558
|
+
const total = results.length;
|
|
3559
|
+
for (let i = 0; i < results.length; i++) {
|
|
3560
|
+
const result = results[i];
|
|
3561
|
+
const progress = `[${i + 1}/${total}]`;
|
|
3562
|
+
if (result.success) {
|
|
3563
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
3564
|
+
successCount++;
|
|
3565
|
+
} else {
|
|
3566
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
3567
|
+
failCount++;
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
const configRoot = getConfigRoot(tool2, level2);
|
|
3571
|
+
logger.blank();
|
|
3572
|
+
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)))}`);
|
|
3573
|
+
logger.blank();
|
|
3574
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
3575
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
3576
|
+
logger.blank();
|
|
3577
|
+
return;
|
|
3578
|
+
}
|
|
2477
3579
|
if (names.length === 0) {
|
|
2478
|
-
const scanSpinner2 = createSpinner("
|
|
3580
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2479
3581
|
scanSpinner2.start();
|
|
2480
3582
|
try {
|
|
2481
3583
|
const scanRepoManager = createRepositoryManager();
|
|
2482
3584
|
await scanRepoManager.ensureRepoAndScan();
|
|
2483
|
-
scanSpinner2.succeed("
|
|
3585
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2484
3586
|
} catch {
|
|
2485
|
-
scanSpinner2.warn("
|
|
3587
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2486
3588
|
}
|
|
2487
3589
|
let tool2;
|
|
2488
3590
|
if (options.tool) {
|
|
2489
|
-
tool2 = options.tool;
|
|
3591
|
+
tool2 = validateTool(options.tool);
|
|
2490
3592
|
} else {
|
|
2491
3593
|
const detected = await selectToolWithDetection();
|
|
2492
|
-
if (detected === "back")
|
|
3594
|
+
if (detected === "back" || detected === "cancel") {
|
|
3595
|
+
logger.info(t("init_cancelled"));
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
2493
3598
|
tool2 = detected;
|
|
2494
3599
|
}
|
|
3600
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2495
3601
|
let step = 1;
|
|
2496
3602
|
while (true) {
|
|
2497
3603
|
switch (step) {
|
|
2498
3604
|
case 1: {
|
|
2499
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
3605
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2500
3606
|
if (selectedSkills === "back") {
|
|
2501
3607
|
const redetected = await selectToolWithDetection();
|
|
2502
|
-
if (redetected === "back")
|
|
3608
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
3609
|
+
logger.info(t("init_cancelled"));
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
2503
3612
|
tool2 = redetected;
|
|
2504
3613
|
break;
|
|
2505
3614
|
}
|
|
3615
|
+
if (selectedSkills === "cancel") {
|
|
3616
|
+
logger.info(t("init_cancelled"));
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
2506
3619
|
if (selectedSkills.length === 0) {
|
|
2507
|
-
logger.info("
|
|
3620
|
+
logger.info(t("install_no_skill_selected"));
|
|
2508
3621
|
return;
|
|
2509
3622
|
}
|
|
2510
3623
|
names = selectedSkills;
|
|
2511
3624
|
const repoManager = createRepositoryManager();
|
|
2512
|
-
const spinner = createSpinner(t("
|
|
3625
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2513
3626
|
spinner.start();
|
|
2514
3627
|
const repoPath = await repoManager.ensureRepo();
|
|
2515
|
-
spinner.succeed(t("
|
|
2516
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2517
|
-
const level2 = options.level || "project";
|
|
3628
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3629
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(names.length)))}...`);
|
|
2518
3630
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2519
3631
|
let successCount = 0;
|
|
2520
3632
|
let failCount = 0;
|
|
2521
|
-
|
|
3633
|
+
const total = results.length;
|
|
3634
|
+
for (let i = 0; i < results.length; i++) {
|
|
3635
|
+
const result = results[i];
|
|
3636
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2522
3637
|
if (result.success) {
|
|
2523
|
-
logger.success(`${result.skillId}`);
|
|
3638
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2524
3639
|
successCount++;
|
|
2525
3640
|
} else {
|
|
2526
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
3641
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2527
3642
|
failCount++;
|
|
2528
3643
|
}
|
|
2529
3644
|
}
|
|
2530
3645
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2531
3646
|
logger.blank();
|
|
2532
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
3647
|
+
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)))}`);
|
|
2533
3648
|
logger.blank();
|
|
2534
|
-
logger.info(
|
|
2535
|
-
logger.info(
|
|
3649
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
3650
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2536
3651
|
logger.blank();
|
|
2537
3652
|
return;
|
|
2538
3653
|
}
|
|
2539
3654
|
}
|
|
2540
3655
|
}
|
|
2541
3656
|
}
|
|
2542
|
-
const scanSpinner = createSpinner("
|
|
3657
|
+
const scanSpinner = createSpinner(t("loading_skills_list"));
|
|
2543
3658
|
scanSpinner.start();
|
|
2544
3659
|
try {
|
|
2545
3660
|
const scanRepoManager = createRepositoryManager();
|
|
2546
3661
|
await scanRepoManager.ensureRepoAndScan();
|
|
2547
|
-
scanSpinner.succeed("
|
|
3662
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2548
3663
|
} catch {
|
|
2549
|
-
scanSpinner.warn("
|
|
3664
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2550
3665
|
}
|
|
2551
3666
|
const plugins = [];
|
|
2552
3667
|
const skills = [];
|
|
@@ -2563,22 +3678,25 @@ async function installCommand(names, options) {
|
|
|
2563
3678
|
}
|
|
2564
3679
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2565
3680
|
const allPlugins = getAllPlugins();
|
|
2566
|
-
logger.info("
|
|
3681
|
+
logger.info(t("available_plugins") + ":");
|
|
2567
3682
|
for (const p of allPlugins) {
|
|
2568
3683
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2569
3684
|
}
|
|
2570
|
-
logger.info(
|
|
3685
|
+
logger.info(`${t("available_skills")}: ${chalk10.cyan("install-helper install --list")}`);
|
|
2571
3686
|
return;
|
|
2572
3687
|
}
|
|
2573
3688
|
let tool;
|
|
2574
3689
|
if (options.tool) {
|
|
2575
|
-
tool = options.tool;
|
|
3690
|
+
tool = validateTool(options.tool);
|
|
2576
3691
|
} else {
|
|
2577
3692
|
const detected = await selectToolWithDetection();
|
|
2578
|
-
if (detected === "back")
|
|
3693
|
+
if (detected === "back" || detected === "cancel") {
|
|
3694
|
+
logger.info(t("init_cancelled"));
|
|
3695
|
+
return;
|
|
3696
|
+
}
|
|
2579
3697
|
tool = detected;
|
|
2580
3698
|
}
|
|
2581
|
-
const level = options.level
|
|
3699
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2582
3700
|
if (plugins.length > 0) {
|
|
2583
3701
|
const configRoot = getConfigRoot(tool, level);
|
|
2584
3702
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2606,16 +3724,18 @@ async function installCommand(names, options) {
|
|
|
2606
3724
|
}
|
|
2607
3725
|
if (pluginsToInstall.length > 0) {
|
|
2608
3726
|
const repoManager = createRepositoryManager();
|
|
2609
|
-
const spinner = createSpinner(t("
|
|
3727
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2610
3728
|
spinner.start();
|
|
2611
3729
|
const repoPath = await repoManager.ensureRepo();
|
|
2612
|
-
spinner.succeed(t("
|
|
3730
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2613
3731
|
const allPlugins = getAllPlugins();
|
|
2614
3732
|
const results = await installPlugins(
|
|
2615
3733
|
pluginsToInstall,
|
|
2616
3734
|
tool,
|
|
2617
3735
|
level,
|
|
2618
|
-
repoPath
|
|
3736
|
+
repoPath,
|
|
3737
|
+
void 0,
|
|
3738
|
+
options.yes
|
|
2619
3739
|
);
|
|
2620
3740
|
const summary = results.map((result) => {
|
|
2621
3741
|
const plugin = allPlugins.find((p) => p.id === result.pluginId);
|
|
@@ -2633,32 +3753,35 @@ async function installCommand(names, options) {
|
|
|
2633
3753
|
}
|
|
2634
3754
|
if (skills.length > 0) {
|
|
2635
3755
|
const repoManager = createRepositoryManager();
|
|
2636
|
-
const spinner = createSpinner(t("
|
|
3756
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2637
3757
|
spinner.start();
|
|
2638
3758
|
const repoPath = await repoManager.ensureRepo();
|
|
2639
|
-
spinner.succeed(t("
|
|
2640
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
3759
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3760
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(skills.length)))}...`);
|
|
2641
3761
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2642
3762
|
let successCount = 0;
|
|
2643
3763
|
let failCount = 0;
|
|
2644
|
-
|
|
3764
|
+
const total = results.length;
|
|
3765
|
+
for (let i = 0; i < results.length; i++) {
|
|
3766
|
+
const result = results[i];
|
|
3767
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2645
3768
|
if (result.success) {
|
|
2646
|
-
logger.success(`${result.skillId}`);
|
|
3769
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2647
3770
|
successCount++;
|
|
2648
3771
|
} else {
|
|
2649
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
3772
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2650
3773
|
failCount++;
|
|
2651
3774
|
}
|
|
2652
3775
|
}
|
|
2653
3776
|
logger.blank();
|
|
2654
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
3777
|
+
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)))}`);
|
|
2655
3778
|
logger.blank();
|
|
2656
|
-
logger.info(
|
|
2657
|
-
logger.info(
|
|
3779
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
3780
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
2658
3781
|
logger.blank();
|
|
2659
3782
|
}
|
|
2660
3783
|
}
|
|
2661
|
-
async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
3784
|
+
async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes) {
|
|
2662
3785
|
const results = [];
|
|
2663
3786
|
const total = pluginIds.length;
|
|
2664
3787
|
for (let i = 0; i < pluginIds.length; i++) {
|
|
@@ -2670,7 +3793,9 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2670
3793
|
pluginId,
|
|
2671
3794
|
tool,
|
|
2672
3795
|
level,
|
|
2673
|
-
repoPath
|
|
3796
|
+
repoPath,
|
|
3797
|
+
installPath,
|
|
3798
|
+
yes
|
|
2674
3799
|
});
|
|
2675
3800
|
if (result.success) {
|
|
2676
3801
|
spinner.succeed(
|
|
@@ -2686,11 +3811,12 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2686
3811
|
}
|
|
2687
3812
|
|
|
2688
3813
|
// src/commands/uninstall.ts
|
|
2689
|
-
import { existsSync as
|
|
2690
|
-
import
|
|
3814
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync7, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
|
|
3815
|
+
import { resolve, basename as basename2 } from "path";
|
|
3816
|
+
import chalk11 from "chalk";
|
|
2691
3817
|
async function uninstallCommand(names, options) {
|
|
2692
|
-
const tool = options.tool
|
|
2693
|
-
const level = options.level
|
|
3818
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
3819
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2694
3820
|
const plugins = [];
|
|
2695
3821
|
const skills = [];
|
|
2696
3822
|
for (const name of names) {
|
|
@@ -2704,40 +3830,50 @@ async function uninstallCommand(names, options) {
|
|
|
2704
3830
|
skills.push(skill.id);
|
|
2705
3831
|
continue;
|
|
2706
3832
|
}
|
|
2707
|
-
logger.error(
|
|
3833
|
+
logger.error(`${t("uninstall_not_found")}: ${name}`);
|
|
2708
3834
|
}
|
|
2709
3835
|
for (const pluginId of plugins) {
|
|
2710
3836
|
const plugin = findPlugin(pluginId);
|
|
2711
3837
|
if (!plugin) continue;
|
|
2712
3838
|
const record = readRecord(plugin.id);
|
|
2713
3839
|
if (!record) {
|
|
2714
|
-
logger.warn(`${plugin.displayName}
|
|
2715
|
-
logger.info(
|
|
3840
|
+
logger.warn(`${plugin.displayName} ${t("uninstall_no_record")}`);
|
|
3841
|
+
logger.info(`${t("uninstall_no_record_hint")}`);
|
|
2716
3842
|
continue;
|
|
2717
3843
|
}
|
|
2718
|
-
logger.info(
|
|
3844
|
+
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
3845
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3846
|
+
const allowedBases = [resolve(configRoot), resolve(record.installPath)];
|
|
3847
|
+
const isSafePath = (p) => {
|
|
3848
|
+
const resolved = resolve(p);
|
|
3849
|
+
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + "/"));
|
|
3850
|
+
};
|
|
2719
3851
|
let removedFiles = 0;
|
|
2720
3852
|
let removedDirs = 0;
|
|
2721
3853
|
for (const filePath of record.files) {
|
|
3854
|
+
if (!isSafePath(filePath)) continue;
|
|
2722
3855
|
try {
|
|
2723
|
-
if (
|
|
2724
|
-
|
|
2725
|
-
const name = filePath
|
|
2726
|
-
logger.step(`
|
|
3856
|
+
if (existsSync17(filePath) || isSymlink(filePath)) {
|
|
3857
|
+
unlinkSync7(filePath);
|
|
3858
|
+
const name = basename2(filePath);
|
|
3859
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2727
3860
|
removedFiles++;
|
|
2728
3861
|
}
|
|
2729
3862
|
} catch {
|
|
2730
3863
|
}
|
|
2731
3864
|
}
|
|
2732
|
-
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
3865
|
+
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
2733
3866
|
for (const dirPath of sortedDirs) {
|
|
2734
3867
|
try {
|
|
2735
|
-
if (
|
|
2736
|
-
|
|
3868
|
+
if (resolve(dirPath) === resolve(configRoot)) {
|
|
3869
|
+
continue;
|
|
3870
|
+
}
|
|
3871
|
+
if (existsSync17(dirPath)) {
|
|
3872
|
+
const entries = readdirSync6(dirPath);
|
|
2737
3873
|
if (entries.length === 0) {
|
|
2738
3874
|
rmdirSync2(dirPath);
|
|
2739
|
-
const name = dirPath
|
|
2740
|
-
logger.step(`
|
|
3875
|
+
const name = basename2(dirPath);
|
|
3876
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
2741
3877
|
removedDirs++;
|
|
2742
3878
|
}
|
|
2743
3879
|
}
|
|
@@ -2746,15 +3882,32 @@ async function uninstallCommand(names, options) {
|
|
|
2746
3882
|
}
|
|
2747
3883
|
deleteRecord(plugin.id);
|
|
2748
3884
|
removeInstalledPlugin(plugin.id);
|
|
3885
|
+
const backups = findBackups(configRoot);
|
|
3886
|
+
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
3887
|
+
if (otherBackups.length > 0) {
|
|
3888
|
+
const choice = await showRestorePrompt(otherBackups);
|
|
3889
|
+
if (choice !== "none") {
|
|
3890
|
+
const restored = restoreBackup(choice, configRoot, record.tool);
|
|
3891
|
+
if (restored) {
|
|
3892
|
+
const backupInfo = otherBackups.find((b) => b.filePath === choice);
|
|
3893
|
+
logger.success(`${t("backup_restore_success")}: ${backupInfo?.pluginName || ""}`);
|
|
3894
|
+
deleteBackup(choice);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
const currentBackups = backups.filter((b) => b.pluginId === pluginId);
|
|
3899
|
+
for (const backup of currentBackups) {
|
|
3900
|
+
deleteBackup(backup.filePath);
|
|
3901
|
+
}
|
|
2749
3902
|
const totalRemoved = removedFiles + removedDirs;
|
|
2750
3903
|
if (totalRemoved > 0) {
|
|
2751
|
-
logger.success(
|
|
3904
|
+
logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
|
|
2752
3905
|
} else {
|
|
2753
|
-
logger.warn(`${plugin.displayName}
|
|
3906
|
+
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
2754
3907
|
}
|
|
2755
3908
|
}
|
|
2756
3909
|
if (skills.length > 0) {
|
|
2757
|
-
logger.info(
|
|
3910
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
2758
3911
|
const results = await uninstallSkills(skills, tool, level);
|
|
2759
3912
|
let successCount = 0;
|
|
2760
3913
|
let failCount = 0;
|
|
@@ -2766,30 +3919,26 @@ async function uninstallCommand(names, options) {
|
|
|
2766
3919
|
}
|
|
2767
3920
|
}
|
|
2768
3921
|
logger.blank();
|
|
2769
|
-
logger.success(`${t("skill_uninstall_done")}: ${
|
|
3922
|
+
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)))}`);
|
|
2770
3923
|
logger.blank();
|
|
2771
3924
|
}
|
|
2772
3925
|
}
|
|
2773
|
-
function isSymlink3(path) {
|
|
2774
|
-
try {
|
|
2775
|
-
return lstatSync4(path).isSymbolicLink();
|
|
2776
|
-
} catch {
|
|
2777
|
-
return false;
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
3926
|
|
|
2781
3927
|
// src/commands/update.ts
|
|
2782
|
-
import
|
|
3928
|
+
import chalk12 from "chalk";
|
|
2783
3929
|
async function updateCommand(pluginNames, options) {
|
|
2784
3930
|
let tool;
|
|
2785
3931
|
if (options.tool) {
|
|
2786
|
-
tool = options.tool;
|
|
3932
|
+
tool = validateTool(options.tool);
|
|
2787
3933
|
} else {
|
|
2788
3934
|
const detected = await selectToolWithDetection();
|
|
2789
|
-
if (detected === "back")
|
|
3935
|
+
if (detected === "back" || detected === "cancel") {
|
|
3936
|
+
logger.info(t("init_cancelled"));
|
|
3937
|
+
return;
|
|
3938
|
+
}
|
|
2790
3939
|
tool = detected;
|
|
2791
3940
|
}
|
|
2792
|
-
const level = options.level
|
|
3941
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2793
3942
|
let pluginsToUpdate = [];
|
|
2794
3943
|
if (pluginNames.length === 0) {
|
|
2795
3944
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -2797,7 +3946,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2797
3946
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
2798
3947
|
if (pluginsToUpdate.length === 0) {
|
|
2799
3948
|
logger.info(t("update_no_plugins"));
|
|
2800
|
-
logger.info(
|
|
3949
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk12.cyan("install-helper install <plugin>")));
|
|
2801
3950
|
return;
|
|
2802
3951
|
}
|
|
2803
3952
|
} else {
|
|
@@ -2813,9 +3962,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2813
3962
|
const repoManager = createRepositoryManager();
|
|
2814
3963
|
const updateSpinner = createSpinner(t("update_updating") + "...");
|
|
2815
3964
|
updateSpinner.start();
|
|
2816
|
-
await repoManager.
|
|
2817
|
-
const repoPath = repoManager.getRepoPath();
|
|
2818
|
-
await repoManager.ensureRepoAndScan();
|
|
3965
|
+
const repoPath = await repoManager.ensureRepoAndScan();
|
|
2819
3966
|
updateSpinner.succeed(t("install_repo_ready"));
|
|
2820
3967
|
const allPlugins = getAllPlugins();
|
|
2821
3968
|
const results = [];
|
|
@@ -2859,42 +4006,43 @@ async function updateCommand(pluginNames, options) {
|
|
|
2859
4006
|
}
|
|
2860
4007
|
|
|
2861
4008
|
// src/commands/info.ts
|
|
2862
|
-
import
|
|
2863
|
-
import { existsSync as
|
|
2864
|
-
import { join as
|
|
4009
|
+
import chalk13 from "chalk";
|
|
4010
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
|
|
4011
|
+
import { join as join16 } from "path";
|
|
2865
4012
|
async function infoCommand(pluginName) {
|
|
2866
4013
|
const plugin = findPlugin(pluginName);
|
|
2867
4014
|
if (!plugin) {
|
|
2868
|
-
console.log(
|
|
4015
|
+
console.log(chalk13.red(` ${t("info_not_found")}: ${pluginName}`));
|
|
2869
4016
|
return;
|
|
2870
4017
|
}
|
|
2871
4018
|
const installed = scanInstalled();
|
|
2872
|
-
const
|
|
2873
|
-
const
|
|
4019
|
+
const inst = installed.find((p) => p.id === plugin.id);
|
|
4020
|
+
const isInstalled = !!inst;
|
|
4021
|
+
const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
|
|
2874
4022
|
console.log();
|
|
2875
|
-
console.log(
|
|
2876
|
-
console.log(
|
|
4023
|
+
console.log(chalk13.bold(` ${plugin.displayName}`));
|
|
4024
|
+
console.log(chalk13.dim(" " + "\u2500".repeat(40)));
|
|
2877
4025
|
console.log();
|
|
2878
|
-
console.log(` ${
|
|
2879
|
-
console.log(` ${
|
|
2880
|
-
console.log(` ${
|
|
2881
|
-
console.log(` ${
|
|
2882
|
-
console.log(` ${
|
|
2883
|
-
console.log(` ${
|
|
4026
|
+
console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
4027
|
+
console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
|
|
4028
|
+
console.log(` ${chalk13.dim(t("info_status") + ":")} ${statusText}`);
|
|
4029
|
+
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${inst ? inst.skillsCount : plugin.skills}`);
|
|
4030
|
+
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${inst ? inst.agentsCount : plugin.agents}`);
|
|
4031
|
+
console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
2884
4032
|
console.log();
|
|
2885
4033
|
try {
|
|
2886
4034
|
const repoManager = createRepositoryManager();
|
|
2887
4035
|
const repoPath = await repoManager.ensureRepo();
|
|
2888
|
-
const quickstartPath =
|
|
2889
|
-
if (
|
|
2890
|
-
console.log(` ${
|
|
4036
|
+
const quickstartPath = join16(repoPath, plugin.dir, "quickstart.md");
|
|
4037
|
+
if (existsSync18(quickstartPath)) {
|
|
4038
|
+
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
2891
4039
|
}
|
|
2892
|
-
const agentsDir =
|
|
2893
|
-
if (
|
|
2894
|
-
const agentFiles =
|
|
4040
|
+
const agentsDir = join16(repoPath, plugin.dir, "agents");
|
|
4041
|
+
if (existsSync18(agentsDir)) {
|
|
4042
|
+
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
2895
4043
|
if (agentFiles.length > 0) {
|
|
2896
4044
|
console.log();
|
|
2897
|
-
console.log(` ${
|
|
4045
|
+
console.log(` ${chalk13.bold("Agents:")}`);
|
|
2898
4046
|
for (const agent of agentFiles) {
|
|
2899
4047
|
console.log(` \u2022 ${agent.replace(".md", "")}`);
|
|
2900
4048
|
}
|
|
@@ -2906,49 +4054,74 @@ async function infoCommand(pluginName) {
|
|
|
2906
4054
|
}
|
|
2907
4055
|
|
|
2908
4056
|
// src/commands/lang.ts
|
|
2909
|
-
import
|
|
4057
|
+
import chalk14 from "chalk";
|
|
2910
4058
|
async function langCommand(action, value) {
|
|
2911
4059
|
const config = readConfig();
|
|
2912
4060
|
if (!action || action === "show") {
|
|
2913
4061
|
console.log();
|
|
2914
|
-
console.log(
|
|
2915
|
-
console.log(
|
|
4062
|
+
console.log(chalk14.bold(` ${t("lang_title")}`));
|
|
4063
|
+
console.log(chalk14.dim(" " + "\u2500".repeat(40)));
|
|
2916
4064
|
console.log();
|
|
2917
|
-
console.log(` ${
|
|
4065
|
+
console.log(` ${chalk14.dim(t("lang_current") + ":")} ${config.language}`);
|
|
2918
4066
|
console.log();
|
|
2919
|
-
console.log(
|
|
2920
|
-
console.log(
|
|
4067
|
+
console.log(chalk14.dim(` ${t("lang_supported")}`));
|
|
4068
|
+
console.log(chalk14.dim(` ${t("lang_usage")}`));
|
|
2921
4069
|
console.log();
|
|
2922
4070
|
return;
|
|
2923
4071
|
}
|
|
2924
4072
|
if (action === "set") {
|
|
2925
4073
|
if (!value) {
|
|
2926
|
-
console.log(
|
|
4074
|
+
console.log(chalk14.red(` ${t("lang_specify")}`));
|
|
2927
4075
|
return;
|
|
2928
4076
|
}
|
|
2929
4077
|
if (value !== "zh_CN" && value !== "en_US") {
|
|
2930
|
-
console.log(
|
|
4078
|
+
console.log(chalk14.red(` ${t("lang_invalid")}`));
|
|
2931
4079
|
return;
|
|
2932
4080
|
}
|
|
2933
4081
|
updateConfig({ language: value });
|
|
2934
4082
|
setLanguage(value);
|
|
2935
4083
|
console.log();
|
|
2936
|
-
console.log(
|
|
4084
|
+
console.log(chalk14.green(` \u2713 ${t("lang_set")} ${value}`));
|
|
2937
4085
|
console.log();
|
|
2938
4086
|
return;
|
|
2939
4087
|
}
|
|
2940
|
-
console.log(
|
|
2941
|
-
console.log(
|
|
4088
|
+
console.log(chalk14.red(` ${t("lang_unknown_action")}: ${action}`));
|
|
4089
|
+
console.log(chalk14.dim(` ${t("lang_supported_actions")}`));
|
|
2942
4090
|
}
|
|
2943
4091
|
|
|
2944
4092
|
// src/cli.ts
|
|
4093
|
+
import { readFileSync as readFileSync10, existsSync as existsSync19 } from "fs";
|
|
4094
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
4095
|
+
import { dirname as dirname8, join as join17 } from "path";
|
|
4096
|
+
function readVersion() {
|
|
4097
|
+
try {
|
|
4098
|
+
const __filename5 = fileURLToPath5(import.meta.url);
|
|
4099
|
+
const __dirname5 = dirname8(__filename5);
|
|
4100
|
+
const candidates = [
|
|
4101
|
+
join17(__dirname5, "..", "package.json"),
|
|
4102
|
+
join17(__dirname5, "package.json"),
|
|
4103
|
+
join17(process.cwd(), "package.json")
|
|
4104
|
+
];
|
|
4105
|
+
for (const pkgPath of candidates) {
|
|
4106
|
+
if (existsSync19(pkgPath)) {
|
|
4107
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
4108
|
+
if (pkg.name === "@cannbot-ai/install-helper" && pkg.version) {
|
|
4109
|
+
return pkg.version;
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
return "0.0.0";
|
|
4114
|
+
} catch {
|
|
4115
|
+
return "0.0.0";
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
2945
4118
|
function createCLI() {
|
|
2946
4119
|
const config = readConfig();
|
|
2947
4120
|
if (config.language) {
|
|
2948
4121
|
setLanguage(config.language);
|
|
2949
4122
|
}
|
|
2950
4123
|
const program2 = new Command();
|
|
2951
|
-
program2.name("install-helper").description("Install Helper - Interactive installer for CANN operator development skills").version(
|
|
4124
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
|
|
2952
4125
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
2953
4126
|
await initCommand();
|
|
2954
4127
|
});
|
|
@@ -2961,7 +4134,7 @@ function createCLI() {
|
|
|
2961
4134
|
program2.command("status").description("Show installed plugins").action(async () => {
|
|
2962
4135
|
await statusCommand();
|
|
2963
4136
|
});
|
|
2964
|
-
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) => {
|
|
4137
|
+
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) => {
|
|
2965
4138
|
await installCommand(names, options);
|
|
2966
4139
|
});
|
|
2967
4140
|
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) => {
|
|
@@ -2985,7 +4158,7 @@ function createCLI() {
|
|
|
2985
4158
|
// src/index.ts
|
|
2986
4159
|
process.on("uncaughtException", (error) => {
|
|
2987
4160
|
if (error.name === "ExitPromptError") {
|
|
2988
|
-
console.log("\n
|
|
4161
|
+
console.log("\n" + t("init_cancelled"));
|
|
2989
4162
|
process.exit(0);
|
|
2990
4163
|
}
|
|
2991
4164
|
throw error;
|