@cannbot-ai/install-helper 0.0.2-beta.14 → 0.0.2-beta.16
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 +1 -1
- package/dist/config/defaults.yaml +15 -0
- package/dist/index.js +326 -297
- package/dist/index.js.map +1 -1
- package/dist/plugins.d/_defaults.yml +4 -0
- package/dist/plugins.d/catlass-op-generator.yml +7 -0
- package/dist/plugins.d/model-infer-optimize.yml +7 -0
- package/dist/plugins.d/ops-code-reviewer.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke.yml +7 -0
- package/dist/plugins.d/ops-registry-invoke.yml +7 -0
- package/dist/plugins.d/pypto-op-orchestrator.yml +7 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +7 -0
- package/dist/plugins.d/torch-compile.yml +7 -0
- package/dist/plugins.d/triton-op-generator.yml +8 -0
- package/package.json +5 -3
- package/plugins.d/_defaults.yml +4 -0
- package/plugins.d/catlass-op-generator.yml +7 -0
- package/plugins.d/model-infer-optimize.yml +7 -0
- package/plugins.d/ops-code-reviewer.yml +7 -0
- package/plugins.d/ops-direct-invoke-flash.yml +7 -0
- package/plugins.d/ops-direct-invoke.yml +7 -0
- package/plugins.d/ops-registry-invoke.yml +7 -0
- package/plugins.d/pypto-op-orchestrator.yml +7 -0
- package/plugins.d/tilelang-op-orchestrator.yml +7 -0
- package/plugins.d/torch-compile.yml +7 -0
- package/plugins.d/triton-op-generator.yml +8 -0
package/dist/index.js
CHANGED
|
@@ -12,108 +12,45 @@ import { select, checkbox, Separator } from "@inquirer/prompts";
|
|
|
12
12
|
import chalk2 from "chalk";
|
|
13
13
|
|
|
14
14
|
// src/core/registry.ts
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
dir: "plugins-official/ops-direct-invoke-flash",
|
|
29
|
-
displayName: "AscendC Kernel \u4ECE\u96F6\u6784\u5EFA",
|
|
30
|
-
script: "init.sh",
|
|
31
|
-
aliases: ["flash", "kernel-flash"],
|
|
32
|
-
skills: 1,
|
|
33
|
-
agents: 1,
|
|
34
|
-
description: "Ascend C / Ascend950 Reg API \u6838\u51FD\u6570\u4ECE\u96F6\u6784\u5EFA"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
id: "ops-registry-invoke",
|
|
38
|
-
dir: "plugins-official/ops-registry-invoke",
|
|
39
|
-
displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
|
|
40
|
-
script: "init.sh",
|
|
41
|
-
aliases: ["ops-registry", "ascendc-registry", "registry"],
|
|
42
|
-
skills: 12,
|
|
43
|
-
agents: 4,
|
|
44
|
-
description: "Ascend C \u7B97\u5B50\u6CE8\u518C\u8C03\u7528\u5F00\u53D1\u6D41\u7A0B"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: "pypto-op-orchestrator",
|
|
48
|
-
dir: "plugins-official/pypto-op-orchestrator",
|
|
49
|
-
displayName: "PyPTO \u7B97\u5B50",
|
|
50
|
-
script: "init.sh",
|
|
51
|
-
aliases: ["pypto", "pytorch"],
|
|
52
|
-
skills: 8,
|
|
53
|
-
agents: 3,
|
|
54
|
-
description: "PyPTO \u7B97\u5B50\u7AEF\u5230\u7AEF\u5F00\u53D1\u7F16\u6392"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: "triton-op-generator",
|
|
58
|
-
dir: "plugins-official/triton-op-generator",
|
|
59
|
-
displayName: "Triton \u7B97\u5B50\u751F\u6210",
|
|
60
|
-
script: "install.sh",
|
|
61
|
-
aliases: ["triton"],
|
|
62
|
-
skills: 6,
|
|
63
|
-
agents: 0,
|
|
64
|
-
description: "Triton-Ascend \u7B97\u5B50\u4EE3\u7801\u751F\u6210\u4E0E\u4F18\u5316"
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
id: "tilelang-op-orchestrator",
|
|
68
|
-
dir: "plugins-official/tilelang-op-orchestrator",
|
|
69
|
-
displayName: "TileLang \u7B97\u5B50",
|
|
70
|
-
script: "init.sh",
|
|
71
|
-
aliases: ["tilelang"],
|
|
72
|
-
skills: 9,
|
|
73
|
-
agents: 3,
|
|
74
|
-
description: "TileLang \u7B97\u5B50\u5F00\u53D1\u6D41\u7A0B"
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
id: "model-infer-optimize",
|
|
78
|
-
dir: "plugins-official/model-infer-optimize",
|
|
79
|
-
displayName: "NPU \u63A8\u7406\u4F18\u5316",
|
|
80
|
-
script: "init.sh",
|
|
81
|
-
aliases: ["model-infer", "infer", "inference"],
|
|
82
|
-
skills: 11,
|
|
83
|
-
agents: 3,
|
|
84
|
-
description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316"
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
id: "catlass-op-generator",
|
|
88
|
-
dir: "plugins-official/catlass-op-generator",
|
|
89
|
-
displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
|
|
90
|
-
script: "init.sh",
|
|
91
|
-
aliases: ["catlass"],
|
|
92
|
-
skills: 10,
|
|
93
|
-
agents: 3,
|
|
94
|
-
description: "Catlass \u7B97\u5B50\u76F4\u8C03\u5F00\u53D1"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
id: "ops-code-reviewer",
|
|
98
|
-
dir: "plugins-official/ops-code-reviewer",
|
|
99
|
-
displayName: "\u4EE3\u7801\u68C0\u89C6",
|
|
100
|
-
script: "init.sh",
|
|
101
|
-
aliases: ["code-review", "reviewer", "review"],
|
|
102
|
-
skills: 5,
|
|
103
|
-
agents: 1,
|
|
104
|
-
description: "\u4EE3\u7801\u68C0\u89C6\u4E0E\u89C4\u8303\u68C0\u67E5"
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
id: "torch-compile",
|
|
108
|
-
dir: "plugins-official/torch-compile",
|
|
109
|
-
displayName: "torch.compile \u56FE\u6A21\u5F0F",
|
|
110
|
-
script: "init.sh",
|
|
111
|
-
aliases: ["torch", "compile", "graph"],
|
|
112
|
-
skills: 6,
|
|
113
|
-
agents: 1,
|
|
114
|
-
description: "PyTorch torch.compile \u56FE\u6A21\u5F0F\u9002\u914D"
|
|
15
|
+
import { readdirSync, readFileSync } from "fs";
|
|
16
|
+
import { join, dirname } from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
import { parse as parseYaml } from "yaml";
|
|
19
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
var __dirname = dirname(__filename);
|
|
21
|
+
function loadPluginsFromYaml() {
|
|
22
|
+
const pluginsDir = join(__dirname, "plugins.d");
|
|
23
|
+
const defaultsPath = join(pluginsDir, "_defaults.yml");
|
|
24
|
+
let defaults = {};
|
|
25
|
+
try {
|
|
26
|
+
defaults = parseYaml(readFileSync(defaultsPath, "utf-8")) || {};
|
|
27
|
+
} catch {
|
|
115
28
|
}
|
|
116
|
-
];
|
|
29
|
+
const plugins = [];
|
|
30
|
+
try {
|
|
31
|
+
for (const file of readdirSync(pluginsDir)) {
|
|
32
|
+
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
33
|
+
try {
|
|
34
|
+
const content = parseYaml(readFileSync(join(pluginsDir, file), "utf-8"));
|
|
35
|
+
if (!content || !content.id) continue;
|
|
36
|
+
plugins.push({
|
|
37
|
+
id: content.id,
|
|
38
|
+
dir: content.dir,
|
|
39
|
+
displayName: content.displayName || content.id,
|
|
40
|
+
script: content.script || defaults.script || "init.sh",
|
|
41
|
+
aliases: content.aliases || [],
|
|
42
|
+
skills: content.skills ?? defaults.skills ?? 0,
|
|
43
|
+
agents: content.agents ?? defaults.agents ?? 0,
|
|
44
|
+
description: content.description || ""
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
return plugins;
|
|
52
|
+
}
|
|
53
|
+
var PLUGIN_REGISTRY = loadPluginsFromYaml();
|
|
117
54
|
function findPlugin(query) {
|
|
118
55
|
const normalized = query.toLowerCase().trim();
|
|
119
56
|
const exactMatch = PLUGIN_REGISTRY.find(
|
|
@@ -139,11 +76,11 @@ function getPluginById(id) {
|
|
|
139
76
|
import { execa } from "execa";
|
|
140
77
|
import { existsSync as existsSync2 } from "fs";
|
|
141
78
|
import { homedir as homedir2 } from "os";
|
|
142
|
-
import { join as
|
|
79
|
+
import { join as join3 } from "path";
|
|
143
80
|
|
|
144
81
|
// src/utils/paths.ts
|
|
145
82
|
import { homedir } from "os";
|
|
146
|
-
import { join } from "path";
|
|
83
|
+
import { join as join2 } from "path";
|
|
147
84
|
import { existsSync } from "fs";
|
|
148
85
|
|
|
149
86
|
// src/utils/i18n.ts
|
|
@@ -282,7 +219,38 @@ var zh_CN = {
|
|
|
282
219
|
wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
|
|
283
220
|
install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
|
|
284
221
|
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"
|
|
222
|
+
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
223
|
+
skill_count_format: "{count} \u4E2A Skills",
|
|
224
|
+
result_success_format: "{count} \u6210\u529F",
|
|
225
|
+
result_failed_format: "{count} \u5931\u8D25",
|
|
226
|
+
uninstall_remove_label: "\u79FB\u9664",
|
|
227
|
+
uninstall_remove_file_format: "\u79FB\u9664 {count} \u4E2A\u6587\u4EF6",
|
|
228
|
+
uninstall_remove_dir_format: "{count} \u4E2A\u76EE\u5F55",
|
|
229
|
+
uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
|
|
230
|
+
uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
|
|
231
|
+
available_plugins: "\u53EF\u7528\u63D2\u4EF6",
|
|
232
|
+
available_skills: "\u53EF\u7528 Skills",
|
|
233
|
+
skill_total_count: "\u5171 {count} \u4E2A Skills",
|
|
234
|
+
wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
|
|
235
|
+
wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
|
|
236
|
+
update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
|
|
237
|
+
hint_select_move: "\u79FB\u52A8",
|
|
238
|
+
hint_select_space: "\u7A7A\u683C",
|
|
239
|
+
hint_select_check: "\u52FE\u9009",
|
|
240
|
+
hint_select_confirm: "\u786E\u8BA4",
|
|
241
|
+
hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
|
|
242
|
+
doctor_not_installed: "\u672A\u5B89\u88C5",
|
|
243
|
+
doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
|
|
244
|
+
doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
245
|
+
doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
246
|
+
doctor_not_exist: "\u4E0D\u5B58\u5728",
|
|
247
|
+
doctor_created: "\u5DF2\u521B\u5EFA {path}",
|
|
248
|
+
doctor_config_exists: "\u5B58\u5728",
|
|
249
|
+
doctor_config_not_exist: "\u4E0D\u5B58\u5728",
|
|
250
|
+
doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF, {fixes} \u9879\u5DF2\u4FEE\u590D",
|
|
251
|
+
doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF",
|
|
252
|
+
count_suffix: "\u4E2A",
|
|
253
|
+
try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
|
|
286
254
|
};
|
|
287
255
|
var en_US = {
|
|
288
256
|
wizard_title: "CANNBot Install Helper",
|
|
@@ -419,7 +387,38 @@ var en_US = {
|
|
|
419
387
|
wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
|
|
420
388
|
install_level_project: "Install to current directory (recommended, current project only)",
|
|
421
389
|
install_level_global: "Install globally (all projects)",
|
|
422
|
-
wizard_select_mode_title: "Select installation type"
|
|
390
|
+
wizard_select_mode_title: "Select installation type",
|
|
391
|
+
skill_count_format: "{count} Skills",
|
|
392
|
+
result_success_format: "{count} succeeded",
|
|
393
|
+
result_failed_format: "{count} failed",
|
|
394
|
+
uninstall_remove_label: "Removed",
|
|
395
|
+
uninstall_remove_file_format: "Removed {count} files",
|
|
396
|
+
uninstall_remove_dir_format: "{count} directories",
|
|
397
|
+
uninstall_clean_empty_dir: "Cleaned empty directory",
|
|
398
|
+
uninstall_skill_not_installed: "Not installed",
|
|
399
|
+
available_plugins: "Available plugins",
|
|
400
|
+
available_skills: "Available Skills",
|
|
401
|
+
skill_total_count: "Total {count} Skills",
|
|
402
|
+
wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
|
|
403
|
+
wizard_install_path: "Install path",
|
|
404
|
+
update_install_hint: "Use {cmd} to install plugins",
|
|
405
|
+
hint_select_move: "Navigate",
|
|
406
|
+
hint_select_space: "Space",
|
|
407
|
+
hint_select_check: "Toggle",
|
|
408
|
+
hint_select_confirm: "Confirm",
|
|
409
|
+
hint_checkbox_suffix: "Press enter without selection to go back",
|
|
410
|
+
doctor_not_installed: "Not installed",
|
|
411
|
+
doctor_links_valid: "{count} symlinks all valid",
|
|
412
|
+
doctor_broken_links: "{count} broken links",
|
|
413
|
+
doctor_fixed: "Cleaned {count} broken links",
|
|
414
|
+
doctor_not_exist: "Does not exist",
|
|
415
|
+
doctor_created: "Created {path}",
|
|
416
|
+
doctor_config_exists: "Exists",
|
|
417
|
+
doctor_config_not_exist: "Does not exist",
|
|
418
|
+
doctor_result_with_fix: "{warnings} warnings, {errors} errors, {fixes} fixed",
|
|
419
|
+
doctor_result_without_fix: "{warnings} warnings, {errors} errors",
|
|
420
|
+
count_suffix: "",
|
|
421
|
+
try_prompt: "help me develop an Abs operator"
|
|
423
422
|
};
|
|
424
423
|
var currentLang = "zh_CN";
|
|
425
424
|
function setLanguage(lang) {
|
|
@@ -450,9 +449,9 @@ function validateLevel(level) {
|
|
|
450
449
|
}
|
|
451
450
|
function detectTraeVariant() {
|
|
452
451
|
const home = homedir();
|
|
453
|
-
if (existsSync(
|
|
454
|
-
if (existsSync(
|
|
455
|
-
if (existsSync(
|
|
452
|
+
if (existsSync(join2(home, ".trae-cn"))) return "ide";
|
|
453
|
+
if (existsSync(join2(home, ".marscode"))) return "plugin";
|
|
454
|
+
if (existsSync(join2(home, ".traecli"))) return "cli";
|
|
456
455
|
return "unknown";
|
|
457
456
|
}
|
|
458
457
|
function getConfigRoot(tool, level, base) {
|
|
@@ -460,24 +459,24 @@ function getConfigRoot(tool, level, base) {
|
|
|
460
459
|
if (level === "global") {
|
|
461
460
|
switch (tool) {
|
|
462
461
|
case "opencode":
|
|
463
|
-
return
|
|
462
|
+
return join2(home, ".config", "opencode");
|
|
464
463
|
case "claude":
|
|
465
|
-
return
|
|
464
|
+
return join2(home, ".claude");
|
|
466
465
|
case "trae": {
|
|
467
466
|
const variant = detectTraeVariant();
|
|
468
467
|
switch (variant) {
|
|
469
468
|
case "plugin":
|
|
470
|
-
return
|
|
469
|
+
return join2(home, ".marscode");
|
|
471
470
|
case "cli":
|
|
472
|
-
return
|
|
471
|
+
return join2(home, ".traecli");
|
|
473
472
|
default:
|
|
474
|
-
return
|
|
473
|
+
return join2(home, ".trae-cn");
|
|
475
474
|
}
|
|
476
475
|
}
|
|
477
476
|
case "cursor":
|
|
478
|
-
return
|
|
477
|
+
return join2(home, ".cursor");
|
|
479
478
|
case "copilot":
|
|
480
|
-
return
|
|
479
|
+
return join2(home, ".copilot");
|
|
481
480
|
default:
|
|
482
481
|
throw new Error(
|
|
483
482
|
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
@@ -487,24 +486,24 @@ function getConfigRoot(tool, level, base) {
|
|
|
487
486
|
const baseDir = base || process.cwd();
|
|
488
487
|
switch (tool) {
|
|
489
488
|
case "opencode":
|
|
490
|
-
return
|
|
489
|
+
return join2(baseDir, ".opencode");
|
|
491
490
|
case "claude":
|
|
492
|
-
return
|
|
491
|
+
return join2(baseDir, ".claude");
|
|
493
492
|
case "trae": {
|
|
494
493
|
const variant = detectTraeVariant();
|
|
495
494
|
switch (variant) {
|
|
496
495
|
case "plugin":
|
|
497
|
-
return
|
|
496
|
+
return join2(baseDir, ".marscode");
|
|
498
497
|
case "cli":
|
|
499
|
-
return
|
|
498
|
+
return join2(baseDir, ".traecli");
|
|
500
499
|
default:
|
|
501
|
-
return
|
|
500
|
+
return join2(baseDir, ".trae");
|
|
502
501
|
}
|
|
503
502
|
}
|
|
504
503
|
case "cursor":
|
|
505
|
-
return
|
|
504
|
+
return join2(baseDir, ".cursor");
|
|
506
505
|
case "copilot":
|
|
507
|
-
return
|
|
506
|
+
return join2(baseDir, ".github");
|
|
508
507
|
default:
|
|
509
508
|
throw new Error(
|
|
510
509
|
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
@@ -515,19 +514,19 @@ function getConfigFileName(tool) {
|
|
|
515
514
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
516
515
|
}
|
|
517
516
|
function getSkillsDir(configRoot) {
|
|
518
|
-
return
|
|
517
|
+
return join2(configRoot, "skills");
|
|
519
518
|
}
|
|
520
519
|
function getAgentsDir(configRoot) {
|
|
521
|
-
return
|
|
520
|
+
return join2(configRoot, "agents");
|
|
522
521
|
}
|
|
523
522
|
function getManifestPath(configRoot) {
|
|
524
|
-
return
|
|
523
|
+
return join2(configRoot, "cannbot-manifest.json");
|
|
525
524
|
}
|
|
526
525
|
function getCannbotConfigDir() {
|
|
527
|
-
return
|
|
526
|
+
return join2(homedir(), ".cannbot");
|
|
528
527
|
}
|
|
529
528
|
function getCannbotRepoPath() {
|
|
530
|
-
return
|
|
529
|
+
return join2(getCannbotConfigDir(), "repo");
|
|
531
530
|
}
|
|
532
531
|
|
|
533
532
|
// src/core/detector.ts
|
|
@@ -565,12 +564,12 @@ async function detectTrae() {
|
|
|
565
564
|
const home = homedir2();
|
|
566
565
|
const variant = detectTraeVariant();
|
|
567
566
|
let path;
|
|
568
|
-
if (variant === "ide" && existsSync2(
|
|
569
|
-
path =
|
|
570
|
-
} else if (variant === "plugin" && existsSync2(
|
|
571
|
-
path =
|
|
572
|
-
} else if (variant === "cli" && existsSync2(
|
|
573
|
-
path =
|
|
567
|
+
if (variant === "ide" && existsSync2(join3(home, ".trae-cn"))) {
|
|
568
|
+
path = join3(home, ".trae-cn");
|
|
569
|
+
} else if (variant === "plugin" && existsSync2(join3(home, ".marscode"))) {
|
|
570
|
+
path = join3(home, ".marscode");
|
|
571
|
+
} else if (variant === "cli" && existsSync2(join3(home, ".traecli"))) {
|
|
572
|
+
path = join3(home, ".traecli");
|
|
574
573
|
} else if (variant === "unknown") {
|
|
575
574
|
const cmdPath = await getCommandPath("trae");
|
|
576
575
|
if (cmdPath) path = cmdPath;
|
|
@@ -634,13 +633,13 @@ function getAllTools() {
|
|
|
634
633
|
}
|
|
635
634
|
|
|
636
635
|
// src/core/manifest.ts
|
|
637
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
638
|
-
import { join as
|
|
636
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
637
|
+
import { join as join4 } from "path";
|
|
639
638
|
function readManifest(configRoot) {
|
|
640
639
|
const manifestPath = getManifestPath(configRoot);
|
|
641
640
|
if (existsSync3(manifestPath)) {
|
|
642
641
|
try {
|
|
643
|
-
const content =
|
|
642
|
+
const content = readFileSync2(manifestPath, "utf-8");
|
|
644
643
|
return JSON.parse(content);
|
|
645
644
|
} catch {
|
|
646
645
|
}
|
|
@@ -653,16 +652,16 @@ function readAllManifests(configRoot) {
|
|
|
653
652
|
const standardPath = getManifestPath(configRoot);
|
|
654
653
|
if (existsSync3(standardPath)) {
|
|
655
654
|
try {
|
|
656
|
-
const content =
|
|
655
|
+
const content = readFileSync2(standardPath, "utf-8");
|
|
657
656
|
manifests.push(JSON.parse(content));
|
|
658
657
|
} catch {
|
|
659
658
|
}
|
|
660
659
|
}
|
|
661
660
|
for (const plugin of plugins) {
|
|
662
|
-
const pluginManifestPath =
|
|
661
|
+
const pluginManifestPath = join4(configRoot, `${plugin.id}-manifest.json`);
|
|
663
662
|
if (existsSync3(pluginManifestPath)) {
|
|
664
663
|
try {
|
|
665
|
-
const content =
|
|
664
|
+
const content = readFileSync2(pluginManifestPath, "utf-8");
|
|
666
665
|
manifests.push(JSON.parse(content));
|
|
667
666
|
} catch {
|
|
668
667
|
}
|
|
@@ -705,27 +704,27 @@ function scanInstalled() {
|
|
|
705
704
|
return installed;
|
|
706
705
|
}
|
|
707
706
|
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
708
|
-
const skillsDir =
|
|
709
|
-
const agentsDir =
|
|
707
|
+
const skillsDir = join4(configRoot, "skills");
|
|
708
|
+
const agentsDir = join4(configRoot, "agents");
|
|
710
709
|
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
711
710
|
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
712
|
-
const skillPath =
|
|
711
|
+
const skillPath = join4(skillsDir, skillId);
|
|
713
712
|
return existsSync3(skillPath);
|
|
714
713
|
});
|
|
715
714
|
if (!hasAnySkill) return false;
|
|
716
715
|
}
|
|
717
716
|
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
718
717
|
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
719
|
-
const agentPath =
|
|
718
|
+
const agentPath = join4(agentsDir, agentId);
|
|
720
719
|
return existsSync3(agentPath);
|
|
721
720
|
});
|
|
722
721
|
if (!hasAnyAgent) return false;
|
|
723
722
|
}
|
|
724
723
|
let agentsMdPath;
|
|
725
724
|
if (tool === "opencode" && level === "project") {
|
|
726
|
-
agentsMdPath =
|
|
725
|
+
agentsMdPath = join4(configRoot, "..", "AGENTS.md");
|
|
727
726
|
} else {
|
|
728
|
-
agentsMdPath =
|
|
727
|
+
agentsMdPath = join4(configRoot, "AGENTS.md");
|
|
729
728
|
}
|
|
730
729
|
if (!existsSync3(agentsMdPath)) return false;
|
|
731
730
|
return true;
|
|
@@ -785,24 +784,24 @@ function printBoxTitle(title, width = 65) {
|
|
|
785
784
|
}
|
|
786
785
|
function showOperationHints(isCheckbox = false) {
|
|
787
786
|
if (isCheckbox) {
|
|
788
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
787
|
+
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
|
|
789
788
|
`);
|
|
790
789
|
} else {
|
|
791
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
790
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
792
791
|
`);
|
|
793
792
|
}
|
|
794
793
|
}
|
|
795
794
|
|
|
796
795
|
// src/utils/config.ts
|
|
797
|
-
import { existsSync as existsSync4, mkdirSync, readFileSync as
|
|
798
|
-
import { join as
|
|
796
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
797
|
+
import { join as join5 } from "path";
|
|
799
798
|
import { homedir as homedir3 } from "os";
|
|
800
799
|
import { parse, stringify } from "yaml";
|
|
801
800
|
function getConfigDir() {
|
|
802
|
-
return
|
|
801
|
+
return join5(homedir3(), ".cannbot");
|
|
803
802
|
}
|
|
804
803
|
function getConfigPath() {
|
|
805
|
-
return
|
|
804
|
+
return join5(getConfigDir(), "config.yaml");
|
|
806
805
|
}
|
|
807
806
|
function readConfig() {
|
|
808
807
|
const configPath = getConfigPath();
|
|
@@ -813,7 +812,7 @@ function readConfig() {
|
|
|
813
812
|
};
|
|
814
813
|
}
|
|
815
814
|
try {
|
|
816
|
-
const content =
|
|
815
|
+
const content = readFileSync3(configPath, "utf-8");
|
|
817
816
|
const parsed = parse(content);
|
|
818
817
|
return {
|
|
819
818
|
language: parsed.language || "zh_CN",
|
|
@@ -1097,8 +1096,8 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
1097
1096
|
const displayPath = getConfigRoot(tool, level);
|
|
1098
1097
|
const toolName = getToolDisplayName(tool);
|
|
1099
1098
|
const levelText = level === "project" ? "project" : "global";
|
|
1100
|
-
logger.info(
|
|
1101
|
-
logger.info(
|
|
1099
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk2.bold(String(plugins.length))).replace("{tool}", chalk2.green(toolName)).replace("{level}", chalk2.cyan(levelText)));
|
|
1100
|
+
logger.info(`${t("wizard_install_path")}: ${chalk2.cyan(displayPath)}`);
|
|
1102
1101
|
for (const plugin of selectedPlugins) {
|
|
1103
1102
|
if (plugin) {
|
|
1104
1103
|
logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
|
|
@@ -1119,14 +1118,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
1119
1118
|
}
|
|
1120
1119
|
|
|
1121
1120
|
// src/core/repository.ts
|
|
1122
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
1123
|
-
import { join as
|
|
1121
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
1122
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
1123
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1124
1124
|
import { execa as execa2 } from "execa";
|
|
1125
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1125
1126
|
|
|
1126
1127
|
// src/core/scanner.ts
|
|
1127
|
-
import { existsSync as existsSync5, readdirSync, readFileSync as
|
|
1128
|
-
import { join as
|
|
1129
|
-
import {
|
|
1128
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1129
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
1130
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1131
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1132
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
1133
|
+
var __dirname2 = dirname2(__filename2);
|
|
1130
1134
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
1131
1135
|
"node_modules",
|
|
1132
1136
|
".git",
|
|
@@ -1147,36 +1151,43 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
1147
1151
|
"docs",
|
|
1148
1152
|
"scripts"
|
|
1149
1153
|
]);
|
|
1150
|
-
|
|
1151
|
-
"
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
]
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
]
|
|
1154
|
+
function loadScanConfig() {
|
|
1155
|
+
const configPath = join6(__dirname2, "config", "defaults.yaml");
|
|
1156
|
+
try {
|
|
1157
|
+
const config = parseYaml2(readFileSync4(configPath, "utf-8"));
|
|
1158
|
+
return {
|
|
1159
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
|
|
1160
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
|
|
1161
|
+
};
|
|
1162
|
+
} catch {
|
|
1163
|
+
return {
|
|
1164
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
|
|
1165
|
+
pluginDirs: ["plugins-official", "plugins-community"]
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
var scanConfig = loadScanConfig();
|
|
1170
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
1171
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1161
1172
|
function scanSkills(repoPath) {
|
|
1162
1173
|
const skills = [];
|
|
1163
1174
|
const seen = /* @__PURE__ */ new Set();
|
|
1164
1175
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1165
|
-
const fullPath =
|
|
1176
|
+
const fullPath = join6(repoPath, dir);
|
|
1166
1177
|
if (!existsSync5(fullPath)) continue;
|
|
1167
1178
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1168
1179
|
}
|
|
1169
1180
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1170
|
-
const pluginParent =
|
|
1181
|
+
const pluginParent = join6(repoPath, pluginDir);
|
|
1171
1182
|
if (!existsSync5(pluginParent)) continue;
|
|
1172
1183
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1173
|
-
const pluginPath =
|
|
1184
|
+
const pluginPath = join6(pluginParent, plugin);
|
|
1174
1185
|
if (!isDirectory(pluginPath)) continue;
|
|
1175
|
-
const skillsDir =
|
|
1186
|
+
const skillsDir = join6(pluginPath, "skills");
|
|
1176
1187
|
if (existsSync5(skillsDir)) {
|
|
1177
1188
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1178
1189
|
}
|
|
1179
|
-
const skillDir =
|
|
1190
|
+
const skillDir = join6(pluginPath, "skill");
|
|
1180
1191
|
if (existsSync5(skillDir)) {
|
|
1181
1192
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1182
1193
|
}
|
|
@@ -1186,7 +1197,7 @@ function scanSkills(repoPath) {
|
|
|
1186
1197
|
}
|
|
1187
1198
|
function parseFrontmatter(filePath) {
|
|
1188
1199
|
try {
|
|
1189
|
-
const content =
|
|
1200
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
1190
1201
|
const lines = content.split("\n");
|
|
1191
1202
|
if (lines.length < 3) return null;
|
|
1192
1203
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1199,7 +1210,7 @@ function parseFrontmatter(filePath) {
|
|
|
1199
1210
|
}
|
|
1200
1211
|
if (endIndex === -1) return null;
|
|
1201
1212
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1202
|
-
const parsed =
|
|
1213
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1203
1214
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1204
1215
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1205
1216
|
return {
|
|
@@ -1212,17 +1223,17 @@ function parseFrontmatter(filePath) {
|
|
|
1212
1223
|
}
|
|
1213
1224
|
function getCurrentCommit(repoPath) {
|
|
1214
1225
|
try {
|
|
1215
|
-
const headFile =
|
|
1226
|
+
const headFile = join6(repoPath, ".git", "HEAD");
|
|
1216
1227
|
if (!existsSync5(headFile)) return "unknown";
|
|
1217
|
-
const headContent =
|
|
1228
|
+
const headContent = readFileSync4(headFile, "utf-8").trim();
|
|
1218
1229
|
if (headContent.startsWith("ref: ")) {
|
|
1219
|
-
const refPath =
|
|
1230
|
+
const refPath = join6(repoPath, ".git", headContent.slice(5));
|
|
1220
1231
|
if (existsSync5(refPath)) {
|
|
1221
|
-
return
|
|
1232
|
+
return readFileSync4(refPath, "utf-8").trim();
|
|
1222
1233
|
}
|
|
1223
|
-
const packedRefsPath =
|
|
1234
|
+
const packedRefsPath = join6(repoPath, ".git", "packed-refs");
|
|
1224
1235
|
if (existsSync5(packedRefsPath)) {
|
|
1225
|
-
const packedRefs =
|
|
1236
|
+
const packedRefs = readFileSync4(packedRefsPath, "utf-8");
|
|
1226
1237
|
const refName = headContent.slice(5);
|
|
1227
1238
|
const lines = packedRefs.split("\n");
|
|
1228
1239
|
for (const line of lines) {
|
|
@@ -1241,7 +1252,7 @@ function readScanCache() {
|
|
|
1241
1252
|
try {
|
|
1242
1253
|
const cachePath = getCachePath();
|
|
1243
1254
|
if (!existsSync5(cachePath)) return null;
|
|
1244
|
-
const content =
|
|
1255
|
+
const content = readFileSync4(cachePath, "utf-8");
|
|
1245
1256
|
const cache = JSON.parse(content);
|
|
1246
1257
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1247
1258
|
const age = Date.now() - cache.timestamp;
|
|
@@ -1254,7 +1265,7 @@ function readScanCache() {
|
|
|
1254
1265
|
function writeScanCache(cache) {
|
|
1255
1266
|
try {
|
|
1256
1267
|
const cachePath = getCachePath();
|
|
1257
|
-
const cacheDir =
|
|
1268
|
+
const cacheDir = dirname2(cachePath);
|
|
1258
1269
|
if (!existsSync5(cacheDir)) {
|
|
1259
1270
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1260
1271
|
}
|
|
@@ -1263,10 +1274,10 @@ function writeScanCache(cache) {
|
|
|
1263
1274
|
}
|
|
1264
1275
|
}
|
|
1265
1276
|
function getCachePath() {
|
|
1266
|
-
return
|
|
1277
|
+
return join6(getCannbotConfigDir(), "scan-cache.json");
|
|
1267
1278
|
}
|
|
1268
1279
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1269
|
-
const selfSkillMd =
|
|
1280
|
+
const selfSkillMd = join6(dirPath, "SKILL.md");
|
|
1270
1281
|
if (existsSync5(selfSkillMd)) {
|
|
1271
1282
|
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1272
1283
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
@@ -1281,10 +1292,10 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1281
1292
|
return;
|
|
1282
1293
|
}
|
|
1283
1294
|
for (const entry of safeReaddir(dirPath)) {
|
|
1284
|
-
const fullPath =
|
|
1295
|
+
const fullPath = join6(dirPath, entry);
|
|
1285
1296
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1286
1297
|
if (!isDirectory(fullPath)) continue;
|
|
1287
|
-
const skillMd =
|
|
1298
|
+
const skillMd = join6(fullPath, "SKILL.md");
|
|
1288
1299
|
if (existsSync5(skillMd)) {
|
|
1289
1300
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1290
1301
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
@@ -1303,7 +1314,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1303
1314
|
}
|
|
1304
1315
|
function safeReaddir(dir) {
|
|
1305
1316
|
try {
|
|
1306
|
-
return
|
|
1317
|
+
return readdirSync2(dir);
|
|
1307
1318
|
} catch {
|
|
1308
1319
|
return [];
|
|
1309
1320
|
}
|
|
@@ -1543,7 +1554,21 @@ function getAllCategories() {
|
|
|
1543
1554
|
}
|
|
1544
1555
|
|
|
1545
1556
|
// src/core/repository.ts
|
|
1546
|
-
var
|
|
1557
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
1558
|
+
var __dirname3 = dirname3(__filename3);
|
|
1559
|
+
function loadRepoUrl() {
|
|
1560
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
1561
|
+
return process.env.CANNBOT_REPO_URL;
|
|
1562
|
+
}
|
|
1563
|
+
const configPath = join7(__dirname3, "config", "defaults.yaml");
|
|
1564
|
+
try {
|
|
1565
|
+
const config = parseYaml3(readFileSync5(configPath, "utf-8"));
|
|
1566
|
+
return config.repository?.url || "https://gitcode.com/cann/cannbot-skills.git";
|
|
1567
|
+
} catch {
|
|
1568
|
+
return "https://gitcode.com/cann/cannbot-skills.git";
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
var REPO_URL = loadRepoUrl();
|
|
1547
1572
|
var RepositoryManager = class {
|
|
1548
1573
|
repoPath;
|
|
1549
1574
|
constructor(customPath) {
|
|
@@ -1601,8 +1626,8 @@ var RepositoryManager = class {
|
|
|
1601
1626
|
}
|
|
1602
1627
|
isValidRepo(path) {
|
|
1603
1628
|
if (!existsSync6(path)) return false;
|
|
1604
|
-
const gitDir =
|
|
1605
|
-
const pluginsDir =
|
|
1629
|
+
const gitDir = join7(path, ".git");
|
|
1630
|
+
const pluginsDir = join7(path, "plugins-official");
|
|
1606
1631
|
return existsSync6(gitDir) && existsSync6(pluginsDir);
|
|
1607
1632
|
}
|
|
1608
1633
|
async cloneRepo() {
|
|
@@ -1638,17 +1663,17 @@ function createRepositoryManager(customPath) {
|
|
|
1638
1663
|
|
|
1639
1664
|
// src/core/installer.ts
|
|
1640
1665
|
import { existsSync as existsSync9 } from "fs";
|
|
1641
|
-
import { join as
|
|
1666
|
+
import { join as join10 } from "path";
|
|
1642
1667
|
import { execa as execa3 } from "execa";
|
|
1643
1668
|
|
|
1644
1669
|
// src/core/record.ts
|
|
1645
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as
|
|
1646
|
-
import { join as
|
|
1670
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
|
|
1671
|
+
import { join as join8 } from "path";
|
|
1647
1672
|
function getInstallsDir() {
|
|
1648
|
-
return
|
|
1673
|
+
return join8(getCannbotConfigDir(), "installs");
|
|
1649
1674
|
}
|
|
1650
1675
|
function getRecordPath(pluginId) {
|
|
1651
|
-
return
|
|
1676
|
+
return join8(getInstallsDir(), `${pluginId}.json`);
|
|
1652
1677
|
}
|
|
1653
1678
|
function readRecord(pluginId) {
|
|
1654
1679
|
const recordPath = getRecordPath(pluginId);
|
|
@@ -1656,7 +1681,7 @@ function readRecord(pluginId) {
|
|
|
1656
1681
|
return null;
|
|
1657
1682
|
}
|
|
1658
1683
|
try {
|
|
1659
|
-
const content =
|
|
1684
|
+
const content = readFileSync6(recordPath, "utf-8");
|
|
1660
1685
|
return JSON.parse(content);
|
|
1661
1686
|
} catch {
|
|
1662
1687
|
return null;
|
|
@@ -1680,22 +1705,22 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1680
1705
|
const files = [];
|
|
1681
1706
|
const directories = [];
|
|
1682
1707
|
if (manifest) {
|
|
1683
|
-
const skillsDir =
|
|
1708
|
+
const skillsDir = join8(configRoot, "skills");
|
|
1684
1709
|
if (existsSync7(skillsDir)) {
|
|
1685
1710
|
directories.push(skillsDir);
|
|
1686
1711
|
for (const skillName of manifest.installed_skills || []) {
|
|
1687
|
-
const skillPath =
|
|
1712
|
+
const skillPath = join8(skillsDir, skillName);
|
|
1688
1713
|
if (existsSync7(skillPath) || isSymlink(skillPath)) {
|
|
1689
1714
|
files.push(skillPath);
|
|
1690
1715
|
}
|
|
1691
1716
|
}
|
|
1692
1717
|
}
|
|
1693
|
-
const agentsDir =
|
|
1718
|
+
const agentsDir = join8(configRoot, "agents");
|
|
1694
1719
|
if (existsSync7(agentsDir)) {
|
|
1695
1720
|
directories.push(agentsDir);
|
|
1696
1721
|
for (const agentName of manifest.installed_agents || []) {
|
|
1697
|
-
const agentPath =
|
|
1698
|
-
const agentPathMd =
|
|
1722
|
+
const agentPath = join8(agentsDir, agentName);
|
|
1723
|
+
const agentPathMd = join8(agentsDir, agentName + ".md");
|
|
1699
1724
|
if (existsSync7(agentPath) || isSymlink(agentPath)) {
|
|
1700
1725
|
files.push(agentPath);
|
|
1701
1726
|
} else if (existsSync7(agentPathMd) || isSymlink(agentPathMd)) {
|
|
@@ -1704,30 +1729,30 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1704
1729
|
}
|
|
1705
1730
|
}
|
|
1706
1731
|
}
|
|
1707
|
-
const workflowsLink =
|
|
1732
|
+
const workflowsLink = join8(configRoot, "workflows");
|
|
1708
1733
|
if (isSymlink(workflowsLink)) {
|
|
1709
1734
|
files.push(workflowsLink);
|
|
1710
1735
|
}
|
|
1711
|
-
const manifestPath =
|
|
1736
|
+
const manifestPath = join8(configRoot, "cannbot-manifest.json");
|
|
1712
1737
|
if (existsSync7(manifestPath)) {
|
|
1713
1738
|
files.push(manifestPath);
|
|
1714
1739
|
}
|
|
1715
|
-
const pluginManifestPath =
|
|
1740
|
+
const pluginManifestPath = join8(configRoot, `${pluginId}-manifest.json`);
|
|
1716
1741
|
if (existsSync7(pluginManifestPath)) {
|
|
1717
1742
|
files.push(pluginManifestPath);
|
|
1718
1743
|
}
|
|
1719
1744
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1720
|
-
const configFilePath = level === "project" ?
|
|
1745
|
+
const configFilePath = level === "project" ? join8(installPath, configFileName) : join8(configRoot, configFileName);
|
|
1721
1746
|
if (existsSync7(configFilePath)) {
|
|
1722
1747
|
files.push(configFilePath);
|
|
1723
1748
|
}
|
|
1724
1749
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1725
1750
|
for (const repoName of repoLinks) {
|
|
1726
|
-
const repoLinkPath =
|
|
1751
|
+
const repoLinkPath = join8(installPath, repoName);
|
|
1727
1752
|
if (isSymlink(repoLinkPath)) {
|
|
1728
1753
|
files.push(repoLinkPath);
|
|
1729
1754
|
}
|
|
1730
|
-
const repoLinkInConfig =
|
|
1755
|
+
const repoLinkInConfig = join8(configRoot, repoName);
|
|
1731
1756
|
if (isSymlink(repoLinkInConfig)) {
|
|
1732
1757
|
files.push(repoLinkInConfig);
|
|
1733
1758
|
}
|
|
@@ -1752,7 +1777,7 @@ function isSymlink(path) {
|
|
|
1752
1777
|
}
|
|
1753
1778
|
}
|
|
1754
1779
|
function getSkillRecordPath() {
|
|
1755
|
-
return
|
|
1780
|
+
return join8(getInstallsDir(), "skills.json");
|
|
1756
1781
|
}
|
|
1757
1782
|
function readSkillRecord() {
|
|
1758
1783
|
const recordPath = getSkillRecordPath();
|
|
@@ -1760,7 +1785,7 @@ function readSkillRecord() {
|
|
|
1760
1785
|
return {};
|
|
1761
1786
|
}
|
|
1762
1787
|
try {
|
|
1763
|
-
const content =
|
|
1788
|
+
const content = readFileSync6(recordPath, "utf-8");
|
|
1764
1789
|
return JSON.parse(content);
|
|
1765
1790
|
} catch {
|
|
1766
1791
|
return {};
|
|
@@ -1810,16 +1835,16 @@ function getInstalledSkills(tool, level, installPath) {
|
|
|
1810
1835
|
const record = readSkillRecord();
|
|
1811
1836
|
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1812
1837
|
const configRoot = getConfigRoot(tool, level);
|
|
1813
|
-
const skillsDir =
|
|
1838
|
+
const skillsDir = join8(configRoot, "skills");
|
|
1814
1839
|
return recordedSkills.filter((skillId) => {
|
|
1815
|
-
const skillPath =
|
|
1840
|
+
const skillPath = join8(skillsDir, skillId);
|
|
1816
1841
|
return existsSync7(skillPath) || isSymlink(skillPath);
|
|
1817
1842
|
});
|
|
1818
1843
|
}
|
|
1819
1844
|
|
|
1820
1845
|
// src/core/backup.ts
|
|
1821
|
-
import { existsSync as existsSync8, copyFileSync, readdirSync as
|
|
1822
|
-
import { join as
|
|
1846
|
+
import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
1847
|
+
import { join as join9 } from "path";
|
|
1823
1848
|
function getAgentsFileName(tool) {
|
|
1824
1849
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1825
1850
|
}
|
|
@@ -1834,7 +1859,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1834
1859
|
};
|
|
1835
1860
|
}
|
|
1836
1861
|
}
|
|
1837
|
-
const agentsFile =
|
|
1862
|
+
const agentsFile = join9(configRoot, getAgentsFileName(tool));
|
|
1838
1863
|
if (!existsSync8(agentsFile)) {
|
|
1839
1864
|
return null;
|
|
1840
1865
|
}
|
|
@@ -1853,13 +1878,13 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1853
1878
|
return null;
|
|
1854
1879
|
}
|
|
1855
1880
|
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1856
|
-
const agentsFile =
|
|
1881
|
+
const agentsFile = join9(configRoot, getAgentsFileName(tool));
|
|
1857
1882
|
if (!existsSync8(agentsFile)) {
|
|
1858
1883
|
return null;
|
|
1859
1884
|
}
|
|
1860
1885
|
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
1861
1886
|
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
1862
|
-
const backupPath =
|
|
1887
|
+
const backupPath = join9(configRoot, backupFileName);
|
|
1863
1888
|
try {
|
|
1864
1889
|
copyFileSync(agentsFile, backupPath);
|
|
1865
1890
|
return {
|
|
@@ -1876,7 +1901,7 @@ function findBackups(configRoot) {
|
|
|
1876
1901
|
if (!existsSync8(configRoot)) {
|
|
1877
1902
|
return [];
|
|
1878
1903
|
}
|
|
1879
|
-
const files =
|
|
1904
|
+
const files = readdirSync3(configRoot);
|
|
1880
1905
|
const backups = [];
|
|
1881
1906
|
for (const file of files) {
|
|
1882
1907
|
if (file.includes(".cannbot-backup.")) {
|
|
@@ -1889,7 +1914,7 @@ function findBackups(configRoot) {
|
|
|
1889
1914
|
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
1890
1915
|
const plugin = findPlugin(pluginId);
|
|
1891
1916
|
backups.push({
|
|
1892
|
-
filePath:
|
|
1917
|
+
filePath: join9(configRoot, file),
|
|
1893
1918
|
pluginId,
|
|
1894
1919
|
pluginName: plugin?.displayName || pluginId,
|
|
1895
1920
|
backupTime: timestamp
|
|
@@ -1902,7 +1927,7 @@ function findBackups(configRoot) {
|
|
|
1902
1927
|
}
|
|
1903
1928
|
function restoreBackup(backupPath, configRoot, tool) {
|
|
1904
1929
|
try {
|
|
1905
|
-
const agentsFile =
|
|
1930
|
+
const agentsFile = join9(configRoot, getAgentsFileName(tool));
|
|
1906
1931
|
copyFileSync(backupPath, agentsFile);
|
|
1907
1932
|
return true;
|
|
1908
1933
|
} catch {
|
|
@@ -2003,7 +2028,7 @@ async function installPlugin(opts) {
|
|
|
2003
2028
|
warnings: []
|
|
2004
2029
|
};
|
|
2005
2030
|
}
|
|
2006
|
-
const scriptPath =
|
|
2031
|
+
const scriptPath = join10(opts.repoPath, plugin.dir, plugin.script);
|
|
2007
2032
|
if (!existsSync9(scriptPath)) {
|
|
2008
2033
|
return {
|
|
2009
2034
|
success: false,
|
|
@@ -2020,7 +2045,7 @@ async function installPlugin(opts) {
|
|
|
2020
2045
|
}
|
|
2021
2046
|
const cwd = opts.installPath || process.cwd();
|
|
2022
2047
|
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
2023
|
-
const agentsFile =
|
|
2048
|
+
const agentsFile = join10(configRoot, getAgentsFileName(opts.tool));
|
|
2024
2049
|
let backupInfo = null;
|
|
2025
2050
|
if (existsSync9(agentsFile)) {
|
|
2026
2051
|
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
@@ -2115,14 +2140,14 @@ async function installPlugin(opts) {
|
|
|
2115
2140
|
}
|
|
2116
2141
|
|
|
2117
2142
|
// src/core/skill-installer.ts
|
|
2118
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as
|
|
2119
|
-
import { join as
|
|
2143
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
|
|
2144
|
+
import { join as join11 } from "path";
|
|
2120
2145
|
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
2121
2146
|
import chalk4 from "chalk";
|
|
2122
2147
|
import Table from "cli-table3";
|
|
2123
2148
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
2124
2149
|
const configRoot = getConfigRoot(tool, level);
|
|
2125
|
-
const skillsDir =
|
|
2150
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2126
2151
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
2127
2152
|
if (!existsSync10(skillsDir)) {
|
|
2128
2153
|
mkdirSync5(skillsDir, { recursive: true });
|
|
@@ -2135,8 +2160,8 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
2135
2160
|
results.push({ skillId, success: false, error: "Skill not found in registry" });
|
|
2136
2161
|
continue;
|
|
2137
2162
|
}
|
|
2138
|
-
const sourcePath =
|
|
2139
|
-
const targetPath =
|
|
2163
|
+
const sourcePath = join11(repoPath, skill.source, skill.id);
|
|
2164
|
+
const targetPath = join11(skillsDir, skillId);
|
|
2140
2165
|
if (!existsSync10(sourcePath)) {
|
|
2141
2166
|
results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
|
|
2142
2167
|
continue;
|
|
@@ -2159,21 +2184,21 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
2159
2184
|
}
|
|
2160
2185
|
async function uninstallSkills(skillIds, tool, level) {
|
|
2161
2186
|
const configRoot = getConfigRoot(tool, level);
|
|
2162
|
-
const skillsDir =
|
|
2187
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2163
2188
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
2164
2189
|
const results = [];
|
|
2165
2190
|
const removedIds = [];
|
|
2166
2191
|
for (const skillId of skillIds) {
|
|
2167
|
-
const targetPath =
|
|
2192
|
+
const targetPath = join11(skillsDir, skillId);
|
|
2168
2193
|
try {
|
|
2169
2194
|
if (existsSync10(targetPath) || isSymlink2(targetPath)) {
|
|
2170
2195
|
unlinkSync3(targetPath);
|
|
2171
2196
|
results.push({ skillId, success: true });
|
|
2172
2197
|
removedIds.push(skillId);
|
|
2173
|
-
logger.step(`
|
|
2198
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
2174
2199
|
} else {
|
|
2175
2200
|
results.push({ skillId, success: false, error: "Not installed" });
|
|
2176
|
-
logger.warn(`${skillId}
|
|
2201
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
2177
2202
|
}
|
|
2178
2203
|
} catch (error) {
|
|
2179
2204
|
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
|
|
@@ -2184,10 +2209,10 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2184
2209
|
}
|
|
2185
2210
|
if (existsSync10(skillsDir)) {
|
|
2186
2211
|
try {
|
|
2187
|
-
const entries =
|
|
2212
|
+
const entries = readdirSync4(skillsDir);
|
|
2188
2213
|
if (entries.length === 0) {
|
|
2189
2214
|
rmdirSync(skillsDir);
|
|
2190
|
-
logger.step(`
|
|
2215
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
2191
2216
|
}
|
|
2192
2217
|
} catch {
|
|
2193
2218
|
}
|
|
@@ -2296,7 +2321,7 @@ function listAllSkills() {
|
|
|
2296
2321
|
console.log(table.toString());
|
|
2297
2322
|
}
|
|
2298
2323
|
console.log();
|
|
2299
|
-
console.log(chalk4.dim(`
|
|
2324
|
+
console.log(chalk4.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
2300
2325
|
console.log();
|
|
2301
2326
|
}
|
|
2302
2327
|
function isSymlink2(path) {
|
|
@@ -2333,7 +2358,8 @@ function printPluginList(plugins, installed) {
|
|
|
2333
2358
|
]);
|
|
2334
2359
|
});
|
|
2335
2360
|
console.log();
|
|
2336
|
-
|
|
2361
|
+
const countSuffix = t("count_suffix");
|
|
2362
|
+
console.log(chalk5.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
2337
2363
|
console.log(table.toString());
|
|
2338
2364
|
console.log();
|
|
2339
2365
|
}
|
|
@@ -2366,22 +2392,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2366
2392
|
return;
|
|
2367
2393
|
}
|
|
2368
2394
|
const toolName = getToolDisplayName(tool);
|
|
2395
|
+
const countSuffix = t("count_suffix");
|
|
2369
2396
|
const lines = [
|
|
2370
2397
|
chalk5.bold(` ${t("install_done")}!`),
|
|
2371
2398
|
"",
|
|
2372
2399
|
` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
2373
|
-
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}
|
|
2400
|
+
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2374
2401
|
"",
|
|
2375
2402
|
chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
2376
2403
|
` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
|
|
2377
|
-
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("
|
|
2404
|
+
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green(t("try_prompt"))}`,
|
|
2378
2405
|
` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
|
|
2379
2406
|
` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
|
|
2380
2407
|
""
|
|
2381
2408
|
];
|
|
2382
2409
|
if (successResults.length === 1) {
|
|
2410
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
2411
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2383
2412
|
lines.push(
|
|
2384
|
-
` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(
|
|
2413
|
+
` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(docsPath)}`
|
|
2385
2414
|
);
|
|
2386
2415
|
}
|
|
2387
2416
|
console.log();
|
|
@@ -2392,15 +2421,15 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2392
2421
|
}
|
|
2393
2422
|
|
|
2394
2423
|
// src/commands/init.ts
|
|
2395
|
-
import { existsSync as existsSync11, readFileSync as
|
|
2396
|
-
import { join as
|
|
2424
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
2425
|
+
import { join as join12 } from "path";
|
|
2397
2426
|
var BACK2 = "__back__";
|
|
2398
2427
|
var CANCEL2 = "__cancel__";
|
|
2399
2428
|
async function initCommand() {
|
|
2400
2429
|
const cwd = process.cwd();
|
|
2401
|
-
if (existsSync11(
|
|
2430
|
+
if (existsSync11(join12(cwd, "package.json")) && (() => {
|
|
2402
2431
|
try {
|
|
2403
|
-
const pkg = JSON.parse(
|
|
2432
|
+
const pkg = JSON.parse(readFileSync7(join12(cwd, "package.json"), "utf-8"));
|
|
2404
2433
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2405
2434
|
} catch {
|
|
2406
2435
|
return false;
|
|
@@ -2582,7 +2611,7 @@ async function skillInstallFlow() {
|
|
|
2582
2611
|
spinner.start();
|
|
2583
2612
|
const repoPath = await repoManager.ensureRepo();
|
|
2584
2613
|
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2585
|
-
logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)}
|
|
2614
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk6.bold(String(selectedSkills.length)))}...`);
|
|
2586
2615
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2587
2616
|
let successCount = 0;
|
|
2588
2617
|
let failCount = 0;
|
|
@@ -2600,7 +2629,7 @@ async function skillInstallFlow() {
|
|
|
2600
2629
|
}
|
|
2601
2630
|
const configRoot = getConfigRoot(tool, level);
|
|
2602
2631
|
logger.blank();
|
|
2603
|
-
logger.success(`${t("skill_install_done")}: ${chalk6.green(
|
|
2632
|
+
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)))}`);
|
|
2604
2633
|
logger.blank();
|
|
2605
2634
|
logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
|
|
2606
2635
|
logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
|
|
@@ -2626,8 +2655,8 @@ async function listCommand() {
|
|
|
2626
2655
|
|
|
2627
2656
|
// src/commands/doctor.ts
|
|
2628
2657
|
import chalk7 from "chalk";
|
|
2629
|
-
import { existsSync as existsSync12, readdirSync as
|
|
2630
|
-
import { join as
|
|
2658
|
+
import { existsSync as existsSync12, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
2659
|
+
import { join as join13 } from "path";
|
|
2631
2660
|
async function doctorCommand(options = {}) {
|
|
2632
2661
|
console.log();
|
|
2633
2662
|
console.log(chalk7.bold(` ${t("doctor_title")}`));
|
|
@@ -2646,7 +2675,7 @@ async function doctorCommand(options = {}) {
|
|
|
2646
2675
|
chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2647
2676
|
);
|
|
2648
2677
|
} else {
|
|
2649
|
-
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014
|
|
2678
|
+
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2650
2679
|
}
|
|
2651
2680
|
}
|
|
2652
2681
|
console.log();
|
|
@@ -2661,7 +2690,7 @@ async function doctorCommand(options = {}) {
|
|
|
2661
2690
|
chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2662
2691
|
);
|
|
2663
2692
|
} else {
|
|
2664
|
-
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014
|
|
2693
|
+
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2665
2694
|
}
|
|
2666
2695
|
}
|
|
2667
2696
|
console.log();
|
|
@@ -2673,65 +2702,65 @@ async function doctorCommand(options = {}) {
|
|
|
2673
2702
|
if (existsSync12(skillsDir)) {
|
|
2674
2703
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2675
2704
|
if (brokenLinks === 0) {
|
|
2676
|
-
const count =
|
|
2677
|
-
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count}
|
|
2705
|
+
const count = readdirSync5(skillsDir).length;
|
|
2706
|
+
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2678
2707
|
} else {
|
|
2679
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks}
|
|
2708
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2680
2709
|
warnings++;
|
|
2681
2710
|
if (options.fix) {
|
|
2682
2711
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2683
2712
|
fixes += fixed;
|
|
2684
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2713
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2685
2714
|
}
|
|
2686
2715
|
}
|
|
2687
2716
|
} else {
|
|
2688
|
-
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014
|
|
2717
|
+
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2689
2718
|
if (options.fix) {
|
|
2690
2719
|
mkdirSync6(skillsDir, { recursive: true });
|
|
2691
2720
|
fixes++;
|
|
2692
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2721
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2693
2722
|
}
|
|
2694
2723
|
}
|
|
2695
2724
|
if (existsSync12(agentsDir)) {
|
|
2696
2725
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2697
2726
|
if (brokenLinks === 0) {
|
|
2698
|
-
const count =
|
|
2699
|
-
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count}
|
|
2727
|
+
const count = readdirSync5(agentsDir).length;
|
|
2728
|
+
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2700
2729
|
} else {
|
|
2701
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks}
|
|
2730
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2702
2731
|
warnings++;
|
|
2703
2732
|
if (options.fix) {
|
|
2704
2733
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2705
2734
|
fixes += fixed;
|
|
2706
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2735
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2707
2736
|
}
|
|
2708
2737
|
}
|
|
2709
2738
|
} else {
|
|
2710
|
-
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014
|
|
2739
|
+
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2711
2740
|
if (options.fix) {
|
|
2712
2741
|
mkdirSync6(agentsDir, { recursive: true });
|
|
2713
2742
|
fixes++;
|
|
2714
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2743
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2715
2744
|
}
|
|
2716
2745
|
}
|
|
2717
2746
|
console.log();
|
|
2718
2747
|
console.log(chalk7.bold(` ${t("doctor_config")}`));
|
|
2719
2748
|
const configFile = getConfigFileName(primaryTool);
|
|
2720
|
-
const configPath =
|
|
2749
|
+
const configPath = join13(process.cwd(), configFile);
|
|
2721
2750
|
if (existsSync12(configPath)) {
|
|
2722
|
-
console.log(chalk7.green(" \u2713") + ` ${configFile}
|
|
2751
|
+
console.log(chalk7.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2723
2752
|
} else {
|
|
2724
|
-
console.log(chalk7.dim(" \u2014") + ` ${configFile}
|
|
2753
|
+
console.log(chalk7.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2725
2754
|
}
|
|
2726
2755
|
console.log();
|
|
2727
2756
|
console.log(chalk7.dim(" " + "\u2500".repeat(46)));
|
|
2728
2757
|
if (options.fix && fixes > 0) {
|
|
2729
2758
|
console.log(
|
|
2730
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2759
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
|
|
2731
2760
|
);
|
|
2732
2761
|
} else {
|
|
2733
2762
|
console.log(
|
|
2734
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2763
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
|
|
2735
2764
|
);
|
|
2736
2765
|
}
|
|
2737
2766
|
console.log();
|
|
@@ -2739,9 +2768,9 @@ async function doctorCommand(options = {}) {
|
|
|
2739
2768
|
function checkBrokenLinks(dir) {
|
|
2740
2769
|
let broken = 0;
|
|
2741
2770
|
try {
|
|
2742
|
-
const entries =
|
|
2771
|
+
const entries = readdirSync5(dir);
|
|
2743
2772
|
for (const entry of entries) {
|
|
2744
|
-
const fullPath =
|
|
2773
|
+
const fullPath = join13(dir, entry);
|
|
2745
2774
|
try {
|
|
2746
2775
|
const stats = lstatSync3(fullPath);
|
|
2747
2776
|
if (stats.isSymbolicLink()) {
|
|
@@ -2760,9 +2789,9 @@ function checkBrokenLinks(dir) {
|
|
|
2760
2789
|
function fixBrokenLinks(dir) {
|
|
2761
2790
|
let fixed = 0;
|
|
2762
2791
|
try {
|
|
2763
|
-
const entries =
|
|
2792
|
+
const entries = readdirSync5(dir);
|
|
2764
2793
|
for (const entry of entries) {
|
|
2765
|
-
const fullPath =
|
|
2794
|
+
const fullPath = join13(dir, entry);
|
|
2766
2795
|
try {
|
|
2767
2796
|
const stats = lstatSync3(fullPath);
|
|
2768
2797
|
if (stats.isSymbolicLink()) {
|
|
@@ -2826,9 +2855,9 @@ async function installCommand(names, options) {
|
|
|
2826
2855
|
try {
|
|
2827
2856
|
const scanRepoManager = createRepositoryManager();
|
|
2828
2857
|
await scanRepoManager.ensureRepoAndScan();
|
|
2829
|
-
scanSpinner2.succeed("
|
|
2858
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2830
2859
|
} catch {
|
|
2831
|
-
scanSpinner2.warn("
|
|
2860
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2832
2861
|
}
|
|
2833
2862
|
let tool2;
|
|
2834
2863
|
if (options.tool) {
|
|
@@ -2860,8 +2889,8 @@ async function installCommand(names, options) {
|
|
|
2860
2889
|
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2861
2890
|
spinner.start();
|
|
2862
2891
|
const repoPath = await repoManager.ensureRepo();
|
|
2863
|
-
spinner.succeed("
|
|
2864
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)}
|
|
2892
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2893
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(names.length)))}...`);
|
|
2865
2894
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2866
2895
|
let successCount = 0;
|
|
2867
2896
|
let failCount = 0;
|
|
@@ -2879,7 +2908,7 @@ async function installCommand(names, options) {
|
|
|
2879
2908
|
}
|
|
2880
2909
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2881
2910
|
logger.blank();
|
|
2882
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
2911
|
+
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)))}`);
|
|
2883
2912
|
logger.blank();
|
|
2884
2913
|
logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
|
|
2885
2914
|
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
|
|
@@ -2894,9 +2923,9 @@ async function installCommand(names, options) {
|
|
|
2894
2923
|
try {
|
|
2895
2924
|
const scanRepoManager = createRepositoryManager();
|
|
2896
2925
|
await scanRepoManager.ensureRepoAndScan();
|
|
2897
|
-
scanSpinner.succeed("
|
|
2926
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2898
2927
|
} catch {
|
|
2899
|
-
scanSpinner.warn("
|
|
2928
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2900
2929
|
}
|
|
2901
2930
|
const plugins = [];
|
|
2902
2931
|
const skills = [];
|
|
@@ -2913,11 +2942,11 @@ async function installCommand(names, options) {
|
|
|
2913
2942
|
}
|
|
2914
2943
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2915
2944
|
const allPlugins = getAllPlugins();
|
|
2916
|
-
logger.info("
|
|
2945
|
+
logger.info(t("available_plugins") + ":");
|
|
2917
2946
|
for (const p of allPlugins) {
|
|
2918
2947
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2919
2948
|
}
|
|
2920
|
-
logger.info(
|
|
2949
|
+
logger.info(`${t("available_skills")}: ${chalk9.cyan("install-helper install --list")}`);
|
|
2921
2950
|
return;
|
|
2922
2951
|
}
|
|
2923
2952
|
let tool;
|
|
@@ -2989,7 +3018,7 @@ async function installCommand(names, options) {
|
|
|
2989
3018
|
spinner.start();
|
|
2990
3019
|
const repoPath = await repoManager.ensureRepo();
|
|
2991
3020
|
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2992
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)}
|
|
3021
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(skills.length)))}...`);
|
|
2993
3022
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2994
3023
|
let successCount = 0;
|
|
2995
3024
|
let failCount = 0;
|
|
@@ -3006,7 +3035,7 @@ async function installCommand(names, options) {
|
|
|
3006
3035
|
}
|
|
3007
3036
|
}
|
|
3008
3037
|
logger.blank();
|
|
3009
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
3038
|
+
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)))}`);
|
|
3010
3039
|
logger.blank();
|
|
3011
3040
|
logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
3012
3041
|
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
|
|
@@ -3041,7 +3070,7 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
3041
3070
|
}
|
|
3042
3071
|
|
|
3043
3072
|
// src/commands/uninstall.ts
|
|
3044
|
-
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as
|
|
3073
|
+
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
|
|
3045
3074
|
import chalk10 from "chalk";
|
|
3046
3075
|
async function uninstallCommand(names, options) {
|
|
3047
3076
|
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
@@ -3078,7 +3107,7 @@ async function uninstallCommand(names, options) {
|
|
|
3078
3107
|
if (existsSync13(filePath) || isSymlink3(filePath)) {
|
|
3079
3108
|
unlinkSync5(filePath);
|
|
3080
3109
|
const name = filePath.split("/").pop() || filePath;
|
|
3081
|
-
logger.step(`
|
|
3110
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
3082
3111
|
removedFiles++;
|
|
3083
3112
|
}
|
|
3084
3113
|
} catch {
|
|
@@ -3092,11 +3121,11 @@ async function uninstallCommand(names, options) {
|
|
|
3092
3121
|
continue;
|
|
3093
3122
|
}
|
|
3094
3123
|
if (existsSync13(dirPath)) {
|
|
3095
|
-
const entries =
|
|
3124
|
+
const entries = readdirSync6(dirPath);
|
|
3096
3125
|
if (entries.length === 0) {
|
|
3097
3126
|
rmdirSync2(dirPath);
|
|
3098
3127
|
const name = dirPath.split("/").pop() || dirPath;
|
|
3099
|
-
logger.step(`
|
|
3128
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
3100
3129
|
removedDirs++;
|
|
3101
3130
|
}
|
|
3102
3131
|
}
|
|
@@ -3124,13 +3153,13 @@ async function uninstallCommand(names, options) {
|
|
|
3124
3153
|
}
|
|
3125
3154
|
const totalRemoved = removedFiles + removedDirs;
|
|
3126
3155
|
if (totalRemoved > 0) {
|
|
3127
|
-
logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08
|
|
3156
|
+
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`);
|
|
3128
3157
|
} else {
|
|
3129
3158
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3130
3159
|
}
|
|
3131
3160
|
}
|
|
3132
3161
|
if (skills.length > 0) {
|
|
3133
|
-
logger.info(`${t("uninstall_skills_in_progress")} ${skills.length}
|
|
3162
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
3134
3163
|
const results = await uninstallSkills(skills, tool, level);
|
|
3135
3164
|
let successCount = 0;
|
|
3136
3165
|
let failCount = 0;
|
|
@@ -3142,7 +3171,7 @@ async function uninstallCommand(names, options) {
|
|
|
3142
3171
|
}
|
|
3143
3172
|
}
|
|
3144
3173
|
logger.blank();
|
|
3145
|
-
logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(
|
|
3174
|
+
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)))}`);
|
|
3146
3175
|
logger.blank();
|
|
3147
3176
|
}
|
|
3148
3177
|
}
|
|
@@ -3173,7 +3202,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3173
3202
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
3174
3203
|
if (pluginsToUpdate.length === 0) {
|
|
3175
3204
|
logger.info(t("update_no_plugins"));
|
|
3176
|
-
logger.info(
|
|
3205
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk11.cyan("install-helper install <plugin>")));
|
|
3177
3206
|
return;
|
|
3178
3207
|
}
|
|
3179
3208
|
} else {
|
|
@@ -3236,8 +3265,8 @@ async function updateCommand(pluginNames, options) {
|
|
|
3236
3265
|
|
|
3237
3266
|
// src/commands/info.ts
|
|
3238
3267
|
import chalk12 from "chalk";
|
|
3239
|
-
import { existsSync as existsSync14, readdirSync as
|
|
3240
|
-
import { join as
|
|
3268
|
+
import { existsSync as existsSync14, readdirSync as readdirSync7 } from "fs";
|
|
3269
|
+
import { join as join14 } from "path";
|
|
3241
3270
|
async function infoCommand(pluginName) {
|
|
3242
3271
|
const plugin = findPlugin(pluginName);
|
|
3243
3272
|
if (!plugin) {
|
|
@@ -3261,13 +3290,13 @@ async function infoCommand(pluginName) {
|
|
|
3261
3290
|
try {
|
|
3262
3291
|
const repoManager = createRepositoryManager();
|
|
3263
3292
|
const repoPath = await repoManager.ensureRepo();
|
|
3264
|
-
const quickstartPath =
|
|
3293
|
+
const quickstartPath = join14(repoPath, plugin.dir, "quickstart.md");
|
|
3265
3294
|
if (existsSync14(quickstartPath)) {
|
|
3266
3295
|
console.log(` ${chalk12.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
3267
3296
|
}
|
|
3268
|
-
const agentsDir =
|
|
3297
|
+
const agentsDir = join14(repoPath, plugin.dir, "agents");
|
|
3269
3298
|
if (existsSync14(agentsDir)) {
|
|
3270
|
-
const agentFiles =
|
|
3299
|
+
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3271
3300
|
if (agentFiles.length > 0) {
|
|
3272
3301
|
console.log();
|
|
3273
3302
|
console.log(` ${chalk12.bold("Agents:")}`);
|
|
@@ -3324,7 +3353,7 @@ function createCLI() {
|
|
|
3324
3353
|
setLanguage(config.language);
|
|
3325
3354
|
}
|
|
3326
3355
|
const program2 = new Command();
|
|
3327
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.
|
|
3356
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.16");
|
|
3328
3357
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3329
3358
|
await initCommand();
|
|
3330
3359
|
});
|
|
@@ -3361,7 +3390,7 @@ function createCLI() {
|
|
|
3361
3390
|
// src/index.ts
|
|
3362
3391
|
process.on("uncaughtException", (error) => {
|
|
3363
3392
|
if (error.name === "ExitPromptError") {
|
|
3364
|
-
console.log("\n
|
|
3393
|
+
console.log("\n" + t("init_cancelled"));
|
|
3365
3394
|
process.exit(0);
|
|
3366
3395
|
}
|
|
3367
3396
|
throw error;
|