@cannbot-ai/install-helper 0.0.2-beta.8 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -4
- package/dist/index.js +531 -381
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -145,251 +145,6 @@ import { join as join2 } from "path";
|
|
|
145
145
|
import { homedir } from "os";
|
|
146
146
|
import { join } from "path";
|
|
147
147
|
import { existsSync } from "fs";
|
|
148
|
-
function detectTraeVariant() {
|
|
149
|
-
const home = homedir();
|
|
150
|
-
if (existsSync(join(home, ".trae-cn"))) return "ide";
|
|
151
|
-
if (existsSync(join(home, ".marscode"))) return "plugin";
|
|
152
|
-
if (existsSync(join(home, ".traecli"))) return "cli";
|
|
153
|
-
return "unknown";
|
|
154
|
-
}
|
|
155
|
-
function getConfigRoot(tool, level, base) {
|
|
156
|
-
const home = homedir();
|
|
157
|
-
if (level === "global") {
|
|
158
|
-
switch (tool) {
|
|
159
|
-
case "opencode":
|
|
160
|
-
return join(home, ".config", "opencode");
|
|
161
|
-
case "claude":
|
|
162
|
-
return join(home, ".claude");
|
|
163
|
-
case "trae": {
|
|
164
|
-
const variant = detectTraeVariant();
|
|
165
|
-
switch (variant) {
|
|
166
|
-
case "plugin":
|
|
167
|
-
return join(home, ".marscode");
|
|
168
|
-
case "cli":
|
|
169
|
-
return join(home, ".traecli");
|
|
170
|
-
default:
|
|
171
|
-
return join(home, ".trae-cn");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
case "cursor":
|
|
175
|
-
return join(home, ".cursor");
|
|
176
|
-
case "copilot":
|
|
177
|
-
return join(home, ".copilot");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
const baseDir = base || process.cwd();
|
|
181
|
-
switch (tool) {
|
|
182
|
-
case "opencode":
|
|
183
|
-
return join(baseDir, ".opencode");
|
|
184
|
-
case "claude":
|
|
185
|
-
return join(baseDir, ".claude");
|
|
186
|
-
case "trae": {
|
|
187
|
-
const variant = detectTraeVariant();
|
|
188
|
-
switch (variant) {
|
|
189
|
-
case "plugin":
|
|
190
|
-
return join(baseDir, ".marscode");
|
|
191
|
-
case "cli":
|
|
192
|
-
return join(baseDir, ".traecli");
|
|
193
|
-
default:
|
|
194
|
-
return join(baseDir, ".trae");
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
case "cursor":
|
|
198
|
-
return join(baseDir, ".cursor");
|
|
199
|
-
case "copilot":
|
|
200
|
-
return join(baseDir, ".github");
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function getConfigFileName(tool) {
|
|
204
|
-
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
|
-
}
|
|
206
|
-
function getSkillsDir(configRoot) {
|
|
207
|
-
return join(configRoot, "skills");
|
|
208
|
-
}
|
|
209
|
-
function getAgentsDir(configRoot) {
|
|
210
|
-
return join(configRoot, "agents");
|
|
211
|
-
}
|
|
212
|
-
function getManifestPath(configRoot) {
|
|
213
|
-
return join(configRoot, "cannbot-manifest.json");
|
|
214
|
-
}
|
|
215
|
-
function getCannbotConfigDir() {
|
|
216
|
-
return join(homedir(), ".cannbot");
|
|
217
|
-
}
|
|
218
|
-
function getCannbotRepoPath() {
|
|
219
|
-
return join(getCannbotConfigDir(), "repo");
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// src/core/detector.ts
|
|
223
|
-
async function getCommandVersion(cmd, args = ["--version"]) {
|
|
224
|
-
try {
|
|
225
|
-
const result = await execa(cmd, args, { timeout: 5e3 });
|
|
226
|
-
const output = result.stdout.trim();
|
|
227
|
-
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
228
|
-
return match ? match[1] : output.split("\n")[0];
|
|
229
|
-
} catch {
|
|
230
|
-
return void 0;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
async function getCommandPath(cmd) {
|
|
234
|
-
try {
|
|
235
|
-
const result = await execa("sh", ["-c", `command -v ${cmd}`], { timeout: 5e3 });
|
|
236
|
-
return result.stdout.trim();
|
|
237
|
-
} catch {
|
|
238
|
-
return void 0;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
async function detectOpenCode() {
|
|
242
|
-
const path = await getCommandPath("opencode");
|
|
243
|
-
if (!path) return void 0;
|
|
244
|
-
const version = await getCommandVersion("opencode");
|
|
245
|
-
return { name: "opencode", version, path };
|
|
246
|
-
}
|
|
247
|
-
async function detectClaude() {
|
|
248
|
-
const path = await getCommandPath("claude");
|
|
249
|
-
if (!path) return void 0;
|
|
250
|
-
const version = await getCommandVersion("claude");
|
|
251
|
-
return { name: "claude", version, path };
|
|
252
|
-
}
|
|
253
|
-
async function detectTrae() {
|
|
254
|
-
const home = homedir2();
|
|
255
|
-
const variant = detectTraeVariant();
|
|
256
|
-
let path;
|
|
257
|
-
if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
|
|
258
|
-
path = join2(home, ".trae-cn");
|
|
259
|
-
} else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
|
|
260
|
-
path = join2(home, ".marscode");
|
|
261
|
-
} else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
|
|
262
|
-
path = join2(home, ".traecli");
|
|
263
|
-
} else if (variant === "unknown") {
|
|
264
|
-
const cmdPath = await getCommandPath("trae");
|
|
265
|
-
if (cmdPath) path = cmdPath;
|
|
266
|
-
}
|
|
267
|
-
if (!path) return void 0;
|
|
268
|
-
return { name: "trae", version: variant, path };
|
|
269
|
-
}
|
|
270
|
-
async function detectCursor() {
|
|
271
|
-
const path = await getCommandPath("cursor");
|
|
272
|
-
if (path) {
|
|
273
|
-
const version = await getCommandVersion("cursor");
|
|
274
|
-
return { name: "cursor", version, path };
|
|
275
|
-
}
|
|
276
|
-
if (process.platform === "darwin") {
|
|
277
|
-
const appPath = "/Applications/Cursor.app";
|
|
278
|
-
if (existsSync2(appPath)) {
|
|
279
|
-
return { name: "cursor", path: appPath };
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return void 0;
|
|
283
|
-
}
|
|
284
|
-
async function detectCopilot() {
|
|
285
|
-
const path = await getCommandPath("gh");
|
|
286
|
-
if (!path) return void 0;
|
|
287
|
-
try {
|
|
288
|
-
await execa("gh", ["extension", "list"], { timeout: 5e3 });
|
|
289
|
-
return { name: "copilot", path };
|
|
290
|
-
} catch {
|
|
291
|
-
return void 0;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
async function detectTools() {
|
|
295
|
-
const detectors = [
|
|
296
|
-
detectOpenCode,
|
|
297
|
-
detectClaude,
|
|
298
|
-
detectTrae,
|
|
299
|
-
detectCursor,
|
|
300
|
-
detectCopilot
|
|
301
|
-
];
|
|
302
|
-
const results = await Promise.allSettled(detectors.map((d) => d()));
|
|
303
|
-
const tools = [];
|
|
304
|
-
for (const result of results) {
|
|
305
|
-
if (result.status === "fulfilled" && result.value) {
|
|
306
|
-
tools.push(result.value);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return tools;
|
|
310
|
-
}
|
|
311
|
-
function getToolDisplayName(tool) {
|
|
312
|
-
const names = {
|
|
313
|
-
opencode: "OpenCode",
|
|
314
|
-
claude: "Claude Code",
|
|
315
|
-
trae: "Trae",
|
|
316
|
-
cursor: "Cursor",
|
|
317
|
-
copilot: "GitHub Copilot"
|
|
318
|
-
};
|
|
319
|
-
return names[tool];
|
|
320
|
-
}
|
|
321
|
-
function getAllTools() {
|
|
322
|
-
return ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
327
|
-
import { join as join3 } from "path";
|
|
328
|
-
function readManifest(configRoot) {
|
|
329
|
-
const manifestPath = getManifestPath(configRoot);
|
|
330
|
-
if (existsSync3(manifestPath)) {
|
|
331
|
-
try {
|
|
332
|
-
const content = readFileSync(manifestPath, "utf-8");
|
|
333
|
-
return JSON.parse(content);
|
|
334
|
-
} catch {
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
function readAllManifests(configRoot) {
|
|
340
|
-
const manifests = [];
|
|
341
|
-
const plugins = getAllPlugins();
|
|
342
|
-
const standardPath = getManifestPath(configRoot);
|
|
343
|
-
if (existsSync3(standardPath)) {
|
|
344
|
-
try {
|
|
345
|
-
const content = readFileSync(standardPath, "utf-8");
|
|
346
|
-
manifests.push(JSON.parse(content));
|
|
347
|
-
} catch {
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
|
|
352
|
-
if (existsSync3(pluginManifestPath)) {
|
|
353
|
-
try {
|
|
354
|
-
const content = readFileSync(pluginManifestPath, "utf-8");
|
|
355
|
-
manifests.push(JSON.parse(content));
|
|
356
|
-
} catch {
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return manifests;
|
|
361
|
-
}
|
|
362
|
-
function scanInstalled() {
|
|
363
|
-
const plugins = getAllPlugins();
|
|
364
|
-
const installed = [];
|
|
365
|
-
const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
366
|
-
const levels = ["project", "global"];
|
|
367
|
-
for (const tool of tools) {
|
|
368
|
-
for (const level of levels) {
|
|
369
|
-
const configRoot = getConfigRoot(tool, level);
|
|
370
|
-
const manifests = readAllManifests(configRoot);
|
|
371
|
-
for (const manifest of manifests) {
|
|
372
|
-
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
|
-
if (!plugin) continue;
|
|
374
|
-
const alreadyAdded = installed.some(
|
|
375
|
-
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
|
-
);
|
|
377
|
-
if (alreadyAdded) continue;
|
|
378
|
-
installed.push({
|
|
379
|
-
id: plugin.id,
|
|
380
|
-
displayName: plugin.displayName,
|
|
381
|
-
tool,
|
|
382
|
-
level,
|
|
383
|
-
skillsCount: manifest.installed_skills?.length || 0,
|
|
384
|
-
agentsCount: manifest.installed_agents?.length || 0,
|
|
385
|
-
installTime: manifest.install_time,
|
|
386
|
-
configRoot
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return installed;
|
|
392
|
-
}
|
|
393
148
|
|
|
394
149
|
// src/utils/i18n.ts
|
|
395
150
|
var zh_CN = {
|
|
@@ -454,6 +209,8 @@ var zh_CN = {
|
|
|
454
209
|
error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
455
210
|
error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
456
211
|
error_repo_not_found: "\u4ED3\u5E93\u672A\u627E\u5230",
|
|
212
|
+
error_invalid_tool: "\u6682\u4E0D\u652F\u6301\u7684 AI \u5DE5\u5177: {tool}\n\u652F\u6301\u7684\u5DE5\u5177: {tools}",
|
|
213
|
+
error_invalid_level: "\u6682\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u7EA7\u522B: {level}\n\u652F\u6301\u7684\u7EA7\u522B: {levels}",
|
|
457
214
|
quick_start: "\u542F\u52A8\u547D\u4EE4",
|
|
458
215
|
quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
|
|
459
216
|
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
@@ -508,7 +265,55 @@ var zh_CN = {
|
|
|
508
265
|
backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
|
|
509
266
|
backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
510
267
|
backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
|
|
511
|
-
backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD"
|
|
268
|
+
backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
|
|
269
|
+
loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
|
|
270
|
+
loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
|
|
271
|
+
loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
|
|
272
|
+
loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
|
|
273
|
+
loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
|
|
274
|
+
install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
|
|
275
|
+
install_to: "\u5B89\u88C5\u5230",
|
|
276
|
+
start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
|
|
277
|
+
wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
278
|
+
wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
|
|
279
|
+
wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
280
|
+
wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
|
|
281
|
+
wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
|
|
282
|
+
wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
|
|
283
|
+
install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
|
|
284
|
+
install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
|
|
285
|
+
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
286
|
+
skill_count_format: "{count} \u4E2A Skills",
|
|
287
|
+
result_success_format: "{count} \u6210\u529F",
|
|
288
|
+
result_failed_format: "{count} \u5931\u8D25",
|
|
289
|
+
uninstall_remove_label: "\u79FB\u9664",
|
|
290
|
+
uninstall_remove_file_format: "\u79FB\u9664 {count} \u4E2A\u6587\u4EF6",
|
|
291
|
+
uninstall_remove_dir_format: "{count} \u4E2A\u76EE\u5F55",
|
|
292
|
+
uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
|
|
293
|
+
uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
|
|
294
|
+
available_plugins: "\u53EF\u7528\u63D2\u4EF6",
|
|
295
|
+
available_skills: "\u53EF\u7528 Skills",
|
|
296
|
+
skill_total_count: "\u5171 {count} \u4E2A Skills",
|
|
297
|
+
wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
|
|
298
|
+
wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
|
|
299
|
+
update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
|
|
300
|
+
hint_select_move: "\u79FB\u52A8",
|
|
301
|
+
hint_select_space: "\u7A7A\u683C",
|
|
302
|
+
hint_select_check: "\u52FE\u9009",
|
|
303
|
+
hint_select_confirm: "\u786E\u8BA4",
|
|
304
|
+
hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
|
|
305
|
+
doctor_not_installed: "\u672A\u5B89\u88C5",
|
|
306
|
+
doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
|
|
307
|
+
doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
308
|
+
doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
309
|
+
doctor_not_exist: "\u4E0D\u5B58\u5728",
|
|
310
|
+
doctor_created: "\u5DF2\u521B\u5EFA {path}",
|
|
311
|
+
doctor_config_exists: "\u5B58\u5728",
|
|
312
|
+
doctor_config_not_exist: "\u4E0D\u5B58\u5728",
|
|
313
|
+
doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF, {fixes} \u9879\u5DF2\u4FEE\u590D",
|
|
314
|
+
doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF",
|
|
315
|
+
count_suffix: "\u4E2A",
|
|
316
|
+
try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
|
|
512
317
|
};
|
|
513
318
|
var en_US = {
|
|
514
319
|
wizard_title: "CANNBot Install Helper",
|
|
@@ -572,6 +377,8 @@ var en_US = {
|
|
|
572
377
|
error_tool_not_found: "No AI coding tool detected",
|
|
573
378
|
error_plugin_not_found: "Plugin not found",
|
|
574
379
|
error_repo_not_found: "Repository not found",
|
|
380
|
+
error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
|
|
381
|
+
error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
|
|
575
382
|
quick_start: "Launch command",
|
|
576
383
|
quick_start_hint: "Try: help me develop an Abs operator",
|
|
577
384
|
skill_select_category: "Select Skill category",
|
|
@@ -626,14 +433,364 @@ var en_US = {
|
|
|
626
433
|
backup_only_uninstall: "Uninstall only (no restore)",
|
|
627
434
|
backup_select_action: "Please select an action",
|
|
628
435
|
backup_restore_success: "Configuration restored",
|
|
629
|
-
backup_created: "Backup created"
|
|
436
|
+
backup_created: "Backup created",
|
|
437
|
+
loading_skills_list: "Loading Skills list...",
|
|
438
|
+
loading_skills_list_complete: "Skills list loaded",
|
|
439
|
+
loading_plugin_data: "Loading plugin data...",
|
|
440
|
+
loading_plugin_data_complete: "Plugin data loaded",
|
|
441
|
+
loading_skills_list_failed: "Failed to load Skills list, using built-in data",
|
|
442
|
+
install_no_skill_selected: "No Skill selected",
|
|
443
|
+
install_to: "Install to",
|
|
444
|
+
start_to_use: "Start {tool} to use",
|
|
445
|
+
wizard_step_1_title: "[1/4] Select AI coding tool",
|
|
446
|
+
wizard_step_2_title: "[2/4] Select installation location",
|
|
447
|
+
wizard_step_3_title: "[3/4] Select plugins to install",
|
|
448
|
+
wizard_step_4_title: "[4/4] Confirm installation",
|
|
449
|
+
wizard_step_3_skill_title: "[3/4] Select Skill category",
|
|
450
|
+
wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
|
|
451
|
+
install_level_project: "Install to current directory (recommended, current project only)",
|
|
452
|
+
install_level_global: "Install globally (all projects)",
|
|
453
|
+
wizard_select_mode_title: "Select installation type",
|
|
454
|
+
skill_count_format: "{count} Skills",
|
|
455
|
+
result_success_format: "{count} succeeded",
|
|
456
|
+
result_failed_format: "{count} failed",
|
|
457
|
+
uninstall_remove_label: "Removed",
|
|
458
|
+
uninstall_remove_file_format: "Removed {count} files",
|
|
459
|
+
uninstall_remove_dir_format: "{count} directories",
|
|
460
|
+
uninstall_clean_empty_dir: "Cleaned empty directory",
|
|
461
|
+
uninstall_skill_not_installed: "Not installed",
|
|
462
|
+
available_plugins: "Available plugins",
|
|
463
|
+
available_skills: "Available Skills",
|
|
464
|
+
skill_total_count: "Total {count} Skills",
|
|
465
|
+
wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
|
|
466
|
+
wizard_install_path: "Install path",
|
|
467
|
+
update_install_hint: "Use {cmd} to install plugins",
|
|
468
|
+
hint_select_move: "Navigate",
|
|
469
|
+
hint_select_space: "Space",
|
|
470
|
+
hint_select_check: "Toggle",
|
|
471
|
+
hint_select_confirm: "Confirm",
|
|
472
|
+
hint_checkbox_suffix: "Press enter without selection to go back",
|
|
473
|
+
doctor_not_installed: "Not installed",
|
|
474
|
+
doctor_links_valid: "{count} symlinks all valid",
|
|
475
|
+
doctor_broken_links: "{count} broken links",
|
|
476
|
+
doctor_fixed: "Cleaned {count} broken links",
|
|
477
|
+
doctor_not_exist: "Does not exist",
|
|
478
|
+
doctor_created: "Created {path}",
|
|
479
|
+
doctor_config_exists: "Exists",
|
|
480
|
+
doctor_config_not_exist: "Does not exist",
|
|
481
|
+
doctor_result_with_fix: "{warnings} warnings, {errors} errors, {fixes} fixed",
|
|
482
|
+
doctor_result_without_fix: "{warnings} warnings, {errors} errors",
|
|
483
|
+
count_suffix: "",
|
|
484
|
+
try_prompt: "help me develop an Abs operator"
|
|
630
485
|
};
|
|
631
486
|
var currentLang = "zh_CN";
|
|
632
487
|
function setLanguage(lang) {
|
|
633
488
|
currentLang = lang;
|
|
634
489
|
}
|
|
635
|
-
function t(key) {
|
|
636
|
-
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
490
|
+
function t(key) {
|
|
491
|
+
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/utils/paths.ts
|
|
495
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
496
|
+
var VALID_LEVELS = ["project", "global"];
|
|
497
|
+
function validateTool(tool) {
|
|
498
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
499
|
+
throw new Error(
|
|
500
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
return tool;
|
|
504
|
+
}
|
|
505
|
+
function validateLevel(level) {
|
|
506
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
return level;
|
|
512
|
+
}
|
|
513
|
+
function detectTraeVariant() {
|
|
514
|
+
const home = homedir();
|
|
515
|
+
if (existsSync(join(home, ".trae-cn"))) return "ide";
|
|
516
|
+
if (existsSync(join(home, ".marscode"))) return "plugin";
|
|
517
|
+
if (existsSync(join(home, ".traecli"))) return "cli";
|
|
518
|
+
return "unknown";
|
|
519
|
+
}
|
|
520
|
+
function getConfigRoot(tool, level, base) {
|
|
521
|
+
const home = homedir();
|
|
522
|
+
if (level === "global") {
|
|
523
|
+
switch (tool) {
|
|
524
|
+
case "opencode":
|
|
525
|
+
return join(home, ".config", "opencode");
|
|
526
|
+
case "claude":
|
|
527
|
+
return join(home, ".claude");
|
|
528
|
+
case "trae": {
|
|
529
|
+
const variant = detectTraeVariant();
|
|
530
|
+
switch (variant) {
|
|
531
|
+
case "plugin":
|
|
532
|
+
return join(home, ".marscode");
|
|
533
|
+
case "cli":
|
|
534
|
+
return join(home, ".traecli");
|
|
535
|
+
default:
|
|
536
|
+
return join(home, ".trae-cn");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
case "cursor":
|
|
540
|
+
return join(home, ".cursor");
|
|
541
|
+
case "copilot":
|
|
542
|
+
return join(home, ".copilot");
|
|
543
|
+
default:
|
|
544
|
+
throw new Error(
|
|
545
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
const baseDir = base || process.cwd();
|
|
550
|
+
switch (tool) {
|
|
551
|
+
case "opencode":
|
|
552
|
+
return join(baseDir, ".opencode");
|
|
553
|
+
case "claude":
|
|
554
|
+
return join(baseDir, ".claude");
|
|
555
|
+
case "trae": {
|
|
556
|
+
const variant = detectTraeVariant();
|
|
557
|
+
switch (variant) {
|
|
558
|
+
case "plugin":
|
|
559
|
+
return join(baseDir, ".marscode");
|
|
560
|
+
case "cli":
|
|
561
|
+
return join(baseDir, ".traecli");
|
|
562
|
+
default:
|
|
563
|
+
return join(baseDir, ".trae");
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
case "cursor":
|
|
567
|
+
return join(baseDir, ".cursor");
|
|
568
|
+
case "copilot":
|
|
569
|
+
return join(baseDir, ".github");
|
|
570
|
+
default:
|
|
571
|
+
throw new Error(
|
|
572
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function getConfigFileName(tool) {
|
|
577
|
+
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
578
|
+
}
|
|
579
|
+
function getSkillsDir(configRoot) {
|
|
580
|
+
return join(configRoot, "skills");
|
|
581
|
+
}
|
|
582
|
+
function getAgentsDir(configRoot) {
|
|
583
|
+
return join(configRoot, "agents");
|
|
584
|
+
}
|
|
585
|
+
function getManifestPath(configRoot) {
|
|
586
|
+
return join(configRoot, "cannbot-manifest.json");
|
|
587
|
+
}
|
|
588
|
+
function getCannbotConfigDir() {
|
|
589
|
+
return join(homedir(), ".cannbot");
|
|
590
|
+
}
|
|
591
|
+
function getCannbotRepoPath() {
|
|
592
|
+
return join(getCannbotConfigDir(), "repo");
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/core/detector.ts
|
|
596
|
+
async function getCommandVersion(cmd, args = ["--version"]) {
|
|
597
|
+
try {
|
|
598
|
+
const result = await execa(cmd, args, { timeout: 5e3 });
|
|
599
|
+
const output = result.stdout.trim();
|
|
600
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
601
|
+
return match ? match[1] : output.split("\n")[0];
|
|
602
|
+
} catch {
|
|
603
|
+
return void 0;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function getCommandPath(cmd) {
|
|
607
|
+
try {
|
|
608
|
+
const result = await execa("which", [cmd], { timeout: 5e3 });
|
|
609
|
+
return result.stdout.trim();
|
|
610
|
+
} catch {
|
|
611
|
+
return void 0;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
async function detectOpenCode() {
|
|
615
|
+
const path = await getCommandPath("opencode");
|
|
616
|
+
if (!path) return void 0;
|
|
617
|
+
const version = await getCommandVersion("opencode");
|
|
618
|
+
return { name: "opencode", version, path };
|
|
619
|
+
}
|
|
620
|
+
async function detectClaude() {
|
|
621
|
+
const path = await getCommandPath("claude");
|
|
622
|
+
if (!path) return void 0;
|
|
623
|
+
const version = await getCommandVersion("claude");
|
|
624
|
+
return { name: "claude", version, path };
|
|
625
|
+
}
|
|
626
|
+
async function detectTrae() {
|
|
627
|
+
const home = homedir2();
|
|
628
|
+
const variant = detectTraeVariant();
|
|
629
|
+
let path;
|
|
630
|
+
if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
|
|
631
|
+
path = join2(home, ".trae-cn");
|
|
632
|
+
} else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
|
|
633
|
+
path = join2(home, ".marscode");
|
|
634
|
+
} else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
|
|
635
|
+
path = join2(home, ".traecli");
|
|
636
|
+
} else if (variant === "unknown") {
|
|
637
|
+
const cmdPath = await getCommandPath("trae");
|
|
638
|
+
if (cmdPath) path = cmdPath;
|
|
639
|
+
}
|
|
640
|
+
if (!path) return void 0;
|
|
641
|
+
return { name: "trae", version: variant, path };
|
|
642
|
+
}
|
|
643
|
+
async function detectCursor() {
|
|
644
|
+
const path = await getCommandPath("cursor");
|
|
645
|
+
if (path) {
|
|
646
|
+
const version = await getCommandVersion("cursor");
|
|
647
|
+
return { name: "cursor", version, path };
|
|
648
|
+
}
|
|
649
|
+
if (process.platform === "darwin") {
|
|
650
|
+
const appPath = "/Applications/Cursor.app";
|
|
651
|
+
if (existsSync2(appPath)) {
|
|
652
|
+
return { name: "cursor", path: appPath };
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return void 0;
|
|
656
|
+
}
|
|
657
|
+
async function detectCopilot() {
|
|
658
|
+
const path = await getCommandPath("gh");
|
|
659
|
+
if (!path) return void 0;
|
|
660
|
+
try {
|
|
661
|
+
await execa("gh", ["extension", "list"], { timeout: 5e3 });
|
|
662
|
+
return { name: "copilot", path };
|
|
663
|
+
} catch {
|
|
664
|
+
return void 0;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
async function detectTools() {
|
|
668
|
+
const detectors = [
|
|
669
|
+
detectOpenCode,
|
|
670
|
+
detectClaude,
|
|
671
|
+
detectTrae,
|
|
672
|
+
detectCursor,
|
|
673
|
+
detectCopilot
|
|
674
|
+
];
|
|
675
|
+
const results = await Promise.allSettled(detectors.map((d) => d()));
|
|
676
|
+
const tools = [];
|
|
677
|
+
for (const result of results) {
|
|
678
|
+
if (result.status === "fulfilled" && result.value) {
|
|
679
|
+
tools.push(result.value);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return tools;
|
|
683
|
+
}
|
|
684
|
+
function getToolDisplayName(tool) {
|
|
685
|
+
const names = {
|
|
686
|
+
opencode: "OpenCode",
|
|
687
|
+
claude: "Claude Code",
|
|
688
|
+
trae: "Trae",
|
|
689
|
+
cursor: "Cursor",
|
|
690
|
+
copilot: "GitHub Copilot"
|
|
691
|
+
};
|
|
692
|
+
return names[tool];
|
|
693
|
+
}
|
|
694
|
+
function getAllTools() {
|
|
695
|
+
return ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// src/core/manifest.ts
|
|
699
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
700
|
+
import { join as join3 } from "path";
|
|
701
|
+
function readManifest(configRoot) {
|
|
702
|
+
const manifestPath = getManifestPath(configRoot);
|
|
703
|
+
if (existsSync3(manifestPath)) {
|
|
704
|
+
try {
|
|
705
|
+
const content = readFileSync(manifestPath, "utf-8");
|
|
706
|
+
return JSON.parse(content);
|
|
707
|
+
} catch {
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
function readAllManifests(configRoot) {
|
|
713
|
+
const manifests = [];
|
|
714
|
+
const plugins = getAllPlugins();
|
|
715
|
+
const standardPath = getManifestPath(configRoot);
|
|
716
|
+
if (existsSync3(standardPath)) {
|
|
717
|
+
try {
|
|
718
|
+
const content = readFileSync(standardPath, "utf-8");
|
|
719
|
+
manifests.push(JSON.parse(content));
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
for (const plugin of plugins) {
|
|
724
|
+
const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
|
|
725
|
+
if (existsSync3(pluginManifestPath)) {
|
|
726
|
+
try {
|
|
727
|
+
const content = readFileSync(pluginManifestPath, "utf-8");
|
|
728
|
+
manifests.push(JSON.parse(content));
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return manifests;
|
|
734
|
+
}
|
|
735
|
+
function scanInstalled() {
|
|
736
|
+
const plugins = getAllPlugins();
|
|
737
|
+
const installed = [];
|
|
738
|
+
const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
739
|
+
const levels = ["project", "global"];
|
|
740
|
+
for (const tool of tools) {
|
|
741
|
+
for (const level of levels) {
|
|
742
|
+
const configRoot = getConfigRoot(tool, level);
|
|
743
|
+
const manifests = readAllManifests(configRoot);
|
|
744
|
+
for (const manifest of manifests) {
|
|
745
|
+
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
746
|
+
if (!plugin) continue;
|
|
747
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
const alreadyAdded = installed.some(
|
|
751
|
+
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
752
|
+
);
|
|
753
|
+
if (alreadyAdded) continue;
|
|
754
|
+
installed.push({
|
|
755
|
+
id: plugin.id,
|
|
756
|
+
displayName: plugin.displayName,
|
|
757
|
+
tool,
|
|
758
|
+
level,
|
|
759
|
+
skillsCount: manifest.installed_skills?.length || 0,
|
|
760
|
+
agentsCount: manifest.installed_agents?.length || 0,
|
|
761
|
+
installTime: manifest.install_time,
|
|
762
|
+
configRoot
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return installed;
|
|
768
|
+
}
|
|
769
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
770
|
+
const skillsDir = join3(configRoot, "skills");
|
|
771
|
+
const agentsDir = join3(configRoot, "agents");
|
|
772
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
773
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
774
|
+
const skillPath = join3(skillsDir, skillId);
|
|
775
|
+
return existsSync3(skillPath);
|
|
776
|
+
});
|
|
777
|
+
if (!hasAnySkill) return false;
|
|
778
|
+
}
|
|
779
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
780
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
781
|
+
const agentPath = join3(agentsDir, agentId);
|
|
782
|
+
return existsSync3(agentPath);
|
|
783
|
+
});
|
|
784
|
+
if (!hasAnyAgent) return false;
|
|
785
|
+
}
|
|
786
|
+
let agentsMdPath;
|
|
787
|
+
if (tool === "opencode" && level === "project") {
|
|
788
|
+
agentsMdPath = join3(configRoot, "..", "AGENTS.md");
|
|
789
|
+
} else {
|
|
790
|
+
agentsMdPath = join3(configRoot, "AGENTS.md");
|
|
791
|
+
}
|
|
792
|
+
if (!existsSync3(agentsMdPath)) return false;
|
|
793
|
+
return true;
|
|
637
794
|
}
|
|
638
795
|
|
|
639
796
|
// src/utils/logger.ts
|
|
@@ -690,10 +847,10 @@ function printBoxTitle(title, width = 65) {
|
|
|
690
847
|
}
|
|
691
848
|
function showOperationHints(isCheckbox = false) {
|
|
692
849
|
if (isCheckbox) {
|
|
693
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
850
|
+
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
|
|
694
851
|
`);
|
|
695
852
|
} else {
|
|
696
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
853
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
697
854
|
`);
|
|
698
855
|
}
|
|
699
856
|
}
|
|
@@ -859,7 +1016,7 @@ async function runWizard() {
|
|
|
859
1016
|
}
|
|
860
1017
|
}
|
|
861
1018
|
async function stepTool(detectedTools, lastTool) {
|
|
862
|
-
printBoxTitle("
|
|
1019
|
+
printBoxTitle(t("wizard_step_1_title"));
|
|
863
1020
|
if (detectedTools.length === 0) {
|
|
864
1021
|
logger.error(t("error_tool_not_found"));
|
|
865
1022
|
const choices2 = [
|
|
@@ -934,10 +1091,10 @@ async function stepToolManual(lastTool) {
|
|
|
934
1091
|
return result;
|
|
935
1092
|
}
|
|
936
1093
|
async function stepLevel(lastLevel) {
|
|
937
|
-
printBoxTitle("
|
|
1094
|
+
printBoxTitle(t("wizard_step_2_title"));
|
|
938
1095
|
const choices = [
|
|
939
|
-
{ name:
|
|
940
|
-
{ name:
|
|
1096
|
+
{ name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
|
|
1097
|
+
{ name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
|
|
941
1098
|
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
942
1099
|
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
943
1100
|
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
@@ -955,7 +1112,7 @@ async function stepLevel(lastLevel) {
|
|
|
955
1112
|
return result;
|
|
956
1113
|
}
|
|
957
1114
|
async function stepPlugins(tool, level) {
|
|
958
|
-
printBoxTitle("
|
|
1115
|
+
printBoxTitle(t("wizard_step_3_title"));
|
|
959
1116
|
const plugins = getAllPlugins();
|
|
960
1117
|
const configRoot = getConfigRoot(tool, level);
|
|
961
1118
|
const manifests = readAllManifests(configRoot);
|
|
@@ -996,14 +1153,14 @@ async function stepPlugins(tool, level) {
|
|
|
996
1153
|
return pluginIds;
|
|
997
1154
|
}
|
|
998
1155
|
async function stepConfirm(tool, level, plugins) {
|
|
999
|
-
printBoxTitle("
|
|
1156
|
+
printBoxTitle(t("wizard_step_4_title"));
|
|
1000
1157
|
const allPlugins = getAllPlugins();
|
|
1001
1158
|
const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
|
|
1002
1159
|
const displayPath = getConfigRoot(tool, level);
|
|
1003
1160
|
const toolName = getToolDisplayName(tool);
|
|
1004
1161
|
const levelText = level === "project" ? "project" : "global";
|
|
1005
|
-
logger.info(
|
|
1006
|
-
logger.info(
|
|
1162
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk2.bold(String(plugins.length))).replace("{tool}", chalk2.green(toolName)).replace("{level}", chalk2.cyan(levelText)));
|
|
1163
|
+
logger.info(`${t("wizard_install_path")}: ${chalk2.cyan(displayPath)}`);
|
|
1007
1164
|
for (const plugin of selectedPlugins) {
|
|
1008
1165
|
if (plugin) {
|
|
1009
1166
|
logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
|
|
@@ -1030,7 +1187,7 @@ import { execa as execa2 } from "execa";
|
|
|
1030
1187
|
|
|
1031
1188
|
// src/core/scanner.ts
|
|
1032
1189
|
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1033
|
-
import { join as join5 } from "path";
|
|
1190
|
+
import { join as join5, dirname } from "path";
|
|
1034
1191
|
import { parse as parseYaml } from "yaml";
|
|
1035
1192
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
1036
1193
|
"node_modules",
|
|
@@ -1168,8 +1325,7 @@ function writeScanCache(cache) {
|
|
|
1168
1325
|
}
|
|
1169
1326
|
}
|
|
1170
1327
|
function getCachePath() {
|
|
1171
|
-
|
|
1172
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
1328
|
+
return join5(getCannbotConfigDir(), "scan-cache.json");
|
|
1173
1329
|
}
|
|
1174
1330
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1175
1331
|
const selfSkillMd = join5(dirPath, "SKILL.md");
|
|
@@ -1638,7 +1794,6 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1638
1794
|
files.push(repoLinkInConfig);
|
|
1639
1795
|
}
|
|
1640
1796
|
}
|
|
1641
|
-
directories.push(configRoot);
|
|
1642
1797
|
return {
|
|
1643
1798
|
pluginId,
|
|
1644
1799
|
displayName,
|
|
@@ -1715,11 +1870,17 @@ function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
|
1715
1870
|
}
|
|
1716
1871
|
function getInstalledSkills(tool, level, installPath) {
|
|
1717
1872
|
const record = readSkillRecord();
|
|
1718
|
-
|
|
1873
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1874
|
+
const configRoot = getConfigRoot(tool, level);
|
|
1875
|
+
const skillsDir = join7(configRoot, "skills");
|
|
1876
|
+
return recordedSkills.filter((skillId) => {
|
|
1877
|
+
const skillPath = join7(skillsDir, skillId);
|
|
1878
|
+
return existsSync7(skillPath) || isSymlink(skillPath);
|
|
1879
|
+
});
|
|
1719
1880
|
}
|
|
1720
1881
|
|
|
1721
1882
|
// src/core/backup.ts
|
|
1722
|
-
import { existsSync as existsSync8, copyFileSync, readdirSync as
|
|
1883
|
+
import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1723
1884
|
import { join as join8 } from "path";
|
|
1724
1885
|
function getAgentsFileName(tool) {
|
|
1725
1886
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
@@ -1777,7 +1938,7 @@ function findBackups(configRoot) {
|
|
|
1777
1938
|
if (!existsSync8(configRoot)) {
|
|
1778
1939
|
return [];
|
|
1779
1940
|
}
|
|
1780
|
-
const files =
|
|
1941
|
+
const files = readdirSync2(configRoot);
|
|
1781
1942
|
const backups = [];
|
|
1782
1943
|
for (const file of files) {
|
|
1783
1944
|
if (file.includes(".cannbot-backup.")) {
|
|
@@ -2016,7 +2177,7 @@ async function installPlugin(opts) {
|
|
|
2016
2177
|
}
|
|
2017
2178
|
|
|
2018
2179
|
// src/core/skill-installer.ts
|
|
2019
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as
|
|
2180
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
|
|
2020
2181
|
import { join as join10 } from "path";
|
|
2021
2182
|
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
2022
2183
|
import chalk4 from "chalk";
|
|
@@ -2071,10 +2232,10 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2071
2232
|
unlinkSync3(targetPath);
|
|
2072
2233
|
results.push({ skillId, success: true });
|
|
2073
2234
|
removedIds.push(skillId);
|
|
2074
|
-
logger.step(`
|
|
2235
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
2075
2236
|
} else {
|
|
2076
2237
|
results.push({ skillId, success: false, error: "Not installed" });
|
|
2077
|
-
logger.warn(`${skillId}
|
|
2238
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
2078
2239
|
}
|
|
2079
2240
|
} catch (error) {
|
|
2080
2241
|
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
|
|
@@ -2085,27 +2246,17 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2085
2246
|
}
|
|
2086
2247
|
if (existsSync10(skillsDir)) {
|
|
2087
2248
|
try {
|
|
2088
|
-
const entries =
|
|
2249
|
+
const entries = readdirSync3(skillsDir);
|
|
2089
2250
|
if (entries.length === 0) {
|
|
2090
2251
|
rmdirSync(skillsDir);
|
|
2091
|
-
logger.step(`
|
|
2092
|
-
}
|
|
2093
|
-
} catch {
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
if (existsSync10(configRoot)) {
|
|
2097
|
-
try {
|
|
2098
|
-
const entries = readdirSync4(configRoot);
|
|
2099
|
-
if (entries.length === 0) {
|
|
2100
|
-
rmdirSync(configRoot);
|
|
2101
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
2252
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
2102
2253
|
}
|
|
2103
2254
|
} catch {
|
|
2104
2255
|
}
|
|
2105
2256
|
}
|
|
2106
2257
|
return results;
|
|
2107
2258
|
}
|
|
2108
|
-
async function interactiveSkillSelect() {
|
|
2259
|
+
async function interactiveSkillSelect(tool, level) {
|
|
2109
2260
|
const BACK3 = "__back__";
|
|
2110
2261
|
const CANCEL3 = "__cancel__";
|
|
2111
2262
|
const categories = getAllCategories();
|
|
@@ -2114,7 +2265,7 @@ async function interactiveSkillSelect() {
|
|
|
2114
2265
|
while (true) {
|
|
2115
2266
|
switch (step) {
|
|
2116
2267
|
case 0: {
|
|
2117
|
-
printBoxTitle("
|
|
2268
|
+
printBoxTitle(t("wizard_step_3_skill_title"));
|
|
2118
2269
|
const categoryChoices = [
|
|
2119
2270
|
...categories.map((cat) => ({
|
|
2120
2271
|
name: `> ${cat.name} (${cat.skills.length} skills)`,
|
|
@@ -2140,16 +2291,9 @@ async function interactiveSkillSelect() {
|
|
|
2140
2291
|
case 1: {
|
|
2141
2292
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
2142
2293
|
if (!category) return [];
|
|
2143
|
-
printBoxTitle(
|
|
2144
|
-
const
|
|
2145
|
-
const
|
|
2146
|
-
const installedSkills = [];
|
|
2147
|
-
for (const tk of tools) {
|
|
2148
|
-
for (const l of levels) {
|
|
2149
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
2150
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2294
|
+
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
2295
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
2296
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
2153
2297
|
const installedSet = new Set(installedSkills);
|
|
2154
2298
|
const skillChoices = category.skills.map((skill) => {
|
|
2155
2299
|
const isInstalled = installedSet.has(skill.id);
|
|
@@ -2214,7 +2358,7 @@ function listAllSkills() {
|
|
|
2214
2358
|
console.log(table.toString());
|
|
2215
2359
|
}
|
|
2216
2360
|
console.log();
|
|
2217
|
-
console.log(chalk4.dim(`
|
|
2361
|
+
console.log(chalk4.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
2218
2362
|
console.log();
|
|
2219
2363
|
}
|
|
2220
2364
|
function isSymlink2(path) {
|
|
@@ -2251,7 +2395,8 @@ function printPluginList(plugins, installed) {
|
|
|
2251
2395
|
]);
|
|
2252
2396
|
});
|
|
2253
2397
|
console.log();
|
|
2254
|
-
|
|
2398
|
+
const countSuffix = t("count_suffix");
|
|
2399
|
+
console.log(chalk5.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
2255
2400
|
console.log(table.toString());
|
|
2256
2401
|
console.log();
|
|
2257
2402
|
}
|
|
@@ -2284,15 +2429,16 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2284
2429
|
return;
|
|
2285
2430
|
}
|
|
2286
2431
|
const toolName = getToolDisplayName(tool);
|
|
2432
|
+
const countSuffix = t("count_suffix");
|
|
2287
2433
|
const lines = [
|
|
2288
2434
|
chalk5.bold(` ${t("install_done")}!`),
|
|
2289
2435
|
"",
|
|
2290
2436
|
` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
2291
|
-
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}
|
|
2437
|
+
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2292
2438
|
"",
|
|
2293
2439
|
chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
2294
2440
|
` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
|
|
2295
|
-
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("
|
|
2441
|
+
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green(t("try_prompt"))}`,
|
|
2296
2442
|
` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
|
|
2297
2443
|
` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
|
|
2298
2444
|
""
|
|
@@ -2330,14 +2476,14 @@ async function initCommand() {
|
|
|
2330
2476
|
return;
|
|
2331
2477
|
}
|
|
2332
2478
|
printBanner(t("wizard_title"));
|
|
2333
|
-
const spinner = createSpinner("
|
|
2479
|
+
const spinner = createSpinner(t("loading_skills_list"));
|
|
2334
2480
|
spinner.start();
|
|
2335
2481
|
try {
|
|
2336
2482
|
const repoManager = createRepositoryManager();
|
|
2337
2483
|
await repoManager.ensureRepoAndScan();
|
|
2338
|
-
spinner.succeed("
|
|
2484
|
+
spinner.succeed(t("loading_skills_list_complete"));
|
|
2339
2485
|
} catch {
|
|
2340
|
-
spinner.warn("
|
|
2486
|
+
spinner.warn(t("loading_skills_list_failed"));
|
|
2341
2487
|
}
|
|
2342
2488
|
let step = 0;
|
|
2343
2489
|
let mode = "plugin";
|
|
@@ -2371,7 +2517,7 @@ async function initCommand() {
|
|
|
2371
2517
|
}
|
|
2372
2518
|
}
|
|
2373
2519
|
async function selectMode() {
|
|
2374
|
-
printBoxTitle("
|
|
2520
|
+
printBoxTitle(t("wizard_select_mode_title"));
|
|
2375
2521
|
const CANCEL3 = "__cancel__";
|
|
2376
2522
|
const choices = [
|
|
2377
2523
|
{
|
|
@@ -2403,10 +2549,10 @@ async function pluginInstallFlow() {
|
|
|
2403
2549
|
return "done";
|
|
2404
2550
|
}
|
|
2405
2551
|
const repoManager = createRepositoryManager();
|
|
2406
|
-
const spinner = createSpinner("
|
|
2552
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2407
2553
|
spinner.start();
|
|
2408
2554
|
const repoPath = await repoManager.ensureRepo();
|
|
2409
|
-
spinner.succeed("
|
|
2555
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2410
2556
|
const allPlugins = getAllPlugins();
|
|
2411
2557
|
const selectedPlugins = answers.plugins.map(
|
|
2412
2558
|
(id) => allPlugins.find((p) => p.id === id)
|
|
@@ -2482,7 +2628,7 @@ async function skillInstallFlow() {
|
|
|
2482
2628
|
break;
|
|
2483
2629
|
}
|
|
2484
2630
|
case 2: {
|
|
2485
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2631
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2486
2632
|
if (selectedSkills === "back") {
|
|
2487
2633
|
step = 1;
|
|
2488
2634
|
break;
|
|
@@ -2492,15 +2638,15 @@ async function skillInstallFlow() {
|
|
|
2492
2638
|
return "done";
|
|
2493
2639
|
}
|
|
2494
2640
|
if (selectedSkills.length === 0) {
|
|
2495
|
-
logger.info("
|
|
2641
|
+
logger.info(t("install_no_skill_selected"));
|
|
2496
2642
|
return "done";
|
|
2497
2643
|
}
|
|
2498
2644
|
const repoManager = createRepositoryManager();
|
|
2499
|
-
const spinner = createSpinner("
|
|
2645
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2500
2646
|
spinner.start();
|
|
2501
2647
|
const repoPath = await repoManager.ensureRepo();
|
|
2502
|
-
spinner.succeed("
|
|
2503
|
-
logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)}
|
|
2648
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2649
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk6.bold(String(selectedSkills.length)))}...`);
|
|
2504
2650
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2505
2651
|
let successCount = 0;
|
|
2506
2652
|
let failCount = 0;
|
|
@@ -2518,10 +2664,10 @@ async function skillInstallFlow() {
|
|
|
2518
2664
|
}
|
|
2519
2665
|
const configRoot = getConfigRoot(tool, level);
|
|
2520
2666
|
logger.blank();
|
|
2521
|
-
logger.success(`${t("skill_install_done")}: ${chalk6.green(
|
|
2667
|
+
logger.success(`${t("skill_install_done")}: ${chalk6.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk6.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk6.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2522
2668
|
logger.blank();
|
|
2523
|
-
logger.info(
|
|
2524
|
-
logger.info(
|
|
2669
|
+
logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
|
|
2670
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
|
|
2525
2671
|
logger.blank();
|
|
2526
2672
|
return "done";
|
|
2527
2673
|
}
|
|
@@ -2544,7 +2690,7 @@ async function listCommand() {
|
|
|
2544
2690
|
|
|
2545
2691
|
// src/commands/doctor.ts
|
|
2546
2692
|
import chalk7 from "chalk";
|
|
2547
|
-
import { existsSync as existsSync12, readdirSync as
|
|
2693
|
+
import { existsSync as existsSync12, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
2548
2694
|
import { join as join12 } from "path";
|
|
2549
2695
|
async function doctorCommand(options = {}) {
|
|
2550
2696
|
console.log();
|
|
@@ -2564,7 +2710,7 @@ async function doctorCommand(options = {}) {
|
|
|
2564
2710
|
chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2565
2711
|
);
|
|
2566
2712
|
} else {
|
|
2567
|
-
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014
|
|
2713
|
+
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2568
2714
|
}
|
|
2569
2715
|
}
|
|
2570
2716
|
console.log();
|
|
@@ -2579,7 +2725,7 @@ async function doctorCommand(options = {}) {
|
|
|
2579
2725
|
chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2580
2726
|
);
|
|
2581
2727
|
} else {
|
|
2582
|
-
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014
|
|
2728
|
+
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2583
2729
|
}
|
|
2584
2730
|
}
|
|
2585
2731
|
console.log();
|
|
@@ -2591,45 +2737,45 @@ async function doctorCommand(options = {}) {
|
|
|
2591
2737
|
if (existsSync12(skillsDir)) {
|
|
2592
2738
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2593
2739
|
if (brokenLinks === 0) {
|
|
2594
|
-
const count =
|
|
2595
|
-
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count}
|
|
2740
|
+
const count = readdirSync4(skillsDir).length;
|
|
2741
|
+
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2596
2742
|
} else {
|
|
2597
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks}
|
|
2743
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2598
2744
|
warnings++;
|
|
2599
2745
|
if (options.fix) {
|
|
2600
2746
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2601
2747
|
fixes += fixed;
|
|
2602
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2748
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2603
2749
|
}
|
|
2604
2750
|
}
|
|
2605
2751
|
} else {
|
|
2606
|
-
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014
|
|
2752
|
+
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2607
2753
|
if (options.fix) {
|
|
2608
2754
|
mkdirSync6(skillsDir, { recursive: true });
|
|
2609
2755
|
fixes++;
|
|
2610
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2756
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2611
2757
|
}
|
|
2612
2758
|
}
|
|
2613
2759
|
if (existsSync12(agentsDir)) {
|
|
2614
2760
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2615
2761
|
if (brokenLinks === 0) {
|
|
2616
|
-
const count =
|
|
2617
|
-
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count}
|
|
2762
|
+
const count = readdirSync4(agentsDir).length;
|
|
2763
|
+
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2618
2764
|
} else {
|
|
2619
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks}
|
|
2765
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2620
2766
|
warnings++;
|
|
2621
2767
|
if (options.fix) {
|
|
2622
2768
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2623
2769
|
fixes += fixed;
|
|
2624
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2770
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2625
2771
|
}
|
|
2626
2772
|
}
|
|
2627
2773
|
} else {
|
|
2628
|
-
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014
|
|
2774
|
+
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2629
2775
|
if (options.fix) {
|
|
2630
2776
|
mkdirSync6(agentsDir, { recursive: true });
|
|
2631
2777
|
fixes++;
|
|
2632
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2778
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2633
2779
|
}
|
|
2634
2780
|
}
|
|
2635
2781
|
console.log();
|
|
@@ -2637,19 +2783,19 @@ async function doctorCommand(options = {}) {
|
|
|
2637
2783
|
const configFile = getConfigFileName(primaryTool);
|
|
2638
2784
|
const configPath = join12(process.cwd(), configFile);
|
|
2639
2785
|
if (existsSync12(configPath)) {
|
|
2640
|
-
console.log(chalk7.green(" \u2713") + ` ${configFile}
|
|
2786
|
+
console.log(chalk7.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2641
2787
|
} else {
|
|
2642
|
-
console.log(chalk7.dim(" \u2014") + ` ${configFile}
|
|
2788
|
+
console.log(chalk7.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2643
2789
|
}
|
|
2644
2790
|
console.log();
|
|
2645
2791
|
console.log(chalk7.dim(" " + "\u2500".repeat(46)));
|
|
2646
2792
|
if (options.fix && fixes > 0) {
|
|
2647
2793
|
console.log(
|
|
2648
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2794
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
|
|
2649
2795
|
);
|
|
2650
2796
|
} else {
|
|
2651
2797
|
console.log(
|
|
2652
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2798
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
|
|
2653
2799
|
);
|
|
2654
2800
|
}
|
|
2655
2801
|
console.log();
|
|
@@ -2657,7 +2803,7 @@ async function doctorCommand(options = {}) {
|
|
|
2657
2803
|
function checkBrokenLinks(dir) {
|
|
2658
2804
|
let broken = 0;
|
|
2659
2805
|
try {
|
|
2660
|
-
const entries =
|
|
2806
|
+
const entries = readdirSync4(dir);
|
|
2661
2807
|
for (const entry of entries) {
|
|
2662
2808
|
const fullPath = join12(dir, entry);
|
|
2663
2809
|
try {
|
|
@@ -2678,7 +2824,7 @@ function checkBrokenLinks(dir) {
|
|
|
2678
2824
|
function fixBrokenLinks(dir) {
|
|
2679
2825
|
let fixed = 0;
|
|
2680
2826
|
try {
|
|
2681
|
-
const entries =
|
|
2827
|
+
const entries = readdirSync4(dir);
|
|
2682
2828
|
for (const entry of entries) {
|
|
2683
2829
|
const fullPath = join12(dir, entry);
|
|
2684
2830
|
try {
|
|
@@ -2739,46 +2885,47 @@ async function installCommand(names, options) {
|
|
|
2739
2885
|
return;
|
|
2740
2886
|
}
|
|
2741
2887
|
if (names.length === 0) {
|
|
2742
|
-
const scanSpinner2 = createSpinner("
|
|
2888
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2743
2889
|
scanSpinner2.start();
|
|
2744
2890
|
try {
|
|
2745
2891
|
const scanRepoManager = createRepositoryManager();
|
|
2746
2892
|
await scanRepoManager.ensureRepoAndScan();
|
|
2747
|
-
scanSpinner2.succeed("
|
|
2893
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2748
2894
|
} catch {
|
|
2749
|
-
scanSpinner2.warn("
|
|
2895
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2750
2896
|
}
|
|
2751
2897
|
let tool2;
|
|
2752
2898
|
if (options.tool) {
|
|
2753
|
-
tool2 = options.tool;
|
|
2899
|
+
tool2 = validateTool(options.tool);
|
|
2754
2900
|
} else {
|
|
2755
2901
|
const detected = await selectToolWithDetection();
|
|
2756
|
-
if (detected === "back") return;
|
|
2902
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2757
2903
|
tool2 = detected;
|
|
2758
2904
|
}
|
|
2905
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2759
2906
|
let step = 1;
|
|
2760
2907
|
while (true) {
|
|
2761
2908
|
switch (step) {
|
|
2762
2909
|
case 1: {
|
|
2763
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2910
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2764
2911
|
if (selectedSkills === "back") {
|
|
2765
2912
|
const redetected = await selectToolWithDetection();
|
|
2766
|
-
if (redetected === "back") return;
|
|
2913
|
+
if (redetected === "back" || redetected === "cancel") return;
|
|
2767
2914
|
tool2 = redetected;
|
|
2768
2915
|
break;
|
|
2769
2916
|
}
|
|
2917
|
+
if (selectedSkills === "cancel") return;
|
|
2770
2918
|
if (selectedSkills.length === 0) {
|
|
2771
|
-
logger.info("
|
|
2919
|
+
logger.info(t("install_no_skill_selected"));
|
|
2772
2920
|
return;
|
|
2773
2921
|
}
|
|
2774
2922
|
names = selectedSkills;
|
|
2775
2923
|
const repoManager = createRepositoryManager();
|
|
2776
|
-
const spinner = createSpinner("
|
|
2924
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2777
2925
|
spinner.start();
|
|
2778
2926
|
const repoPath = await repoManager.ensureRepo();
|
|
2779
|
-
spinner.succeed("
|
|
2780
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)}
|
|
2781
|
-
const level2 = options.level || "project";
|
|
2927
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2928
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(names.length)))}...`);
|
|
2782
2929
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2783
2930
|
let successCount = 0;
|
|
2784
2931
|
let failCount = 0;
|
|
@@ -2796,24 +2943,24 @@ async function installCommand(names, options) {
|
|
|
2796
2943
|
}
|
|
2797
2944
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2798
2945
|
logger.blank();
|
|
2799
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
2946
|
+
logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2800
2947
|
logger.blank();
|
|
2801
|
-
logger.info(
|
|
2802
|
-
logger.info(
|
|
2948
|
+
logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
|
|
2949
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
|
|
2803
2950
|
logger.blank();
|
|
2804
2951
|
return;
|
|
2805
2952
|
}
|
|
2806
2953
|
}
|
|
2807
2954
|
}
|
|
2808
2955
|
}
|
|
2809
|
-
const scanSpinner = createSpinner("
|
|
2956
|
+
const scanSpinner = createSpinner(t("loading_skills_list"));
|
|
2810
2957
|
scanSpinner.start();
|
|
2811
2958
|
try {
|
|
2812
2959
|
const scanRepoManager = createRepositoryManager();
|
|
2813
2960
|
await scanRepoManager.ensureRepoAndScan();
|
|
2814
|
-
scanSpinner.succeed("
|
|
2961
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2815
2962
|
} catch {
|
|
2816
|
-
scanSpinner.warn("
|
|
2963
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2817
2964
|
}
|
|
2818
2965
|
const plugins = [];
|
|
2819
2966
|
const skills = [];
|
|
@@ -2830,22 +2977,22 @@ async function installCommand(names, options) {
|
|
|
2830
2977
|
}
|
|
2831
2978
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2832
2979
|
const allPlugins = getAllPlugins();
|
|
2833
|
-
logger.info("
|
|
2980
|
+
logger.info(t("available_plugins") + ":");
|
|
2834
2981
|
for (const p of allPlugins) {
|
|
2835
2982
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2836
2983
|
}
|
|
2837
|
-
logger.info(
|
|
2984
|
+
logger.info(`${t("available_skills")}: ${chalk9.cyan("install-helper install --list")}`);
|
|
2838
2985
|
return;
|
|
2839
2986
|
}
|
|
2840
2987
|
let tool;
|
|
2841
2988
|
if (options.tool) {
|
|
2842
|
-
tool = options.tool;
|
|
2989
|
+
tool = validateTool(options.tool);
|
|
2843
2990
|
} else {
|
|
2844
2991
|
const detected = await selectToolWithDetection();
|
|
2845
|
-
if (detected === "back") return;
|
|
2992
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2846
2993
|
tool = detected;
|
|
2847
2994
|
}
|
|
2848
|
-
const level = options.level
|
|
2995
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2849
2996
|
if (plugins.length > 0) {
|
|
2850
2997
|
const configRoot = getConfigRoot(tool, level);
|
|
2851
2998
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2873,10 +3020,10 @@ async function installCommand(names, options) {
|
|
|
2873
3020
|
}
|
|
2874
3021
|
if (pluginsToInstall.length > 0) {
|
|
2875
3022
|
const repoManager = createRepositoryManager();
|
|
2876
|
-
const spinner = createSpinner("
|
|
3023
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2877
3024
|
spinner.start();
|
|
2878
3025
|
const repoPath = await repoManager.ensureRepo();
|
|
2879
|
-
spinner.succeed("
|
|
3026
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2880
3027
|
const allPlugins = getAllPlugins();
|
|
2881
3028
|
const results = await installPlugins(
|
|
2882
3029
|
pluginsToInstall,
|
|
@@ -2902,11 +3049,11 @@ async function installCommand(names, options) {
|
|
|
2902
3049
|
}
|
|
2903
3050
|
if (skills.length > 0) {
|
|
2904
3051
|
const repoManager = createRepositoryManager();
|
|
2905
|
-
const spinner = createSpinner("
|
|
3052
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2906
3053
|
spinner.start();
|
|
2907
3054
|
const repoPath = await repoManager.ensureRepo();
|
|
2908
|
-
spinner.succeed("
|
|
2909
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)}
|
|
3055
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3056
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(skills.length)))}...`);
|
|
2910
3057
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2911
3058
|
let successCount = 0;
|
|
2912
3059
|
let failCount = 0;
|
|
@@ -2923,10 +3070,10 @@ async function installCommand(names, options) {
|
|
|
2923
3070
|
}
|
|
2924
3071
|
}
|
|
2925
3072
|
logger.blank();
|
|
2926
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
3073
|
+
logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2927
3074
|
logger.blank();
|
|
2928
|
-
logger.info(
|
|
2929
|
-
logger.info(
|
|
3075
|
+
logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
3076
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
|
|
2930
3077
|
logger.blank();
|
|
2931
3078
|
}
|
|
2932
3079
|
}
|
|
@@ -2958,11 +3105,11 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2958
3105
|
}
|
|
2959
3106
|
|
|
2960
3107
|
// src/commands/uninstall.ts
|
|
2961
|
-
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as
|
|
3108
|
+
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
|
|
2962
3109
|
import chalk10 from "chalk";
|
|
2963
3110
|
async function uninstallCommand(names, options) {
|
|
2964
|
-
const tool = options.tool
|
|
2965
|
-
const level = options.level
|
|
3111
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
3112
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2966
3113
|
const plugins = [];
|
|
2967
3114
|
const skills = [];
|
|
2968
3115
|
for (const name of names) {
|
|
@@ -2995,21 +3142,25 @@ async function uninstallCommand(names, options) {
|
|
|
2995
3142
|
if (existsSync13(filePath) || isSymlink3(filePath)) {
|
|
2996
3143
|
unlinkSync5(filePath);
|
|
2997
3144
|
const name = filePath.split("/").pop() || filePath;
|
|
2998
|
-
logger.step(`
|
|
3145
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2999
3146
|
removedFiles++;
|
|
3000
3147
|
}
|
|
3001
3148
|
} catch {
|
|
3002
3149
|
}
|
|
3003
3150
|
}
|
|
3151
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3004
3152
|
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
3005
3153
|
for (const dirPath of sortedDirs) {
|
|
3006
3154
|
try {
|
|
3155
|
+
if (dirPath === configRoot) {
|
|
3156
|
+
continue;
|
|
3157
|
+
}
|
|
3007
3158
|
if (existsSync13(dirPath)) {
|
|
3008
|
-
const entries =
|
|
3159
|
+
const entries = readdirSync5(dirPath);
|
|
3009
3160
|
if (entries.length === 0) {
|
|
3010
3161
|
rmdirSync2(dirPath);
|
|
3011
3162
|
const name = dirPath.split("/").pop() || dirPath;
|
|
3012
|
-
logger.step(`
|
|
3163
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
3013
3164
|
removedDirs++;
|
|
3014
3165
|
}
|
|
3015
3166
|
}
|
|
@@ -3018,7 +3169,6 @@ async function uninstallCommand(names, options) {
|
|
|
3018
3169
|
}
|
|
3019
3170
|
deleteRecord(plugin.id);
|
|
3020
3171
|
removeInstalledPlugin(plugin.id);
|
|
3021
|
-
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3022
3172
|
const backups = findBackups(configRoot);
|
|
3023
3173
|
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
3024
3174
|
if (otherBackups.length > 0) {
|
|
@@ -3038,13 +3188,13 @@ async function uninstallCommand(names, options) {
|
|
|
3038
3188
|
}
|
|
3039
3189
|
const totalRemoved = removedFiles + removedDirs;
|
|
3040
3190
|
if (totalRemoved > 0) {
|
|
3041
|
-
logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08
|
|
3191
|
+
logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08${t("uninstall_remove_file_format").replace("{count}", String(removedFiles))}\uFF0C${t("uninstall_remove_dir_format").replace("{count}", String(removedDirs))}\uFF09`);
|
|
3042
3192
|
} else {
|
|
3043
3193
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3044
3194
|
}
|
|
3045
3195
|
}
|
|
3046
3196
|
if (skills.length > 0) {
|
|
3047
|
-
logger.info(`${t("uninstall_skills_in_progress")} ${skills.length}
|
|
3197
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
3048
3198
|
const results = await uninstallSkills(skills, tool, level);
|
|
3049
3199
|
let successCount = 0;
|
|
3050
3200
|
let failCount = 0;
|
|
@@ -3056,7 +3206,7 @@ async function uninstallCommand(names, options) {
|
|
|
3056
3206
|
}
|
|
3057
3207
|
}
|
|
3058
3208
|
logger.blank();
|
|
3059
|
-
logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(
|
|
3209
|
+
logger.success(`${t("skill_uninstall_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)))}`);
|
|
3060
3210
|
logger.blank();
|
|
3061
3211
|
}
|
|
3062
3212
|
}
|
|
@@ -3073,13 +3223,13 @@ import chalk11 from "chalk";
|
|
|
3073
3223
|
async function updateCommand(pluginNames, options) {
|
|
3074
3224
|
let tool;
|
|
3075
3225
|
if (options.tool) {
|
|
3076
|
-
tool = options.tool;
|
|
3226
|
+
tool = validateTool(options.tool);
|
|
3077
3227
|
} else {
|
|
3078
3228
|
const detected = await selectToolWithDetection();
|
|
3079
|
-
if (detected === "back") return;
|
|
3229
|
+
if (detected === "back" || detected === "cancel") return;
|
|
3080
3230
|
tool = detected;
|
|
3081
3231
|
}
|
|
3082
|
-
const level = options.level
|
|
3232
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
3083
3233
|
let pluginsToUpdate = [];
|
|
3084
3234
|
if (pluginNames.length === 0) {
|
|
3085
3235
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -3087,7 +3237,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3087
3237
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
3088
3238
|
if (pluginsToUpdate.length === 0) {
|
|
3089
3239
|
logger.info(t("update_no_plugins"));
|
|
3090
|
-
logger.info(
|
|
3240
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk11.cyan("install-helper install <plugin>")));
|
|
3091
3241
|
return;
|
|
3092
3242
|
}
|
|
3093
3243
|
} else {
|
|
@@ -3150,7 +3300,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3150
3300
|
|
|
3151
3301
|
// src/commands/info.ts
|
|
3152
3302
|
import chalk12 from "chalk";
|
|
3153
|
-
import { existsSync as existsSync14, readdirSync as
|
|
3303
|
+
import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
|
|
3154
3304
|
import { join as join13 } from "path";
|
|
3155
3305
|
async function infoCommand(pluginName) {
|
|
3156
3306
|
const plugin = findPlugin(pluginName);
|
|
@@ -3181,7 +3331,7 @@ async function infoCommand(pluginName) {
|
|
|
3181
3331
|
}
|
|
3182
3332
|
const agentsDir = join13(repoPath, plugin.dir, "agents");
|
|
3183
3333
|
if (existsSync14(agentsDir)) {
|
|
3184
|
-
const agentFiles =
|
|
3334
|
+
const agentFiles = readdirSync6(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3185
3335
|
if (agentFiles.length > 0) {
|
|
3186
3336
|
console.log();
|
|
3187
3337
|
console.log(` ${chalk12.bold("Agents:")}`);
|
|
@@ -3238,7 +3388,7 @@ function createCLI() {
|
|
|
3238
3388
|
setLanguage(config.language);
|
|
3239
3389
|
}
|
|
3240
3390
|
const program2 = new Command();
|
|
3241
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("
|
|
3391
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("1.0.2");
|
|
3242
3392
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3243
3393
|
await initCommand();
|
|
3244
3394
|
});
|
|
@@ -3275,7 +3425,7 @@ function createCLI() {
|
|
|
3275
3425
|
// src/index.ts
|
|
3276
3426
|
process.on("uncaughtException", (error) => {
|
|
3277
3427
|
if (error.name === "ExitPromptError") {
|
|
3278
|
-
console.log("\n
|
|
3428
|
+
console.log("\n" + t("init_cancelled"));
|
|
3279
3429
|
process.exit(0);
|
|
3280
3430
|
}
|
|
3281
3431
|
throw error;
|