@cannbot-ai/install-helper 0.0.2-beta.9 → 1.0.3
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 +66 -5
- package/dist/config/repository.yaml +25 -0
- package/dist/index.js +357 -590
- package/dist/index.js.map +1 -1
- package/dist/locales/en_US.json +168 -0
- package/dist/locales/zh_CN.json +168 -0
- package/dist/plugins.d/_defaults.yml +4 -0
- package/dist/plugins.d/catlass-op-generator.yml +7 -0
- package/dist/plugins.d/model-infer-optimize.yml +7 -0
- package/dist/plugins.d/ops-code-reviewer.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke.yml +7 -0
- package/dist/plugins.d/ops-registry-invoke.yml +7 -0
- package/dist/plugins.d/pypto-op-orchestrator.yml +7 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +7 -0
- package/dist/plugins.d/torch-compile.yml +7 -0
- package/dist/plugins.d/triton-op-generator.yml +8 -0
- package/package.json +3 -2
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,17 +76,68 @@ 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 join4 } 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 join3 } from "path";
|
|
147
84
|
import { existsSync } from "fs";
|
|
85
|
+
|
|
86
|
+
// src/utils/i18n.ts
|
|
87
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
88
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
89
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
90
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
91
|
+
var __dirname2 = dirname2(__filename2);
|
|
92
|
+
function loadLocale(locale) {
|
|
93
|
+
const localePath = join2(__dirname2, "locales", `${locale}.json`);
|
|
94
|
+
try {
|
|
95
|
+
const content = readFileSync2(localePath, "utf-8");
|
|
96
|
+
return JSON.parse(content);
|
|
97
|
+
} catch {
|
|
98
|
+
const fallbackPath = join2(__dirname2, "locales", "zh_CN.json");
|
|
99
|
+
try {
|
|
100
|
+
const content = readFileSync2(fallbackPath, "utf-8");
|
|
101
|
+
return JSON.parse(content);
|
|
102
|
+
} catch {
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var currentLang = "zh_CN";
|
|
108
|
+
var messages = loadLocale("zh_CN");
|
|
109
|
+
function setLanguage(lang) {
|
|
110
|
+
currentLang = lang;
|
|
111
|
+
messages = loadLocale(lang);
|
|
112
|
+
}
|
|
113
|
+
function t(key) {
|
|
114
|
+
return messages[key] !== void 0 ? messages[key] : key;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/utils/paths.ts
|
|
118
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
119
|
+
var VALID_LEVELS = ["project", "global"];
|
|
120
|
+
function validateTool(tool) {
|
|
121
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return tool;
|
|
127
|
+
}
|
|
128
|
+
function validateLevel(level) {
|
|
129
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return level;
|
|
135
|
+
}
|
|
148
136
|
function detectTraeVariant() {
|
|
149
137
|
const home = homedir();
|
|
150
|
-
if (existsSync(
|
|
151
|
-
if (existsSync(
|
|
152
|
-
if (existsSync(
|
|
138
|
+
if (existsSync(join3(home, ".trae-cn"))) return "ide";
|
|
139
|
+
if (existsSync(join3(home, ".marscode"))) return "plugin";
|
|
140
|
+
if (existsSync(join3(home, ".traecli"))) return "cli";
|
|
153
141
|
return "unknown";
|
|
154
142
|
}
|
|
155
143
|
function getConfigRoot(tool, level, base) {
|
|
@@ -157,66 +145,74 @@ function getConfigRoot(tool, level, base) {
|
|
|
157
145
|
if (level === "global") {
|
|
158
146
|
switch (tool) {
|
|
159
147
|
case "opencode":
|
|
160
|
-
return
|
|
148
|
+
return join3(home, ".config", "opencode");
|
|
161
149
|
case "claude":
|
|
162
|
-
return
|
|
150
|
+
return join3(home, ".claude");
|
|
163
151
|
case "trae": {
|
|
164
152
|
const variant = detectTraeVariant();
|
|
165
153
|
switch (variant) {
|
|
166
154
|
case "plugin":
|
|
167
|
-
return
|
|
155
|
+
return join3(home, ".marscode");
|
|
168
156
|
case "cli":
|
|
169
|
-
return
|
|
157
|
+
return join3(home, ".traecli");
|
|
170
158
|
default:
|
|
171
|
-
return
|
|
159
|
+
return join3(home, ".trae-cn");
|
|
172
160
|
}
|
|
173
161
|
}
|
|
174
162
|
case "cursor":
|
|
175
|
-
return
|
|
163
|
+
return join3(home, ".cursor");
|
|
176
164
|
case "copilot":
|
|
177
|
-
return
|
|
165
|
+
return join3(home, ".copilot");
|
|
166
|
+
default:
|
|
167
|
+
throw new Error(
|
|
168
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
169
|
+
);
|
|
178
170
|
}
|
|
179
171
|
}
|
|
180
172
|
const baseDir = base || process.cwd();
|
|
181
173
|
switch (tool) {
|
|
182
174
|
case "opencode":
|
|
183
|
-
return
|
|
175
|
+
return join3(baseDir, ".opencode");
|
|
184
176
|
case "claude":
|
|
185
|
-
return
|
|
177
|
+
return join3(baseDir, ".claude");
|
|
186
178
|
case "trae": {
|
|
187
179
|
const variant = detectTraeVariant();
|
|
188
180
|
switch (variant) {
|
|
189
181
|
case "plugin":
|
|
190
|
-
return
|
|
182
|
+
return join3(baseDir, ".marscode");
|
|
191
183
|
case "cli":
|
|
192
|
-
return
|
|
184
|
+
return join3(baseDir, ".traecli");
|
|
193
185
|
default:
|
|
194
|
-
return
|
|
186
|
+
return join3(baseDir, ".trae");
|
|
195
187
|
}
|
|
196
188
|
}
|
|
197
189
|
case "cursor":
|
|
198
|
-
return
|
|
190
|
+
return join3(baseDir, ".cursor");
|
|
199
191
|
case "copilot":
|
|
200
|
-
return
|
|
192
|
+
return join3(baseDir, ".github");
|
|
193
|
+
default:
|
|
194
|
+
throw new Error(
|
|
195
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
196
|
+
);
|
|
201
197
|
}
|
|
202
198
|
}
|
|
203
199
|
function getConfigFileName(tool) {
|
|
204
200
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
201
|
}
|
|
206
202
|
function getSkillsDir(configRoot) {
|
|
207
|
-
return
|
|
203
|
+
return join3(configRoot, "skills");
|
|
208
204
|
}
|
|
209
205
|
function getAgentsDir(configRoot) {
|
|
210
|
-
return
|
|
206
|
+
return join3(configRoot, "agents");
|
|
211
207
|
}
|
|
212
208
|
function getManifestPath(configRoot) {
|
|
213
|
-
return
|
|
209
|
+
return join3(configRoot, "cannbot-manifest.json");
|
|
214
210
|
}
|
|
215
211
|
function getCannbotConfigDir() {
|
|
216
|
-
return
|
|
212
|
+
return join3(homedir(), ".cannbot");
|
|
217
213
|
}
|
|
218
214
|
function getCannbotRepoPath() {
|
|
219
|
-
return
|
|
215
|
+
return join3(getCannbotConfigDir(), "repo");
|
|
220
216
|
}
|
|
221
217
|
|
|
222
218
|
// src/core/detector.ts
|
|
@@ -254,12 +250,12 @@ async function detectTrae() {
|
|
|
254
250
|
const home = homedir2();
|
|
255
251
|
const variant = detectTraeVariant();
|
|
256
252
|
let path;
|
|
257
|
-
if (variant === "ide" && existsSync2(
|
|
258
|
-
path =
|
|
259
|
-
} else if (variant === "plugin" && existsSync2(
|
|
260
|
-
path =
|
|
261
|
-
} else if (variant === "cli" && existsSync2(
|
|
262
|
-
path =
|
|
253
|
+
if (variant === "ide" && existsSync2(join4(home, ".trae-cn"))) {
|
|
254
|
+
path = join4(home, ".trae-cn");
|
|
255
|
+
} else if (variant === "plugin" && existsSync2(join4(home, ".marscode"))) {
|
|
256
|
+
path = join4(home, ".marscode");
|
|
257
|
+
} else if (variant === "cli" && existsSync2(join4(home, ".traecli"))) {
|
|
258
|
+
path = join4(home, ".traecli");
|
|
263
259
|
} else if (variant === "unknown") {
|
|
264
260
|
const cmdPath = await getCommandPath("trae");
|
|
265
261
|
if (cmdPath) path = cmdPath;
|
|
@@ -323,13 +319,13 @@ function getAllTools() {
|
|
|
323
319
|
}
|
|
324
320
|
|
|
325
321
|
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
327
|
-
import { join as
|
|
322
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
323
|
+
import { join as join5 } from "path";
|
|
328
324
|
function readManifest(configRoot) {
|
|
329
325
|
const manifestPath = getManifestPath(configRoot);
|
|
330
326
|
if (existsSync3(manifestPath)) {
|
|
331
327
|
try {
|
|
332
|
-
const content =
|
|
328
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
333
329
|
return JSON.parse(content);
|
|
334
330
|
} catch {
|
|
335
331
|
}
|
|
@@ -342,16 +338,16 @@ function readAllManifests(configRoot) {
|
|
|
342
338
|
const standardPath = getManifestPath(configRoot);
|
|
343
339
|
if (existsSync3(standardPath)) {
|
|
344
340
|
try {
|
|
345
|
-
const content =
|
|
341
|
+
const content = readFileSync3(standardPath, "utf-8");
|
|
346
342
|
manifests.push(JSON.parse(content));
|
|
347
343
|
} catch {
|
|
348
344
|
}
|
|
349
345
|
}
|
|
350
346
|
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath =
|
|
347
|
+
const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
|
|
352
348
|
if (existsSync3(pluginManifestPath)) {
|
|
353
349
|
try {
|
|
354
|
-
const content =
|
|
350
|
+
const content = readFileSync3(pluginManifestPath, "utf-8");
|
|
355
351
|
manifests.push(JSON.parse(content));
|
|
356
352
|
} catch {
|
|
357
353
|
}
|
|
@@ -371,6 +367,9 @@ function scanInstalled() {
|
|
|
371
367
|
for (const manifest of manifests) {
|
|
372
368
|
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
369
|
if (!plugin) continue;
|
|
370
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
374
373
|
const alreadyAdded = installed.some(
|
|
375
374
|
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
375
|
);
|
|
@@ -390,284 +389,31 @@ function scanInstalled() {
|
|
|
390
389
|
}
|
|
391
390
|
return installed;
|
|
392
391
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
install_enhanced_more: "\u66F4\u591A",
|
|
419
|
-
install_enhanced_check: "\u68C0\u67E5",
|
|
420
|
-
install_enhanced_docs: "\u6587\u6863",
|
|
421
|
-
update_title: "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 Skills",
|
|
422
|
-
update_updating: "\u6B63\u5728\u66F4\u65B0",
|
|
423
|
-
update_done: "\u66F4\u65B0\u5B8C\u6210",
|
|
424
|
-
update_no_plugins: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
425
|
-
doctor_title: "CANNBot Doctor",
|
|
426
|
-
doctor_tools: "AI \u5DE5\u5177",
|
|
427
|
-
doctor_plugins: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
428
|
-
doctor_links: "\u94FE\u63A5\u5B8C\u6574\u6027",
|
|
429
|
-
doctor_config: "\u914D\u7F6E\u6587\u4EF6",
|
|
430
|
-
doctor_result: "\u7ED3\u679C",
|
|
431
|
-
doctor_fix_title: "\u81EA\u52A8\u4FEE\u590D",
|
|
432
|
-
doctor_fix_cleaning: "\u6E05\u7406\u5931\u6548\u94FE\u63A5",
|
|
433
|
-
doctor_fix_rebuilding: "\u91CD\u5EFA\u76EE\u5F55",
|
|
434
|
-
doctor_fix_done: "\u4FEE\u590D\u5B8C\u6210",
|
|
435
|
-
info_title: "\u63D2\u4EF6\u8BE6\u60C5",
|
|
436
|
-
info_description: "\u63CF\u8FF0",
|
|
437
|
-
info_skills: "Skills",
|
|
438
|
-
info_agents: "Agents",
|
|
439
|
-
info_status: "\u72B6\u6001",
|
|
440
|
-
info_quickstart: "\u5FEB\u901F\u5F00\u59CB",
|
|
441
|
-
info_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
442
|
-
lang_title: "\u8BED\u8A00\u8BBE\u7F6E",
|
|
443
|
-
lang_current: "\u5F53\u524D\u8BED\u8A00",
|
|
444
|
-
lang_set: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A",
|
|
445
|
-
lang_invalid: "\u65E0\u6548\u7684\u8BED\u8A00\uFF0C\u652F\u6301: zh_CN, en_US",
|
|
446
|
-
list_title: "\u53EF\u7528\u573A\u666F",
|
|
447
|
-
list_id: "\u5E8F\u53F7",
|
|
448
|
-
list_name: "\u573A\u666F\u540D",
|
|
449
|
-
list_status: "\u72B6\u6001",
|
|
450
|
-
list_skills: "Skills",
|
|
451
|
-
list_agents: "Agents",
|
|
452
|
-
status_installed: "\u5DF2\u88C5",
|
|
453
|
-
status_not_installed: "\u672A\u88C5",
|
|
454
|
-
error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
455
|
-
error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
456
|
-
error_repo_not_found: "\u4ED3\u5E93\u672A\u627E\u5230",
|
|
457
|
-
quick_start: "\u542F\u52A8\u547D\u4EE4",
|
|
458
|
-
quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
|
|
459
|
-
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
460
|
-
skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
|
|
461
|
-
skill_already_installed: "\u5DF2\u5B89\u88C5",
|
|
462
|
-
skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
463
|
-
skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
|
|
464
|
-
skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
|
|
465
|
-
skill_list_title: "\u53EF\u7528 Skills",
|
|
466
|
-
wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
467
|
-
wizard_mode_plugin: "\u5B89\u88C5 Plugin",
|
|
468
|
-
wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
|
|
469
|
-
wizard_mode_skill: "\u5B89\u88C5 Skill",
|
|
470
|
-
wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
|
|
471
|
-
wizard_back: "\u8FD4\u56DE",
|
|
472
|
-
wizard_cancel: "\u9000\u51FA",
|
|
473
|
-
wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
474
|
-
wizard_back_to_reselect: "\u8FD4\u56DE",
|
|
475
|
-
status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
476
|
-
status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
477
|
-
status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
|
|
478
|
-
status_install_time: "\u5B89\u88C5\u65F6\u95F4",
|
|
479
|
-
install_installed_to: "\u5B89\u88C5\u5230",
|
|
480
|
-
install_skills_count: "Skills",
|
|
481
|
-
install_agents_count: "Agents",
|
|
482
|
-
lang_supported: "\u652F\u6301\u7684\u8BED\u8A00: zh_CN, en_US",
|
|
483
|
-
lang_usage: "\u7528\u6CD5: install-helper lang set <language>",
|
|
484
|
-
lang_specify: "\u8BF7\u6307\u5B9A\u8BED\u8A00: install-helper lang set zh_CN",
|
|
485
|
-
lang_unknown_action: "\u672A\u77E5\u64CD\u4F5C",
|
|
486
|
-
lang_supported_actions: "\u652F\u6301\u7684\u64CD\u4F5C: show, set",
|
|
487
|
-
info_aliases: "\u522B\u540D",
|
|
488
|
-
uninstall_not_found: "\u672A\u627E\u5230",
|
|
489
|
-
uninstall_no_record: "\u672A\u627E\u5230\u5B89\u88C5\u8BB0\u5F55\uFF0C\u53EF\u80FD\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
|
|
490
|
-
uninstall_no_record_hint: "\u5982\u9700\u5F3A\u5236\u6E05\u7406\uFF0C\u8BF7\u624B\u52A8\u5220\u9664 .opencode/ \u6216 .claude/ \u76EE\u5F55\u4E2D\u7684\u76F8\u5173\u6587\u4EF6",
|
|
491
|
-
uninstall_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
492
|
-
uninstall_removed: "\u5DF2\u5378\u8F7D",
|
|
493
|
-
uninstall_files_gone: "\u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55",
|
|
494
|
-
uninstall_skills_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
495
|
-
init_pkg_dir_warn: "\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55",
|
|
496
|
-
init_pkg_dir_hint: "\u8BF7\u5728\u4F60\u7684\u9879\u76EE\u76EE\u5F55\u4E2D\u8FD0\u884C\u6B64\u547D\u4EE4\uFF0C\u800C\u4E0D\u662F\u5728 install-helper \u5B89\u88C5\u76EE\u5F55\u4E2D",
|
|
497
|
-
init_pkg_dir_example: "\u793A\u4F8B\uFF1Acd ~/my-project && install-helper",
|
|
498
|
-
init_cancelled: "\u5DF2\u53D6\u6D88\u5B89\u88C5",
|
|
499
|
-
backup_detected: "\u68C0\u6D4B\u5230\u5F53\u524D\u76EE\u5F55\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
500
|
-
backup_current: "\u5F53\u524D\u5B89\u88C5",
|
|
501
|
-
backup_installing: "\u5373\u5C06\u5B89\u88C5",
|
|
502
|
-
backup_overwrite_warning: "\u7EE7\u7EED\u5B89\u88C5\u5C06\u8986\u76D6\u73B0\u6709\u7684 AGENTS.md \u914D\u7F6E\u6587\u4EF6\u3002",
|
|
503
|
-
backup_auto_create: "\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u521B\u5EFA\u5907\u4EFD\uFF0C\u4EE5\u4FBF\u540E\u7EED\u6062\u590D\u3002",
|
|
504
|
-
backup_overwrite: "\u8986\u76D6\u5B89\u88C5\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09",
|
|
505
|
-
backup_cancel: "\u53D6\u6D88\u5B89\u88C5",
|
|
506
|
-
backup_restore_prompt: "\u68C0\u6D4B\u5230\u5907\u4EFD\u914D\u7F6E\uFF0C\u662F\u5426\u6062\u590D\uFF1F",
|
|
507
|
-
backup_restore: "\u6062\u590D",
|
|
508
|
-
backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
|
|
509
|
-
backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
510
|
-
backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
|
|
511
|
-
backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
|
|
512
|
-
loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
|
|
513
|
-
loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
|
|
514
|
-
loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
|
|
515
|
-
loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
|
|
516
|
-
loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
|
|
517
|
-
install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
|
|
518
|
-
install_to: "\u5B89\u88C5\u5230",
|
|
519
|
-
start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
|
|
520
|
-
wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
521
|
-
wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
|
|
522
|
-
wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
523
|
-
wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
|
|
524
|
-
wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
|
|
525
|
-
wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
|
|
526
|
-
install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
|
|
527
|
-
install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
|
|
528
|
-
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B"
|
|
529
|
-
};
|
|
530
|
-
var en_US = {
|
|
531
|
-
wizard_title: "CANNBot Install Helper",
|
|
532
|
-
wizard_detect: "Detecting installed AI coding tools...",
|
|
533
|
-
wizard_select_tool: "Select AI coding tool",
|
|
534
|
-
wizard_select_level: "Select install level",
|
|
535
|
-
wizard_select_plugins: "Select scenarios to install",
|
|
536
|
-
wizard_confirm: "Confirm installation",
|
|
537
|
-
wizard_detected_tool: "Detected",
|
|
538
|
-
wizard_confirm_tool: "Confirm using",
|
|
539
|
-
wizard_last_used: "last used",
|
|
540
|
-
wizard_already_installed: "installed",
|
|
541
|
-
install_cloning: "Cloning cannbot-skills repository...",
|
|
542
|
-
install_repo_ready: "Repository ready",
|
|
543
|
-
install_progress: "Installing",
|
|
544
|
-
install_done: "Installation complete",
|
|
545
|
-
install_success: "success",
|
|
546
|
-
install_failed: "failed",
|
|
547
|
-
install_already_installed: "already installed",
|
|
548
|
-
install_reinstall_confirm: "Reinstall (overwrite)",
|
|
549
|
-
install_skip: "skipped",
|
|
550
|
-
install_enhanced_next_steps: "Next steps",
|
|
551
|
-
install_enhanced_launch: "Launch",
|
|
552
|
-
install_enhanced_try: "Try",
|
|
553
|
-
install_enhanced_more: "More",
|
|
554
|
-
install_enhanced_check: "Check",
|
|
555
|
-
install_enhanced_docs: "Docs",
|
|
556
|
-
update_title: "Update installed Skills",
|
|
557
|
-
update_updating: "Updating",
|
|
558
|
-
update_done: "Update complete",
|
|
559
|
-
update_no_plugins: "No installed plugins",
|
|
560
|
-
doctor_title: "CANNBot Doctor",
|
|
561
|
-
doctor_tools: "AI Tools",
|
|
562
|
-
doctor_plugins: "Installed Plugins",
|
|
563
|
-
doctor_links: "Link Integrity",
|
|
564
|
-
doctor_config: "Configuration",
|
|
565
|
-
doctor_result: "Result",
|
|
566
|
-
doctor_fix_title: "Auto-fix",
|
|
567
|
-
doctor_fix_cleaning: "Cleaning broken links",
|
|
568
|
-
doctor_fix_rebuilding: "Rebuilding directories",
|
|
569
|
-
doctor_fix_done: "Fix complete",
|
|
570
|
-
info_title: "Plugin Details",
|
|
571
|
-
info_description: "Description",
|
|
572
|
-
info_skills: "Skills",
|
|
573
|
-
info_agents: "Agents",
|
|
574
|
-
info_status: "Status",
|
|
575
|
-
info_quickstart: "Quick Start",
|
|
576
|
-
info_not_found: "Plugin not found",
|
|
577
|
-
lang_title: "Language Settings",
|
|
578
|
-
lang_current: "Current language",
|
|
579
|
-
lang_set: "Language set to",
|
|
580
|
-
lang_invalid: "Invalid language, supported: zh_CN, en_US",
|
|
581
|
-
list_title: "Available Scenarios",
|
|
582
|
-
list_id: "#",
|
|
583
|
-
list_name: "Name",
|
|
584
|
-
list_status: "Status",
|
|
585
|
-
list_skills: "Skills",
|
|
586
|
-
list_agents: "Agents",
|
|
587
|
-
status_installed: "installed",
|
|
588
|
-
status_not_installed: "not installed",
|
|
589
|
-
error_tool_not_found: "No AI coding tool detected",
|
|
590
|
-
error_plugin_not_found: "Plugin not found",
|
|
591
|
-
error_repo_not_found: "Repository not found",
|
|
592
|
-
quick_start: "Launch command",
|
|
593
|
-
quick_start_hint: "Try: help me develop an Abs operator",
|
|
594
|
-
skill_select_category: "Select Skill category",
|
|
595
|
-
skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
|
|
596
|
-
skill_already_installed: "installed",
|
|
597
|
-
skill_install_progress: "Installing",
|
|
598
|
-
skill_install_done: "Skill installation complete",
|
|
599
|
-
skill_uninstall_done: "Skill uninstallation complete",
|
|
600
|
-
skill_list_title: "Available Skills",
|
|
601
|
-
wizard_select_mode: "Select installation mode",
|
|
602
|
-
wizard_mode_plugin: "Install Plugin",
|
|
603
|
-
wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
|
|
604
|
-
wizard_mode_skill: "Install Skill",
|
|
605
|
-
wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
|
|
606
|
-
wizard_back: "Go back",
|
|
607
|
-
wizard_cancel: "Cancel installation",
|
|
608
|
-
wizard_no_selection: "No items selected, please choose an action",
|
|
609
|
-
wizard_back_to_reselect: "Go back",
|
|
610
|
-
status_title: "Installed Plugins",
|
|
611
|
-
status_none: "No installed plugins",
|
|
612
|
-
status_hint: "Run install-helper init to start",
|
|
613
|
-
status_install_time: "Installed at",
|
|
614
|
-
install_installed_to: "Installed to",
|
|
615
|
-
install_skills_count: "Skills",
|
|
616
|
-
install_agents_count: "Agents",
|
|
617
|
-
lang_supported: "Supported languages: zh_CN, en_US",
|
|
618
|
-
lang_usage: "Usage: install-helper lang set <language>",
|
|
619
|
-
lang_specify: "Please specify language: install-helper lang set zh_CN",
|
|
620
|
-
lang_unknown_action: "Unknown action",
|
|
621
|
-
lang_supported_actions: "Supported actions: show, set",
|
|
622
|
-
info_aliases: "Aliases",
|
|
623
|
-
uninstall_not_found: "Not found",
|
|
624
|
-
uninstall_no_record: "No install record found, may not be installed or record is corrupted",
|
|
625
|
-
uninstall_no_record_hint: "For manual cleanup, delete related files in .opencode/ or .claude/ directory",
|
|
626
|
-
uninstall_in_progress: "Uninstalling",
|
|
627
|
-
uninstall_removed: "Uninstalled",
|
|
628
|
-
uninstall_files_gone: " files no longer exist, install record cleaned",
|
|
629
|
-
uninstall_skills_in_progress: "Uninstalling",
|
|
630
|
-
init_pkg_dir_warn: "Current directory appears to be the install-helper package directory",
|
|
631
|
-
init_pkg_dir_hint: "Please run this command in your project directory, not in the install-helper package directory",
|
|
632
|
-
init_pkg_dir_example: "Example: cd ~/my-project && install-helper",
|
|
633
|
-
init_cancelled: "Installation cancelled",
|
|
634
|
-
backup_detected: "Plugin already installed in current directory",
|
|
635
|
-
backup_current: "Currently installed",
|
|
636
|
-
backup_installing: "About to install",
|
|
637
|
-
backup_overwrite_warning: "Continuing will overwrite existing AGENTS.md configuration.",
|
|
638
|
-
backup_auto_create: "System will automatically create backup for future restoration.",
|
|
639
|
-
backup_overwrite: "Overwrite (auto backup)",
|
|
640
|
-
backup_cancel: "Cancel installation",
|
|
641
|
-
backup_restore_prompt: "Backup configuration detected, restore?",
|
|
642
|
-
backup_restore: "Restore",
|
|
643
|
-
backup_only_uninstall: "Uninstall only (no restore)",
|
|
644
|
-
backup_select_action: "Please select an action",
|
|
645
|
-
backup_restore_success: "Configuration restored",
|
|
646
|
-
backup_created: "Backup created",
|
|
647
|
-
loading_skills_list: "Loading Skills list...",
|
|
648
|
-
loading_skills_list_complete: "Skills list loaded",
|
|
649
|
-
loading_plugin_data: "Loading plugin data...",
|
|
650
|
-
loading_plugin_data_complete: "Plugin data loaded",
|
|
651
|
-
loading_skills_list_failed: "Failed to load Skills list, using built-in data",
|
|
652
|
-
install_no_skill_selected: "No Skill selected",
|
|
653
|
-
install_to: "Install to",
|
|
654
|
-
start_to_use: "Start {tool} to use",
|
|
655
|
-
wizard_step_1_title: "[1/4] Select AI coding tool",
|
|
656
|
-
wizard_step_2_title: "[2/4] Select installation location",
|
|
657
|
-
wizard_step_3_title: "[3/4] Select plugins to install",
|
|
658
|
-
wizard_step_4_title: "[4/4] Confirm installation",
|
|
659
|
-
wizard_step_3_skill_title: "[3/4] Select Skill category",
|
|
660
|
-
wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
|
|
661
|
-
install_level_project: "Install to current directory (recommended, current project only)",
|
|
662
|
-
install_level_global: "Install globally (all projects)",
|
|
663
|
-
wizard_select_mode_title: "Select installation type"
|
|
664
|
-
};
|
|
665
|
-
var currentLang = "zh_CN";
|
|
666
|
-
function setLanguage(lang) {
|
|
667
|
-
currentLang = lang;
|
|
668
|
-
}
|
|
669
|
-
function t(key) {
|
|
670
|
-
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
392
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
393
|
+
const skillsDir = join5(configRoot, "skills");
|
|
394
|
+
const agentsDir = join5(configRoot, "agents");
|
|
395
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
396
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
397
|
+
const skillPath = join5(skillsDir, skillId);
|
|
398
|
+
return existsSync3(skillPath);
|
|
399
|
+
});
|
|
400
|
+
if (!hasAnySkill) return false;
|
|
401
|
+
}
|
|
402
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
403
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
404
|
+
const agentPath = join5(agentsDir, agentId);
|
|
405
|
+
return existsSync3(agentPath);
|
|
406
|
+
});
|
|
407
|
+
if (!hasAnyAgent) return false;
|
|
408
|
+
}
|
|
409
|
+
let agentsMdPath;
|
|
410
|
+
if (tool === "opencode" && level === "project") {
|
|
411
|
+
agentsMdPath = join5(configRoot, "..", "AGENTS.md");
|
|
412
|
+
} else {
|
|
413
|
+
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
414
|
+
}
|
|
415
|
+
if (!existsSync3(agentsMdPath)) return false;
|
|
416
|
+
return true;
|
|
671
417
|
}
|
|
672
418
|
|
|
673
419
|
// src/utils/logger.ts
|
|
@@ -724,24 +470,24 @@ function printBoxTitle(title, width = 65) {
|
|
|
724
470
|
}
|
|
725
471
|
function showOperationHints(isCheckbox = false) {
|
|
726
472
|
if (isCheckbox) {
|
|
727
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
473
|
+
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
|
|
728
474
|
`);
|
|
729
475
|
} else {
|
|
730
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
476
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
731
477
|
`);
|
|
732
478
|
}
|
|
733
479
|
}
|
|
734
480
|
|
|
735
481
|
// src/utils/config.ts
|
|
736
|
-
import { existsSync as existsSync4, mkdirSync, readFileSync as
|
|
737
|
-
import { join as
|
|
482
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
483
|
+
import { join as join6 } from "path";
|
|
738
484
|
import { homedir as homedir3 } from "os";
|
|
739
485
|
import { parse, stringify } from "yaml";
|
|
740
486
|
function getConfigDir() {
|
|
741
|
-
return
|
|
487
|
+
return join6(homedir3(), ".cannbot");
|
|
742
488
|
}
|
|
743
489
|
function getConfigPath() {
|
|
744
|
-
return
|
|
490
|
+
return join6(getConfigDir(), "config.yaml");
|
|
745
491
|
}
|
|
746
492
|
function readConfig() {
|
|
747
493
|
const configPath = getConfigPath();
|
|
@@ -752,7 +498,7 @@ function readConfig() {
|
|
|
752
498
|
};
|
|
753
499
|
}
|
|
754
500
|
try {
|
|
755
|
-
const content =
|
|
501
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
756
502
|
const parsed = parse(content);
|
|
757
503
|
return {
|
|
758
504
|
language: parsed.language || "zh_CN",
|
|
@@ -1036,8 +782,8 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
1036
782
|
const displayPath = getConfigRoot(tool, level);
|
|
1037
783
|
const toolName = getToolDisplayName(tool);
|
|
1038
784
|
const levelText = level === "project" ? "project" : "global";
|
|
1039
|
-
logger.info(
|
|
1040
|
-
logger.info(
|
|
785
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk2.bold(String(plugins.length))).replace("{tool}", chalk2.green(toolName)).replace("{level}", chalk2.cyan(levelText)));
|
|
786
|
+
logger.info(`${t("wizard_install_path")}: ${chalk2.cyan(displayPath)}`);
|
|
1041
787
|
for (const plugin of selectedPlugins) {
|
|
1042
788
|
if (plugin) {
|
|
1043
789
|
logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
|
|
@@ -1058,14 +804,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
1058
804
|
}
|
|
1059
805
|
|
|
1060
806
|
// src/core/repository.ts
|
|
1061
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
1062
|
-
import { join as
|
|
807
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
808
|
+
import { join as join8, dirname as dirname4 } from "path";
|
|
809
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1063
810
|
import { execa as execa2 } from "execa";
|
|
811
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1064
812
|
|
|
1065
813
|
// src/core/scanner.ts
|
|
1066
|
-
import { existsSync as existsSync5, readdirSync, readFileSync as
|
|
1067
|
-
import { join as
|
|
1068
|
-
import {
|
|
814
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
815
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
816
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
817
|
+
import { parse as parseYaml2 } from "yaml";
|
|
818
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
819
|
+
var __dirname3 = dirname3(__filename3);
|
|
1069
820
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
1070
821
|
"node_modules",
|
|
1071
822
|
".git",
|
|
@@ -1086,36 +837,43 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
1086
837
|
"docs",
|
|
1087
838
|
"scripts"
|
|
1088
839
|
]);
|
|
1089
|
-
|
|
1090
|
-
"
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
]
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
]
|
|
840
|
+
function loadScanConfig() {
|
|
841
|
+
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
842
|
+
try {
|
|
843
|
+
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
844
|
+
return {
|
|
845
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
|
|
846
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
|
|
847
|
+
};
|
|
848
|
+
} catch {
|
|
849
|
+
return {
|
|
850
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
|
|
851
|
+
pluginDirs: ["plugins-official", "plugins-community"]
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
var scanConfig = loadScanConfig();
|
|
856
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
857
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1100
858
|
function scanSkills(repoPath) {
|
|
1101
859
|
const skills = [];
|
|
1102
860
|
const seen = /* @__PURE__ */ new Set();
|
|
1103
861
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1104
|
-
const fullPath =
|
|
862
|
+
const fullPath = join7(repoPath, dir);
|
|
1105
863
|
if (!existsSync5(fullPath)) continue;
|
|
1106
864
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1107
865
|
}
|
|
1108
866
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1109
|
-
const pluginParent =
|
|
867
|
+
const pluginParent = join7(repoPath, pluginDir);
|
|
1110
868
|
if (!existsSync5(pluginParent)) continue;
|
|
1111
869
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1112
|
-
const pluginPath =
|
|
870
|
+
const pluginPath = join7(pluginParent, plugin);
|
|
1113
871
|
if (!isDirectory(pluginPath)) continue;
|
|
1114
|
-
const skillsDir =
|
|
872
|
+
const skillsDir = join7(pluginPath, "skills");
|
|
1115
873
|
if (existsSync5(skillsDir)) {
|
|
1116
874
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1117
875
|
}
|
|
1118
|
-
const skillDir =
|
|
876
|
+
const skillDir = join7(pluginPath, "skill");
|
|
1119
877
|
if (existsSync5(skillDir)) {
|
|
1120
878
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1121
879
|
}
|
|
@@ -1125,7 +883,7 @@ function scanSkills(repoPath) {
|
|
|
1125
883
|
}
|
|
1126
884
|
function parseFrontmatter(filePath) {
|
|
1127
885
|
try {
|
|
1128
|
-
const content =
|
|
886
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1129
887
|
const lines = content.split("\n");
|
|
1130
888
|
if (lines.length < 3) return null;
|
|
1131
889
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1138,7 +896,7 @@ function parseFrontmatter(filePath) {
|
|
|
1138
896
|
}
|
|
1139
897
|
if (endIndex === -1) return null;
|
|
1140
898
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1141
|
-
const parsed =
|
|
899
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1142
900
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1143
901
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1144
902
|
return {
|
|
@@ -1151,17 +909,17 @@ function parseFrontmatter(filePath) {
|
|
|
1151
909
|
}
|
|
1152
910
|
function getCurrentCommit(repoPath) {
|
|
1153
911
|
try {
|
|
1154
|
-
const headFile =
|
|
912
|
+
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1155
913
|
if (!existsSync5(headFile)) return "unknown";
|
|
1156
|
-
const headContent =
|
|
914
|
+
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1157
915
|
if (headContent.startsWith("ref: ")) {
|
|
1158
|
-
const refPath =
|
|
916
|
+
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1159
917
|
if (existsSync5(refPath)) {
|
|
1160
|
-
return
|
|
918
|
+
return readFileSync5(refPath, "utf-8").trim();
|
|
1161
919
|
}
|
|
1162
|
-
const packedRefsPath =
|
|
920
|
+
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1163
921
|
if (existsSync5(packedRefsPath)) {
|
|
1164
|
-
const packedRefs =
|
|
922
|
+
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1165
923
|
const refName = headContent.slice(5);
|
|
1166
924
|
const lines = packedRefs.split("\n");
|
|
1167
925
|
for (const line of lines) {
|
|
@@ -1180,7 +938,7 @@ function readScanCache() {
|
|
|
1180
938
|
try {
|
|
1181
939
|
const cachePath = getCachePath();
|
|
1182
940
|
if (!existsSync5(cachePath)) return null;
|
|
1183
|
-
const content =
|
|
941
|
+
const content = readFileSync5(cachePath, "utf-8");
|
|
1184
942
|
const cache = JSON.parse(content);
|
|
1185
943
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1186
944
|
const age = Date.now() - cache.timestamp;
|
|
@@ -1193,7 +951,7 @@ function readScanCache() {
|
|
|
1193
951
|
function writeScanCache(cache) {
|
|
1194
952
|
try {
|
|
1195
953
|
const cachePath = getCachePath();
|
|
1196
|
-
const cacheDir =
|
|
954
|
+
const cacheDir = dirname3(cachePath);
|
|
1197
955
|
if (!existsSync5(cacheDir)) {
|
|
1198
956
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1199
957
|
}
|
|
@@ -1202,11 +960,10 @@ function writeScanCache(cache) {
|
|
|
1202
960
|
}
|
|
1203
961
|
}
|
|
1204
962
|
function getCachePath() {
|
|
1205
|
-
|
|
1206
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
963
|
+
return join7(getCannbotConfigDir(), "scan-cache.json");
|
|
1207
964
|
}
|
|
1208
965
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1209
|
-
const selfSkillMd =
|
|
966
|
+
const selfSkillMd = join7(dirPath, "SKILL.md");
|
|
1210
967
|
if (existsSync5(selfSkillMd)) {
|
|
1211
968
|
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1212
969
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
@@ -1221,10 +978,10 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1221
978
|
return;
|
|
1222
979
|
}
|
|
1223
980
|
for (const entry of safeReaddir(dirPath)) {
|
|
1224
|
-
const fullPath =
|
|
981
|
+
const fullPath = join7(dirPath, entry);
|
|
1225
982
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1226
983
|
if (!isDirectory(fullPath)) continue;
|
|
1227
|
-
const skillMd =
|
|
984
|
+
const skillMd = join7(fullPath, "SKILL.md");
|
|
1228
985
|
if (existsSync5(skillMd)) {
|
|
1229
986
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1230
987
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
@@ -1243,7 +1000,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1243
1000
|
}
|
|
1244
1001
|
function safeReaddir(dir) {
|
|
1245
1002
|
try {
|
|
1246
|
-
return
|
|
1003
|
+
return readdirSync2(dir);
|
|
1247
1004
|
} catch {
|
|
1248
1005
|
return [];
|
|
1249
1006
|
}
|
|
@@ -1483,7 +1240,21 @@ function getAllCategories() {
|
|
|
1483
1240
|
}
|
|
1484
1241
|
|
|
1485
1242
|
// src/core/repository.ts
|
|
1486
|
-
var
|
|
1243
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1244
|
+
var __dirname4 = dirname4(__filename4);
|
|
1245
|
+
function loadRepoUrl() {
|
|
1246
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
1247
|
+
return process.env.CANNBOT_REPO_URL;
|
|
1248
|
+
}
|
|
1249
|
+
const configPath = join8(__dirname4, "config", "repository.yaml");
|
|
1250
|
+
try {
|
|
1251
|
+
const config = parseYaml3(readFileSync6(configPath, "utf-8"));
|
|
1252
|
+
return config.repository?.url || "https://gitcode.com/cann/cannbot-skills.git";
|
|
1253
|
+
} catch {
|
|
1254
|
+
return "https://gitcode.com/cann/cannbot-skills.git";
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
var REPO_URL = loadRepoUrl();
|
|
1487
1258
|
var RepositoryManager = class {
|
|
1488
1259
|
repoPath;
|
|
1489
1260
|
constructor(customPath) {
|
|
@@ -1541,8 +1312,8 @@ var RepositoryManager = class {
|
|
|
1541
1312
|
}
|
|
1542
1313
|
isValidRepo(path) {
|
|
1543
1314
|
if (!existsSync6(path)) return false;
|
|
1544
|
-
const gitDir =
|
|
1545
|
-
const pluginsDir =
|
|
1315
|
+
const gitDir = join8(path, ".git");
|
|
1316
|
+
const pluginsDir = join8(path, "plugins-official");
|
|
1546
1317
|
return existsSync6(gitDir) && existsSync6(pluginsDir);
|
|
1547
1318
|
}
|
|
1548
1319
|
async cloneRepo() {
|
|
@@ -1578,17 +1349,17 @@ function createRepositoryManager(customPath) {
|
|
|
1578
1349
|
|
|
1579
1350
|
// src/core/installer.ts
|
|
1580
1351
|
import { existsSync as existsSync9 } from "fs";
|
|
1581
|
-
import { join as
|
|
1352
|
+
import { join as join11 } from "path";
|
|
1582
1353
|
import { execa as execa3 } from "execa";
|
|
1583
1354
|
|
|
1584
1355
|
// src/core/record.ts
|
|
1585
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as
|
|
1586
|
-
import { join as
|
|
1356
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
|
|
1357
|
+
import { join as join9 } from "path";
|
|
1587
1358
|
function getInstallsDir() {
|
|
1588
|
-
return
|
|
1359
|
+
return join9(getCannbotConfigDir(), "installs");
|
|
1589
1360
|
}
|
|
1590
1361
|
function getRecordPath(pluginId) {
|
|
1591
|
-
return
|
|
1362
|
+
return join9(getInstallsDir(), `${pluginId}.json`);
|
|
1592
1363
|
}
|
|
1593
1364
|
function readRecord(pluginId) {
|
|
1594
1365
|
const recordPath = getRecordPath(pluginId);
|
|
@@ -1596,7 +1367,7 @@ function readRecord(pluginId) {
|
|
|
1596
1367
|
return null;
|
|
1597
1368
|
}
|
|
1598
1369
|
try {
|
|
1599
|
-
const content =
|
|
1370
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1600
1371
|
return JSON.parse(content);
|
|
1601
1372
|
} catch {
|
|
1602
1373
|
return null;
|
|
@@ -1620,22 +1391,22 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1620
1391
|
const files = [];
|
|
1621
1392
|
const directories = [];
|
|
1622
1393
|
if (manifest) {
|
|
1623
|
-
const skillsDir =
|
|
1394
|
+
const skillsDir = join9(configRoot, "skills");
|
|
1624
1395
|
if (existsSync7(skillsDir)) {
|
|
1625
1396
|
directories.push(skillsDir);
|
|
1626
1397
|
for (const skillName of manifest.installed_skills || []) {
|
|
1627
|
-
const skillPath =
|
|
1398
|
+
const skillPath = join9(skillsDir, skillName);
|
|
1628
1399
|
if (existsSync7(skillPath) || isSymlink(skillPath)) {
|
|
1629
1400
|
files.push(skillPath);
|
|
1630
1401
|
}
|
|
1631
1402
|
}
|
|
1632
1403
|
}
|
|
1633
|
-
const agentsDir =
|
|
1404
|
+
const agentsDir = join9(configRoot, "agents");
|
|
1634
1405
|
if (existsSync7(agentsDir)) {
|
|
1635
1406
|
directories.push(agentsDir);
|
|
1636
1407
|
for (const agentName of manifest.installed_agents || []) {
|
|
1637
|
-
const agentPath =
|
|
1638
|
-
const agentPathMd =
|
|
1408
|
+
const agentPath = join9(agentsDir, agentName);
|
|
1409
|
+
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
1639
1410
|
if (existsSync7(agentPath) || isSymlink(agentPath)) {
|
|
1640
1411
|
files.push(agentPath);
|
|
1641
1412
|
} else if (existsSync7(agentPathMd) || isSymlink(agentPathMd)) {
|
|
@@ -1644,35 +1415,34 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1644
1415
|
}
|
|
1645
1416
|
}
|
|
1646
1417
|
}
|
|
1647
|
-
const workflowsLink =
|
|
1418
|
+
const workflowsLink = join9(configRoot, "workflows");
|
|
1648
1419
|
if (isSymlink(workflowsLink)) {
|
|
1649
1420
|
files.push(workflowsLink);
|
|
1650
1421
|
}
|
|
1651
|
-
const manifestPath =
|
|
1422
|
+
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
1652
1423
|
if (existsSync7(manifestPath)) {
|
|
1653
1424
|
files.push(manifestPath);
|
|
1654
1425
|
}
|
|
1655
|
-
const pluginManifestPath =
|
|
1426
|
+
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
1656
1427
|
if (existsSync7(pluginManifestPath)) {
|
|
1657
1428
|
files.push(pluginManifestPath);
|
|
1658
1429
|
}
|
|
1659
1430
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1660
|
-
const configFilePath = level === "project" ?
|
|
1431
|
+
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
1661
1432
|
if (existsSync7(configFilePath)) {
|
|
1662
1433
|
files.push(configFilePath);
|
|
1663
1434
|
}
|
|
1664
1435
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1665
1436
|
for (const repoName of repoLinks) {
|
|
1666
|
-
const repoLinkPath =
|
|
1437
|
+
const repoLinkPath = join9(installPath, repoName);
|
|
1667
1438
|
if (isSymlink(repoLinkPath)) {
|
|
1668
1439
|
files.push(repoLinkPath);
|
|
1669
1440
|
}
|
|
1670
|
-
const repoLinkInConfig =
|
|
1441
|
+
const repoLinkInConfig = join9(configRoot, repoName);
|
|
1671
1442
|
if (isSymlink(repoLinkInConfig)) {
|
|
1672
1443
|
files.push(repoLinkInConfig);
|
|
1673
1444
|
}
|
|
1674
1445
|
}
|
|
1675
|
-
directories.push(configRoot);
|
|
1676
1446
|
return {
|
|
1677
1447
|
pluginId,
|
|
1678
1448
|
displayName,
|
|
@@ -1693,7 +1463,7 @@ function isSymlink(path) {
|
|
|
1693
1463
|
}
|
|
1694
1464
|
}
|
|
1695
1465
|
function getSkillRecordPath() {
|
|
1696
|
-
return
|
|
1466
|
+
return join9(getInstallsDir(), "skills.json");
|
|
1697
1467
|
}
|
|
1698
1468
|
function readSkillRecord() {
|
|
1699
1469
|
const recordPath = getSkillRecordPath();
|
|
@@ -1701,7 +1471,7 @@ function readSkillRecord() {
|
|
|
1701
1471
|
return {};
|
|
1702
1472
|
}
|
|
1703
1473
|
try {
|
|
1704
|
-
const content =
|
|
1474
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1705
1475
|
return JSON.parse(content);
|
|
1706
1476
|
} catch {
|
|
1707
1477
|
return {};
|
|
@@ -1749,12 +1519,18 @@ function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
|
1749
1519
|
}
|
|
1750
1520
|
function getInstalledSkills(tool, level, installPath) {
|
|
1751
1521
|
const record = readSkillRecord();
|
|
1752
|
-
|
|
1522
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1523
|
+
const configRoot = getConfigRoot(tool, level);
|
|
1524
|
+
const skillsDir = join9(configRoot, "skills");
|
|
1525
|
+
return recordedSkills.filter((skillId) => {
|
|
1526
|
+
const skillPath = join9(skillsDir, skillId);
|
|
1527
|
+
return existsSync7(skillPath) || isSymlink(skillPath);
|
|
1528
|
+
});
|
|
1753
1529
|
}
|
|
1754
1530
|
|
|
1755
1531
|
// src/core/backup.ts
|
|
1756
1532
|
import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
1757
|
-
import { join as
|
|
1533
|
+
import { join as join10 } from "path";
|
|
1758
1534
|
function getAgentsFileName(tool) {
|
|
1759
1535
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1760
1536
|
}
|
|
@@ -1769,7 +1545,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1769
1545
|
};
|
|
1770
1546
|
}
|
|
1771
1547
|
}
|
|
1772
|
-
const agentsFile =
|
|
1548
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1773
1549
|
if (!existsSync8(agentsFile)) {
|
|
1774
1550
|
return null;
|
|
1775
1551
|
}
|
|
@@ -1788,13 +1564,13 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1788
1564
|
return null;
|
|
1789
1565
|
}
|
|
1790
1566
|
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1791
|
-
const agentsFile =
|
|
1567
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1792
1568
|
if (!existsSync8(agentsFile)) {
|
|
1793
1569
|
return null;
|
|
1794
1570
|
}
|
|
1795
1571
|
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
1796
1572
|
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
1797
|
-
const backupPath =
|
|
1573
|
+
const backupPath = join10(configRoot, backupFileName);
|
|
1798
1574
|
try {
|
|
1799
1575
|
copyFileSync(agentsFile, backupPath);
|
|
1800
1576
|
return {
|
|
@@ -1824,7 +1600,7 @@ function findBackups(configRoot) {
|
|
|
1824
1600
|
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
1825
1601
|
const plugin = findPlugin(pluginId);
|
|
1826
1602
|
backups.push({
|
|
1827
|
-
filePath:
|
|
1603
|
+
filePath: join10(configRoot, file),
|
|
1828
1604
|
pluginId,
|
|
1829
1605
|
pluginName: plugin?.displayName || pluginId,
|
|
1830
1606
|
backupTime: timestamp
|
|
@@ -1837,7 +1613,7 @@ function findBackups(configRoot) {
|
|
|
1837
1613
|
}
|
|
1838
1614
|
function restoreBackup(backupPath, configRoot, tool) {
|
|
1839
1615
|
try {
|
|
1840
|
-
const agentsFile =
|
|
1616
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1841
1617
|
copyFileSync(backupPath, agentsFile);
|
|
1842
1618
|
return true;
|
|
1843
1619
|
} catch {
|
|
@@ -1938,7 +1714,7 @@ async function installPlugin(opts) {
|
|
|
1938
1714
|
warnings: []
|
|
1939
1715
|
};
|
|
1940
1716
|
}
|
|
1941
|
-
const scriptPath =
|
|
1717
|
+
const scriptPath = join11(opts.repoPath, plugin.dir, plugin.script);
|
|
1942
1718
|
if (!existsSync9(scriptPath)) {
|
|
1943
1719
|
return {
|
|
1944
1720
|
success: false,
|
|
@@ -1955,7 +1731,7 @@ async function installPlugin(opts) {
|
|
|
1955
1731
|
}
|
|
1956
1732
|
const cwd = opts.installPath || process.cwd();
|
|
1957
1733
|
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1958
|
-
const agentsFile =
|
|
1734
|
+
const agentsFile = join11(configRoot, getAgentsFileName(opts.tool));
|
|
1959
1735
|
let backupInfo = null;
|
|
1960
1736
|
if (existsSync9(agentsFile)) {
|
|
1961
1737
|
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
@@ -2051,13 +1827,13 @@ async function installPlugin(opts) {
|
|
|
2051
1827
|
|
|
2052
1828
|
// src/core/skill-installer.ts
|
|
2053
1829
|
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
|
|
2054
|
-
import { join as
|
|
1830
|
+
import { join as join12 } from "path";
|
|
2055
1831
|
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
2056
1832
|
import chalk4 from "chalk";
|
|
2057
1833
|
import Table from "cli-table3";
|
|
2058
1834
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
2059
1835
|
const configRoot = getConfigRoot(tool, level);
|
|
2060
|
-
const skillsDir =
|
|
1836
|
+
const skillsDir = join12(configRoot, "skills");
|
|
2061
1837
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
2062
1838
|
if (!existsSync10(skillsDir)) {
|
|
2063
1839
|
mkdirSync5(skillsDir, { recursive: true });
|
|
@@ -2070,8 +1846,8 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
2070
1846
|
results.push({ skillId, success: false, error: "Skill not found in registry" });
|
|
2071
1847
|
continue;
|
|
2072
1848
|
}
|
|
2073
|
-
const sourcePath =
|
|
2074
|
-
const targetPath =
|
|
1849
|
+
const sourcePath = join12(repoPath, skill.source, skill.id);
|
|
1850
|
+
const targetPath = join12(skillsDir, skillId);
|
|
2075
1851
|
if (!existsSync10(sourcePath)) {
|
|
2076
1852
|
results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
|
|
2077
1853
|
continue;
|
|
@@ -2094,21 +1870,21 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
2094
1870
|
}
|
|
2095
1871
|
async function uninstallSkills(skillIds, tool, level) {
|
|
2096
1872
|
const configRoot = getConfigRoot(tool, level);
|
|
2097
|
-
const skillsDir =
|
|
1873
|
+
const skillsDir = join12(configRoot, "skills");
|
|
2098
1874
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
2099
1875
|
const results = [];
|
|
2100
1876
|
const removedIds = [];
|
|
2101
1877
|
for (const skillId of skillIds) {
|
|
2102
|
-
const targetPath =
|
|
1878
|
+
const targetPath = join12(skillsDir, skillId);
|
|
2103
1879
|
try {
|
|
2104
1880
|
if (existsSync10(targetPath) || isSymlink2(targetPath)) {
|
|
2105
1881
|
unlinkSync3(targetPath);
|
|
2106
1882
|
results.push({ skillId, success: true });
|
|
2107
1883
|
removedIds.push(skillId);
|
|
2108
|
-
logger.step(`
|
|
1884
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
2109
1885
|
} else {
|
|
2110
1886
|
results.push({ skillId, success: false, error: "Not installed" });
|
|
2111
|
-
logger.warn(`${skillId}
|
|
1887
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
2112
1888
|
}
|
|
2113
1889
|
} catch (error) {
|
|
2114
1890
|
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
|
|
@@ -2122,24 +1898,14 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2122
1898
|
const entries = readdirSync4(skillsDir);
|
|
2123
1899
|
if (entries.length === 0) {
|
|
2124
1900
|
rmdirSync(skillsDir);
|
|
2125
|
-
logger.step(`
|
|
2126
|
-
}
|
|
2127
|
-
} catch {
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
if (existsSync10(configRoot)) {
|
|
2131
|
-
try {
|
|
2132
|
-
const entries = readdirSync4(configRoot);
|
|
2133
|
-
if (entries.length === 0) {
|
|
2134
|
-
rmdirSync(configRoot);
|
|
2135
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
1901
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
2136
1902
|
}
|
|
2137
1903
|
} catch {
|
|
2138
1904
|
}
|
|
2139
1905
|
}
|
|
2140
1906
|
return results;
|
|
2141
1907
|
}
|
|
2142
|
-
async function interactiveSkillSelect() {
|
|
1908
|
+
async function interactiveSkillSelect(tool, level) {
|
|
2143
1909
|
const BACK3 = "__back__";
|
|
2144
1910
|
const CANCEL3 = "__cancel__";
|
|
2145
1911
|
const categories = getAllCategories();
|
|
@@ -2175,15 +1941,8 @@ async function interactiveSkillSelect() {
|
|
|
2175
1941
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
2176
1942
|
if (!category) return [];
|
|
2177
1943
|
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
2178
|
-
const
|
|
2179
|
-
const
|
|
2180
|
-
const installedSkills = [];
|
|
2181
|
-
for (const tk of tools) {
|
|
2182
|
-
for (const l of levels) {
|
|
2183
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
2184
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
1944
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
1945
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
2187
1946
|
const installedSet = new Set(installedSkills);
|
|
2188
1947
|
const skillChoices = category.skills.map((skill) => {
|
|
2189
1948
|
const isInstalled = installedSet.has(skill.id);
|
|
@@ -2248,7 +2007,7 @@ function listAllSkills() {
|
|
|
2248
2007
|
console.log(table.toString());
|
|
2249
2008
|
}
|
|
2250
2009
|
console.log();
|
|
2251
|
-
console.log(chalk4.dim(`
|
|
2010
|
+
console.log(chalk4.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
2252
2011
|
console.log();
|
|
2253
2012
|
}
|
|
2254
2013
|
function isSymlink2(path) {
|
|
@@ -2285,7 +2044,8 @@ function printPluginList(plugins, installed) {
|
|
|
2285
2044
|
]);
|
|
2286
2045
|
});
|
|
2287
2046
|
console.log();
|
|
2288
|
-
|
|
2047
|
+
const countSuffix = t("count_suffix");
|
|
2048
|
+
console.log(chalk5.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
2289
2049
|
console.log(table.toString());
|
|
2290
2050
|
console.log();
|
|
2291
2051
|
}
|
|
@@ -2318,22 +2078,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2318
2078
|
return;
|
|
2319
2079
|
}
|
|
2320
2080
|
const toolName = getToolDisplayName(tool);
|
|
2081
|
+
const countSuffix = t("count_suffix");
|
|
2321
2082
|
const lines = [
|
|
2322
2083
|
chalk5.bold(` ${t("install_done")}!`),
|
|
2323
2084
|
"",
|
|
2324
2085
|
` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
2325
|
-
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}
|
|
2086
|
+
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2326
2087
|
"",
|
|
2327
2088
|
chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
2328
2089
|
` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
|
|
2329
|
-
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("
|
|
2090
|
+
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green(t("try_prompt"))}`,
|
|
2330
2091
|
` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
|
|
2331
2092
|
` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
|
|
2332
2093
|
""
|
|
2333
2094
|
];
|
|
2334
2095
|
if (successResults.length === 1) {
|
|
2096
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
2097
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2335
2098
|
lines.push(
|
|
2336
|
-
` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(
|
|
2099
|
+
` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(docsPath)}`
|
|
2337
2100
|
);
|
|
2338
2101
|
}
|
|
2339
2102
|
console.log();
|
|
@@ -2344,15 +2107,15 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2344
2107
|
}
|
|
2345
2108
|
|
|
2346
2109
|
// src/commands/init.ts
|
|
2347
|
-
import { existsSync as existsSync11, readFileSync as
|
|
2348
|
-
import { join as
|
|
2110
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
2111
|
+
import { join as join13 } from "path";
|
|
2349
2112
|
var BACK2 = "__back__";
|
|
2350
2113
|
var CANCEL2 = "__cancel__";
|
|
2351
2114
|
async function initCommand() {
|
|
2352
2115
|
const cwd = process.cwd();
|
|
2353
|
-
if (existsSync11(
|
|
2116
|
+
if (existsSync11(join13(cwd, "package.json")) && (() => {
|
|
2354
2117
|
try {
|
|
2355
|
-
const pkg = JSON.parse(
|
|
2118
|
+
const pkg = JSON.parse(readFileSync8(join13(cwd, "package.json"), "utf-8"));
|
|
2356
2119
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2357
2120
|
} catch {
|
|
2358
2121
|
return false;
|
|
@@ -2516,7 +2279,7 @@ async function skillInstallFlow() {
|
|
|
2516
2279
|
break;
|
|
2517
2280
|
}
|
|
2518
2281
|
case 2: {
|
|
2519
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2282
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2520
2283
|
if (selectedSkills === "back") {
|
|
2521
2284
|
step = 1;
|
|
2522
2285
|
break;
|
|
@@ -2534,7 +2297,7 @@ async function skillInstallFlow() {
|
|
|
2534
2297
|
spinner.start();
|
|
2535
2298
|
const repoPath = await repoManager.ensureRepo();
|
|
2536
2299
|
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2537
|
-
logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)}
|
|
2300
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk6.bold(String(selectedSkills.length)))}...`);
|
|
2538
2301
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2539
2302
|
let successCount = 0;
|
|
2540
2303
|
let failCount = 0;
|
|
@@ -2552,7 +2315,7 @@ async function skillInstallFlow() {
|
|
|
2552
2315
|
}
|
|
2553
2316
|
const configRoot = getConfigRoot(tool, level);
|
|
2554
2317
|
logger.blank();
|
|
2555
|
-
logger.success(`${t("skill_install_done")}: ${chalk6.green(
|
|
2318
|
+
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)))}`);
|
|
2556
2319
|
logger.blank();
|
|
2557
2320
|
logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
|
|
2558
2321
|
logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
|
|
@@ -2579,7 +2342,7 @@ async function listCommand() {
|
|
|
2579
2342
|
// src/commands/doctor.ts
|
|
2580
2343
|
import chalk7 from "chalk";
|
|
2581
2344
|
import { existsSync as existsSync12, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
2582
|
-
import { join as
|
|
2345
|
+
import { join as join14 } from "path";
|
|
2583
2346
|
async function doctorCommand(options = {}) {
|
|
2584
2347
|
console.log();
|
|
2585
2348
|
console.log(chalk7.bold(` ${t("doctor_title")}`));
|
|
@@ -2598,7 +2361,7 @@ async function doctorCommand(options = {}) {
|
|
|
2598
2361
|
chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2599
2362
|
);
|
|
2600
2363
|
} else {
|
|
2601
|
-
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014
|
|
2364
|
+
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2602
2365
|
}
|
|
2603
2366
|
}
|
|
2604
2367
|
console.log();
|
|
@@ -2613,7 +2376,7 @@ async function doctorCommand(options = {}) {
|
|
|
2613
2376
|
chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2614
2377
|
);
|
|
2615
2378
|
} else {
|
|
2616
|
-
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014
|
|
2379
|
+
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2617
2380
|
}
|
|
2618
2381
|
}
|
|
2619
2382
|
console.log();
|
|
@@ -2626,64 +2389,64 @@ async function doctorCommand(options = {}) {
|
|
|
2626
2389
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2627
2390
|
if (brokenLinks === 0) {
|
|
2628
2391
|
const count = readdirSync5(skillsDir).length;
|
|
2629
|
-
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count}
|
|
2392
|
+
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2630
2393
|
} else {
|
|
2631
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks}
|
|
2394
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2632
2395
|
warnings++;
|
|
2633
2396
|
if (options.fix) {
|
|
2634
2397
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2635
2398
|
fixes += fixed;
|
|
2636
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2399
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2637
2400
|
}
|
|
2638
2401
|
}
|
|
2639
2402
|
} else {
|
|
2640
|
-
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014
|
|
2403
|
+
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2641
2404
|
if (options.fix) {
|
|
2642
2405
|
mkdirSync6(skillsDir, { recursive: true });
|
|
2643
2406
|
fixes++;
|
|
2644
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2407
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2645
2408
|
}
|
|
2646
2409
|
}
|
|
2647
2410
|
if (existsSync12(agentsDir)) {
|
|
2648
2411
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2649
2412
|
if (brokenLinks === 0) {
|
|
2650
2413
|
const count = readdirSync5(agentsDir).length;
|
|
2651
|
-
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count}
|
|
2414
|
+
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2652
2415
|
} else {
|
|
2653
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks}
|
|
2416
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2654
2417
|
warnings++;
|
|
2655
2418
|
if (options.fix) {
|
|
2656
2419
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2657
2420
|
fixes += fixed;
|
|
2658
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2421
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2659
2422
|
}
|
|
2660
2423
|
}
|
|
2661
2424
|
} else {
|
|
2662
|
-
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014
|
|
2425
|
+
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2663
2426
|
if (options.fix) {
|
|
2664
2427
|
mkdirSync6(agentsDir, { recursive: true });
|
|
2665
2428
|
fixes++;
|
|
2666
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2429
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2667
2430
|
}
|
|
2668
2431
|
}
|
|
2669
2432
|
console.log();
|
|
2670
2433
|
console.log(chalk7.bold(` ${t("doctor_config")}`));
|
|
2671
2434
|
const configFile = getConfigFileName(primaryTool);
|
|
2672
|
-
const configPath =
|
|
2435
|
+
const configPath = join14(process.cwd(), configFile);
|
|
2673
2436
|
if (existsSync12(configPath)) {
|
|
2674
|
-
console.log(chalk7.green(" \u2713") + ` ${configFile}
|
|
2437
|
+
console.log(chalk7.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2675
2438
|
} else {
|
|
2676
|
-
console.log(chalk7.dim(" \u2014") + ` ${configFile}
|
|
2439
|
+
console.log(chalk7.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2677
2440
|
}
|
|
2678
2441
|
console.log();
|
|
2679
2442
|
console.log(chalk7.dim(" " + "\u2500".repeat(46)));
|
|
2680
2443
|
if (options.fix && fixes > 0) {
|
|
2681
2444
|
console.log(
|
|
2682
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2445
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
|
|
2683
2446
|
);
|
|
2684
2447
|
} else {
|
|
2685
2448
|
console.log(
|
|
2686
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2449
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
|
|
2687
2450
|
);
|
|
2688
2451
|
}
|
|
2689
2452
|
console.log();
|
|
@@ -2693,7 +2456,7 @@ function checkBrokenLinks(dir) {
|
|
|
2693
2456
|
try {
|
|
2694
2457
|
const entries = readdirSync5(dir);
|
|
2695
2458
|
for (const entry of entries) {
|
|
2696
|
-
const fullPath =
|
|
2459
|
+
const fullPath = join14(dir, entry);
|
|
2697
2460
|
try {
|
|
2698
2461
|
const stats = lstatSync3(fullPath);
|
|
2699
2462
|
if (stats.isSymbolicLink()) {
|
|
@@ -2714,7 +2477,7 @@ function fixBrokenLinks(dir) {
|
|
|
2714
2477
|
try {
|
|
2715
2478
|
const entries = readdirSync5(dir);
|
|
2716
2479
|
for (const entry of entries) {
|
|
2717
|
-
const fullPath =
|
|
2480
|
+
const fullPath = join14(dir, entry);
|
|
2718
2481
|
try {
|
|
2719
2482
|
const stats = lstatSync3(fullPath);
|
|
2720
2483
|
if (stats.isSymbolicLink()) {
|
|
@@ -2778,29 +2541,31 @@ async function installCommand(names, options) {
|
|
|
2778
2541
|
try {
|
|
2779
2542
|
const scanRepoManager = createRepositoryManager();
|
|
2780
2543
|
await scanRepoManager.ensureRepoAndScan();
|
|
2781
|
-
scanSpinner2.succeed("
|
|
2544
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2782
2545
|
} catch {
|
|
2783
|
-
scanSpinner2.warn("
|
|
2546
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2784
2547
|
}
|
|
2785
2548
|
let tool2;
|
|
2786
2549
|
if (options.tool) {
|
|
2787
|
-
tool2 = options.tool;
|
|
2550
|
+
tool2 = validateTool(options.tool);
|
|
2788
2551
|
} else {
|
|
2789
2552
|
const detected = await selectToolWithDetection();
|
|
2790
|
-
if (detected === "back") return;
|
|
2553
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2791
2554
|
tool2 = detected;
|
|
2792
2555
|
}
|
|
2556
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2793
2557
|
let step = 1;
|
|
2794
2558
|
while (true) {
|
|
2795
2559
|
switch (step) {
|
|
2796
2560
|
case 1: {
|
|
2797
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2561
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2798
2562
|
if (selectedSkills === "back") {
|
|
2799
2563
|
const redetected = await selectToolWithDetection();
|
|
2800
|
-
if (redetected === "back") return;
|
|
2564
|
+
if (redetected === "back" || redetected === "cancel") return;
|
|
2801
2565
|
tool2 = redetected;
|
|
2802
2566
|
break;
|
|
2803
2567
|
}
|
|
2568
|
+
if (selectedSkills === "cancel") return;
|
|
2804
2569
|
if (selectedSkills.length === 0) {
|
|
2805
2570
|
logger.info(t("install_no_skill_selected"));
|
|
2806
2571
|
return;
|
|
@@ -2810,9 +2575,8 @@ async function installCommand(names, options) {
|
|
|
2810
2575
|
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2811
2576
|
spinner.start();
|
|
2812
2577
|
const repoPath = await repoManager.ensureRepo();
|
|
2813
|
-
spinner.succeed("
|
|
2814
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)}
|
|
2815
|
-
const level2 = options.level || "project";
|
|
2578
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2579
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(names.length)))}...`);
|
|
2816
2580
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2817
2581
|
let successCount = 0;
|
|
2818
2582
|
let failCount = 0;
|
|
@@ -2830,7 +2594,7 @@ async function installCommand(names, options) {
|
|
|
2830
2594
|
}
|
|
2831
2595
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2832
2596
|
logger.blank();
|
|
2833
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
2597
|
+
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)))}`);
|
|
2834
2598
|
logger.blank();
|
|
2835
2599
|
logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
|
|
2836
2600
|
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
|
|
@@ -2845,9 +2609,9 @@ async function installCommand(names, options) {
|
|
|
2845
2609
|
try {
|
|
2846
2610
|
const scanRepoManager = createRepositoryManager();
|
|
2847
2611
|
await scanRepoManager.ensureRepoAndScan();
|
|
2848
|
-
scanSpinner.succeed("
|
|
2612
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2849
2613
|
} catch {
|
|
2850
|
-
scanSpinner.warn("
|
|
2614
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2851
2615
|
}
|
|
2852
2616
|
const plugins = [];
|
|
2853
2617
|
const skills = [];
|
|
@@ -2864,22 +2628,22 @@ async function installCommand(names, options) {
|
|
|
2864
2628
|
}
|
|
2865
2629
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2866
2630
|
const allPlugins = getAllPlugins();
|
|
2867
|
-
logger.info("
|
|
2631
|
+
logger.info(t("available_plugins") + ":");
|
|
2868
2632
|
for (const p of allPlugins) {
|
|
2869
2633
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2870
2634
|
}
|
|
2871
|
-
logger.info(
|
|
2635
|
+
logger.info(`${t("available_skills")}: ${chalk9.cyan("install-helper install --list")}`);
|
|
2872
2636
|
return;
|
|
2873
2637
|
}
|
|
2874
2638
|
let tool;
|
|
2875
2639
|
if (options.tool) {
|
|
2876
|
-
tool = options.tool;
|
|
2640
|
+
tool = validateTool(options.tool);
|
|
2877
2641
|
} else {
|
|
2878
2642
|
const detected = await selectToolWithDetection();
|
|
2879
|
-
if (detected === "back") return;
|
|
2643
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2880
2644
|
tool = detected;
|
|
2881
2645
|
}
|
|
2882
|
-
const level = options.level
|
|
2646
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2883
2647
|
if (plugins.length > 0) {
|
|
2884
2648
|
const configRoot = getConfigRoot(tool, level);
|
|
2885
2649
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2940,7 +2704,7 @@ async function installCommand(names, options) {
|
|
|
2940
2704
|
spinner.start();
|
|
2941
2705
|
const repoPath = await repoManager.ensureRepo();
|
|
2942
2706
|
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2943
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)}
|
|
2707
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(skills.length)))}...`);
|
|
2944
2708
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2945
2709
|
let successCount = 0;
|
|
2946
2710
|
let failCount = 0;
|
|
@@ -2957,7 +2721,7 @@ async function installCommand(names, options) {
|
|
|
2957
2721
|
}
|
|
2958
2722
|
}
|
|
2959
2723
|
logger.blank();
|
|
2960
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
2724
|
+
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)))}`);
|
|
2961
2725
|
logger.blank();
|
|
2962
2726
|
logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
2963
2727
|
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
|
|
@@ -2995,8 +2759,8 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2995
2759
|
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
|
|
2996
2760
|
import chalk10 from "chalk";
|
|
2997
2761
|
async function uninstallCommand(names, options) {
|
|
2998
|
-
const tool = options.tool
|
|
2999
|
-
const level = options.level
|
|
2762
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
2763
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
3000
2764
|
const plugins = [];
|
|
3001
2765
|
const skills = [];
|
|
3002
2766
|
for (const name of names) {
|
|
@@ -3029,21 +2793,25 @@ async function uninstallCommand(names, options) {
|
|
|
3029
2793
|
if (existsSync13(filePath) || isSymlink3(filePath)) {
|
|
3030
2794
|
unlinkSync5(filePath);
|
|
3031
2795
|
const name = filePath.split("/").pop() || filePath;
|
|
3032
|
-
logger.step(`
|
|
2796
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
3033
2797
|
removedFiles++;
|
|
3034
2798
|
}
|
|
3035
2799
|
} catch {
|
|
3036
2800
|
}
|
|
3037
2801
|
}
|
|
2802
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3038
2803
|
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
3039
2804
|
for (const dirPath of sortedDirs) {
|
|
3040
2805
|
try {
|
|
2806
|
+
if (dirPath === configRoot) {
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
3041
2809
|
if (existsSync13(dirPath)) {
|
|
3042
2810
|
const entries = readdirSync6(dirPath);
|
|
3043
2811
|
if (entries.length === 0) {
|
|
3044
2812
|
rmdirSync2(dirPath);
|
|
3045
2813
|
const name = dirPath.split("/").pop() || dirPath;
|
|
3046
|
-
logger.step(`
|
|
2814
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
3047
2815
|
removedDirs++;
|
|
3048
2816
|
}
|
|
3049
2817
|
}
|
|
@@ -3052,7 +2820,6 @@ async function uninstallCommand(names, options) {
|
|
|
3052
2820
|
}
|
|
3053
2821
|
deleteRecord(plugin.id);
|
|
3054
2822
|
removeInstalledPlugin(plugin.id);
|
|
3055
|
-
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3056
2823
|
const backups = findBackups(configRoot);
|
|
3057
2824
|
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
3058
2825
|
if (otherBackups.length > 0) {
|
|
@@ -3072,13 +2839,13 @@ async function uninstallCommand(names, options) {
|
|
|
3072
2839
|
}
|
|
3073
2840
|
const totalRemoved = removedFiles + removedDirs;
|
|
3074
2841
|
if (totalRemoved > 0) {
|
|
3075
|
-
logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08
|
|
2842
|
+
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`);
|
|
3076
2843
|
} else {
|
|
3077
2844
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3078
2845
|
}
|
|
3079
2846
|
}
|
|
3080
2847
|
if (skills.length > 0) {
|
|
3081
|
-
logger.info(`${t("uninstall_skills_in_progress")} ${skills.length}
|
|
2848
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
3082
2849
|
const results = await uninstallSkills(skills, tool, level);
|
|
3083
2850
|
let successCount = 0;
|
|
3084
2851
|
let failCount = 0;
|
|
@@ -3090,7 +2857,7 @@ async function uninstallCommand(names, options) {
|
|
|
3090
2857
|
}
|
|
3091
2858
|
}
|
|
3092
2859
|
logger.blank();
|
|
3093
|
-
logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(
|
|
2860
|
+
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)))}`);
|
|
3094
2861
|
logger.blank();
|
|
3095
2862
|
}
|
|
3096
2863
|
}
|
|
@@ -3107,13 +2874,13 @@ import chalk11 from "chalk";
|
|
|
3107
2874
|
async function updateCommand(pluginNames, options) {
|
|
3108
2875
|
let tool;
|
|
3109
2876
|
if (options.tool) {
|
|
3110
|
-
tool = options.tool;
|
|
2877
|
+
tool = validateTool(options.tool);
|
|
3111
2878
|
} else {
|
|
3112
2879
|
const detected = await selectToolWithDetection();
|
|
3113
|
-
if (detected === "back") return;
|
|
2880
|
+
if (detected === "back" || detected === "cancel") return;
|
|
3114
2881
|
tool = detected;
|
|
3115
2882
|
}
|
|
3116
|
-
const level = options.level
|
|
2883
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
3117
2884
|
let pluginsToUpdate = [];
|
|
3118
2885
|
if (pluginNames.length === 0) {
|
|
3119
2886
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -3121,7 +2888,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3121
2888
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
3122
2889
|
if (pluginsToUpdate.length === 0) {
|
|
3123
2890
|
logger.info(t("update_no_plugins"));
|
|
3124
|
-
logger.info(
|
|
2891
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk11.cyan("install-helper install <plugin>")));
|
|
3125
2892
|
return;
|
|
3126
2893
|
}
|
|
3127
2894
|
} else {
|
|
@@ -3185,7 +2952,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3185
2952
|
// src/commands/info.ts
|
|
3186
2953
|
import chalk12 from "chalk";
|
|
3187
2954
|
import { existsSync as existsSync14, readdirSync as readdirSync7 } from "fs";
|
|
3188
|
-
import { join as
|
|
2955
|
+
import { join as join15 } from "path";
|
|
3189
2956
|
async function infoCommand(pluginName) {
|
|
3190
2957
|
const plugin = findPlugin(pluginName);
|
|
3191
2958
|
if (!plugin) {
|
|
@@ -3209,11 +2976,11 @@ async function infoCommand(pluginName) {
|
|
|
3209
2976
|
try {
|
|
3210
2977
|
const repoManager = createRepositoryManager();
|
|
3211
2978
|
const repoPath = await repoManager.ensureRepo();
|
|
3212
|
-
const quickstartPath =
|
|
2979
|
+
const quickstartPath = join15(repoPath, plugin.dir, "quickstart.md");
|
|
3213
2980
|
if (existsSync14(quickstartPath)) {
|
|
3214
2981
|
console.log(` ${chalk12.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
3215
2982
|
}
|
|
3216
|
-
const agentsDir =
|
|
2983
|
+
const agentsDir = join15(repoPath, plugin.dir, "agents");
|
|
3217
2984
|
if (existsSync14(agentsDir)) {
|
|
3218
2985
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3219
2986
|
if (agentFiles.length > 0) {
|
|
@@ -3272,7 +3039,7 @@ function createCLI() {
|
|
|
3272
3039
|
setLanguage(config.language);
|
|
3273
3040
|
}
|
|
3274
3041
|
const program2 = new Command();
|
|
3275
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("
|
|
3042
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("1.0.3");
|
|
3276
3043
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3277
3044
|
await initCommand();
|
|
3278
3045
|
});
|
|
@@ -3309,7 +3076,7 @@ function createCLI() {
|
|
|
3309
3076
|
// src/index.ts
|
|
3310
3077
|
process.on("uncaughtException", (error) => {
|
|
3311
3078
|
if (error.name === "ExitPromptError") {
|
|
3312
|
-
console.log("\n
|
|
3079
|
+
console.log("\n" + t("init_cancelled"));
|
|
3313
3080
|
process.exit(0);
|
|
3314
3081
|
}
|
|
3315
3082
|
throw error;
|