@cannbot-ai/install-helper 0.0.2-beta.5 → 0.0.2-beta.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -35
- package/bin/install-helper.js +59 -0
- package/bin/package.json +3 -0
- package/dist/config/repository.yaml +28 -0
- package/dist/index.js +2468 -916
- package/dist/index.js.map +1 -1
- package/dist/install.ps1 +168 -0
- 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 +18 -0
- package/dist/plugins.d/model-infer-optimize.yml +19 -0
- package/dist/plugins.d/ops-code-reviewer.yml +14 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +18 -0
- package/dist/plugins.d/ops-direct-invoke.yml +20 -0
- package/dist/plugins.d/ops-registry-invoke.yml +24 -0
- package/dist/plugins.d/pypto-op-orchestrator.yml +18 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +20 -0
- package/dist/plugins.d/torch-compile.yml +14 -0
- package/dist/plugins.d/triton-op-generator.yml +14 -0
- package/package.json +17 -6
package/dist/index.js
CHANGED
|
@@ -4,116 +4,1135 @@
|
|
|
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",
|
|
587
|
+
version: "1.2.2",
|
|
588
|
+
configFile: "AGENTS.md",
|
|
589
|
+
configRootConfigLink: false,
|
|
590
|
+
installSkills: [
|
|
591
|
+
{
|
|
592
|
+
dir: "ops",
|
|
593
|
+
skills: [
|
|
594
|
+
"ascendc-tiling-design",
|
|
595
|
+
"npu-arch",
|
|
596
|
+
"ascendc-api-best-practices",
|
|
597
|
+
"ascendc-regbase-best-practice",
|
|
598
|
+
"ascendc-blaze-best-practice",
|
|
599
|
+
"ops-precision-standard",
|
|
600
|
+
"ascendc-docs-search",
|
|
601
|
+
"ascendc-env-check",
|
|
602
|
+
"ascendc-precision-debug",
|
|
603
|
+
"ops-profiling",
|
|
604
|
+
"ascendc-direct-invoke-template",
|
|
605
|
+
"torch-ascendc-op-extension",
|
|
606
|
+
"ascendc-runtime-debug",
|
|
607
|
+
"ascendc-crash-debug",
|
|
608
|
+
"ascendc-code-review"
|
|
609
|
+
]
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
dir: "infra",
|
|
613
|
+
skills: [
|
|
614
|
+
"gitcode-toolkit",
|
|
615
|
+
"gitcode-pr-handler",
|
|
616
|
+
"gitcode-issue-gen",
|
|
617
|
+
"gitcode-issue-handler"
|
|
618
|
+
]
|
|
619
|
+
}
|
|
620
|
+
],
|
|
621
|
+
installAgents: [
|
|
622
|
+
"ascendc-kernel-architect",
|
|
623
|
+
"ascendc-kernel-design-reviewer",
|
|
624
|
+
"ascendc-kernel-developer",
|
|
625
|
+
"ascendc-kernel-reviewer"
|
|
626
|
+
],
|
|
627
|
+
externalRepos: [
|
|
628
|
+
{
|
|
629
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
630
|
+
dir: "plugins-official/ops-direct-invoke/asc-devkit",
|
|
631
|
+
configRootLink: false
|
|
632
|
+
}
|
|
633
|
+
]
|
|
25
634
|
},
|
|
26
635
|
{
|
|
27
636
|
id: "ops-direct-invoke-flash",
|
|
28
637
|
dir: "plugins-official/ops-direct-invoke-flash",
|
|
29
|
-
displayName: "AscendC Kernel \
|
|
638
|
+
displayName: "AscendC Kernel \u76F4\u8C03\uFF08\u5FEB\u901F\u7248\uFF09",
|
|
30
639
|
script: "init.sh",
|
|
31
|
-
aliases: [
|
|
640
|
+
aliases: [
|
|
641
|
+
"flash",
|
|
642
|
+
"kernel-flash"
|
|
643
|
+
],
|
|
32
644
|
skills: 1,
|
|
33
645
|
agents: 1,
|
|
34
|
-
description: "Ascend
|
|
646
|
+
description: "\u4ECE\u6570\u5B66\u516C\u5F0F\u6216\u4EE3\u7801\u7247\u6BB5\u5FEB\u901F\u6784\u5EFA\u5E76\u9A8C\u8BC1 Ascend NPU \u6838\u51FD\u6570",
|
|
647
|
+
version: "1.2.2",
|
|
648
|
+
configFile: "AGENTS.md",
|
|
649
|
+
configRootConfigLink: true,
|
|
650
|
+
installSkills: [
|
|
651
|
+
{
|
|
652
|
+
dir: "plugins-official/ops-direct-invoke-flash/skills",
|
|
653
|
+
skills: [
|
|
654
|
+
"ops-direct-invoke-flash"
|
|
655
|
+
]
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
dir: "ops",
|
|
659
|
+
skills: [
|
|
660
|
+
"ascendc-st-design",
|
|
661
|
+
"ascendc-whitebox-design"
|
|
662
|
+
]
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
dir: "infra",
|
|
666
|
+
skills: [
|
|
667
|
+
"gitcode-toolkit",
|
|
668
|
+
"gitcode-pr-handler",
|
|
669
|
+
"gitcode-issue-gen",
|
|
670
|
+
"gitcode-issue-handler"
|
|
671
|
+
]
|
|
672
|
+
}
|
|
673
|
+
],
|
|
674
|
+
installAgents: [
|
|
675
|
+
"ops-direct-invoke-flash-reviewer"
|
|
676
|
+
]
|
|
35
677
|
},
|
|
36
678
|
{
|
|
37
679
|
id: "ops-registry-invoke",
|
|
38
680
|
dir: "plugins-official/ops-registry-invoke",
|
|
39
681
|
displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
|
|
40
682
|
script: "init.sh",
|
|
41
|
-
aliases: [
|
|
683
|
+
aliases: [
|
|
684
|
+
"ops-registry",
|
|
685
|
+
"ascendc-registry",
|
|
686
|
+
"registry"
|
|
687
|
+
],
|
|
42
688
|
skills: 12,
|
|
43
689
|
agents: 4,
|
|
44
|
-
description: "
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
690
|
+
description: "\u751F\u4EA7\u7EA7 ACLNN \u6CE8\u518C\u7B97\u5B50\u5168\u6D41\u7A0B\uFF1A\u4EE3\u7801\u2192UT/ST\u2192\u4E0A\u5E93",
|
|
691
|
+
version: "1.2.2",
|
|
692
|
+
configFile: "AGENTS.md",
|
|
693
|
+
configRootConfigLink: false,
|
|
694
|
+
installSkills: [
|
|
695
|
+
{
|
|
696
|
+
dir: "ops",
|
|
697
|
+
skills: [
|
|
698
|
+
"ascendc-blaze-best-practice",
|
|
699
|
+
"ascendc-api-best-practices",
|
|
700
|
+
"ascendc-code-review",
|
|
701
|
+
"ascendc-crash-debug",
|
|
702
|
+
"ascendc-direct-invoke-template",
|
|
703
|
+
"ascendc-docs-gen",
|
|
704
|
+
"ascendc-docs-search",
|
|
705
|
+
"ascendc-env-check",
|
|
706
|
+
"npu-arch",
|
|
707
|
+
"ascendc-performance-best-practices",
|
|
708
|
+
"ascendc-precision-debug",
|
|
709
|
+
"ascendc-regbase-best-practice",
|
|
710
|
+
"ascendc-registry-invoke-template",
|
|
711
|
+
"ascendc-runtime-debug",
|
|
712
|
+
"ascendc-st-design",
|
|
713
|
+
"ascendc-tiling-design",
|
|
714
|
+
"ascendc-ut-develop",
|
|
715
|
+
"ascendc-whitebox-design",
|
|
716
|
+
"ops-precision-standard",
|
|
717
|
+
"ops-profiling",
|
|
718
|
+
"ops-spec-gen"
|
|
719
|
+
]
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
dir: "infra",
|
|
723
|
+
skills: [
|
|
724
|
+
"gitcode-toolkit",
|
|
725
|
+
"gitcode-pr-handler",
|
|
726
|
+
"gitcode-issue-gen",
|
|
727
|
+
"gitcode-issue-handler"
|
|
728
|
+
]
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
dir: "plugins-official/ops-registry-invoke",
|
|
732
|
+
skills: [
|
|
733
|
+
{
|
|
734
|
+
name: "workflow",
|
|
735
|
+
as: "ops-registry-invoke-workflow"
|
|
736
|
+
}
|
|
737
|
+
]
|
|
738
|
+
}
|
|
739
|
+
],
|
|
740
|
+
installAgents: [
|
|
741
|
+
"ascendc-ops-architect",
|
|
742
|
+
"ascendc-ops-developer",
|
|
743
|
+
"ascendc-ops-tester"
|
|
744
|
+
],
|
|
745
|
+
externalRepos: [
|
|
746
|
+
{
|
|
747
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
748
|
+
dir: "plugins-official/ops-registry-invoke/asc-devkit",
|
|
749
|
+
configRootLink: false
|
|
750
|
+
}
|
|
751
|
+
]
|
|
65
752
|
},
|
|
66
753
|
{
|
|
67
|
-
id: "
|
|
68
|
-
dir: "plugins-official/
|
|
69
|
-
displayName: "
|
|
754
|
+
id: "catlass-op-generator",
|
|
755
|
+
dir: "plugins-official/catlass-op-generator",
|
|
756
|
+
displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
|
|
70
757
|
script: "init.sh",
|
|
71
|
-
aliases: [
|
|
72
|
-
|
|
758
|
+
aliases: [
|
|
759
|
+
"catlass"
|
|
760
|
+
],
|
|
761
|
+
skills: 10,
|
|
73
762
|
agents: 3,
|
|
74
|
-
description: "
|
|
763
|
+
description: "Catlass \u6A21\u677F\u62FC\u88C5 GEMM/Matmul/Epilogue \u7B97\u5B50\u5F00\u53D1",
|
|
764
|
+
version: "1.2.2",
|
|
765
|
+
configFile: "AGENTS.md",
|
|
766
|
+
configRootConfigLink: true,
|
|
767
|
+
installSkills: [
|
|
768
|
+
{
|
|
769
|
+
dir: "ops",
|
|
770
|
+
skills: [
|
|
771
|
+
"catlass-op-design",
|
|
772
|
+
"catlass-op-develop",
|
|
773
|
+
"catlass-op-perf-tune",
|
|
774
|
+
"ascendc-tiling-design",
|
|
775
|
+
"npu-arch",
|
|
776
|
+
"ascendc-api-best-practices",
|
|
777
|
+
"ops-precision-standard",
|
|
778
|
+
"ascendc-docs-search",
|
|
779
|
+
"ascendc-env-check",
|
|
780
|
+
"ascendc-precision-debug",
|
|
781
|
+
"ops-profiling",
|
|
782
|
+
"ascendc-runtime-debug",
|
|
783
|
+
"ascendc-code-review",
|
|
784
|
+
"torch-ascendc-op-extension"
|
|
785
|
+
]
|
|
786
|
+
}
|
|
787
|
+
],
|
|
788
|
+
installAgents: [
|
|
789
|
+
"catlass-op-architect",
|
|
790
|
+
"catlass-op-generator",
|
|
791
|
+
"catlass-op-reviewer"
|
|
792
|
+
],
|
|
793
|
+
externalRepos: [
|
|
794
|
+
{
|
|
795
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
796
|
+
dir: "plugins-official/catlass-op-generator/asc-devkit",
|
|
797
|
+
configRootLink: false
|
|
798
|
+
}
|
|
799
|
+
]
|
|
75
800
|
},
|
|
76
801
|
{
|
|
77
802
|
id: "model-infer-optimize",
|
|
78
803
|
dir: "plugins-official/model-infer-optimize",
|
|
79
804
|
displayName: "NPU \u63A8\u7406\u4F18\u5316",
|
|
80
805
|
script: "init.sh",
|
|
81
|
-
aliases: [
|
|
806
|
+
aliases: [
|
|
807
|
+
"model-infer",
|
|
808
|
+
"infer",
|
|
809
|
+
"inference"
|
|
810
|
+
],
|
|
82
811
|
skills: 11,
|
|
83
812
|
agents: 3,
|
|
84
|
-
description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316"
|
|
813
|
+
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",
|
|
814
|
+
version: "1.2.2",
|
|
815
|
+
configFile: "AGENTS.md",
|
|
816
|
+
configRootConfigLink: true,
|
|
817
|
+
installSkills: [
|
|
818
|
+
{
|
|
819
|
+
dir: "model",
|
|
820
|
+
skills: [
|
|
821
|
+
"model-infer-migrator",
|
|
822
|
+
"model-infer-parallel-analysis",
|
|
823
|
+
"model-infer-parallel-impl",
|
|
824
|
+
"model-infer-kvcache",
|
|
825
|
+
"model-infer-fusion",
|
|
826
|
+
"model-infer-quantization",
|
|
827
|
+
"model-infer-graph-mode",
|
|
828
|
+
"model-infer-precision-debug",
|
|
829
|
+
"model-infer-runtime-debug",
|
|
830
|
+
"model-infer-multi-stream",
|
|
831
|
+
"model-infer-prefetch",
|
|
832
|
+
"model-infer-superkernel"
|
|
833
|
+
]
|
|
834
|
+
}
|
|
835
|
+
],
|
|
836
|
+
installAgents: [
|
|
837
|
+
"model-infer-analyzer",
|
|
838
|
+
"model-infer-implementer",
|
|
839
|
+
"model-infer-reviewer"
|
|
840
|
+
],
|
|
841
|
+
externalRepos: [
|
|
842
|
+
{
|
|
843
|
+
url: "https://gitcode.com/cann/cann-recipes-infer.git",
|
|
844
|
+
dir: "plugins-official/model-infer-optimize/cann-recipes-infer",
|
|
845
|
+
depth: 1,
|
|
846
|
+
configRootLink: true
|
|
847
|
+
}
|
|
848
|
+
]
|
|
85
849
|
},
|
|
86
850
|
{
|
|
87
|
-
id: "
|
|
88
|
-
dir: "plugins-official/
|
|
89
|
-
displayName: "
|
|
851
|
+
id: "pypto-op-orchestrator",
|
|
852
|
+
dir: "plugins-official/pypto-op-orchestrator",
|
|
853
|
+
displayName: "PyPTO \u7B97\u5B50\u5F00\u53D1",
|
|
90
854
|
script: "init.sh",
|
|
91
|
-
aliases: [
|
|
92
|
-
|
|
855
|
+
aliases: [
|
|
856
|
+
"pypto",
|
|
857
|
+
"pytorch"
|
|
858
|
+
],
|
|
859
|
+
skills: 8,
|
|
93
860
|
agents: 3,
|
|
94
|
-
description: "
|
|
861
|
+
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",
|
|
862
|
+
version: "1.2.2",
|
|
863
|
+
configFile: "AGENTS.md",
|
|
864
|
+
configRootConfigLink: false,
|
|
865
|
+
installSkills: [
|
|
866
|
+
{
|
|
867
|
+
dir: "ops",
|
|
868
|
+
skills: [
|
|
869
|
+
"pypto-intent-understand",
|
|
870
|
+
"pypto-api-explore",
|
|
871
|
+
"pypto-op-design",
|
|
872
|
+
"pypto-op-develop",
|
|
873
|
+
"pypto-precision-debug",
|
|
874
|
+
"pypto-precision-compare",
|
|
875
|
+
"pypto-op-perf-tune",
|
|
876
|
+
"pypto-golden-generate"
|
|
877
|
+
]
|
|
878
|
+
}
|
|
879
|
+
],
|
|
880
|
+
installAgents: [
|
|
881
|
+
"pypto-op-analyst",
|
|
882
|
+
"pypto-op-developer",
|
|
883
|
+
"pypto-op-perf-tuner"
|
|
884
|
+
],
|
|
885
|
+
externalRepos: [
|
|
886
|
+
{
|
|
887
|
+
url: "https://gitcode.com/cann/pypto.git",
|
|
888
|
+
dir: "plugins-official/pypto-op-orchestrator/pypto",
|
|
889
|
+
configRootLink: false
|
|
890
|
+
}
|
|
891
|
+
]
|
|
95
892
|
},
|
|
96
893
|
{
|
|
97
|
-
id: "
|
|
98
|
-
dir: "plugins-official/
|
|
99
|
-
displayName: "\
|
|
894
|
+
id: "tilelang-op-orchestrator",
|
|
895
|
+
dir: "plugins-official/tilelang-op-orchestrator",
|
|
896
|
+
displayName: "TileLang \u7B97\u5B50\u5F00\u53D1",
|
|
100
897
|
script: "init.sh",
|
|
101
|
-
aliases: [
|
|
102
|
-
|
|
898
|
+
aliases: [
|
|
899
|
+
"tilelang"
|
|
900
|
+
],
|
|
901
|
+
skills: 9,
|
|
902
|
+
agents: 3,
|
|
903
|
+
description: "TileLang Python DSL + @tilelang.jit \u7B97\u5B50\u5F00\u53D1\uFF08\u652F\u6301 Developer/Expert \u53CC\u6A21\u5F0F\uFF09",
|
|
904
|
+
version: "1.2.2",
|
|
905
|
+
configFile: "AGENTS.md",
|
|
906
|
+
configRootConfigLink: false,
|
|
907
|
+
installSkills: [
|
|
908
|
+
{
|
|
909
|
+
dir: "ops",
|
|
910
|
+
skills: [
|
|
911
|
+
"tilelang-api-best-practices",
|
|
912
|
+
"tilelang-env-check",
|
|
913
|
+
"tilelang-op-design",
|
|
914
|
+
"tilelang-op-develop",
|
|
915
|
+
"tilelang-op-test-design",
|
|
916
|
+
"tilelang-perf-optimization",
|
|
917
|
+
"tilelang-programming-model-guide",
|
|
918
|
+
"tilelang-review",
|
|
919
|
+
"tilelang-submodule-pull"
|
|
920
|
+
]
|
|
921
|
+
}
|
|
922
|
+
],
|
|
923
|
+
installAgents: [
|
|
924
|
+
"tilelang-op-analyst",
|
|
925
|
+
"tilelang-op-developer",
|
|
926
|
+
"tilelang-op-perf-tuner"
|
|
927
|
+
],
|
|
928
|
+
externalRepos: [
|
|
929
|
+
{
|
|
930
|
+
url: "https://github.com/tile-ai/tilelang-ascend.git",
|
|
931
|
+
dir: "plugins-official/tilelang-op-orchestrator/tilelang-ascend",
|
|
932
|
+
depth: 1,
|
|
933
|
+
recursive: true,
|
|
934
|
+
configRootLink: false
|
|
935
|
+
}
|
|
936
|
+
]
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
id: "triton-op-generator",
|
|
940
|
+
dir: "plugins-official/triton-op-generator",
|
|
941
|
+
displayName: "Triton \u7B97\u5B50\u5F00\u53D1",
|
|
942
|
+
script: "init.sh",
|
|
943
|
+
aliases: [
|
|
944
|
+
"triton"
|
|
945
|
+
],
|
|
946
|
+
skills: 6,
|
|
103
947
|
agents: 1,
|
|
104
|
-
description: "\u4EE3\u7801\
|
|
948
|
+
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",
|
|
949
|
+
version: "1.2.2",
|
|
950
|
+
configFile: "AGENTS.md",
|
|
951
|
+
configRootConfigLink: false,
|
|
952
|
+
installSkills: [
|
|
953
|
+
{
|
|
954
|
+
dir: "ops",
|
|
955
|
+
skills: [
|
|
956
|
+
"triton-task-extractor",
|
|
957
|
+
"triton-op-designer",
|
|
958
|
+
"triton-op-coding",
|
|
959
|
+
"triton-op-verifier",
|
|
960
|
+
"triton-latency-optimizer",
|
|
961
|
+
"npu-arch"
|
|
962
|
+
]
|
|
963
|
+
}
|
|
964
|
+
],
|
|
965
|
+
installAgents: []
|
|
105
966
|
},
|
|
106
967
|
{
|
|
107
968
|
id: "torch-compile",
|
|
108
969
|
dir: "plugins-official/torch-compile",
|
|
109
970
|
displayName: "torch.compile \u56FE\u6A21\u5F0F",
|
|
110
971
|
script: "init.sh",
|
|
111
|
-
aliases: [
|
|
972
|
+
aliases: [
|
|
973
|
+
"torch",
|
|
974
|
+
"compile",
|
|
975
|
+
"graph"
|
|
976
|
+
],
|
|
112
977
|
skills: 6,
|
|
113
978
|
agents: 1,
|
|
114
|
-
description: "
|
|
979
|
+
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",
|
|
980
|
+
version: "1.2.2",
|
|
981
|
+
configFile: "AGENTS.md",
|
|
982
|
+
configRootConfigLink: true,
|
|
983
|
+
installSkills: [
|
|
984
|
+
{
|
|
985
|
+
dir: "graph",
|
|
986
|
+
skills: [
|
|
987
|
+
"torch-npugraph-ex-knowledge",
|
|
988
|
+
"torch-npugraph-ex-template",
|
|
989
|
+
"torch-npugraph-ex-dfx-triage",
|
|
990
|
+
"torch-npugraph-ex-compile-error-diagnosis",
|
|
991
|
+
"torch-npugraph-ex-runtime-error-diagnosis",
|
|
992
|
+
"torch-custom-ops-guide"
|
|
993
|
+
]
|
|
994
|
+
}
|
|
995
|
+
],
|
|
996
|
+
installAgents: [
|
|
997
|
+
"torch-npugraph-ex"
|
|
998
|
+
]
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
id: "ops-code-reviewer",
|
|
1002
|
+
dir: "plugins-official/ops-code-reviewer",
|
|
1003
|
+
displayName: "\u4EE3\u7801\u68C0\u89C6",
|
|
1004
|
+
script: "init.sh",
|
|
1005
|
+
aliases: [
|
|
1006
|
+
"code-review",
|
|
1007
|
+
"reviewer",
|
|
1008
|
+
"review"
|
|
1009
|
+
],
|
|
1010
|
+
skills: 5,
|
|
1011
|
+
agents: 1,
|
|
1012
|
+
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",
|
|
1013
|
+
version: "1.2.2",
|
|
1014
|
+
configFile: "AGENTS.md",
|
|
1015
|
+
configRootConfigLink: false,
|
|
1016
|
+
installSkills: [
|
|
1017
|
+
{
|
|
1018
|
+
dir: "ops",
|
|
1019
|
+
skills: [
|
|
1020
|
+
"ascendc-code-review",
|
|
1021
|
+
"ascendc-docs-search",
|
|
1022
|
+
"ascendc-task-focus",
|
|
1023
|
+
"ascendc-api-best-practices",
|
|
1024
|
+
"npu-arch"
|
|
1025
|
+
]
|
|
1026
|
+
}
|
|
1027
|
+
],
|
|
1028
|
+
installAgents: [
|
|
1029
|
+
"ascendc-code-summarizer",
|
|
1030
|
+
"ascendc-ops-reviewer"
|
|
1031
|
+
]
|
|
115
1032
|
}
|
|
116
1033
|
];
|
|
1034
|
+
|
|
1035
|
+
// src/core/registry.ts
|
|
1036
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
1037
|
+
var __dirname2 = dirname2(__filename2);
|
|
1038
|
+
function findPluginsDir() {
|
|
1039
|
+
const candidates = [
|
|
1040
|
+
join2(__dirname2, "plugins.d"),
|
|
1041
|
+
join2(__dirname2, "..", "plugins.d"),
|
|
1042
|
+
join2(__dirname2, "..", "..", "plugins.d")
|
|
1043
|
+
];
|
|
1044
|
+
for (const dir of candidates) {
|
|
1045
|
+
if (existsSync2(dir)) return dir;
|
|
1046
|
+
}
|
|
1047
|
+
return candidates[0];
|
|
1048
|
+
}
|
|
1049
|
+
function loadPluginsFromYaml() {
|
|
1050
|
+
const pluginsDir = findPluginsDir();
|
|
1051
|
+
const defaultsPath = join2(pluginsDir, "_defaults.yml");
|
|
1052
|
+
let defaults = {};
|
|
1053
|
+
try {
|
|
1054
|
+
defaults = parseYaml(readFileSync2(defaultsPath, "utf-8")) || {};
|
|
1055
|
+
} catch {
|
|
1056
|
+
}
|
|
1057
|
+
const plugins = [];
|
|
1058
|
+
try {
|
|
1059
|
+
for (const file of readdirSync(pluginsDir)) {
|
|
1060
|
+
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
1061
|
+
try {
|
|
1062
|
+
const content = parseYaml(readFileSync2(join2(pluginsDir, file), "utf-8"));
|
|
1063
|
+
if (!content || !content.id) continue;
|
|
1064
|
+
plugins.push({
|
|
1065
|
+
id: content.id,
|
|
1066
|
+
dir: content.dir,
|
|
1067
|
+
displayName: content.displayName || content.id,
|
|
1068
|
+
script: content.script || defaults.script || "init.sh",
|
|
1069
|
+
aliases: content.aliases || [],
|
|
1070
|
+
skills: content.skills ?? defaults.skills ?? 0,
|
|
1071
|
+
agents: content.agents ?? defaults.agents ?? 0,
|
|
1072
|
+
description: content.description || "",
|
|
1073
|
+
version: content.version,
|
|
1074
|
+
configFile: content.configFile,
|
|
1075
|
+
configRootConfigLink: content.configRootConfigLink,
|
|
1076
|
+
installSkills: content.installSkills,
|
|
1077
|
+
installAgents: content.installAgents,
|
|
1078
|
+
externalRepos: content.externalRepos
|
|
1079
|
+
});
|
|
1080
|
+
} catch {
|
|
1081
|
+
logger.warn(t("plugin_yaml_error").replace("{file}", file));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
} catch {
|
|
1085
|
+
}
|
|
1086
|
+
if (plugins.length === 0) {
|
|
1087
|
+
return [...embedded_plugins_default];
|
|
1088
|
+
}
|
|
1089
|
+
plugins.sort(
|
|
1090
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
1091
|
+
);
|
|
1092
|
+
return plugins;
|
|
1093
|
+
}
|
|
1094
|
+
var PLUGIN_REGISTRY = loadPluginsFromYaml();
|
|
1095
|
+
var PLUGIN_DIRS = ["plugins-official", "plugins-community"];
|
|
1096
|
+
function mergeDynamicPlugins(repoPath) {
|
|
1097
|
+
if (!existsSync2(repoPath)) return;
|
|
1098
|
+
const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
|
|
1099
|
+
const dynamic = [];
|
|
1100
|
+
for (const pluginDir of PLUGIN_DIRS) {
|
|
1101
|
+
const parent = join2(repoPath, pluginDir);
|
|
1102
|
+
if (!existsSync2(parent)) continue;
|
|
1103
|
+
let entries = [];
|
|
1104
|
+
try {
|
|
1105
|
+
entries = readdirSync(parent);
|
|
1106
|
+
} catch {
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
for (const entry of entries) {
|
|
1110
|
+
const pluginPath = join2(parent, entry);
|
|
1111
|
+
if (!isDirectory(pluginPath)) continue;
|
|
1112
|
+
if (entry === "install-helper") continue;
|
|
1113
|
+
const initSh = join2(pluginPath, "init.sh");
|
|
1114
|
+
if (!existsSync2(initSh)) continue;
|
|
1115
|
+
const pluginId = entry;
|
|
1116
|
+
if (existingIds.has(pluginId)) continue;
|
|
1117
|
+
dynamic.push({
|
|
1118
|
+
id: pluginId,
|
|
1119
|
+
dir: `${pluginDir}/${entry}`,
|
|
1120
|
+
displayName: entry,
|
|
1121
|
+
script: "init.sh",
|
|
1122
|
+
aliases: [],
|
|
1123
|
+
skills: 0,
|
|
1124
|
+
agents: 0,
|
|
1125
|
+
description: ""
|
|
1126
|
+
});
|
|
1127
|
+
existingIds.add(pluginId);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
if (dynamic.length > 0) {
|
|
1131
|
+
PLUGIN_REGISTRY = [...PLUGIN_REGISTRY, ...dynamic].sort(
|
|
1132
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
117
1136
|
function findPlugin(query) {
|
|
118
1137
|
const normalized = query.toLowerCase().trim();
|
|
119
1138
|
const exactMatch = PLUGIN_REGISTRY.find(
|
|
@@ -137,19 +1156,37 @@ function getPluginById(id) {
|
|
|
137
1156
|
|
|
138
1157
|
// src/core/detector.ts
|
|
139
1158
|
import { execa } from "execa";
|
|
140
|
-
import { existsSync as
|
|
1159
|
+
import { existsSync as existsSync4 } from "fs";
|
|
141
1160
|
import { homedir as homedir2 } from "os";
|
|
142
|
-
import { join as
|
|
1161
|
+
import { join as join4 } from "path";
|
|
143
1162
|
|
|
144
1163
|
// src/utils/paths.ts
|
|
145
1164
|
import { homedir } from "os";
|
|
146
|
-
import { join } from "path";
|
|
147
|
-
import { existsSync } from "fs";
|
|
1165
|
+
import { join as join3 } from "path";
|
|
1166
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1167
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
1168
|
+
var VALID_LEVELS = ["project", "global"];
|
|
1169
|
+
function validateTool(tool) {
|
|
1170
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
1171
|
+
throw new Error(
|
|
1172
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
return tool;
|
|
1176
|
+
}
|
|
1177
|
+
function validateLevel(level) {
|
|
1178
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
1179
|
+
throw new Error(
|
|
1180
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
return level;
|
|
1184
|
+
}
|
|
148
1185
|
function detectTraeVariant() {
|
|
149
1186
|
const home = homedir();
|
|
150
|
-
if (
|
|
151
|
-
if (
|
|
152
|
-
if (
|
|
1187
|
+
if (existsSync3(join3(home, ".trae-cn"))) return "ide";
|
|
1188
|
+
if (existsSync3(join3(home, ".marscode"))) return "plugin";
|
|
1189
|
+
if (existsSync3(join3(home, ".traecli"))) return "cli";
|
|
153
1190
|
return "unknown";
|
|
154
1191
|
}
|
|
155
1192
|
function getConfigRoot(tool, level, base) {
|
|
@@ -157,66 +1194,77 @@ function getConfigRoot(tool, level, base) {
|
|
|
157
1194
|
if (level === "global") {
|
|
158
1195
|
switch (tool) {
|
|
159
1196
|
case "opencode":
|
|
160
|
-
return
|
|
1197
|
+
return join3(home, ".config", "opencode");
|
|
161
1198
|
case "claude":
|
|
162
|
-
return
|
|
1199
|
+
return join3(home, ".claude");
|
|
163
1200
|
case "trae": {
|
|
164
1201
|
const variant = detectTraeVariant();
|
|
165
1202
|
switch (variant) {
|
|
166
1203
|
case "plugin":
|
|
167
|
-
return
|
|
1204
|
+
return join3(home, ".marscode");
|
|
168
1205
|
case "cli":
|
|
169
|
-
return
|
|
1206
|
+
return join3(home, ".traecli");
|
|
170
1207
|
default:
|
|
171
|
-
return
|
|
1208
|
+
return join3(home, ".trae-cn");
|
|
172
1209
|
}
|
|
173
1210
|
}
|
|
174
1211
|
case "cursor":
|
|
175
|
-
return
|
|
1212
|
+
return join3(home, ".cursor");
|
|
176
1213
|
case "copilot":
|
|
177
|
-
return
|
|
1214
|
+
return join3(home, ".copilot");
|
|
1215
|
+
default:
|
|
1216
|
+
throw new Error(
|
|
1217
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1218
|
+
);
|
|
178
1219
|
}
|
|
179
1220
|
}
|
|
180
1221
|
const baseDir = base || process.cwd();
|
|
181
1222
|
switch (tool) {
|
|
182
1223
|
case "opencode":
|
|
183
|
-
return
|
|
1224
|
+
return join3(baseDir, ".opencode");
|
|
184
1225
|
case "claude":
|
|
185
|
-
return
|
|
1226
|
+
return join3(baseDir, ".claude");
|
|
186
1227
|
case "trae": {
|
|
187
1228
|
const variant = detectTraeVariant();
|
|
188
1229
|
switch (variant) {
|
|
189
1230
|
case "plugin":
|
|
190
|
-
return
|
|
1231
|
+
return join3(baseDir, ".marscode");
|
|
191
1232
|
case "cli":
|
|
192
|
-
return
|
|
1233
|
+
return join3(baseDir, ".traecli");
|
|
193
1234
|
default:
|
|
194
|
-
return
|
|
1235
|
+
return join3(baseDir, ".trae");
|
|
195
1236
|
}
|
|
196
1237
|
}
|
|
197
1238
|
case "cursor":
|
|
198
|
-
return
|
|
1239
|
+
return join3(baseDir, ".cursor");
|
|
199
1240
|
case "copilot":
|
|
200
|
-
return
|
|
1241
|
+
return join3(baseDir, ".github");
|
|
1242
|
+
default:
|
|
1243
|
+
throw new Error(
|
|
1244
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1245
|
+
);
|
|
201
1246
|
}
|
|
202
1247
|
}
|
|
203
1248
|
function getConfigFileName(tool) {
|
|
204
1249
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
1250
|
}
|
|
1251
|
+
function getAgentsFileName(tool) {
|
|
1252
|
+
return getConfigFileName(tool);
|
|
1253
|
+
}
|
|
206
1254
|
function getSkillsDir(configRoot) {
|
|
207
|
-
return
|
|
1255
|
+
return join3(configRoot, "skills");
|
|
208
1256
|
}
|
|
209
1257
|
function getAgentsDir(configRoot) {
|
|
210
|
-
return
|
|
1258
|
+
return join3(configRoot, "agents");
|
|
211
1259
|
}
|
|
212
1260
|
function getManifestPath(configRoot) {
|
|
213
|
-
return
|
|
1261
|
+
return join3(configRoot, "cannbot-manifest.json");
|
|
214
1262
|
}
|
|
215
1263
|
function getCannbotConfigDir() {
|
|
216
|
-
return
|
|
1264
|
+
return join3(homedir(), ".cannbot");
|
|
217
1265
|
}
|
|
218
1266
|
function getCannbotRepoPath() {
|
|
219
|
-
return
|
|
1267
|
+
return join3(getCannbotConfigDir(), "repo");
|
|
220
1268
|
}
|
|
221
1269
|
|
|
222
1270
|
// src/core/detector.ts
|
|
@@ -232,8 +1280,9 @@ async function getCommandVersion(cmd, args = ["--version"]) {
|
|
|
232
1280
|
}
|
|
233
1281
|
async function getCommandPath(cmd) {
|
|
234
1282
|
try {
|
|
235
|
-
const
|
|
236
|
-
|
|
1283
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1284
|
+
const result = await execa(whichCmd, [cmd], { timeout: 5e3 });
|
|
1285
|
+
return result.stdout.trim().split("\n")[0];
|
|
237
1286
|
} catch {
|
|
238
1287
|
return void 0;
|
|
239
1288
|
}
|
|
@@ -254,12 +1303,12 @@ async function detectTrae() {
|
|
|
254
1303
|
const home = homedir2();
|
|
255
1304
|
const variant = detectTraeVariant();
|
|
256
1305
|
let path;
|
|
257
|
-
if (variant === "ide" &&
|
|
258
|
-
path =
|
|
259
|
-
} else if (variant === "plugin" &&
|
|
260
|
-
path =
|
|
261
|
-
} else if (variant === "cli" &&
|
|
262
|
-
path =
|
|
1306
|
+
if (variant === "ide" && existsSync4(join4(home, ".trae-cn"))) {
|
|
1307
|
+
path = join4(home, ".trae-cn");
|
|
1308
|
+
} else if (variant === "plugin" && existsSync4(join4(home, ".marscode"))) {
|
|
1309
|
+
path = join4(home, ".marscode");
|
|
1310
|
+
} else if (variant === "cli" && existsSync4(join4(home, ".traecli"))) {
|
|
1311
|
+
path = join4(home, ".traecli");
|
|
263
1312
|
} else if (variant === "unknown") {
|
|
264
1313
|
const cmdPath = await getCommandPath("trae");
|
|
265
1314
|
if (cmdPath) path = cmdPath;
|
|
@@ -275,7 +1324,7 @@ async function detectCursor() {
|
|
|
275
1324
|
}
|
|
276
1325
|
if (process.platform === "darwin") {
|
|
277
1326
|
const appPath = "/Applications/Cursor.app";
|
|
278
|
-
if (
|
|
1327
|
+
if (existsSync4(appPath)) {
|
|
279
1328
|
return { name: "cursor", path: appPath };
|
|
280
1329
|
}
|
|
281
1330
|
}
|
|
@@ -319,17 +1368,17 @@ function getToolDisplayName(tool) {
|
|
|
319
1368
|
return names[tool];
|
|
320
1369
|
}
|
|
321
1370
|
function getAllTools() {
|
|
322
|
-
return
|
|
1371
|
+
return VALID_TOOLS;
|
|
323
1372
|
}
|
|
324
1373
|
|
|
325
1374
|
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as
|
|
327
|
-
import { join as
|
|
1375
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
1376
|
+
import { join as join5 } from "path";
|
|
328
1377
|
function readManifest(configRoot) {
|
|
329
1378
|
const manifestPath = getManifestPath(configRoot);
|
|
330
|
-
if (
|
|
1379
|
+
if (existsSync5(manifestPath)) {
|
|
331
1380
|
try {
|
|
332
|
-
const content =
|
|
1381
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
333
1382
|
return JSON.parse(content);
|
|
334
1383
|
} catch {
|
|
335
1384
|
}
|
|
@@ -340,18 +1389,18 @@ function readAllManifests(configRoot) {
|
|
|
340
1389
|
const manifests = [];
|
|
341
1390
|
const plugins = getAllPlugins();
|
|
342
1391
|
const standardPath = getManifestPath(configRoot);
|
|
343
|
-
if (
|
|
1392
|
+
if (existsSync5(standardPath)) {
|
|
344
1393
|
try {
|
|
345
|
-
const content =
|
|
1394
|
+
const content = readFileSync3(standardPath, "utf-8");
|
|
346
1395
|
manifests.push(JSON.parse(content));
|
|
347
1396
|
} catch {
|
|
348
1397
|
}
|
|
349
1398
|
}
|
|
350
1399
|
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath =
|
|
352
|
-
if (
|
|
1400
|
+
const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
|
|
1401
|
+
if (existsSync5(pluginManifestPath)) {
|
|
353
1402
|
try {
|
|
354
|
-
const content =
|
|
1403
|
+
const content = readFileSync3(pluginManifestPath, "utf-8");
|
|
355
1404
|
manifests.push(JSON.parse(content));
|
|
356
1405
|
} catch {
|
|
357
1406
|
}
|
|
@@ -362,7 +1411,7 @@ function readAllManifests(configRoot) {
|
|
|
362
1411
|
function scanInstalled() {
|
|
363
1412
|
const plugins = getAllPlugins();
|
|
364
1413
|
const installed = [];
|
|
365
|
-
const tools =
|
|
1414
|
+
const tools = VALID_TOOLS;
|
|
366
1415
|
const levels = ["project", "global"];
|
|
367
1416
|
for (const tool of tools) {
|
|
368
1417
|
for (const level of levels) {
|
|
@@ -371,6 +1420,9 @@ function scanInstalled() {
|
|
|
371
1420
|
for (const manifest of manifests) {
|
|
372
1421
|
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
1422
|
if (!plugin) continue;
|
|
1423
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
1424
|
+
continue;
|
|
1425
|
+
}
|
|
374
1426
|
const alreadyAdded = installed.some(
|
|
375
1427
|
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
1428
|
);
|
|
@@ -383,282 +1435,81 @@ function scanInstalled() {
|
|
|
383
1435
|
skillsCount: manifest.installed_skills?.length || 0,
|
|
384
1436
|
agentsCount: manifest.installed_agents?.length || 0,
|
|
385
1437
|
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();
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
function getDisplayWidth(str) {
|
|
607
|
-
let width = 0;
|
|
608
|
-
for (const ch of str) {
|
|
609
|
-
const code = ch.codePointAt(0);
|
|
610
|
-
if (code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65519 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983) {
|
|
611
|
-
width += 2;
|
|
612
|
-
} else {
|
|
613
|
-
width += 1;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
return width;
|
|
617
|
-
}
|
|
618
|
-
function printBoxTitle(title, width = 65) {
|
|
619
|
-
const border = "\u2550".repeat(width);
|
|
620
|
-
const displayWidth = getDisplayWidth(title);
|
|
621
|
-
const totalPadding = width - displayWidth;
|
|
622
|
-
const leftPadding = Math.floor(totalPadding / 2);
|
|
623
|
-
const rightPadding = totalPadding - leftPadding;
|
|
624
|
-
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
625
|
-
console.log();
|
|
626
|
-
console.log(chalk.cyan.bold(` \u2554${border}\u2557`));
|
|
627
|
-
console.log(chalk.cyan.bold(` \u2551`) + chalk.cyan.bold(paddedTitle) + chalk.cyan.bold(`\u2551`));
|
|
628
|
-
console.log(chalk.cyan.bold(` \u255A${border}\u255D`));
|
|
629
|
-
console.log();
|
|
1438
|
+
configRoot
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return installed;
|
|
630
1444
|
}
|
|
631
|
-
function
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
1445
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
1446
|
+
const skillsDir = join5(configRoot, "skills");
|
|
1447
|
+
const agentsDir = join5(configRoot, "agents");
|
|
1448
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
1449
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
1450
|
+
const skillPath = join5(skillsDir, skillId);
|
|
1451
|
+
return existsSync5(skillPath);
|
|
1452
|
+
});
|
|
1453
|
+
if (!hasAnySkill) return false;
|
|
1454
|
+
}
|
|
1455
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
1456
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
1457
|
+
const agentPath = join5(agentsDir, agentId);
|
|
1458
|
+
return existsSync5(agentPath);
|
|
1459
|
+
});
|
|
1460
|
+
if (!hasAnyAgent) return false;
|
|
1461
|
+
}
|
|
1462
|
+
let agentsMdPath;
|
|
1463
|
+
if (tool === "opencode" && level === "project") {
|
|
1464
|
+
agentsMdPath = join5(configRoot, "..", "AGENTS.md");
|
|
635
1465
|
} else {
|
|
636
|
-
|
|
637
|
-
`);
|
|
1466
|
+
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
638
1467
|
}
|
|
1468
|
+
if (!existsSync5(agentsMdPath)) return false;
|
|
1469
|
+
return true;
|
|
639
1470
|
}
|
|
640
1471
|
|
|
641
1472
|
// src/utils/config.ts
|
|
642
|
-
import { existsSync as
|
|
643
|
-
import { join as
|
|
644
|
-
import { homedir as homedir3 } from "os";
|
|
1473
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
1474
|
+
import { join as join6 } from "path";
|
|
645
1475
|
import { parse, stringify } from "yaml";
|
|
1476
|
+
|
|
1477
|
+
// src/utils/fs.ts
|
|
1478
|
+
import { writeFileSync, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
1479
|
+
import { dirname as dirname3 } from "path";
|
|
1480
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
1481
|
+
const dir = dirname3(filePath);
|
|
1482
|
+
if (!existsSync6(dir)) {
|
|
1483
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
1484
|
+
}
|
|
1485
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1486
|
+
writeFileSync(tmpPath, data, encoding);
|
|
1487
|
+
if (existsSync6(filePath)) {
|
|
1488
|
+
try {
|
|
1489
|
+
unlinkSync(filePath);
|
|
1490
|
+
} catch {
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
renameSync(tmpPath, filePath);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
// src/utils/config.ts
|
|
646
1497
|
function getConfigDir() {
|
|
647
|
-
return
|
|
1498
|
+
return getCannbotConfigDir();
|
|
648
1499
|
}
|
|
649
1500
|
function getConfigPath() {
|
|
650
|
-
return
|
|
1501
|
+
return join6(getConfigDir(), "config.yaml");
|
|
651
1502
|
}
|
|
652
1503
|
function readConfig() {
|
|
653
1504
|
const configPath = getConfigPath();
|
|
654
|
-
if (!
|
|
1505
|
+
if (!existsSync7(configPath)) {
|
|
655
1506
|
return {
|
|
656
1507
|
language: "zh_CN",
|
|
657
1508
|
installedPlugins: []
|
|
658
1509
|
};
|
|
659
1510
|
}
|
|
660
1511
|
try {
|
|
661
|
-
const content =
|
|
1512
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
662
1513
|
const parsed = parse(content);
|
|
663
1514
|
return {
|
|
664
1515
|
language: parsed.language || "zh_CN",
|
|
@@ -668,6 +1519,7 @@ function readConfig() {
|
|
|
668
1519
|
installedPlugins: parsed.installedPlugins || []
|
|
669
1520
|
};
|
|
670
1521
|
} catch {
|
|
1522
|
+
logger.warn(t("config_corrupted"));
|
|
671
1523
|
return {
|
|
672
1524
|
language: "zh_CN",
|
|
673
1525
|
installedPlugins: []
|
|
@@ -676,12 +1528,12 @@ function readConfig() {
|
|
|
676
1528
|
}
|
|
677
1529
|
function writeConfig(config) {
|
|
678
1530
|
const configDir = getConfigDir();
|
|
679
|
-
if (!
|
|
1531
|
+
if (!existsSync7(configDir)) {
|
|
680
1532
|
mkdirSync(configDir, { recursive: true });
|
|
681
1533
|
}
|
|
682
1534
|
const configPath = getConfigPath();
|
|
683
1535
|
const content = stringify(config);
|
|
684
|
-
|
|
1536
|
+
atomicWriteFileSync(configPath, content);
|
|
685
1537
|
}
|
|
686
1538
|
function updateConfig(updates) {
|
|
687
1539
|
const config = readConfig();
|
|
@@ -704,22 +1556,11 @@ function removeInstalledPlugin(pluginId) {
|
|
|
704
1556
|
writeConfig(config);
|
|
705
1557
|
}
|
|
706
1558
|
|
|
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
|
|
1559
|
+
// src/utils/constants.ts
|
|
721
1560
|
var BACK = "__back__";
|
|
722
1561
|
var CANCEL = "__cancel__";
|
|
1562
|
+
|
|
1563
|
+
// src/ui/wizard.ts
|
|
723
1564
|
async function selectToolWithDetection() {
|
|
724
1565
|
const config = readConfig();
|
|
725
1566
|
logger.blank();
|
|
@@ -789,7 +1630,7 @@ async function runWizard() {
|
|
|
789
1630
|
step = 2;
|
|
790
1631
|
break;
|
|
791
1632
|
}
|
|
792
|
-
if (result === CANCEL
|
|
1633
|
+
if (result === CANCEL) {
|
|
793
1634
|
return { language: config.language || "zh_CN", tool: selectedTool, level, plugins: [], confirmed: false };
|
|
794
1635
|
}
|
|
795
1636
|
updateConfig({ lastTool: selectedTool, lastLevel: level });
|
|
@@ -799,11 +1640,11 @@ async function runWizard() {
|
|
|
799
1640
|
}
|
|
800
1641
|
}
|
|
801
1642
|
async function stepTool(detectedTools, lastTool) {
|
|
802
|
-
printBoxTitle("
|
|
1643
|
+
printBoxTitle(t("wizard_step_1_title"));
|
|
803
1644
|
if (detectedTools.length === 0) {
|
|
804
1645
|
logger.error(t("error_tool_not_found"));
|
|
805
1646
|
const choices2 = [
|
|
806
|
-
...
|
|
1647
|
+
...VALID_TOOLS.map((tool) => ({
|
|
807
1648
|
name: `> ${getToolDisplayName(tool)}`,
|
|
808
1649
|
value: tool
|
|
809
1650
|
})),
|
|
@@ -874,10 +1715,10 @@ async function stepToolManual(lastTool) {
|
|
|
874
1715
|
return result;
|
|
875
1716
|
}
|
|
876
1717
|
async function stepLevel(lastLevel) {
|
|
877
|
-
printBoxTitle("
|
|
1718
|
+
printBoxTitle(t("wizard_step_2_title"));
|
|
878
1719
|
const choices = [
|
|
879
|
-
{ name:
|
|
880
|
-
{ name:
|
|
1720
|
+
{ name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
|
|
1721
|
+
{ name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
|
|
881
1722
|
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
882
1723
|
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
883
1724
|
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
@@ -895,7 +1736,7 @@ async function stepLevel(lastLevel) {
|
|
|
895
1736
|
return result;
|
|
896
1737
|
}
|
|
897
1738
|
async function stepPlugins(tool, level) {
|
|
898
|
-
printBoxTitle("
|
|
1739
|
+
printBoxTitle(t("wizard_step_3_title"));
|
|
899
1740
|
const plugins = getAllPlugins();
|
|
900
1741
|
const configRoot = getConfigRoot(tool, level);
|
|
901
1742
|
const manifests = readAllManifests(configRoot);
|
|
@@ -904,14 +1745,12 @@ async function stepPlugins(tool, level) {
|
|
|
904
1745
|
const isInstalled = installedSet.has(p.id);
|
|
905
1746
|
const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
|
|
906
1747
|
return {
|
|
907
|
-
name: `${p.displayName}${suffix}
|
|
1748
|
+
name: `${chalk3.cyan(p.displayName)}${suffix}`,
|
|
908
1749
|
value: p.id,
|
|
909
|
-
checked: false
|
|
1750
|
+
checked: false,
|
|
1751
|
+
description: p.description
|
|
910
1752
|
};
|
|
911
1753
|
});
|
|
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
1754
|
showOperationHints(true);
|
|
916
1755
|
const selected = await checkbox({
|
|
917
1756
|
message: t("wizard_select_plugins"),
|
|
@@ -921,13 +1760,7 @@ async function stepPlugins(tool, level) {
|
|
|
921
1760
|
theme: checkboxTheme,
|
|
922
1761
|
pageSize: 15
|
|
923
1762
|
});
|
|
924
|
-
|
|
925
|
-
return CANCEL;
|
|
926
|
-
}
|
|
927
|
-
if (selected.includes(BACK)) {
|
|
928
|
-
return BACK;
|
|
929
|
-
}
|
|
930
|
-
const pluginIds = selected.filter((v) => v !== BACK && v !== CANCEL);
|
|
1763
|
+
const pluginIds = selected;
|
|
931
1764
|
if (pluginIds.length === 0) {
|
|
932
1765
|
const action = await select({
|
|
933
1766
|
message: t("wizard_no_selection"),
|
|
@@ -945,17 +1778,17 @@ async function stepPlugins(tool, level) {
|
|
|
945
1778
|
return pluginIds;
|
|
946
1779
|
}
|
|
947
1780
|
async function stepConfirm(tool, level, plugins) {
|
|
948
|
-
printBoxTitle("
|
|
1781
|
+
printBoxTitle(t("wizard_step_4_title"));
|
|
949
1782
|
const allPlugins = getAllPlugins();
|
|
950
1783
|
const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
|
|
951
1784
|
const displayPath = getConfigRoot(tool, level);
|
|
952
1785
|
const toolName = getToolDisplayName(tool);
|
|
953
1786
|
const levelText = level === "project" ? "project" : "global";
|
|
954
|
-
logger.info(
|
|
955
|
-
logger.info(
|
|
1787
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk3.bold(String(plugins.length))).replace("{tool}", chalk3.green(toolName)).replace("{level}", chalk3.cyan(levelText)));
|
|
1788
|
+
logger.info(`${t("wizard_install_path")}: ${chalk3.cyan(displayPath)}`);
|
|
956
1789
|
for (const plugin of selectedPlugins) {
|
|
957
1790
|
if (plugin) {
|
|
958
|
-
logger.step(` \u2022 ${
|
|
1791
|
+
logger.step(` \u2022 ${chalk3.bold(plugin.displayName)}`);
|
|
959
1792
|
}
|
|
960
1793
|
}
|
|
961
1794
|
logger.blank();
|
|
@@ -973,14 +1806,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
973
1806
|
}
|
|
974
1807
|
|
|
975
1808
|
// src/core/repository.ts
|
|
976
|
-
import { existsSync as
|
|
977
|
-
import { join as
|
|
1809
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1810
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
1811
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
978
1812
|
import { execa as execa2 } from "execa";
|
|
1813
|
+
import { parse as parseYaml3 } from "yaml";
|
|
979
1814
|
|
|
980
1815
|
// src/core/scanner.ts
|
|
981
|
-
import { existsSync as
|
|
982
|
-
import { join as
|
|
983
|
-
import {
|
|
1816
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
1817
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
1818
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1819
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1820
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
1821
|
+
var __dirname3 = dirname4(__filename3);
|
|
984
1822
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
985
1823
|
"node_modules",
|
|
986
1824
|
".git",
|
|
@@ -1001,46 +1839,70 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
1001
1839
|
"docs",
|
|
1002
1840
|
"scripts"
|
|
1003
1841
|
]);
|
|
1004
|
-
|
|
1005
|
-
"
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
]
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1842
|
+
function loadScanConfig() {
|
|
1843
|
+
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
1844
|
+
try {
|
|
1845
|
+
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
1846
|
+
const ttlHours = config.scanCacheTtlHours || 24;
|
|
1847
|
+
return {
|
|
1848
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1849
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"],
|
|
1850
|
+
cacheTtlMs: ttlHours * 60 * 60 * 1e3
|
|
1851
|
+
};
|
|
1852
|
+
} catch {
|
|
1853
|
+
return {
|
|
1854
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1855
|
+
pluginDirs: ["plugins-official", "plugins-community"],
|
|
1856
|
+
cacheTtlMs: 24 * 60 * 60 * 1e3
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
var scanConfig = loadScanConfig();
|
|
1861
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
1862
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1015
1863
|
function scanSkills(repoPath) {
|
|
1016
1864
|
const skills = [];
|
|
1017
1865
|
const seen = /* @__PURE__ */ new Set();
|
|
1018
1866
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1019
|
-
const fullPath =
|
|
1020
|
-
if (!
|
|
1867
|
+
const fullPath = join7(repoPath, dir);
|
|
1868
|
+
if (!existsSync8(fullPath)) continue;
|
|
1021
1869
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1022
1870
|
}
|
|
1023
1871
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1024
|
-
const pluginParent =
|
|
1025
|
-
if (!
|
|
1872
|
+
const pluginParent = join7(repoPath, pluginDir);
|
|
1873
|
+
if (!existsSync8(pluginParent)) continue;
|
|
1026
1874
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1027
|
-
const pluginPath =
|
|
1875
|
+
const pluginPath = join7(pluginParent, plugin);
|
|
1028
1876
|
if (!isDirectory(pluginPath)) continue;
|
|
1029
|
-
const skillsDir =
|
|
1030
|
-
if (
|
|
1877
|
+
const skillsDir = join7(pluginPath, "skills");
|
|
1878
|
+
if (existsSync8(skillsDir)) {
|
|
1031
1879
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1032
1880
|
}
|
|
1033
|
-
const skillDir =
|
|
1034
|
-
if (
|
|
1881
|
+
const skillDir = join7(pluginPath, "skill");
|
|
1882
|
+
if (existsSync8(skillDir)) {
|
|
1035
1883
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1036
1884
|
}
|
|
1885
|
+
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
1886
|
+
const initSh = join7(pluginPath, "init.sh");
|
|
1887
|
+
if (existsSync8(rootSkillMd) && !existsSync8(initSh)) {
|
|
1888
|
+
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
1889
|
+
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1890
|
+
seen.add(frontmatter.name);
|
|
1891
|
+
skills.push({
|
|
1892
|
+
id: frontmatter.name,
|
|
1893
|
+
description: frontmatter.description,
|
|
1894
|
+
source: `${pluginDir}/${plugin}`,
|
|
1895
|
+
filePath: rootSkillMd
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1037
1899
|
}
|
|
1038
1900
|
}
|
|
1039
1901
|
return skills;
|
|
1040
1902
|
}
|
|
1041
1903
|
function parseFrontmatter(filePath) {
|
|
1042
1904
|
try {
|
|
1043
|
-
const content =
|
|
1905
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1044
1906
|
const lines = content.split("\n");
|
|
1045
1907
|
if (lines.length < 3) return null;
|
|
1046
1908
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1053,11 +1915,15 @@ function parseFrontmatter(filePath) {
|
|
|
1053
1915
|
}
|
|
1054
1916
|
if (endIndex === -1) return null;
|
|
1055
1917
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1056
|
-
const parsed =
|
|
1918
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1057
1919
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1058
1920
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1921
|
+
const trimmedName = parsed.name.trim();
|
|
1922
|
+
if (!trimmedName || /[\/\\]|\.\./.test(trimmedName) || !/^[a-zA-Z0-9._-]+$/.test(trimmedName)) {
|
|
1923
|
+
return null;
|
|
1924
|
+
}
|
|
1059
1925
|
return {
|
|
1060
|
-
name:
|
|
1926
|
+
name: trimmedName,
|
|
1061
1927
|
description: (parsed.description || "").trim()
|
|
1062
1928
|
};
|
|
1063
1929
|
} catch {
|
|
@@ -1066,17 +1932,17 @@ function parseFrontmatter(filePath) {
|
|
|
1066
1932
|
}
|
|
1067
1933
|
function getCurrentCommit(repoPath) {
|
|
1068
1934
|
try {
|
|
1069
|
-
const headFile =
|
|
1070
|
-
if (!
|
|
1071
|
-
const headContent =
|
|
1935
|
+
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1936
|
+
if (!existsSync8(headFile)) return "unknown";
|
|
1937
|
+
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1072
1938
|
if (headContent.startsWith("ref: ")) {
|
|
1073
|
-
const refPath =
|
|
1074
|
-
if (
|
|
1075
|
-
return
|
|
1939
|
+
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1940
|
+
if (existsSync8(refPath)) {
|
|
1941
|
+
return readFileSync5(refPath, "utf-8").trim();
|
|
1076
1942
|
}
|
|
1077
|
-
const packedRefsPath =
|
|
1078
|
-
if (
|
|
1079
|
-
const packedRefs =
|
|
1943
|
+
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1944
|
+
if (existsSync8(packedRefsPath)) {
|
|
1945
|
+
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1080
1946
|
const refName = headContent.slice(5);
|
|
1081
1947
|
const lines = packedRefs.split("\n");
|
|
1082
1948
|
for (const line of lines) {
|
|
@@ -1094,12 +1960,12 @@ function getCurrentCommit(repoPath) {
|
|
|
1094
1960
|
function readScanCache() {
|
|
1095
1961
|
try {
|
|
1096
1962
|
const cachePath = getCachePath();
|
|
1097
|
-
if (!
|
|
1098
|
-
const content =
|
|
1963
|
+
if (!existsSync8(cachePath)) return null;
|
|
1964
|
+
const content = readFileSync5(cachePath, "utf-8");
|
|
1099
1965
|
const cache = JSON.parse(content);
|
|
1100
1966
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1101
1967
|
const age = Date.now() - cache.timestamp;
|
|
1102
|
-
if (age >
|
|
1968
|
+
if (age > scanConfig.cacheTtlMs) return null;
|
|
1103
1969
|
return cache;
|
|
1104
1970
|
} catch {
|
|
1105
1971
|
return null;
|
|
@@ -1108,39 +1974,24 @@ function readScanCache() {
|
|
|
1108
1974
|
function writeScanCache(cache) {
|
|
1109
1975
|
try {
|
|
1110
1976
|
const cachePath = getCachePath();
|
|
1111
|
-
const cacheDir =
|
|
1112
|
-
if (!
|
|
1977
|
+
const cacheDir = dirname4(cachePath);
|
|
1978
|
+
if (!existsSync8(cacheDir)) {
|
|
1113
1979
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1114
1980
|
}
|
|
1115
|
-
|
|
1981
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1116
1982
|
} catch {
|
|
1117
1983
|
}
|
|
1118
1984
|
}
|
|
1119
1985
|
function getCachePath() {
|
|
1120
|
-
|
|
1121
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
1986
|
+
return join7(getCannbotConfigDir(), "scan-cache.json");
|
|
1122
1987
|
}
|
|
1123
1988
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1124
|
-
const selfSkillMd = join5(dirPath, "SKILL.md");
|
|
1125
|
-
if (existsSync5(selfSkillMd)) {
|
|
1126
|
-
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1127
|
-
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1128
|
-
seen.add(frontmatter.name);
|
|
1129
|
-
skills.push({
|
|
1130
|
-
id: frontmatter.name,
|
|
1131
|
-
description: frontmatter.description,
|
|
1132
|
-
source: sourcePrefix,
|
|
1133
|
-
filePath: selfSkillMd
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
1989
|
for (const entry of safeReaddir(dirPath)) {
|
|
1139
|
-
const fullPath =
|
|
1990
|
+
const fullPath = join7(dirPath, entry);
|
|
1140
1991
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1141
1992
|
if (!isDirectory(fullPath)) continue;
|
|
1142
|
-
const skillMd =
|
|
1143
|
-
if (
|
|
1993
|
+
const skillMd = join7(fullPath, "SKILL.md");
|
|
1994
|
+
if (existsSync8(skillMd)) {
|
|
1144
1995
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1145
1996
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1146
1997
|
seen.add(frontmatter.name);
|
|
@@ -1153,38 +2004,32 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1153
2004
|
}
|
|
1154
2005
|
continue;
|
|
1155
2006
|
}
|
|
1156
|
-
scanDirectory(fullPath, repoPath, sourcePrefix, skills, seen);
|
|
2007
|
+
scanDirectory(fullPath, repoPath, sourcePrefix + "/" + entry, skills, seen);
|
|
1157
2008
|
}
|
|
1158
2009
|
}
|
|
1159
2010
|
function safeReaddir(dir) {
|
|
1160
2011
|
try {
|
|
1161
|
-
return
|
|
2012
|
+
return readdirSync2(dir);
|
|
1162
2013
|
} catch {
|
|
1163
2014
|
return [];
|
|
1164
2015
|
}
|
|
1165
2016
|
}
|
|
1166
|
-
function isDirectory(path) {
|
|
1167
|
-
try {
|
|
1168
|
-
return statSync(path).isDirectory();
|
|
1169
|
-
} catch {
|
|
1170
|
-
return false;
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
2017
|
|
|
1174
2018
|
// src/core/skill-registry.ts
|
|
1175
2019
|
var CATEGORY_DEFS = [
|
|
1176
|
-
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
|
|
1177
|
-
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
|
|
1178
|
-
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
|
|
1179
|
-
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
|
|
1180
|
-
{ id: "ascendc", name: "AscendC \u5F00\u53D1" },
|
|
1181
|
-
{ id: "pypto", name: "PyPTO \u5F00\u53D1" },
|
|
1182
|
-
{ id: "tilelang", name: "TileLang \u5F00\u53D1" },
|
|
1183
|
-
{ id: "triton", name: "Triton \u5F00\u53D1" },
|
|
1184
|
-
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
|
|
1185
|
-
{ id: "
|
|
1186
|
-
{ id: "
|
|
1187
|
-
{ id: "
|
|
2020
|
+
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
|
|
2021
|
+
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
|
|
2022
|
+
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
|
|
2023
|
+
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
|
|
2024
|
+
{ id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
|
|
2025
|
+
{ id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
|
|
2026
|
+
{ id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
|
|
2027
|
+
{ id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
|
|
2028
|
+
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
|
|
2029
|
+
{ id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
|
|
2030
|
+
{ id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
|
|
2031
|
+
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
|
|
2032
|
+
{ id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
|
|
1188
2033
|
];
|
|
1189
2034
|
var dynamicSkills = [];
|
|
1190
2035
|
var initialized = false;
|
|
@@ -1192,7 +2037,8 @@ function initFromScan(scanned) {
|
|
|
1192
2037
|
dynamicSkills = scanned.map((s) => ({
|
|
1193
2038
|
id: s.id,
|
|
1194
2039
|
description: s.description,
|
|
1195
|
-
source: s.source
|
|
2040
|
+
source: s.source,
|
|
2041
|
+
filePath: s.filePath
|
|
1196
2042
|
}));
|
|
1197
2043
|
initialized = true;
|
|
1198
2044
|
}
|
|
@@ -1268,9 +2114,7 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1268
2114
|
{ id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
|
|
1269
2115
|
{ id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
|
|
1270
2116
|
{ id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
|
|
1271
|
-
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1272
|
-
{ id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
|
|
1273
|
-
{ id: "ops-easyasc-dsl", description: "EasyASC DSL \u7B97\u5B50\u5F00\u53D1", source: "plugins-community/ops-easyasc-dsl/skill" }
|
|
2117
|
+
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1274
2118
|
]
|
|
1275
2119
|
},
|
|
1276
2120
|
{
|
|
@@ -1327,7 +2171,9 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1327
2171
|
{ id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
|
|
1328
2172
|
{ id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
|
|
1329
2173
|
{ id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
|
|
1330
|
-
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" }
|
|
2174
|
+
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
|
|
2175
|
+
{ id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
|
|
2176
|
+
{ 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" }
|
|
1331
2177
|
]
|
|
1332
2178
|
},
|
|
1333
2179
|
{
|
|
@@ -1343,6 +2189,13 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1343
2189
|
{ id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
|
|
1344
2190
|
]
|
|
1345
2191
|
},
|
|
2192
|
+
{
|
|
2193
|
+
id: "runtime",
|
|
2194
|
+
name: "Runtime \u8FC1\u79FB",
|
|
2195
|
+
skills: [
|
|
2196
|
+
{ id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
|
|
2197
|
+
]
|
|
2198
|
+
},
|
|
1346
2199
|
{
|
|
1347
2200
|
id: "platform",
|
|
1348
2201
|
name: "\u5E73\u53F0\u5DE5\u5177",
|
|
@@ -1386,9 +2239,10 @@ function getAllSkills() {
|
|
|
1386
2239
|
}
|
|
1387
2240
|
function getAllCategories() {
|
|
1388
2241
|
const skills = getActiveSkills();
|
|
2242
|
+
const isEn = t("list_name") === "Name";
|
|
1389
2243
|
return CATEGORY_DEFS.map((def) => ({
|
|
1390
2244
|
id: def.id,
|
|
1391
|
-
name: def.name,
|
|
2245
|
+
name: isEn ? def.nameEn : def.name,
|
|
1392
2246
|
skills: skills.filter((s) => {
|
|
1393
2247
|
const catId = CATEGORY_MAP[s.id];
|
|
1394
2248
|
if (catId) return catId === def.id;
|
|
@@ -1398,7 +2252,21 @@ function getAllCategories() {
|
|
|
1398
2252
|
}
|
|
1399
2253
|
|
|
1400
2254
|
// src/core/repository.ts
|
|
1401
|
-
var
|
|
2255
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
2256
|
+
var __dirname4 = dirname5(__filename4);
|
|
2257
|
+
function loadRepoUrl() {
|
|
2258
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
2259
|
+
return process.env.CANNBOT_REPO_URL;
|
|
2260
|
+
}
|
|
2261
|
+
const configPath = join8(__dirname4, "config", "repository.yaml");
|
|
2262
|
+
try {
|
|
2263
|
+
const config = parseYaml3(readFileSync6(configPath, "utf-8"));
|
|
2264
|
+
return config.repository?.url || "https://gitcode.com/yanhf/cann_skills.git";
|
|
2265
|
+
} catch {
|
|
2266
|
+
return "https://gitcode.com/yanhf/cann_skills.git";
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
var REPO_URL = loadRepoUrl();
|
|
1402
2270
|
var RepositoryManager = class {
|
|
1403
2271
|
repoPath;
|
|
1404
2272
|
constructor(customPath) {
|
|
@@ -1423,10 +2291,13 @@ var RepositoryManager = class {
|
|
|
1423
2291
|
this.repoPath = cachedPath;
|
|
1424
2292
|
return cachedPath;
|
|
1425
2293
|
}
|
|
1426
|
-
|
|
2294
|
+
const clonedPath = await this.cloneRepo();
|
|
2295
|
+
return clonedPath;
|
|
1427
2296
|
}
|
|
1428
2297
|
async ensureRepoAndScan() {
|
|
1429
2298
|
const repoPath = await this.ensureRepo();
|
|
2299
|
+
await this.updateRepo();
|
|
2300
|
+
mergeDynamicPlugins(repoPath);
|
|
1430
2301
|
const cache = readScanCache();
|
|
1431
2302
|
if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
|
|
1432
2303
|
initFromScan(cache.skills);
|
|
@@ -1446,7 +2317,9 @@ var RepositoryManager = class {
|
|
|
1446
2317
|
try {
|
|
1447
2318
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1448
2319
|
} catch {
|
|
2320
|
+
logger.warn(t("repo_pull_failed"));
|
|
1449
2321
|
}
|
|
2322
|
+
mergeDynamicPlugins(repoPath);
|
|
1450
2323
|
}
|
|
1451
2324
|
getRepoPath() {
|
|
1452
2325
|
if (!this.repoPath) {
|
|
@@ -1455,18 +2328,18 @@ var RepositoryManager = class {
|
|
|
1455
2328
|
return this.repoPath;
|
|
1456
2329
|
}
|
|
1457
2330
|
isValidRepo(path) {
|
|
1458
|
-
if (!
|
|
1459
|
-
const gitDir =
|
|
1460
|
-
const pluginsDir =
|
|
1461
|
-
return
|
|
2331
|
+
if (!existsSync9(path)) return false;
|
|
2332
|
+
const gitDir = join8(path, ".git");
|
|
2333
|
+
const pluginsDir = join8(path, "plugins-official");
|
|
2334
|
+
return existsSync9(gitDir) && existsSync9(pluginsDir);
|
|
1462
2335
|
}
|
|
1463
2336
|
async cloneRepo() {
|
|
1464
2337
|
const configDir = getCannbotConfigDir();
|
|
1465
|
-
if (!
|
|
2338
|
+
if (!existsSync9(configDir)) {
|
|
1466
2339
|
mkdirSync3(configDir, { recursive: true });
|
|
1467
2340
|
}
|
|
1468
2341
|
const targetPath = getCannbotRepoPath();
|
|
1469
|
-
if (
|
|
2342
|
+
if (existsSync9(targetPath)) {
|
|
1470
2343
|
if (this.isValidRepo(targetPath)) {
|
|
1471
2344
|
this.repoPath = targetPath;
|
|
1472
2345
|
return targetPath;
|
|
@@ -1492,179 +2365,574 @@ function createRepositoryManager(customPath) {
|
|
|
1492
2365
|
}
|
|
1493
2366
|
|
|
1494
2367
|
// src/core/installer.ts
|
|
1495
|
-
import { existsSync as
|
|
1496
|
-
import { join as
|
|
1497
|
-
import { execa as
|
|
2368
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2369
|
+
import { join as join12 } from "path";
|
|
2370
|
+
import { execa as execa4 } from "execa";
|
|
1498
2371
|
|
|
1499
2372
|
// src/core/record.ts
|
|
1500
|
-
import { existsSync as
|
|
1501
|
-
import { join as
|
|
2373
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
2374
|
+
import { join as join9 } from "path";
|
|
1502
2375
|
function getInstallsDir() {
|
|
1503
|
-
return
|
|
2376
|
+
return join9(getCannbotConfigDir(), "installs");
|
|
1504
2377
|
}
|
|
1505
2378
|
function getRecordPath(pluginId) {
|
|
1506
|
-
return
|
|
2379
|
+
return join9(getInstallsDir(), `${pluginId}.json`);
|
|
1507
2380
|
}
|
|
1508
2381
|
function readRecord(pluginId) {
|
|
1509
2382
|
const recordPath = getRecordPath(pluginId);
|
|
1510
|
-
if (!
|
|
2383
|
+
if (!existsSync10(recordPath)) {
|
|
1511
2384
|
return null;
|
|
1512
2385
|
}
|
|
1513
2386
|
try {
|
|
1514
|
-
const content =
|
|
2387
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1515
2388
|
return JSON.parse(content);
|
|
1516
2389
|
} catch {
|
|
2390
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1517
2391
|
return null;
|
|
1518
2392
|
}
|
|
1519
2393
|
}
|
|
1520
2394
|
function writeRecord(record) {
|
|
1521
2395
|
const installsDir = getInstallsDir();
|
|
1522
|
-
if (!
|
|
2396
|
+
if (!existsSync10(installsDir)) {
|
|
1523
2397
|
mkdirSync4(installsDir, { recursive: true });
|
|
1524
2398
|
}
|
|
1525
2399
|
const recordPath = getRecordPath(record.pluginId);
|
|
1526
|
-
|
|
2400
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1527
2401
|
}
|
|
1528
2402
|
function deleteRecord(pluginId) {
|
|
1529
2403
|
const recordPath = getRecordPath(pluginId);
|
|
1530
|
-
if (
|
|
1531
|
-
|
|
2404
|
+
if (existsSync10(recordPath)) {
|
|
2405
|
+
unlinkSync2(recordPath);
|
|
1532
2406
|
}
|
|
1533
2407
|
}
|
|
1534
2408
|
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
1535
2409
|
const files = [];
|
|
1536
2410
|
const directories = [];
|
|
1537
2411
|
if (manifest) {
|
|
1538
|
-
const skillsDir =
|
|
1539
|
-
if (
|
|
2412
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2413
|
+
if (existsSync10(skillsDir)) {
|
|
1540
2414
|
directories.push(skillsDir);
|
|
1541
2415
|
for (const skillName of manifest.installed_skills || []) {
|
|
1542
|
-
const skillPath =
|
|
1543
|
-
if (
|
|
2416
|
+
const skillPath = join9(skillsDir, skillName);
|
|
2417
|
+
if (existsSync10(skillPath) || isSymlink(skillPath)) {
|
|
1544
2418
|
files.push(skillPath);
|
|
1545
2419
|
}
|
|
1546
2420
|
}
|
|
1547
2421
|
}
|
|
1548
|
-
const agentsDir =
|
|
1549
|
-
if (
|
|
2422
|
+
const agentsDir = join9(configRoot, "agents");
|
|
2423
|
+
if (existsSync10(agentsDir)) {
|
|
1550
2424
|
directories.push(agentsDir);
|
|
1551
2425
|
for (const agentName of manifest.installed_agents || []) {
|
|
1552
|
-
const agentPath =
|
|
1553
|
-
const agentPathMd =
|
|
1554
|
-
if (
|
|
2426
|
+
const agentPath = join9(agentsDir, agentName);
|
|
2427
|
+
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
2428
|
+
if (existsSync10(agentPath) || isSymlink(agentPath)) {
|
|
1555
2429
|
files.push(agentPath);
|
|
1556
|
-
} else if (
|
|
2430
|
+
} else if (existsSync10(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1557
2431
|
files.push(agentPathMd);
|
|
1558
2432
|
}
|
|
1559
2433
|
}
|
|
1560
2434
|
}
|
|
1561
2435
|
}
|
|
1562
|
-
const workflowsLink =
|
|
2436
|
+
const workflowsLink = join9(configRoot, "workflows");
|
|
1563
2437
|
if (isSymlink(workflowsLink)) {
|
|
1564
2438
|
files.push(workflowsLink);
|
|
1565
2439
|
}
|
|
1566
|
-
const manifestPath =
|
|
1567
|
-
if (
|
|
2440
|
+
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
2441
|
+
if (existsSync10(manifestPath)) {
|
|
1568
2442
|
files.push(manifestPath);
|
|
1569
2443
|
}
|
|
1570
|
-
const pluginManifestPath =
|
|
1571
|
-
if (
|
|
2444
|
+
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
2445
|
+
if (existsSync10(pluginManifestPath)) {
|
|
1572
2446
|
files.push(pluginManifestPath);
|
|
1573
2447
|
}
|
|
1574
2448
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1575
|
-
const configFilePath = level === "project" ?
|
|
1576
|
-
if (
|
|
2449
|
+
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
2450
|
+
if (existsSync10(configFilePath)) {
|
|
1577
2451
|
files.push(configFilePath);
|
|
1578
2452
|
}
|
|
1579
2453
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1580
2454
|
for (const repoName of repoLinks) {
|
|
1581
|
-
const repoLinkPath =
|
|
2455
|
+
const repoLinkPath = join9(installPath, repoName);
|
|
1582
2456
|
if (isSymlink(repoLinkPath)) {
|
|
1583
2457
|
files.push(repoLinkPath);
|
|
1584
2458
|
}
|
|
1585
|
-
const repoLinkInConfig =
|
|
1586
|
-
if (isSymlink(repoLinkInConfig)) {
|
|
1587
|
-
files.push(repoLinkInConfig);
|
|
2459
|
+
const repoLinkInConfig = join9(configRoot, repoName);
|
|
2460
|
+
if (isSymlink(repoLinkInConfig)) {
|
|
2461
|
+
files.push(repoLinkInConfig);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
return {
|
|
2465
|
+
pluginId,
|
|
2466
|
+
displayName,
|
|
2467
|
+
tool,
|
|
2468
|
+
level,
|
|
2469
|
+
installPath,
|
|
2470
|
+
configRoot,
|
|
2471
|
+
installTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2472
|
+
files,
|
|
2473
|
+
directories
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
function getSkillRecordPath() {
|
|
2477
|
+
return join9(getInstallsDir(), "skills.json");
|
|
2478
|
+
}
|
|
2479
|
+
function readSkillRecord() {
|
|
2480
|
+
const recordPath = getSkillRecordPath();
|
|
2481
|
+
if (!existsSync10(recordPath)) {
|
|
2482
|
+
return {};
|
|
2483
|
+
}
|
|
2484
|
+
try {
|
|
2485
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
2486
|
+
return JSON.parse(content);
|
|
2487
|
+
} catch {
|
|
2488
|
+
logger.warn(t("skill_record_corrupted"));
|
|
2489
|
+
return {};
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
function writeSkillRecord(record) {
|
|
2493
|
+
const installsDir = getInstallsDir();
|
|
2494
|
+
if (!existsSync10(installsDir)) {
|
|
2495
|
+
mkdirSync4(installsDir, { recursive: true });
|
|
2496
|
+
}
|
|
2497
|
+
const recordPath = getSkillRecordPath();
|
|
2498
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
2499
|
+
}
|
|
2500
|
+
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
2501
|
+
const record = readSkillRecord();
|
|
2502
|
+
if (!record[tool]) record[tool] = {};
|
|
2503
|
+
if (!record[tool][level]) record[tool][level] = {};
|
|
2504
|
+
if (!record[tool][level][installPath]) {
|
|
2505
|
+
record[tool][level][installPath] = { skills: [], installTime: "" };
|
|
2506
|
+
}
|
|
2507
|
+
const entry = record[tool][level][installPath];
|
|
2508
|
+
for (const id of skillIds) {
|
|
2509
|
+
if (!entry.skills.includes(id)) {
|
|
2510
|
+
entry.skills.push(id);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
entry.installTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
2514
|
+
writeSkillRecord(record);
|
|
2515
|
+
}
|
|
2516
|
+
function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
2517
|
+
const record = readSkillRecord();
|
|
2518
|
+
if (!record[tool]?.[level]?.[installPath]) return;
|
|
2519
|
+
const entry = record[tool][level][installPath];
|
|
2520
|
+
entry.skills = entry.skills.filter((id) => !skillIds.includes(id));
|
|
2521
|
+
if (entry.skills.length === 0) {
|
|
2522
|
+
delete record[tool][level][installPath];
|
|
2523
|
+
if (Object.keys(record[tool][level]).length === 0) {
|
|
2524
|
+
delete record[tool][level];
|
|
2525
|
+
if (Object.keys(record[tool]).length === 0) {
|
|
2526
|
+
delete record[tool];
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
writeSkillRecord(record);
|
|
2531
|
+
}
|
|
2532
|
+
function getInstalledSkills(tool, level, installPath) {
|
|
2533
|
+
const record = readSkillRecord();
|
|
2534
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
2535
|
+
const configRoot = getConfigRoot(tool, level);
|
|
2536
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2537
|
+
return recordedSkills.filter((skillId) => {
|
|
2538
|
+
const skillPath = join9(skillsDir, skillId);
|
|
2539
|
+
return existsSync10(skillPath) || isSymlink(skillPath);
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
// src/core/backup.ts
|
|
2544
|
+
import { existsSync as existsSync11, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2545
|
+
import { join as join10 } from "path";
|
|
2546
|
+
function detectCurrentPlugin(configRoot, tool) {
|
|
2547
|
+
const manifest = readManifest(configRoot);
|
|
2548
|
+
if (manifest?.team) {
|
|
2549
|
+
const plugin = findPlugin(manifest.team);
|
|
2550
|
+
if (plugin) {
|
|
2551
|
+
return {
|
|
2552
|
+
pluginId: plugin.id,
|
|
2553
|
+
pluginName: plugin.displayName
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2558
|
+
if (!existsSync11(agentsFile)) {
|
|
2559
|
+
return null;
|
|
2560
|
+
}
|
|
2561
|
+
const allRecords = readAllRecords();
|
|
2562
|
+
for (const record of allRecords) {
|
|
2563
|
+
if (record.configRoot === configRoot && record.tool === tool) {
|
|
2564
|
+
const plugin = findPlugin(record.pluginId);
|
|
2565
|
+
if (plugin) {
|
|
2566
|
+
return {
|
|
2567
|
+
pluginId: plugin.id,
|
|
2568
|
+
pluginName: plugin.displayName
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
return null;
|
|
2574
|
+
}
|
|
2575
|
+
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
2576
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2577
|
+
if (!existsSync11(agentsFile)) {
|
|
2578
|
+
return null;
|
|
2579
|
+
}
|
|
2580
|
+
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
2581
|
+
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
2582
|
+
const backupPath = join10(configRoot, backupFileName);
|
|
2583
|
+
try {
|
|
2584
|
+
copyFileSync(agentsFile, backupPath);
|
|
2585
|
+
return {
|
|
2586
|
+
filePath: backupPath,
|
|
2587
|
+
pluginId: fromPluginId,
|
|
2588
|
+
pluginName: fromPluginName,
|
|
2589
|
+
backupTime: timestamp
|
|
2590
|
+
};
|
|
2591
|
+
} catch {
|
|
2592
|
+
return null;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
function findBackups(configRoot) {
|
|
2596
|
+
if (!existsSync11(configRoot)) {
|
|
2597
|
+
return [];
|
|
2598
|
+
}
|
|
2599
|
+
const files = readdirSync3(configRoot);
|
|
2600
|
+
const backups = [];
|
|
2601
|
+
for (const file of files) {
|
|
2602
|
+
if (file.includes(".cannbot-backup.")) {
|
|
2603
|
+
const parts = file.split(".cannbot-backup.");
|
|
2604
|
+
if (parts.length === 2) {
|
|
2605
|
+
const pluginIdAndTime = parts[1];
|
|
2606
|
+
const lastDotIndex = pluginIdAndTime.lastIndexOf(".");
|
|
2607
|
+
if (lastDotIndex > 0) {
|
|
2608
|
+
const pluginId = pluginIdAndTime.substring(0, lastDotIndex);
|
|
2609
|
+
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
2610
|
+
const plugin = findPlugin(pluginId);
|
|
2611
|
+
backups.push({
|
|
2612
|
+
filePath: join10(configRoot, file),
|
|
2613
|
+
pluginId,
|
|
2614
|
+
pluginName: plugin?.displayName || pluginId,
|
|
2615
|
+
backupTime: timestamp
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
return backups.sort((a, b) => b.backupTime.localeCompare(a.backupTime));
|
|
2622
|
+
}
|
|
2623
|
+
function restoreBackup(backupPath, configRoot, tool) {
|
|
2624
|
+
try {
|
|
2625
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2626
|
+
copyFileSync(backupPath, agentsFile);
|
|
2627
|
+
return true;
|
|
2628
|
+
} catch {
|
|
2629
|
+
return false;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
function deleteBackup(backupPath) {
|
|
2633
|
+
try {
|
|
2634
|
+
if (existsSync11(backupPath)) {
|
|
2635
|
+
unlinkSync3(backupPath);
|
|
2636
|
+
return true;
|
|
2637
|
+
}
|
|
2638
|
+
return false;
|
|
2639
|
+
} catch {
|
|
2640
|
+
return false;
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
function formatTimestamp(date) {
|
|
2644
|
+
const year = date.getFullYear();
|
|
2645
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
2646
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
2647
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
2648
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
2649
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
2650
|
+
return `${year}${month}${day}-${hours}${minutes}${seconds}`;
|
|
2651
|
+
}
|
|
2652
|
+
function readAllRecords() {
|
|
2653
|
+
const records = [];
|
|
2654
|
+
const plugins = getAllPlugins();
|
|
2655
|
+
for (const plugin of plugins) {
|
|
2656
|
+
const record = readRecord(plugin.id);
|
|
2657
|
+
if (record) {
|
|
2658
|
+
records.push(record);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
return records;
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
// src/ui/backup-prompts.ts
|
|
2665
|
+
import { select as select2 } from "@inquirer/prompts";
|
|
2666
|
+
import chalk4 from "chalk";
|
|
2667
|
+
async function showOverwriteWarning(currentPluginName, newPluginName) {
|
|
2668
|
+
console.log();
|
|
2669
|
+
console.log(chalk4.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
|
|
2670
|
+
console.log();
|
|
2671
|
+
console.log(` ${t("backup_current")}: ${chalk4.cyan(currentPluginName)}`);
|
|
2672
|
+
console.log(` ${t("backup_installing")}: ${chalk4.cyan(newPluginName)}`);
|
|
2673
|
+
console.log();
|
|
2674
|
+
console.log(` ${t("backup_overwrite_warning")}`);
|
|
2675
|
+
console.log(` ${t("backup_auto_create")}`);
|
|
2676
|
+
console.log();
|
|
2677
|
+
const choice = await select2({
|
|
2678
|
+
message: t("backup_select_action"),
|
|
2679
|
+
choices: [
|
|
2680
|
+
{ name: t("backup_overwrite"), value: "overwrite" },
|
|
2681
|
+
{ name: t("backup_cancel"), value: "cancel" }
|
|
2682
|
+
]
|
|
2683
|
+
});
|
|
2684
|
+
return choice;
|
|
2685
|
+
}
|
|
2686
|
+
async function showRestorePrompt(backups) {
|
|
2687
|
+
console.log();
|
|
2688
|
+
console.log(chalk4.cyan(` ${t("backup_restore_prompt")}`));
|
|
2689
|
+
console.log();
|
|
2690
|
+
const choices = backups.map((b) => ({
|
|
2691
|
+
name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
|
|
2692
|
+
value: b.filePath
|
|
2693
|
+
}));
|
|
2694
|
+
choices.push({ name: t("backup_only_uninstall"), value: "none" });
|
|
2695
|
+
const choice = await select2({
|
|
2696
|
+
message: t("backup_select_action"),
|
|
2697
|
+
choices
|
|
2698
|
+
});
|
|
2699
|
+
return choice;
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
// src/core/plugin-installer.ts
|
|
2703
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, realpathSync, copyFileSync as copyFileSync2, cpSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2704
|
+
import { join as join11, basename } from "path";
|
|
2705
|
+
import { execa as execa3 } from "execa";
|
|
2706
|
+
function installLink(source, target) {
|
|
2707
|
+
try {
|
|
2708
|
+
const resolved = realpathSync(source);
|
|
2709
|
+
if (existsSync12(target) || isSymlink(target)) {
|
|
2710
|
+
unlinkSync4(target);
|
|
2711
|
+
}
|
|
2712
|
+
try {
|
|
2713
|
+
symlinkSync(resolved, target);
|
|
2714
|
+
return "symlink";
|
|
2715
|
+
} catch (e) {
|
|
2716
|
+
if (e.code === "EPERM") {
|
|
2717
|
+
cpSync(resolved, target, { recursive: true });
|
|
2718
|
+
return "copy";
|
|
2719
|
+
}
|
|
2720
|
+
throw e;
|
|
2721
|
+
}
|
|
2722
|
+
} catch {
|
|
2723
|
+
return "failed";
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
function installConfigFile(pluginDir, configFile, tool, level, configRoot, installPath, configRootConfigLink) {
|
|
2727
|
+
const sourcePath = join11(pluginDir, configFile);
|
|
2728
|
+
if (!existsSync12(sourcePath)) return;
|
|
2729
|
+
const agentsFileName = getAgentsFileName(tool);
|
|
2730
|
+
const isProjectLevel = level === "project";
|
|
2731
|
+
let primaryTarget;
|
|
2732
|
+
if (isProjectLevel) {
|
|
2733
|
+
primaryTarget = join11(installPath, agentsFileName);
|
|
2734
|
+
} else {
|
|
2735
|
+
primaryTarget = join11(configRoot, agentsFileName);
|
|
2736
|
+
}
|
|
2737
|
+
if (existsSync12(primaryTarget) || isSymlink(primaryTarget)) {
|
|
2738
|
+
unlinkSync4(primaryTarget);
|
|
2739
|
+
}
|
|
2740
|
+
try {
|
|
2741
|
+
symlinkSync(realpathSync(sourcePath), primaryTarget);
|
|
2742
|
+
} catch (e) {
|
|
2743
|
+
if (e.code === "EPERM") {
|
|
2744
|
+
copyFileSync2(sourcePath, primaryTarget);
|
|
2745
|
+
} else {
|
|
2746
|
+
throw e;
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
if (isProjectLevel && primaryTarget !== join11(configRoot, agentsFileName)) {
|
|
2750
|
+
if (configRootConfigLink === false) return;
|
|
2751
|
+
const configRootTarget = join11(configRoot, agentsFileName);
|
|
2752
|
+
if (existsSync12(configRootTarget) || isSymlink(configRootTarget)) {
|
|
2753
|
+
unlinkSync4(configRootTarget);
|
|
2754
|
+
}
|
|
2755
|
+
try {
|
|
2756
|
+
symlinkSync(realpathSync(sourcePath), configRootTarget);
|
|
2757
|
+
} catch (e) {
|
|
2758
|
+
if (e.code === "EPERM") {
|
|
2759
|
+
copyFileSync2(sourcePath, configRootTarget);
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
async function installExternalRepos(repos, repoPath, configRoot, installPath, level) {
|
|
2765
|
+
if (!repos || repos.length === 0) return;
|
|
2766
|
+
for (const repo of repos) {
|
|
2767
|
+
const targetDir = join11(repoPath, repo.dir);
|
|
2768
|
+
if (!existsSync12(targetDir)) {
|
|
2769
|
+
const args = ["clone"];
|
|
2770
|
+
if (repo.depth) {
|
|
2771
|
+
args.push("--depth", String(repo.depth));
|
|
2772
|
+
}
|
|
2773
|
+
if (repo.recursive) {
|
|
2774
|
+
args.push("--recursive");
|
|
2775
|
+
}
|
|
2776
|
+
args.push(repo.url, targetDir);
|
|
2777
|
+
try {
|
|
2778
|
+
await execa3("git", args, { timeout: 12e4 });
|
|
2779
|
+
} catch {
|
|
2780
|
+
logger.warn(` git clone failed: ${repo.url}, clone manually: git clone ${repo.url} ${repo.dir}`);
|
|
2781
|
+
continue;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
const linkName = basename(repo.dir);
|
|
2785
|
+
const resolved = realpathSync(targetDir);
|
|
2786
|
+
const shouldConfigRootLink = repo.configRootLink !== false && (level === "global" || repo.configRootLink === true);
|
|
2787
|
+
if (shouldConfigRootLink) {
|
|
2788
|
+
const configRootLink = join11(configRoot, linkName);
|
|
2789
|
+
try {
|
|
2790
|
+
if (existsSync12(configRootLink) || isSymlink(configRootLink)) {
|
|
2791
|
+
unlinkSync4(configRootLink);
|
|
2792
|
+
}
|
|
2793
|
+
try {
|
|
2794
|
+
symlinkSync(resolved, configRootLink);
|
|
2795
|
+
} catch (e) {
|
|
2796
|
+
if (e.code === "EPERM") {
|
|
2797
|
+
cpSync(resolved, configRootLink, { recursive: true });
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
} catch {
|
|
2801
|
+
logger.warn(` Failed to link ${linkName} to config directory`);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
if (level === "project") {
|
|
2805
|
+
const projectLink = join11(installPath, linkName);
|
|
2806
|
+
try {
|
|
2807
|
+
if (existsSync12(projectLink) || isSymlink(projectLink)) {
|
|
2808
|
+
unlinkSync4(projectLink);
|
|
2809
|
+
}
|
|
2810
|
+
try {
|
|
2811
|
+
symlinkSync(resolved, projectLink);
|
|
2812
|
+
} catch (e) {
|
|
2813
|
+
if (e.code === "EPERM") {
|
|
2814
|
+
cpSync(resolved, projectLink, { recursive: true });
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
} catch {
|
|
2818
|
+
logger.warn(` Failed to link ${linkName} to project directory`);
|
|
2819
|
+
}
|
|
1588
2820
|
}
|
|
1589
2821
|
}
|
|
1590
|
-
directories.push(configRoot);
|
|
1591
|
-
return {
|
|
1592
|
-
pluginId,
|
|
1593
|
-
displayName,
|
|
1594
|
-
tool,
|
|
1595
|
-
level,
|
|
1596
|
-
installPath,
|
|
1597
|
-
configRoot,
|
|
1598
|
-
installTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1599
|
-
files,
|
|
1600
|
-
directories
|
|
1601
|
-
};
|
|
1602
2822
|
}
|
|
1603
|
-
function
|
|
2823
|
+
function installWorkflows(pluginDir, configRoot) {
|
|
2824
|
+
const workflowsDir = join11(pluginDir, "workflows");
|
|
2825
|
+
if (!existsSync12(workflowsDir)) return;
|
|
2826
|
+
const targetPath = join11(configRoot, "workflows");
|
|
1604
2827
|
try {
|
|
1605
|
-
|
|
2828
|
+
const resolved = realpathSync(workflowsDir);
|
|
2829
|
+
if (existsSync12(targetPath) || isSymlink(targetPath)) {
|
|
2830
|
+
unlinkSync4(targetPath);
|
|
2831
|
+
}
|
|
2832
|
+
try {
|
|
2833
|
+
symlinkSync(resolved, targetPath);
|
|
2834
|
+
} catch (e) {
|
|
2835
|
+
if (e.code === "EPERM") {
|
|
2836
|
+
cpSync(resolved, targetPath, { recursive: true });
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
1606
2839
|
} catch {
|
|
1607
|
-
|
|
2840
|
+
logger.warn(` Failed to link workflows to config directory`);
|
|
1608
2841
|
}
|
|
1609
2842
|
}
|
|
1610
|
-
function
|
|
1611
|
-
|
|
2843
|
+
function writeCannbotManifest(configRoot, pluginId, version, level, tool, skills, agents) {
|
|
2844
|
+
const manifest = {
|
|
2845
|
+
brand: "CANNBot",
|
|
2846
|
+
version,
|
|
2847
|
+
team: pluginId,
|
|
2848
|
+
level,
|
|
2849
|
+
tool,
|
|
2850
|
+
installed_skills: skills,
|
|
2851
|
+
installed_agents: agents,
|
|
2852
|
+
brand_dir: configRoot,
|
|
2853
|
+
install_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2854
|
+
};
|
|
2855
|
+
const manifestPath = join11(configRoot, "cannbot-manifest.json");
|
|
2856
|
+
writeFileSync3(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
2857
|
+
return manifest;
|
|
1612
2858
|
}
|
|
1613
|
-
function
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
}
|
|
2859
|
+
async function installViaManifest(plugin, repoPath, tool, level, installPath) {
|
|
2860
|
+
const cwd = installPath || process.cwd();
|
|
2861
|
+
const configRoot = getConfigRoot(tool, level, installPath);
|
|
2862
|
+
const pluginDir = join11(repoPath, plugin.dir);
|
|
1618
2863
|
try {
|
|
1619
|
-
const
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
2864
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2865
|
+
const agentsDir = join11(configRoot, "agents");
|
|
2866
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
2867
|
+
mkdirSync5(agentsDir, { recursive: true });
|
|
2868
|
+
const installedSkills = [];
|
|
2869
|
+
const installedAgents = [];
|
|
2870
|
+
for (const source of plugin.installSkills || []) {
|
|
2871
|
+
const sourceDir = join11(repoPath, source.dir);
|
|
2872
|
+
if (!existsSync12(sourceDir)) {
|
|
2873
|
+
logger.warn(` Skill source not found: ${source.dir}`);
|
|
2874
|
+
continue;
|
|
2875
|
+
}
|
|
2876
|
+
for (const skillEntry of source.skills) {
|
|
2877
|
+
const skillName = typeof skillEntry === "string" ? skillEntry : skillEntry.name;
|
|
2878
|
+
const skillAlias = typeof skillEntry === "string" ? skillName : skillEntry.as || skillName;
|
|
2879
|
+
const skillPath = join11(sourceDir, skillName);
|
|
2880
|
+
if (!existsSync12(skillPath)) {
|
|
2881
|
+
logger.warn(` Skill not found: ${skillName} in ${source.dir}`);
|
|
2882
|
+
continue;
|
|
2883
|
+
}
|
|
2884
|
+
const targetPath = join11(skillsDir, skillAlias);
|
|
2885
|
+
const result = installLink(skillPath, targetPath);
|
|
2886
|
+
if (result !== "failed") {
|
|
2887
|
+
installedSkills.push(skillAlias);
|
|
2888
|
+
} else {
|
|
2889
|
+
logger.warn(` Failed to install skill: ${skillAlias}`);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
1644
2892
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
if (Object.keys(record[tool]).length === 0) {
|
|
1659
|
-
delete record[tool];
|
|
2893
|
+
for (const agentName of plugin.installAgents || []) {
|
|
2894
|
+
const agentFile = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
|
|
2895
|
+
const agentPath = join11(pluginDir, "agents", agentFile);
|
|
2896
|
+
if (!existsSync12(agentPath)) {
|
|
2897
|
+
logger.warn(` Agent not found: ${agentFile}`);
|
|
2898
|
+
continue;
|
|
2899
|
+
}
|
|
2900
|
+
const targetPath = join11(agentsDir, agentFile);
|
|
2901
|
+
const result = installLink(agentPath, targetPath);
|
|
2902
|
+
if (result !== "failed") {
|
|
2903
|
+
installedAgents.push(agentFile);
|
|
2904
|
+
} else {
|
|
2905
|
+
logger.warn(` Failed to install agent: ${agentFile}`);
|
|
1660
2906
|
}
|
|
1661
2907
|
}
|
|
2908
|
+
installConfigFile(pluginDir, plugin.configFile || "AGENTS.md", tool, level, configRoot, cwd, plugin.configRootConfigLink);
|
|
2909
|
+
installWorkflows(pluginDir, configRoot);
|
|
2910
|
+
await installExternalRepos(plugin.externalRepos, repoPath, configRoot, cwd, level);
|
|
2911
|
+
const cannbotManifest = writeCannbotManifest(
|
|
2912
|
+
configRoot,
|
|
2913
|
+
plugin.id,
|
|
2914
|
+
plugin.version || "1.0.0",
|
|
2915
|
+
level,
|
|
2916
|
+
tool,
|
|
2917
|
+
installedSkills,
|
|
2918
|
+
installedAgents
|
|
2919
|
+
);
|
|
2920
|
+
return {
|
|
2921
|
+
success: true,
|
|
2922
|
+
skillsCount: installedSkills.length,
|
|
2923
|
+
agentsCount: installedAgents.length,
|
|
2924
|
+
errors: [],
|
|
2925
|
+
manifest: cannbotManifest
|
|
2926
|
+
};
|
|
2927
|
+
} catch (error) {
|
|
2928
|
+
return {
|
|
2929
|
+
success: false,
|
|
2930
|
+
skillsCount: 0,
|
|
2931
|
+
agentsCount: 0,
|
|
2932
|
+
errors: [error instanceof Error ? error.message : t("error_unknown")],
|
|
2933
|
+
manifest: null
|
|
2934
|
+
};
|
|
1662
2935
|
}
|
|
1663
|
-
writeSkillRecord(record);
|
|
1664
|
-
}
|
|
1665
|
-
function getInstalledSkills(tool, level, installPath) {
|
|
1666
|
-
const record = readSkillRecord();
|
|
1667
|
-
return record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1668
2936
|
}
|
|
1669
2937
|
|
|
1670
2938
|
// src/core/installer.ts
|
|
@@ -1676,18 +2944,114 @@ async function installPlugin(opts) {
|
|
|
1676
2944
|
pluginId: opts.pluginId,
|
|
1677
2945
|
skillsCount: 0,
|
|
1678
2946
|
agentsCount: 0,
|
|
1679
|
-
errors: [
|
|
2947
|
+
errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
|
|
1680
2948
|
warnings: []
|
|
1681
2949
|
};
|
|
1682
2950
|
}
|
|
1683
|
-
const
|
|
1684
|
-
|
|
2951
|
+
const pluginDir = join12(opts.repoPath, plugin.dir);
|
|
2952
|
+
const cwd = opts.installPath || process.cwd();
|
|
2953
|
+
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
2954
|
+
const agentsFile = join12(configRoot, getAgentsFileName(opts.tool));
|
|
2955
|
+
let backupInfo = null;
|
|
2956
|
+
if (existsSync13(agentsFile)) {
|
|
2957
|
+
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
2958
|
+
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
2959
|
+
let choice = "overwrite";
|
|
2960
|
+
if (!opts.yes) {
|
|
2961
|
+
choice = await showOverwriteWarning(
|
|
2962
|
+
currentPlugin.pluginName,
|
|
2963
|
+
plugin.displayName
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
if (choice === "cancel") {
|
|
2967
|
+
logger.info(t("backup_cancel"));
|
|
2968
|
+
return {
|
|
2969
|
+
success: false,
|
|
2970
|
+
pluginId: opts.pluginId,
|
|
2971
|
+
skillsCount: 0,
|
|
2972
|
+
agentsCount: 0,
|
|
2973
|
+
errors: [t("backup_cancel")],
|
|
2974
|
+
warnings: []
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
backupInfo = createBackup(
|
|
2978
|
+
configRoot,
|
|
2979
|
+
opts.tool,
|
|
2980
|
+
currentPlugin.pluginId,
|
|
2981
|
+
currentPlugin.pluginName
|
|
2982
|
+
);
|
|
2983
|
+
if (!backupInfo) {
|
|
2984
|
+
logger.error(t("backup_failed"));
|
|
2985
|
+
return {
|
|
2986
|
+
success: false,
|
|
2987
|
+
pluginId: opts.pluginId,
|
|
2988
|
+
skillsCount: 0,
|
|
2989
|
+
agentsCount: 0,
|
|
2990
|
+
errors: [t("backup_failed")],
|
|
2991
|
+
warnings: []
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
if (plugin.installSkills && plugin.installSkills.length > 0) {
|
|
2998
|
+
const result = await installViaManifest(
|
|
2999
|
+
plugin,
|
|
3000
|
+
opts.repoPath,
|
|
3001
|
+
opts.tool,
|
|
3002
|
+
opts.level,
|
|
3003
|
+
opts.installPath
|
|
3004
|
+
);
|
|
3005
|
+
if (result.success) {
|
|
3006
|
+
try {
|
|
3007
|
+
const record = scanInstalledFiles(
|
|
3008
|
+
opts.pluginId,
|
|
3009
|
+
plugin.displayName,
|
|
3010
|
+
opts.tool,
|
|
3011
|
+
opts.level,
|
|
3012
|
+
cwd,
|
|
3013
|
+
configRoot,
|
|
3014
|
+
result.manifest
|
|
3015
|
+
);
|
|
3016
|
+
if (backupInfo) {
|
|
3017
|
+
record.backup = {
|
|
3018
|
+
filePath: backupInfo.filePath,
|
|
3019
|
+
fromPluginId: backupInfo.pluginId,
|
|
3020
|
+
fromPluginName: backupInfo.pluginName,
|
|
3021
|
+
backupTime: backupInfo.backupTime
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
writeRecord(record);
|
|
3025
|
+
} catch {
|
|
3026
|
+
logger.warn(t("record_write_failed"));
|
|
3027
|
+
}
|
|
3028
|
+
return {
|
|
3029
|
+
success: true,
|
|
3030
|
+
pluginId: opts.pluginId,
|
|
3031
|
+
skillsCount: result.skillsCount,
|
|
3032
|
+
agentsCount: result.agentsCount,
|
|
3033
|
+
errors: result.errors,
|
|
3034
|
+
warnings: []
|
|
3035
|
+
};
|
|
3036
|
+
} else {
|
|
3037
|
+
return {
|
|
3038
|
+
success: false,
|
|
3039
|
+
pluginId: opts.pluginId,
|
|
3040
|
+
skillsCount: 0,
|
|
3041
|
+
agentsCount: 0,
|
|
3042
|
+
errors: result.errors,
|
|
3043
|
+
warnings: []
|
|
3044
|
+
};
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
const scriptPath = join12(pluginDir, plugin.script);
|
|
3048
|
+
if (!existsSync13(scriptPath)) {
|
|
1685
3049
|
return {
|
|
1686
3050
|
success: false,
|
|
1687
3051
|
pluginId: opts.pluginId,
|
|
1688
3052
|
skillsCount: 0,
|
|
1689
3053
|
agentsCount: 0,
|
|
1690
|
-
errors: [
|
|
3054
|
+
errors: [t("error_script_not_found").replace("{path}", scriptPath)],
|
|
1691
3055
|
warnings: []
|
|
1692
3056
|
};
|
|
1693
3057
|
}
|
|
@@ -1695,14 +3059,15 @@ async function installPlugin(opts) {
|
|
|
1695
3059
|
if (opts.installPath) {
|
|
1696
3060
|
args.push(opts.installPath);
|
|
1697
3061
|
}
|
|
1698
|
-
const cwd = opts.installPath || process.cwd();
|
|
1699
3062
|
try {
|
|
1700
|
-
|
|
3063
|
+
const shell = process.platform === "win32" ? "sh" : "bash";
|
|
3064
|
+
const execOptions = {
|
|
1701
3065
|
cwd,
|
|
1702
3066
|
timeout: 3e5,
|
|
1703
|
-
stdio: "pipe"
|
|
1704
|
-
|
|
1705
|
-
|
|
3067
|
+
stdio: "pipe",
|
|
3068
|
+
input: opts.yes ? "y\n" : void 0
|
|
3069
|
+
};
|
|
3070
|
+
await execa4(shell, [scriptPath, ...args], execOptions);
|
|
1706
3071
|
const manifest = readManifest(configRoot);
|
|
1707
3072
|
let skillsCount = 0;
|
|
1708
3073
|
let agentsCount = 0;
|
|
@@ -1723,8 +3088,17 @@ async function installPlugin(opts) {
|
|
|
1723
3088
|
configRoot,
|
|
1724
3089
|
manifest
|
|
1725
3090
|
);
|
|
3091
|
+
if (backupInfo) {
|
|
3092
|
+
record.backup = {
|
|
3093
|
+
filePath: backupInfo.filePath,
|
|
3094
|
+
fromPluginId: backupInfo.pluginId,
|
|
3095
|
+
fromPluginName: backupInfo.pluginName,
|
|
3096
|
+
backupTime: backupInfo.backupTime
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
1726
3099
|
writeRecord(record);
|
|
1727
3100
|
} catch {
|
|
3101
|
+
logger.warn(t("record_write_failed"));
|
|
1728
3102
|
}
|
|
1729
3103
|
return {
|
|
1730
3104
|
success: true,
|
|
@@ -1735,7 +3109,7 @@ async function installPlugin(opts) {
|
|
|
1735
3109
|
warnings: []
|
|
1736
3110
|
};
|
|
1737
3111
|
} catch (error) {
|
|
1738
|
-
const errorMessage = error instanceof Error ? error.message : "
|
|
3112
|
+
const errorMessage = error instanceof Error ? error.message : t("error_unknown");
|
|
1739
3113
|
return {
|
|
1740
3114
|
success: false,
|
|
1741
3115
|
pluginId: opts.pluginId,
|
|
@@ -1748,41 +3122,46 @@ async function installPlugin(opts) {
|
|
|
1748
3122
|
}
|
|
1749
3123
|
|
|
1750
3124
|
// src/core/skill-installer.ts
|
|
1751
|
-
import { existsSync as
|
|
1752
|
-
import { join as
|
|
1753
|
-
import { select as
|
|
1754
|
-
import
|
|
3125
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, unlinkSync as unlinkSync5, readdirSync as readdirSync4, rmdirSync, realpathSync as realpathSync2 } from "fs";
|
|
3126
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
3127
|
+
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
3128
|
+
import chalk5 from "chalk";
|
|
1755
3129
|
import Table from "cli-table3";
|
|
1756
3130
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
1757
3131
|
const configRoot = getConfigRoot(tool, level);
|
|
1758
|
-
const skillsDir =
|
|
3132
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1759
3133
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1760
|
-
if (!
|
|
1761
|
-
|
|
3134
|
+
if (!existsSync14(skillsDir)) {
|
|
3135
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
1762
3136
|
}
|
|
1763
3137
|
const results = [];
|
|
1764
3138
|
const installedIds = [];
|
|
1765
3139
|
for (const skillId of skillIds) {
|
|
1766
3140
|
const skill = findSkill(skillId);
|
|
1767
3141
|
if (!skill) {
|
|
1768
|
-
results.push({ skillId, success: false, error: "
|
|
3142
|
+
results.push({ skillId, success: false, error: t("error_skill_not_found") });
|
|
1769
3143
|
continue;
|
|
1770
3144
|
}
|
|
1771
|
-
const sourcePath =
|
|
1772
|
-
const targetPath =
|
|
1773
|
-
if (!
|
|
1774
|
-
results.push({ skillId, success: false, error:
|
|
3145
|
+
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
3146
|
+
const targetPath = join13(skillsDir, skillId);
|
|
3147
|
+
if (!existsSync14(sourcePath)) {
|
|
3148
|
+
results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
|
|
1775
3149
|
continue;
|
|
1776
3150
|
}
|
|
1777
3151
|
try {
|
|
1778
|
-
|
|
1779
|
-
|
|
3152
|
+
const resolvedSource = realpathSync2(sourcePath);
|
|
3153
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3154
|
+
unlinkSync5(targetPath);
|
|
1780
3155
|
}
|
|
1781
|
-
|
|
3156
|
+
symlinkSync2(resolvedSource, targetPath);
|
|
1782
3157
|
results.push({ skillId, success: true });
|
|
1783
3158
|
installedIds.push(skillId);
|
|
1784
3159
|
} catch (error) {
|
|
1785
|
-
|
|
3160
|
+
let msg = error instanceof Error ? error.message : t("error_unknown");
|
|
3161
|
+
if (process.platform === "win32" && msg.includes("EPERM")) {
|
|
3162
|
+
msg = t("error_symlink_windows");
|
|
3163
|
+
}
|
|
3164
|
+
results.push({ skillId, success: false, error: msg });
|
|
1786
3165
|
}
|
|
1787
3166
|
}
|
|
1788
3167
|
if (installedIds.length > 0) {
|
|
@@ -1792,109 +3171,118 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1792
3171
|
}
|
|
1793
3172
|
async function uninstallSkills(skillIds, tool, level) {
|
|
1794
3173
|
const configRoot = getConfigRoot(tool, level);
|
|
1795
|
-
const skillsDir =
|
|
3174
|
+
const skillsDir = join13(configRoot, "skills");
|
|
1796
3175
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1797
3176
|
const results = [];
|
|
1798
3177
|
const removedIds = [];
|
|
1799
3178
|
for (const skillId of skillIds) {
|
|
1800
|
-
const targetPath =
|
|
3179
|
+
const targetPath = join13(skillsDir, skillId);
|
|
1801
3180
|
try {
|
|
1802
|
-
if (
|
|
1803
|
-
|
|
3181
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3182
|
+
unlinkSync5(targetPath);
|
|
1804
3183
|
results.push({ skillId, success: true });
|
|
1805
3184
|
removedIds.push(skillId);
|
|
1806
|
-
logger.step(`
|
|
3185
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
1807
3186
|
} else {
|
|
1808
|
-
results.push({ skillId, success: false, error: "
|
|
1809
|
-
logger.warn(`${skillId}
|
|
3187
|
+
results.push({ skillId, success: false, error: t("error_not_installed") });
|
|
3188
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
1810
3189
|
}
|
|
1811
3190
|
} catch (error) {
|
|
1812
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
3191
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
1813
3192
|
}
|
|
1814
3193
|
}
|
|
1815
3194
|
if (removedIds.length > 0) {
|
|
1816
3195
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
1817
3196
|
}
|
|
1818
|
-
if (
|
|
3197
|
+
if (existsSync14(skillsDir)) {
|
|
1819
3198
|
try {
|
|
1820
|
-
const entries =
|
|
3199
|
+
const entries = readdirSync4(skillsDir);
|
|
1821
3200
|
if (entries.length === 0) {
|
|
1822
3201
|
rmdirSync(skillsDir);
|
|
1823
|
-
logger.step(`
|
|
1824
|
-
}
|
|
1825
|
-
} catch {
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
if (existsSync9(configRoot)) {
|
|
1829
|
-
try {
|
|
1830
|
-
const entries = readdirSync3(configRoot);
|
|
1831
|
-
if (entries.length === 0) {
|
|
1832
|
-
rmdirSync(configRoot);
|
|
1833
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
3202
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
1834
3203
|
}
|
|
1835
3204
|
} catch {
|
|
1836
3205
|
}
|
|
1837
3206
|
}
|
|
1838
3207
|
return results;
|
|
1839
3208
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
3209
|
+
var SELECT_ALL = "__select_all__";
|
|
3210
|
+
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
3211
|
+
var CONTINUE_SELECT = "__continue_select__";
|
|
3212
|
+
async function interactiveSkillSelect(tool, level) {
|
|
1843
3213
|
const categories = getAllCategories();
|
|
3214
|
+
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
3215
|
+
let accumulated = [];
|
|
1844
3216
|
let step = 0;
|
|
1845
3217
|
let selectedCategoryId = "";
|
|
1846
3218
|
while (true) {
|
|
1847
3219
|
switch (step) {
|
|
1848
3220
|
case 0: {
|
|
1849
|
-
|
|
3221
|
+
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");
|
|
3222
|
+
printBoxTitle(titleSuffix);
|
|
1850
3223
|
const categoryChoices = [
|
|
3224
|
+
{
|
|
3225
|
+
name: `>> ${t("skill_select_all").replace("{count}", String(allSkillIds.length))}`,
|
|
3226
|
+
value: SELECT_ALL
|
|
3227
|
+
},
|
|
3228
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1851
3229
|
...categories.map((cat) => ({
|
|
1852
3230
|
name: `> ${cat.name} (${cat.skills.length} skills)`,
|
|
1853
3231
|
value: cat.id
|
|
1854
3232
|
})),
|
|
1855
3233
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1856
|
-
{ name:
|
|
1857
|
-
{ name: "
|
|
3234
|
+
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
3235
|
+
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
3236
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
1858
3237
|
];
|
|
1859
3238
|
showOperationHints();
|
|
1860
|
-
selectedCategoryId = await
|
|
3239
|
+
selectedCategoryId = await select3({
|
|
1861
3240
|
message: t("skill_select_category"),
|
|
1862
3241
|
choices: categoryChoices,
|
|
1863
3242
|
loop: false,
|
|
1864
3243
|
theme: selectTheme,
|
|
1865
3244
|
pageSize: 15
|
|
1866
3245
|
});
|
|
1867
|
-
if (selectedCategoryId ===
|
|
1868
|
-
if (selectedCategoryId ===
|
|
3246
|
+
if (selectedCategoryId === BACK) return "back";
|
|
3247
|
+
if (selectedCategoryId === CANCEL) return "cancel";
|
|
3248
|
+
if (selectedCategoryId === SELECT_ALL) {
|
|
3249
|
+
return allSkillIds;
|
|
3250
|
+
}
|
|
3251
|
+
if (selectedCategoryId === "install") {
|
|
3252
|
+
return accumulated;
|
|
3253
|
+
}
|
|
1869
3254
|
step = 1;
|
|
1870
3255
|
break;
|
|
1871
3256
|
}
|
|
1872
3257
|
case 1: {
|
|
1873
3258
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
1874
3259
|
if (!category) return [];
|
|
1875
|
-
printBoxTitle(
|
|
1876
|
-
const
|
|
1877
|
-
const
|
|
1878
|
-
const installedSkills = [];
|
|
1879
|
-
for (const tk of tools) {
|
|
1880
|
-
for (const l of levels) {
|
|
1881
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
1882
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
3260
|
+
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
3261
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
3262
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
1885
3263
|
const installedSet = new Set(installedSkills);
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
name:
|
|
1891
|
-
value:
|
|
1892
|
-
checked:
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
3264
|
+
const categorySkillIds = new Set(category.skills.map((s) => s.id));
|
|
3265
|
+
const accumulatedInCategory = new Set(accumulated.filter((id) => categorySkillIds.has(id)));
|
|
3266
|
+
const skillChoices = [
|
|
3267
|
+
{
|
|
3268
|
+
name: `>> ${t("skill_select_all_category")}`,
|
|
3269
|
+
value: SELECT_ALL_CATEGORY,
|
|
3270
|
+
checked: accumulatedInCategory.size === category.skills.length,
|
|
3271
|
+
description: t("skill_select_all_category")
|
|
3272
|
+
},
|
|
3273
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
3274
|
+
...category.skills.map((skill) => {
|
|
3275
|
+
const isInstalled = installedSet.has(skill.id);
|
|
3276
|
+
const isAccumulated = accumulatedInCategory.has(skill.id);
|
|
3277
|
+
const suffix = isInstalled ? ` [${t("skill_already_installed")}]` : isAccumulated ? " [+]" : "";
|
|
3278
|
+
return {
|
|
3279
|
+
name: `${chalk5.cyan(skill.id)}${suffix}`,
|
|
3280
|
+
value: skill.id,
|
|
3281
|
+
checked: isAccumulated,
|
|
3282
|
+
description: skill.description
|
|
3283
|
+
};
|
|
3284
|
+
})
|
|
3285
|
+
];
|
|
1898
3286
|
showOperationHints(true);
|
|
1899
3287
|
const selectedSkills = await checkbox2({
|
|
1900
3288
|
message: t("skill_select_items"),
|
|
@@ -1904,18 +3292,21 @@ async function interactiveSkillSelect() {
|
|
|
1904
3292
|
theme: checkboxTheme,
|
|
1905
3293
|
pageSize: 15
|
|
1906
3294
|
});
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
break;
|
|
3295
|
+
let skillIds;
|
|
3296
|
+
if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
|
|
3297
|
+
skillIds = category.skills.map((s) => s.id);
|
|
3298
|
+
} else {
|
|
3299
|
+
skillIds = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
|
|
1913
3300
|
}
|
|
1914
|
-
const skillIds = selectedSkills.filter((v) => v !== BACK3 && v !== CANCEL3);
|
|
1915
3301
|
if (skillIds.length === 0) {
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
3302
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
3303
|
+
const action2 = await select3({
|
|
3304
|
+
message: accumulated.length > 0 ? t("skill_continue_or_install").replace("{count}", String(accumulated.length)) : t("wizard_no_selection"),
|
|
3305
|
+
choices: accumulated.length > 0 ? [
|
|
3306
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
3307
|
+
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
3308
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
3309
|
+
] : [
|
|
1919
3310
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1920
3311
|
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
1921
3312
|
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
@@ -1923,13 +3314,38 @@ async function interactiveSkillSelect() {
|
|
|
1923
3314
|
loop: false,
|
|
1924
3315
|
theme: selectTheme
|
|
1925
3316
|
});
|
|
1926
|
-
if (
|
|
3317
|
+
if (action2 === "install") return accumulated;
|
|
3318
|
+
if (action2 === "back") {
|
|
1927
3319
|
step = 0;
|
|
1928
3320
|
break;
|
|
1929
3321
|
}
|
|
1930
3322
|
return "cancel";
|
|
1931
3323
|
}
|
|
1932
|
-
|
|
3324
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
3325
|
+
for (const id of skillIds) {
|
|
3326
|
+
if (!accumulated.includes(id)) {
|
|
3327
|
+
accumulated.push(id);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
const action = await select3({
|
|
3331
|
+
message: t("skill_continue_or_install").replace("{count}", String(accumulated.length)),
|
|
3332
|
+
choices: [
|
|
3333
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
3334
|
+
{ name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
|
|
3335
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
3336
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
3337
|
+
],
|
|
3338
|
+
loop: false,
|
|
3339
|
+
theme: selectTheme
|
|
3340
|
+
});
|
|
3341
|
+
if (action === "install") {
|
|
3342
|
+
return accumulated;
|
|
3343
|
+
}
|
|
3344
|
+
if (action === CONTINUE_SELECT) {
|
|
3345
|
+
step = 0;
|
|
3346
|
+
break;
|
|
3347
|
+
}
|
|
3348
|
+
return "cancel";
|
|
1933
3349
|
}
|
|
1934
3350
|
}
|
|
1935
3351
|
}
|
|
@@ -1937,11 +3353,11 @@ async function interactiveSkillSelect() {
|
|
|
1937
3353
|
function listAllSkills() {
|
|
1938
3354
|
const categories = getAllCategories();
|
|
1939
3355
|
console.log();
|
|
1940
|
-
console.log(
|
|
1941
|
-
console.log(
|
|
3356
|
+
console.log(chalk5.bold(` ${t("skill_list_title")}`));
|
|
3357
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
1942
3358
|
for (const category of categories) {
|
|
1943
3359
|
console.log();
|
|
1944
|
-
console.log(
|
|
3360
|
+
console.log(chalk5.bold(` ${category.name}`) + chalk5.dim(` (${category.skills.length} skills)`));
|
|
1945
3361
|
const table = new Table({
|
|
1946
3362
|
style: { head: [], border: [] },
|
|
1947
3363
|
colWidths: [35, 45],
|
|
@@ -1949,51 +3365,54 @@ function listAllSkills() {
|
|
|
1949
3365
|
});
|
|
1950
3366
|
for (const skill of category.skills) {
|
|
1951
3367
|
table.push([
|
|
1952
|
-
|
|
3368
|
+
chalk5.cyan(skill.id),
|
|
1953
3369
|
skill.description
|
|
1954
3370
|
]);
|
|
1955
3371
|
}
|
|
1956
3372
|
console.log(table.toString());
|
|
1957
3373
|
}
|
|
1958
3374
|
console.log();
|
|
1959
|
-
console.log(
|
|
3375
|
+
console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
1960
3376
|
console.log();
|
|
1961
3377
|
}
|
|
1962
|
-
function
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
3378
|
+
function resolveSkillSourcePath(skill, repoPath) {
|
|
3379
|
+
if (skill.filePath) {
|
|
3380
|
+
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join13(repoPath, skill.filePath);
|
|
3381
|
+
if (existsSync14(absPath)) {
|
|
3382
|
+
return dirname6(absPath);
|
|
3383
|
+
}
|
|
1967
3384
|
}
|
|
3385
|
+
return join13(repoPath, skill.source, skill.id);
|
|
1968
3386
|
}
|
|
1969
3387
|
|
|
1970
3388
|
// src/ui/display.ts
|
|
1971
|
-
import
|
|
3389
|
+
import chalk6 from "chalk";
|
|
1972
3390
|
import Table2 from "cli-table3";
|
|
1973
3391
|
function printPluginList(plugins, installed) {
|
|
1974
3392
|
const table = new Table2({
|
|
1975
3393
|
head: [
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
chalk4.cyan(t("list_agents"))
|
|
3394
|
+
chalk6.cyan(t("list_id")),
|
|
3395
|
+
chalk6.cyan(t("list_name")),
|
|
3396
|
+
chalk6.cyan(t("list_status")),
|
|
3397
|
+
chalk6.cyan(t("list_description"))
|
|
1981
3398
|
],
|
|
1982
|
-
style: { head: [], border: [] }
|
|
3399
|
+
style: { head: [], border: [] },
|
|
3400
|
+
colWidths: [6, 32, 8, 50],
|
|
3401
|
+
wordWrap: true
|
|
1983
3402
|
});
|
|
1984
3403
|
plugins.forEach((plugin, index) => {
|
|
1985
3404
|
const isInstalled = installed.has(plugin.id);
|
|
1986
|
-
const status = isInstalled ?
|
|
3405
|
+
const status = isInstalled ? chalk6.green(`\u2713 ${t("status_installed")}`) : chalk6.dim(`\u2014`);
|
|
1987
3406
|
table.push([
|
|
1988
3407
|
String(index + 1),
|
|
1989
3408
|
plugin.displayName,
|
|
1990
3409
|
status,
|
|
1991
|
-
|
|
1992
|
-
String(plugin.agents)
|
|
3410
|
+
plugin.description || chalk6.dim("\u2014")
|
|
1993
3411
|
]);
|
|
1994
3412
|
});
|
|
1995
3413
|
console.log();
|
|
1996
|
-
|
|
3414
|
+
const countSuffix = t("count_suffix");
|
|
3415
|
+
console.log(chalk6.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
1997
3416
|
console.log(table.toString());
|
|
1998
3417
|
console.log();
|
|
1999
3418
|
}
|
|
@@ -2002,7 +3421,7 @@ function printInstallSummary(results) {
|
|
|
2002
3421
|
const totalCount = results.length;
|
|
2003
3422
|
console.log();
|
|
2004
3423
|
console.log(
|
|
2005
|
-
|
|
3424
|
+
chalk6.bold(
|
|
2006
3425
|
` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
|
|
2007
3426
|
)
|
|
2008
3427
|
);
|
|
@@ -2010,10 +3429,10 @@ function printInstallSummary(results) {
|
|
|
2010
3429
|
for (const result of results) {
|
|
2011
3430
|
if (result.success) {
|
|
2012
3431
|
console.log(
|
|
2013
|
-
|
|
3432
|
+
chalk6.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
|
|
2014
3433
|
);
|
|
2015
3434
|
} else {
|
|
2016
|
-
console.log(
|
|
3435
|
+
console.log(chalk6.red(" \u2717") + ` ${result.displayName}`);
|
|
2017
3436
|
}
|
|
2018
3437
|
}
|
|
2019
3438
|
console.log();
|
|
@@ -2026,22 +3445,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2026
3445
|
return;
|
|
2027
3446
|
}
|
|
2028
3447
|
const toolName = getToolDisplayName(tool);
|
|
3448
|
+
const countSuffix = t("count_suffix");
|
|
2029
3449
|
const lines = [
|
|
2030
|
-
|
|
3450
|
+
chalk6.bold(` ${t("install_done")}!`),
|
|
2031
3451
|
"",
|
|
2032
|
-
` ${
|
|
2033
|
-
` ${
|
|
3452
|
+
` ${chalk6.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
3453
|
+
` ${chalk6.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk6.dim("|")} ${chalk6.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2034
3454
|
"",
|
|
2035
|
-
|
|
2036
|
-
` ${
|
|
2037
|
-
` ${
|
|
2038
|
-
` ${
|
|
2039
|
-
` ${
|
|
3455
|
+
chalk6.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
3456
|
+
` ${chalk6.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk6.green(toolName.toLowerCase())}`,
|
|
3457
|
+
` ${chalk6.cyan("2.")} ${t("install_enhanced_try")}: ${chalk6.green(t("try_prompt"))}`,
|
|
3458
|
+
` ${chalk6.cyan("3.")} ${t("install_enhanced_more")}: ${chalk6.green("install-helper list")}`,
|
|
3459
|
+
` ${chalk6.cyan("4.")} ${t("install_enhanced_check")}: ${chalk6.green("install-helper doctor")}`,
|
|
2040
3460
|
""
|
|
2041
3461
|
];
|
|
2042
3462
|
if (successResults.length === 1) {
|
|
3463
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
3464
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2043
3465
|
lines.push(
|
|
2044
|
-
` ${
|
|
3466
|
+
` ${chalk6.dim(t("install_enhanced_docs"))}: ${chalk6.dim(docsPath)}`
|
|
2045
3467
|
);
|
|
2046
3468
|
}
|
|
2047
3469
|
console.log();
|
|
@@ -2052,34 +3474,32 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2052
3474
|
}
|
|
2053
3475
|
|
|
2054
3476
|
// src/commands/init.ts
|
|
2055
|
-
import { existsSync as
|
|
2056
|
-
import { join as
|
|
2057
|
-
var BACK2 = "__back__";
|
|
2058
|
-
var CANCEL2 = "__cancel__";
|
|
3477
|
+
import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
|
|
3478
|
+
import { join as join14 } from "path";
|
|
2059
3479
|
async function initCommand() {
|
|
2060
3480
|
const cwd = process.cwd();
|
|
2061
|
-
if (
|
|
3481
|
+
if (existsSync15(join14(cwd, "package.json")) && (() => {
|
|
2062
3482
|
try {
|
|
2063
|
-
const pkg = JSON.parse(
|
|
3483
|
+
const pkg = JSON.parse(readFileSync8(join14(cwd, "package.json"), "utf-8"));
|
|
2064
3484
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2065
3485
|
} catch {
|
|
2066
3486
|
return false;
|
|
2067
3487
|
}
|
|
2068
3488
|
})()) {
|
|
2069
|
-
logger.warn("
|
|
2070
|
-
logger.info("
|
|
2071
|
-
logger.info("
|
|
3489
|
+
logger.warn(t("init_pkg_dir_warn"));
|
|
3490
|
+
logger.info(t("init_pkg_dir_hint"));
|
|
3491
|
+
logger.info(t("init_pkg_dir_example"));
|
|
2072
3492
|
return;
|
|
2073
3493
|
}
|
|
2074
3494
|
printBanner(t("wizard_title"));
|
|
2075
|
-
const spinner = createSpinner("
|
|
3495
|
+
const spinner = createSpinner(t("loading_skills_list"));
|
|
2076
3496
|
spinner.start();
|
|
2077
3497
|
try {
|
|
2078
3498
|
const repoManager = createRepositoryManager();
|
|
2079
3499
|
await repoManager.ensureRepoAndScan();
|
|
2080
|
-
spinner.succeed("
|
|
3500
|
+
spinner.succeed(t("loading_skills_list_complete"));
|
|
2081
3501
|
} catch {
|
|
2082
|
-
spinner.warn("
|
|
3502
|
+
spinner.warn(t("loading_skills_list_failed"));
|
|
2083
3503
|
}
|
|
2084
3504
|
let step = 0;
|
|
2085
3505
|
let mode = "plugin";
|
|
@@ -2113,8 +3533,7 @@ async function initCommand() {
|
|
|
2113
3533
|
}
|
|
2114
3534
|
}
|
|
2115
3535
|
async function selectMode() {
|
|
2116
|
-
printBoxTitle("
|
|
2117
|
-
const CANCEL3 = "__cancel__";
|
|
3536
|
+
printBoxTitle(t("wizard_select_mode_title"));
|
|
2118
3537
|
const choices = [
|
|
2119
3538
|
{
|
|
2120
3539
|
name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
|
|
@@ -2125,30 +3544,30 @@ async function selectMode() {
|
|
|
2125
3544
|
value: "skill"
|
|
2126
3545
|
},
|
|
2127
3546
|
new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2128
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3547
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2129
3548
|
];
|
|
2130
3549
|
showOperationHints();
|
|
2131
|
-
const result = await
|
|
3550
|
+
const result = await select4({
|
|
2132
3551
|
message: t("wizard_select_mode"),
|
|
2133
3552
|
choices,
|
|
2134
3553
|
loop: false,
|
|
2135
3554
|
theme: selectTheme
|
|
2136
3555
|
});
|
|
2137
|
-
if (result ===
|
|
3556
|
+
if (result === CANCEL) return null;
|
|
2138
3557
|
return result;
|
|
2139
3558
|
}
|
|
2140
3559
|
async function pluginInstallFlow() {
|
|
2141
3560
|
const answers = await runWizard();
|
|
2142
3561
|
if (!answers.confirmed) {
|
|
2143
3562
|
if (answers.back) return "back";
|
|
2144
|
-
logger.info("
|
|
3563
|
+
logger.info(t("init_cancelled"));
|
|
2145
3564
|
return "done";
|
|
2146
3565
|
}
|
|
2147
3566
|
const repoManager = createRepositoryManager();
|
|
2148
|
-
const spinner = createSpinner("
|
|
3567
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2149
3568
|
spinner.start();
|
|
2150
3569
|
const repoPath = await repoManager.ensureRepo();
|
|
2151
|
-
spinner.succeed("
|
|
3570
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2152
3571
|
const allPlugins = getAllPlugins();
|
|
2153
3572
|
const selectedPlugins = answers.plugins.map(
|
|
2154
3573
|
(id) => allPlugins.find((p) => p.id === id)
|
|
@@ -2202,7 +3621,7 @@ async function skillInstallFlow() {
|
|
|
2202
3621
|
const result = await selectToolWithDetection();
|
|
2203
3622
|
if (result === "back") return "back";
|
|
2204
3623
|
if (result === "cancel") {
|
|
2205
|
-
logger.info("
|
|
3624
|
+
logger.info(t("init_cancelled"));
|
|
2206
3625
|
return "done";
|
|
2207
3626
|
}
|
|
2208
3627
|
tool = result;
|
|
@@ -2211,12 +3630,12 @@ async function skillInstallFlow() {
|
|
|
2211
3630
|
}
|
|
2212
3631
|
case 1: {
|
|
2213
3632
|
const result = await stepLevel();
|
|
2214
|
-
if (result ===
|
|
3633
|
+
if (result === BACK) {
|
|
2215
3634
|
step = 0;
|
|
2216
3635
|
break;
|
|
2217
3636
|
}
|
|
2218
|
-
if (result ===
|
|
2219
|
-
logger.info("
|
|
3637
|
+
if (result === CANCEL) {
|
|
3638
|
+
logger.info(t("init_cancelled"));
|
|
2220
3639
|
return "done";
|
|
2221
3640
|
}
|
|
2222
3641
|
level = result;
|
|
@@ -2224,25 +3643,25 @@ async function skillInstallFlow() {
|
|
|
2224
3643
|
break;
|
|
2225
3644
|
}
|
|
2226
3645
|
case 2: {
|
|
2227
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
3646
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2228
3647
|
if (selectedSkills === "back") {
|
|
2229
3648
|
step = 1;
|
|
2230
3649
|
break;
|
|
2231
3650
|
}
|
|
2232
3651
|
if (selectedSkills === "cancel") {
|
|
2233
|
-
logger.info("
|
|
3652
|
+
logger.info(t("init_cancelled"));
|
|
2234
3653
|
return "done";
|
|
2235
3654
|
}
|
|
2236
3655
|
if (selectedSkills.length === 0) {
|
|
2237
|
-
logger.info("
|
|
3656
|
+
logger.info(t("install_no_skill_selected"));
|
|
2238
3657
|
return "done";
|
|
2239
3658
|
}
|
|
2240
3659
|
const repoManager = createRepositoryManager();
|
|
2241
|
-
const spinner = createSpinner("
|
|
3660
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2242
3661
|
spinner.start();
|
|
2243
3662
|
const repoPath = await repoManager.ensureRepo();
|
|
2244
|
-
spinner.succeed("
|
|
2245
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
3663
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3664
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk7.bold(String(selectedSkills.length)))}...`);
|
|
2246
3665
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2247
3666
|
let successCount = 0;
|
|
2248
3667
|
let failCount = 0;
|
|
@@ -2260,10 +3679,10 @@ async function skillInstallFlow() {
|
|
|
2260
3679
|
}
|
|
2261
3680
|
const configRoot = getConfigRoot(tool, level);
|
|
2262
3681
|
logger.blank();
|
|
2263
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
3682
|
+
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)))}`);
|
|
2264
3683
|
logger.blank();
|
|
2265
|
-
logger.info(
|
|
2266
|
-
logger.info(
|
|
3684
|
+
logger.info(`${t("install_to")}: ${chalk7.cyan(configRoot + "/skills/")}`);
|
|
3685
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
|
|
2267
3686
|
logger.blank();
|
|
2268
3687
|
return "done";
|
|
2269
3688
|
}
|
|
@@ -2285,32 +3704,31 @@ async function listCommand() {
|
|
|
2285
3704
|
}
|
|
2286
3705
|
|
|
2287
3706
|
// src/commands/doctor.ts
|
|
2288
|
-
import
|
|
2289
|
-
import { existsSync as
|
|
2290
|
-
import { join as
|
|
3707
|
+
import chalk8 from "chalk";
|
|
3708
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
3709
|
+
import { join as join15 } from "path";
|
|
2291
3710
|
async function doctorCommand(options = {}) {
|
|
2292
3711
|
console.log();
|
|
2293
|
-
console.log(
|
|
2294
|
-
console.log(
|
|
2295
|
-
let errors = 0;
|
|
3712
|
+
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
3713
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2296
3714
|
let warnings = 0;
|
|
2297
3715
|
let fixes = 0;
|
|
2298
3716
|
console.log();
|
|
2299
|
-
console.log(
|
|
3717
|
+
console.log(chalk8.bold(` ${t("doctor_tools")}`));
|
|
2300
3718
|
const detectedTools = await detectTools();
|
|
2301
3719
|
const allTools = getAllTools();
|
|
2302
3720
|
for (const tool of allTools) {
|
|
2303
3721
|
const detected = detectedTools.find((d) => d.name === tool);
|
|
2304
3722
|
if (detected) {
|
|
2305
3723
|
console.log(
|
|
2306
|
-
|
|
3724
|
+
chalk8.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2307
3725
|
);
|
|
2308
3726
|
} else {
|
|
2309
|
-
console.log(
|
|
3727
|
+
console.log(chalk8.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2310
3728
|
}
|
|
2311
3729
|
}
|
|
2312
3730
|
console.log();
|
|
2313
|
-
console.log(
|
|
3731
|
+
console.log(chalk8.bold(` ${t("doctor_plugins")}`));
|
|
2314
3732
|
const plugins = getAllPlugins();
|
|
2315
3733
|
const installed = scanInstalled();
|
|
2316
3734
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
@@ -2318,80 +3736,80 @@ async function doctorCommand(options = {}) {
|
|
|
2318
3736
|
const inst = installedMap.get(plugin.id);
|
|
2319
3737
|
if (inst) {
|
|
2320
3738
|
console.log(
|
|
2321
|
-
|
|
3739
|
+
chalk8.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2322
3740
|
);
|
|
2323
3741
|
} else {
|
|
2324
|
-
console.log(
|
|
3742
|
+
console.log(chalk8.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2325
3743
|
}
|
|
2326
3744
|
}
|
|
2327
3745
|
console.log();
|
|
2328
|
-
console.log(
|
|
3746
|
+
console.log(chalk8.bold(` ${t("doctor_links")}`));
|
|
2329
3747
|
const primaryTool = detectedTools[0]?.name || "opencode";
|
|
2330
3748
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2331
3749
|
const skillsDir = getSkillsDir(configRoot);
|
|
2332
3750
|
const agentsDir = getAgentsDir(configRoot);
|
|
2333
|
-
if (
|
|
3751
|
+
if (existsSync16(skillsDir)) {
|
|
2334
3752
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2335
3753
|
if (brokenLinks === 0) {
|
|
2336
|
-
const count =
|
|
2337
|
-
console.log(
|
|
3754
|
+
const count = readdirSync5(skillsDir).length;
|
|
3755
|
+
console.log(chalk8.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2338
3756
|
} else {
|
|
2339
|
-
console.log(
|
|
3757
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2340
3758
|
warnings++;
|
|
2341
3759
|
if (options.fix) {
|
|
2342
3760
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2343
3761
|
fixes += fixed;
|
|
2344
|
-
console.log(
|
|
3762
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2345
3763
|
}
|
|
2346
3764
|
}
|
|
2347
3765
|
} else {
|
|
2348
|
-
console.log(
|
|
3766
|
+
console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2349
3767
|
if (options.fix) {
|
|
2350
|
-
|
|
3768
|
+
mkdirSync7(skillsDir, { recursive: true });
|
|
2351
3769
|
fixes++;
|
|
2352
|
-
console.log(
|
|
3770
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2353
3771
|
}
|
|
2354
3772
|
}
|
|
2355
|
-
if (
|
|
3773
|
+
if (existsSync16(agentsDir)) {
|
|
2356
3774
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2357
3775
|
if (brokenLinks === 0) {
|
|
2358
|
-
const count =
|
|
2359
|
-
console.log(
|
|
3776
|
+
const count = readdirSync5(agentsDir).length;
|
|
3777
|
+
console.log(chalk8.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2360
3778
|
} else {
|
|
2361
|
-
console.log(
|
|
3779
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2362
3780
|
warnings++;
|
|
2363
3781
|
if (options.fix) {
|
|
2364
3782
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2365
3783
|
fixes += fixed;
|
|
2366
|
-
console.log(
|
|
3784
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2367
3785
|
}
|
|
2368
3786
|
}
|
|
2369
3787
|
} else {
|
|
2370
|
-
console.log(
|
|
3788
|
+
console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2371
3789
|
if (options.fix) {
|
|
2372
|
-
|
|
3790
|
+
mkdirSync7(agentsDir, { recursive: true });
|
|
2373
3791
|
fixes++;
|
|
2374
|
-
console.log(
|
|
3792
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2375
3793
|
}
|
|
2376
3794
|
}
|
|
2377
3795
|
console.log();
|
|
2378
|
-
console.log(
|
|
3796
|
+
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2379
3797
|
const configFile = getConfigFileName(primaryTool);
|
|
2380
|
-
const configPath =
|
|
2381
|
-
if (
|
|
2382
|
-
console.log(
|
|
3798
|
+
const configPath = join15(process.cwd(), configFile);
|
|
3799
|
+
if (existsSync16(configPath)) {
|
|
3800
|
+
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2383
3801
|
} else {
|
|
2384
|
-
console.log(
|
|
3802
|
+
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2385
3803
|
}
|
|
2386
3804
|
console.log();
|
|
2387
|
-
console.log(
|
|
3805
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2388
3806
|
if (options.fix && fixes > 0) {
|
|
2389
3807
|
console.log(
|
|
2390
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3808
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
|
|
2391
3809
|
);
|
|
2392
3810
|
} else {
|
|
2393
3811
|
console.log(
|
|
2394
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3812
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
|
|
2395
3813
|
);
|
|
2396
3814
|
}
|
|
2397
3815
|
console.log();
|
|
@@ -2399,13 +3817,13 @@ async function doctorCommand(options = {}) {
|
|
|
2399
3817
|
function checkBrokenLinks(dir) {
|
|
2400
3818
|
let broken = 0;
|
|
2401
3819
|
try {
|
|
2402
|
-
const entries =
|
|
3820
|
+
const entries = readdirSync5(dir);
|
|
2403
3821
|
for (const entry of entries) {
|
|
2404
|
-
const fullPath =
|
|
3822
|
+
const fullPath = join15(dir, entry);
|
|
2405
3823
|
try {
|
|
2406
|
-
const stats =
|
|
3824
|
+
const stats = lstatSync2(fullPath);
|
|
2407
3825
|
if (stats.isSymbolicLink()) {
|
|
2408
|
-
if (!
|
|
3826
|
+
if (!existsSync16(fullPath)) {
|
|
2409
3827
|
broken++;
|
|
2410
3828
|
}
|
|
2411
3829
|
}
|
|
@@ -2420,14 +3838,14 @@ function checkBrokenLinks(dir) {
|
|
|
2420
3838
|
function fixBrokenLinks(dir) {
|
|
2421
3839
|
let fixed = 0;
|
|
2422
3840
|
try {
|
|
2423
|
-
const entries =
|
|
3841
|
+
const entries = readdirSync5(dir);
|
|
2424
3842
|
for (const entry of entries) {
|
|
2425
|
-
const fullPath =
|
|
3843
|
+
const fullPath = join15(dir, entry);
|
|
2426
3844
|
try {
|
|
2427
|
-
const stats =
|
|
3845
|
+
const stats = lstatSync2(fullPath);
|
|
2428
3846
|
if (stats.isSymbolicLink()) {
|
|
2429
|
-
if (!
|
|
2430
|
-
|
|
3847
|
+
if (!existsSync16(fullPath)) {
|
|
3848
|
+
unlinkSync6(fullPath);
|
|
2431
3849
|
fixed++;
|
|
2432
3850
|
}
|
|
2433
3851
|
}
|
|
@@ -2440,36 +3858,35 @@ function fixBrokenLinks(dir) {
|
|
|
2440
3858
|
}
|
|
2441
3859
|
|
|
2442
3860
|
// src/commands/status.ts
|
|
2443
|
-
import
|
|
3861
|
+
import chalk9 from "chalk";
|
|
2444
3862
|
async function statusCommand() {
|
|
2445
|
-
const plugins = getAllPlugins();
|
|
2446
3863
|
const installed = scanInstalled();
|
|
2447
3864
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2448
3865
|
console.log();
|
|
2449
|
-
console.log(
|
|
3866
|
+
console.log(chalk9.bold(` ${t("status_title")}`));
|
|
2450
3867
|
console.log();
|
|
2451
3868
|
if (installed.length === 0) {
|
|
2452
|
-
console.log(
|
|
2453
|
-
console.log(
|
|
3869
|
+
console.log(chalk9.dim(` ${t("status_none")}`));
|
|
3870
|
+
console.log(chalk9.dim(` ${t("status_hint")}`));
|
|
2454
3871
|
console.log();
|
|
2455
3872
|
return;
|
|
2456
3873
|
}
|
|
2457
3874
|
for (const inst of installed) {
|
|
2458
3875
|
console.log(
|
|
2459
|
-
|
|
3876
|
+
chalk9.green(" \u2713") + ` ${inst.displayName}` + chalk9.dim(` (${inst.tool}, ${inst.level})`)
|
|
2460
3877
|
);
|
|
2461
3878
|
console.log(
|
|
2462
|
-
|
|
3879
|
+
chalk9.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
|
|
2463
3880
|
);
|
|
2464
|
-
console.log(
|
|
2465
|
-
console.log(
|
|
3881
|
+
console.log(chalk9.dim(` ${inst.configRoot}`));
|
|
3882
|
+
console.log(chalk9.dim(` ${t("status_install_time")}: ${inst.installTime}`));
|
|
2466
3883
|
console.log();
|
|
2467
3884
|
}
|
|
2468
3885
|
}
|
|
2469
3886
|
|
|
2470
3887
|
// src/commands/install.ts
|
|
2471
3888
|
import { confirm } from "@inquirer/prompts";
|
|
2472
|
-
import
|
|
3889
|
+
import chalk10 from "chalk";
|
|
2473
3890
|
async function installCommand(names, options) {
|
|
2474
3891
|
if (options.list) {
|
|
2475
3892
|
try {
|
|
@@ -2480,79 +3897,150 @@ async function installCommand(names, options) {
|
|
|
2480
3897
|
listAllSkills();
|
|
2481
3898
|
return;
|
|
2482
3899
|
}
|
|
3900
|
+
if (options.all) {
|
|
3901
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
3902
|
+
scanSpinner2.start();
|
|
3903
|
+
try {
|
|
3904
|
+
const scanRepoManager = createRepositoryManager();
|
|
3905
|
+
await scanRepoManager.ensureRepoAndScan();
|
|
3906
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
3907
|
+
} catch {
|
|
3908
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
3909
|
+
}
|
|
3910
|
+
const allSkills = getAllSkills();
|
|
3911
|
+
const skillIds = allSkills.map((s) => s.id);
|
|
3912
|
+
if (skillIds.length === 0) {
|
|
3913
|
+
logger.error(t("install_no_skill_selected"));
|
|
3914
|
+
return;
|
|
3915
|
+
}
|
|
3916
|
+
let tool2;
|
|
3917
|
+
if (options.tool) {
|
|
3918
|
+
tool2 = validateTool(options.tool);
|
|
3919
|
+
} else {
|
|
3920
|
+
const detected = await selectToolWithDetection();
|
|
3921
|
+
if (detected === "back" || detected === "cancel") {
|
|
3922
|
+
logger.info(t("init_cancelled"));
|
|
3923
|
+
return;
|
|
3924
|
+
}
|
|
3925
|
+
tool2 = detected;
|
|
3926
|
+
}
|
|
3927
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
3928
|
+
const repoManager = createRepositoryManager();
|
|
3929
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
3930
|
+
spinner.start();
|
|
3931
|
+
const repoPath = await repoManager.ensureRepo();
|
|
3932
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3933
|
+
logger.info(`${t("skill_all_install_progress").replace("{count}", chalk10.bold(String(skillIds.length)))}`);
|
|
3934
|
+
const results = await installSkills(skillIds, tool2, level2, repoPath);
|
|
3935
|
+
let successCount = 0;
|
|
3936
|
+
let failCount = 0;
|
|
3937
|
+
const total = results.length;
|
|
3938
|
+
for (let i = 0; i < results.length; i++) {
|
|
3939
|
+
const result = results[i];
|
|
3940
|
+
const progress = `[${i + 1}/${total}]`;
|
|
3941
|
+
if (result.success) {
|
|
3942
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
3943
|
+
successCount++;
|
|
3944
|
+
} else {
|
|
3945
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
3946
|
+
failCount++;
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
const configRoot = getConfigRoot(tool2, level2);
|
|
3950
|
+
logger.blank();
|
|
3951
|
+
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)))}`);
|
|
3952
|
+
logger.blank();
|
|
3953
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
3954
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
3955
|
+
logger.blank();
|
|
3956
|
+
return;
|
|
3957
|
+
}
|
|
2483
3958
|
if (names.length === 0) {
|
|
2484
|
-
const scanSpinner2 = createSpinner("
|
|
3959
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2485
3960
|
scanSpinner2.start();
|
|
2486
3961
|
try {
|
|
2487
3962
|
const scanRepoManager = createRepositoryManager();
|
|
2488
3963
|
await scanRepoManager.ensureRepoAndScan();
|
|
2489
|
-
scanSpinner2.succeed("
|
|
3964
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2490
3965
|
} catch {
|
|
2491
|
-
scanSpinner2.warn("
|
|
3966
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2492
3967
|
}
|
|
2493
3968
|
let tool2;
|
|
2494
3969
|
if (options.tool) {
|
|
2495
|
-
tool2 = options.tool;
|
|
3970
|
+
tool2 = validateTool(options.tool);
|
|
2496
3971
|
} else {
|
|
2497
3972
|
const detected = await selectToolWithDetection();
|
|
2498
|
-
if (detected === "back")
|
|
3973
|
+
if (detected === "back" || detected === "cancel") {
|
|
3974
|
+
logger.info(t("init_cancelled"));
|
|
3975
|
+
return;
|
|
3976
|
+
}
|
|
2499
3977
|
tool2 = detected;
|
|
2500
3978
|
}
|
|
3979
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2501
3980
|
let step = 1;
|
|
2502
3981
|
while (true) {
|
|
2503
3982
|
switch (step) {
|
|
2504
3983
|
case 1: {
|
|
2505
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
3984
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2506
3985
|
if (selectedSkills === "back") {
|
|
2507
3986
|
const redetected = await selectToolWithDetection();
|
|
2508
|
-
if (redetected === "back")
|
|
3987
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
3988
|
+
logger.info(t("init_cancelled"));
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
2509
3991
|
tool2 = redetected;
|
|
2510
3992
|
break;
|
|
2511
3993
|
}
|
|
3994
|
+
if (selectedSkills === "cancel") {
|
|
3995
|
+
logger.info(t("init_cancelled"));
|
|
3996
|
+
return;
|
|
3997
|
+
}
|
|
2512
3998
|
if (selectedSkills.length === 0) {
|
|
2513
|
-
logger.info("
|
|
3999
|
+
logger.info(t("install_no_skill_selected"));
|
|
2514
4000
|
return;
|
|
2515
4001
|
}
|
|
2516
4002
|
names = selectedSkills;
|
|
2517
4003
|
const repoManager = createRepositoryManager();
|
|
2518
|
-
const spinner = createSpinner(t("
|
|
4004
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2519
4005
|
spinner.start();
|
|
2520
4006
|
const repoPath = await repoManager.ensureRepo();
|
|
2521
|
-
spinner.succeed(t("
|
|
2522
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2523
|
-
const level2 = options.level || "project";
|
|
4007
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
4008
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(names.length)))}...`);
|
|
2524
4009
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2525
4010
|
let successCount = 0;
|
|
2526
4011
|
let failCount = 0;
|
|
2527
|
-
|
|
4012
|
+
const total = results.length;
|
|
4013
|
+
for (let i = 0; i < results.length; i++) {
|
|
4014
|
+
const result = results[i];
|
|
4015
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2528
4016
|
if (result.success) {
|
|
2529
|
-
logger.success(`${result.skillId}`);
|
|
4017
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2530
4018
|
successCount++;
|
|
2531
4019
|
} else {
|
|
2532
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
4020
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2533
4021
|
failCount++;
|
|
2534
4022
|
}
|
|
2535
4023
|
}
|
|
2536
4024
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2537
4025
|
logger.blank();
|
|
2538
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
4026
|
+
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)))}`);
|
|
2539
4027
|
logger.blank();
|
|
2540
|
-
logger.info(
|
|
2541
|
-
logger.info(
|
|
4028
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
4029
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2542
4030
|
logger.blank();
|
|
2543
4031
|
return;
|
|
2544
4032
|
}
|
|
2545
4033
|
}
|
|
2546
4034
|
}
|
|
2547
4035
|
}
|
|
2548
|
-
const scanSpinner = createSpinner("
|
|
4036
|
+
const scanSpinner = createSpinner(t("loading_skills_list"));
|
|
2549
4037
|
scanSpinner.start();
|
|
2550
4038
|
try {
|
|
2551
4039
|
const scanRepoManager = createRepositoryManager();
|
|
2552
4040
|
await scanRepoManager.ensureRepoAndScan();
|
|
2553
|
-
scanSpinner.succeed("
|
|
4041
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2554
4042
|
} catch {
|
|
2555
|
-
scanSpinner.warn("
|
|
4043
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2556
4044
|
}
|
|
2557
4045
|
const plugins = [];
|
|
2558
4046
|
const skills = [];
|
|
@@ -2569,22 +4057,25 @@ async function installCommand(names, options) {
|
|
|
2569
4057
|
}
|
|
2570
4058
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2571
4059
|
const allPlugins = getAllPlugins();
|
|
2572
|
-
logger.info("
|
|
4060
|
+
logger.info(t("available_plugins") + ":");
|
|
2573
4061
|
for (const p of allPlugins) {
|
|
2574
4062
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2575
4063
|
}
|
|
2576
|
-
logger.info(
|
|
4064
|
+
logger.info(`${t("available_skills")}: ${chalk10.cyan("install-helper install --list")}`);
|
|
2577
4065
|
return;
|
|
2578
4066
|
}
|
|
2579
4067
|
let tool;
|
|
2580
4068
|
if (options.tool) {
|
|
2581
|
-
tool = options.tool;
|
|
4069
|
+
tool = validateTool(options.tool);
|
|
2582
4070
|
} else {
|
|
2583
4071
|
const detected = await selectToolWithDetection();
|
|
2584
|
-
if (detected === "back")
|
|
4072
|
+
if (detected === "back" || detected === "cancel") {
|
|
4073
|
+
logger.info(t("init_cancelled"));
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
2585
4076
|
tool = detected;
|
|
2586
4077
|
}
|
|
2587
|
-
const level = options.level
|
|
4078
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2588
4079
|
if (plugins.length > 0) {
|
|
2589
4080
|
const configRoot = getConfigRoot(tool, level);
|
|
2590
4081
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2612,16 +4103,18 @@ async function installCommand(names, options) {
|
|
|
2612
4103
|
}
|
|
2613
4104
|
if (pluginsToInstall.length > 0) {
|
|
2614
4105
|
const repoManager = createRepositoryManager();
|
|
2615
|
-
const spinner = createSpinner(t("
|
|
4106
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2616
4107
|
spinner.start();
|
|
2617
4108
|
const repoPath = await repoManager.ensureRepo();
|
|
2618
|
-
spinner.succeed(t("
|
|
4109
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2619
4110
|
const allPlugins = getAllPlugins();
|
|
2620
4111
|
const results = await installPlugins(
|
|
2621
4112
|
pluginsToInstall,
|
|
2622
4113
|
tool,
|
|
2623
4114
|
level,
|
|
2624
|
-
repoPath
|
|
4115
|
+
repoPath,
|
|
4116
|
+
void 0,
|
|
4117
|
+
options.yes
|
|
2625
4118
|
);
|
|
2626
4119
|
const summary = results.map((result) => {
|
|
2627
4120
|
const plugin = allPlugins.find((p) => p.id === result.pluginId);
|
|
@@ -2639,32 +4132,35 @@ async function installCommand(names, options) {
|
|
|
2639
4132
|
}
|
|
2640
4133
|
if (skills.length > 0) {
|
|
2641
4134
|
const repoManager = createRepositoryManager();
|
|
2642
|
-
const spinner = createSpinner(t("
|
|
4135
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2643
4136
|
spinner.start();
|
|
2644
4137
|
const repoPath = await repoManager.ensureRepo();
|
|
2645
|
-
spinner.succeed(t("
|
|
2646
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
4138
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
4139
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(skills.length)))}...`);
|
|
2647
4140
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2648
4141
|
let successCount = 0;
|
|
2649
4142
|
let failCount = 0;
|
|
2650
|
-
|
|
4143
|
+
const total = results.length;
|
|
4144
|
+
for (let i = 0; i < results.length; i++) {
|
|
4145
|
+
const result = results[i];
|
|
4146
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2651
4147
|
if (result.success) {
|
|
2652
|
-
logger.success(`${result.skillId}`);
|
|
4148
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2653
4149
|
successCount++;
|
|
2654
4150
|
} else {
|
|
2655
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
4151
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2656
4152
|
failCount++;
|
|
2657
4153
|
}
|
|
2658
4154
|
}
|
|
2659
4155
|
logger.blank();
|
|
2660
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
4156
|
+
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)))}`);
|
|
2661
4157
|
logger.blank();
|
|
2662
|
-
logger.info(
|
|
2663
|
-
logger.info(
|
|
4158
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
4159
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
2664
4160
|
logger.blank();
|
|
2665
4161
|
}
|
|
2666
4162
|
}
|
|
2667
|
-
async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
4163
|
+
async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes) {
|
|
2668
4164
|
const results = [];
|
|
2669
4165
|
const total = pluginIds.length;
|
|
2670
4166
|
for (let i = 0; i < pluginIds.length; i++) {
|
|
@@ -2676,7 +4172,9 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2676
4172
|
pluginId,
|
|
2677
4173
|
tool,
|
|
2678
4174
|
level,
|
|
2679
|
-
repoPath
|
|
4175
|
+
repoPath,
|
|
4176
|
+
installPath,
|
|
4177
|
+
yes
|
|
2680
4178
|
});
|
|
2681
4179
|
if (result.success) {
|
|
2682
4180
|
spinner.succeed(
|
|
@@ -2692,11 +4190,12 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2692
4190
|
}
|
|
2693
4191
|
|
|
2694
4192
|
// src/commands/uninstall.ts
|
|
2695
|
-
import { existsSync as
|
|
2696
|
-
import
|
|
4193
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync7, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
|
|
4194
|
+
import { resolve, basename as basename2 } from "path";
|
|
4195
|
+
import chalk11 from "chalk";
|
|
2697
4196
|
async function uninstallCommand(names, options) {
|
|
2698
|
-
const tool = options.tool
|
|
2699
|
-
const level = options.level
|
|
4197
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
4198
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2700
4199
|
const plugins = [];
|
|
2701
4200
|
const skills = [];
|
|
2702
4201
|
for (const name of names) {
|
|
@@ -2710,40 +4209,50 @@ async function uninstallCommand(names, options) {
|
|
|
2710
4209
|
skills.push(skill.id);
|
|
2711
4210
|
continue;
|
|
2712
4211
|
}
|
|
2713
|
-
logger.error(
|
|
4212
|
+
logger.error(`${t("uninstall_not_found")}: ${name}`);
|
|
2714
4213
|
}
|
|
2715
4214
|
for (const pluginId of plugins) {
|
|
2716
4215
|
const plugin = findPlugin(pluginId);
|
|
2717
4216
|
if (!plugin) continue;
|
|
2718
4217
|
const record = readRecord(plugin.id);
|
|
2719
4218
|
if (!record) {
|
|
2720
|
-
logger.warn(`${plugin.displayName}
|
|
2721
|
-
logger.info(
|
|
4219
|
+
logger.warn(`${plugin.displayName} ${t("uninstall_no_record")}`);
|
|
4220
|
+
logger.info(`${t("uninstall_no_record_hint")}`);
|
|
2722
4221
|
continue;
|
|
2723
4222
|
}
|
|
2724
|
-
logger.info(
|
|
4223
|
+
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
4224
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
4225
|
+
const allowedBases = [resolve(configRoot), resolve(record.installPath)];
|
|
4226
|
+
const isSafePath = (p) => {
|
|
4227
|
+
const resolved = resolve(p);
|
|
4228
|
+
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + "/"));
|
|
4229
|
+
};
|
|
2725
4230
|
let removedFiles = 0;
|
|
2726
4231
|
let removedDirs = 0;
|
|
2727
4232
|
for (const filePath of record.files) {
|
|
4233
|
+
if (!isSafePath(filePath)) continue;
|
|
2728
4234
|
try {
|
|
2729
|
-
if (
|
|
2730
|
-
|
|
2731
|
-
const name = filePath
|
|
2732
|
-
logger.step(`
|
|
4235
|
+
if (existsSync17(filePath) || isSymlink(filePath)) {
|
|
4236
|
+
unlinkSync7(filePath);
|
|
4237
|
+
const name = basename2(filePath);
|
|
4238
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2733
4239
|
removedFiles++;
|
|
2734
4240
|
}
|
|
2735
4241
|
} catch {
|
|
2736
4242
|
}
|
|
2737
4243
|
}
|
|
2738
|
-
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
4244
|
+
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
2739
4245
|
for (const dirPath of sortedDirs) {
|
|
2740
4246
|
try {
|
|
2741
|
-
if (
|
|
2742
|
-
|
|
4247
|
+
if (resolve(dirPath) === resolve(configRoot)) {
|
|
4248
|
+
continue;
|
|
4249
|
+
}
|
|
4250
|
+
if (existsSync17(dirPath)) {
|
|
4251
|
+
const entries = readdirSync6(dirPath);
|
|
2743
4252
|
if (entries.length === 0) {
|
|
2744
4253
|
rmdirSync2(dirPath);
|
|
2745
|
-
const name = dirPath
|
|
2746
|
-
logger.step(`
|
|
4254
|
+
const name = basename2(dirPath);
|
|
4255
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
2747
4256
|
removedDirs++;
|
|
2748
4257
|
}
|
|
2749
4258
|
}
|
|
@@ -2752,15 +4261,32 @@ async function uninstallCommand(names, options) {
|
|
|
2752
4261
|
}
|
|
2753
4262
|
deleteRecord(plugin.id);
|
|
2754
4263
|
removeInstalledPlugin(plugin.id);
|
|
4264
|
+
const backups = findBackups(configRoot);
|
|
4265
|
+
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
4266
|
+
if (otherBackups.length > 0) {
|
|
4267
|
+
const choice = await showRestorePrompt(otherBackups);
|
|
4268
|
+
if (choice !== "none") {
|
|
4269
|
+
const restored = restoreBackup(choice, configRoot, record.tool);
|
|
4270
|
+
if (restored) {
|
|
4271
|
+
const backupInfo = otherBackups.find((b) => b.filePath === choice);
|
|
4272
|
+
logger.success(`${t("backup_restore_success")}: ${backupInfo?.pluginName || ""}`);
|
|
4273
|
+
deleteBackup(choice);
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
const currentBackups = backups.filter((b) => b.pluginId === pluginId);
|
|
4278
|
+
for (const backup of currentBackups) {
|
|
4279
|
+
deleteBackup(backup.filePath);
|
|
4280
|
+
}
|
|
2755
4281
|
const totalRemoved = removedFiles + removedDirs;
|
|
2756
4282
|
if (totalRemoved > 0) {
|
|
2757
|
-
logger.success(
|
|
4283
|
+
logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
|
|
2758
4284
|
} else {
|
|
2759
|
-
logger.warn(`${plugin.displayName}
|
|
4285
|
+
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
2760
4286
|
}
|
|
2761
4287
|
}
|
|
2762
4288
|
if (skills.length > 0) {
|
|
2763
|
-
logger.info(
|
|
4289
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
2764
4290
|
const results = await uninstallSkills(skills, tool, level);
|
|
2765
4291
|
let successCount = 0;
|
|
2766
4292
|
let failCount = 0;
|
|
@@ -2772,30 +4298,26 @@ async function uninstallCommand(names, options) {
|
|
|
2772
4298
|
}
|
|
2773
4299
|
}
|
|
2774
4300
|
logger.blank();
|
|
2775
|
-
logger.success(`${t("skill_uninstall_done")}: ${
|
|
4301
|
+
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)))}`);
|
|
2776
4302
|
logger.blank();
|
|
2777
4303
|
}
|
|
2778
4304
|
}
|
|
2779
|
-
function isSymlink3(path) {
|
|
2780
|
-
try {
|
|
2781
|
-
return lstatSync4(path).isSymbolicLink();
|
|
2782
|
-
} catch {
|
|
2783
|
-
return false;
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
4305
|
|
|
2787
4306
|
// src/commands/update.ts
|
|
2788
|
-
import
|
|
4307
|
+
import chalk12 from "chalk";
|
|
2789
4308
|
async function updateCommand(pluginNames, options) {
|
|
2790
4309
|
let tool;
|
|
2791
4310
|
if (options.tool) {
|
|
2792
|
-
tool = options.tool;
|
|
4311
|
+
tool = validateTool(options.tool);
|
|
2793
4312
|
} else {
|
|
2794
4313
|
const detected = await selectToolWithDetection();
|
|
2795
|
-
if (detected === "back")
|
|
4314
|
+
if (detected === "back" || detected === "cancel") {
|
|
4315
|
+
logger.info(t("init_cancelled"));
|
|
4316
|
+
return;
|
|
4317
|
+
}
|
|
2796
4318
|
tool = detected;
|
|
2797
4319
|
}
|
|
2798
|
-
const level = options.level
|
|
4320
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2799
4321
|
let pluginsToUpdate = [];
|
|
2800
4322
|
if (pluginNames.length === 0) {
|
|
2801
4323
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -2803,7 +4325,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2803
4325
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
2804
4326
|
if (pluginsToUpdate.length === 0) {
|
|
2805
4327
|
logger.info(t("update_no_plugins"));
|
|
2806
|
-
logger.info(
|
|
4328
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk12.cyan("install-helper install <plugin>")));
|
|
2807
4329
|
return;
|
|
2808
4330
|
}
|
|
2809
4331
|
} else {
|
|
@@ -2819,9 +4341,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2819
4341
|
const repoManager = createRepositoryManager();
|
|
2820
4342
|
const updateSpinner = createSpinner(t("update_updating") + "...");
|
|
2821
4343
|
updateSpinner.start();
|
|
2822
|
-
await repoManager.
|
|
2823
|
-
const repoPath = repoManager.getRepoPath();
|
|
2824
|
-
await repoManager.ensureRepoAndScan();
|
|
4344
|
+
const repoPath = await repoManager.ensureRepoAndScan();
|
|
2825
4345
|
updateSpinner.succeed(t("install_repo_ready"));
|
|
2826
4346
|
const allPlugins = getAllPlugins();
|
|
2827
4347
|
const results = [];
|
|
@@ -2865,42 +4385,43 @@ async function updateCommand(pluginNames, options) {
|
|
|
2865
4385
|
}
|
|
2866
4386
|
|
|
2867
4387
|
// src/commands/info.ts
|
|
2868
|
-
import
|
|
2869
|
-
import { existsSync as
|
|
2870
|
-
import { join as
|
|
4388
|
+
import chalk13 from "chalk";
|
|
4389
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
|
|
4390
|
+
import { join as join16 } from "path";
|
|
2871
4391
|
async function infoCommand(pluginName) {
|
|
2872
4392
|
const plugin = findPlugin(pluginName);
|
|
2873
4393
|
if (!plugin) {
|
|
2874
|
-
console.log(
|
|
4394
|
+
console.log(chalk13.red(` ${t("info_not_found")}: ${pluginName}`));
|
|
2875
4395
|
return;
|
|
2876
4396
|
}
|
|
2877
4397
|
const installed = scanInstalled();
|
|
2878
|
-
const
|
|
2879
|
-
const
|
|
4398
|
+
const inst = installed.find((p) => p.id === plugin.id);
|
|
4399
|
+
const isInstalled = !!inst;
|
|
4400
|
+
const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
|
|
2880
4401
|
console.log();
|
|
2881
|
-
console.log(
|
|
2882
|
-
console.log(
|
|
4402
|
+
console.log(chalk13.bold(` ${plugin.displayName}`));
|
|
4403
|
+
console.log(chalk13.dim(" " + "\u2500".repeat(40)));
|
|
2883
4404
|
console.log();
|
|
2884
|
-
console.log(` ${
|
|
2885
|
-
console.log(` ${
|
|
2886
|
-
console.log(` ${
|
|
2887
|
-
console.log(` ${
|
|
2888
|
-
console.log(` ${
|
|
2889
|
-
console.log(` ${
|
|
4405
|
+
console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
4406
|
+
console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
|
|
4407
|
+
console.log(` ${chalk13.dim(t("info_status") + ":")} ${statusText}`);
|
|
4408
|
+
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${inst ? inst.skillsCount : plugin.skills}`);
|
|
4409
|
+
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${inst ? inst.agentsCount : plugin.agents}`);
|
|
4410
|
+
console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
2890
4411
|
console.log();
|
|
2891
4412
|
try {
|
|
2892
4413
|
const repoManager = createRepositoryManager();
|
|
2893
4414
|
const repoPath = await repoManager.ensureRepo();
|
|
2894
|
-
const quickstartPath =
|
|
2895
|
-
if (
|
|
2896
|
-
console.log(` ${
|
|
4415
|
+
const quickstartPath = join16(repoPath, plugin.dir, "quickstart.md");
|
|
4416
|
+
if (existsSync18(quickstartPath)) {
|
|
4417
|
+
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
2897
4418
|
}
|
|
2898
|
-
const agentsDir =
|
|
2899
|
-
if (
|
|
2900
|
-
const agentFiles =
|
|
4419
|
+
const agentsDir = join16(repoPath, plugin.dir, "agents");
|
|
4420
|
+
if (existsSync18(agentsDir)) {
|
|
4421
|
+
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
2901
4422
|
if (agentFiles.length > 0) {
|
|
2902
4423
|
console.log();
|
|
2903
|
-
console.log(` ${
|
|
4424
|
+
console.log(` ${chalk13.bold("Agents:")}`);
|
|
2904
4425
|
for (const agent of agentFiles) {
|
|
2905
4426
|
console.log(` \u2022 ${agent.replace(".md", "")}`);
|
|
2906
4427
|
}
|
|
@@ -2912,49 +4433,80 @@ async function infoCommand(pluginName) {
|
|
|
2912
4433
|
}
|
|
2913
4434
|
|
|
2914
4435
|
// src/commands/lang.ts
|
|
2915
|
-
import
|
|
4436
|
+
import chalk14 from "chalk";
|
|
2916
4437
|
async function langCommand(action, value) {
|
|
2917
4438
|
const config = readConfig();
|
|
2918
4439
|
if (!action || action === "show") {
|
|
2919
4440
|
console.log();
|
|
2920
|
-
console.log(
|
|
2921
|
-
console.log(
|
|
4441
|
+
console.log(chalk14.bold(` ${t("lang_title")}`));
|
|
4442
|
+
console.log(chalk14.dim(" " + "\u2500".repeat(40)));
|
|
2922
4443
|
console.log();
|
|
2923
|
-
console.log(` ${
|
|
4444
|
+
console.log(` ${chalk14.dim(t("lang_current") + ":")} ${config.language}`);
|
|
2924
4445
|
console.log();
|
|
2925
|
-
console.log(
|
|
2926
|
-
console.log(
|
|
4446
|
+
console.log(chalk14.dim(` ${t("lang_supported")}`));
|
|
4447
|
+
console.log(chalk14.dim(` ${t("lang_usage")}`));
|
|
2927
4448
|
console.log();
|
|
2928
4449
|
return;
|
|
2929
4450
|
}
|
|
2930
4451
|
if (action === "set") {
|
|
2931
4452
|
if (!value) {
|
|
2932
|
-
console.log(
|
|
4453
|
+
console.log(chalk14.red(` ${t("lang_specify")}`));
|
|
2933
4454
|
return;
|
|
2934
4455
|
}
|
|
2935
4456
|
if (value !== "zh_CN" && value !== "en_US") {
|
|
2936
|
-
console.log(
|
|
4457
|
+
console.log(chalk14.red(` ${t("lang_invalid")}`));
|
|
2937
4458
|
return;
|
|
2938
4459
|
}
|
|
2939
4460
|
updateConfig({ language: value });
|
|
2940
4461
|
setLanguage(value);
|
|
2941
4462
|
console.log();
|
|
2942
|
-
console.log(
|
|
4463
|
+
console.log(chalk14.green(` \u2713 ${t("lang_set")} ${value}`));
|
|
2943
4464
|
console.log();
|
|
2944
4465
|
return;
|
|
2945
4466
|
}
|
|
2946
|
-
console.log(
|
|
2947
|
-
console.log(
|
|
4467
|
+
console.log(chalk14.red(` ${t("lang_unknown_action")}: ${action}`));
|
|
4468
|
+
console.log(chalk14.dim(` ${t("lang_supported_actions")}`));
|
|
2948
4469
|
}
|
|
2949
4470
|
|
|
2950
4471
|
// src/cli.ts
|
|
4472
|
+
import { readFileSync as readFileSync9, existsSync as existsSync19 } from "fs";
|
|
4473
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
4474
|
+
import { dirname as dirname7, join as join17 } from "path";
|
|
4475
|
+
function readVersion() {
|
|
4476
|
+
try {
|
|
4477
|
+
if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
|
|
4478
|
+
return INSTALL_HELPER_VERSION;
|
|
4479
|
+
}
|
|
4480
|
+
} catch {
|
|
4481
|
+
}
|
|
4482
|
+
try {
|
|
4483
|
+
const __filename5 = fileURLToPath5(import.meta.url);
|
|
4484
|
+
const __dirname5 = dirname7(__filename5);
|
|
4485
|
+
const candidates = [
|
|
4486
|
+
join17(__dirname5, "..", "package.json"),
|
|
4487
|
+
join17(__dirname5, "package.json"),
|
|
4488
|
+
join17(process.cwd(), "package.json")
|
|
4489
|
+
];
|
|
4490
|
+
for (const pkgPath of candidates) {
|
|
4491
|
+
if (existsSync19(pkgPath)) {
|
|
4492
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
4493
|
+
if (pkg.version && (pkg.name === "@cannbot-ai/install-helper" || pkg.name?.startsWith("@cannbot-ai/install-helper-"))) {
|
|
4494
|
+
return pkg.version;
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
return "0.0.0";
|
|
4499
|
+
} catch {
|
|
4500
|
+
return "0.0.0";
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
2951
4503
|
function createCLI() {
|
|
2952
4504
|
const config = readConfig();
|
|
2953
4505
|
if (config.language) {
|
|
2954
4506
|
setLanguage(config.language);
|
|
2955
4507
|
}
|
|
2956
4508
|
const program2 = new Command();
|
|
2957
|
-
program2.name("install-helper").description("Install Helper - Interactive installer for CANN operator development skills").version(
|
|
4509
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
|
|
2958
4510
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
2959
4511
|
await initCommand();
|
|
2960
4512
|
});
|
|
@@ -2967,7 +4519,7 @@ function createCLI() {
|
|
|
2967
4519
|
program2.command("status").description("Show installed plugins").action(async () => {
|
|
2968
4520
|
await statusCommand();
|
|
2969
4521
|
});
|
|
2970
|
-
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) => {
|
|
4522
|
+
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) => {
|
|
2971
4523
|
await installCommand(names, options);
|
|
2972
4524
|
});
|
|
2973
4525
|
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) => {
|
|
@@ -2991,7 +4543,7 @@ function createCLI() {
|
|
|
2991
4543
|
// src/index.ts
|
|
2992
4544
|
process.on("uncaughtException", (error) => {
|
|
2993
4545
|
if (error.name === "ExitPromptError") {
|
|
2994
|
-
console.log("\n
|
|
4546
|
+
console.log("\n" + t("init_cancelled"));
|
|
2995
4547
|
process.exit(0);
|
|
2996
4548
|
}
|
|
2997
4549
|
throw error;
|