@cannbot-ai/install-helper 0.0.2-beta.7 → 0.0.2-beta.71
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 +60 -123
- package/bin/install-helper.js +59 -0
- package/bin/package.json +3 -0
- package/dist/config/repository.yaml +28 -0
- package/dist/index.js +2358 -1026
- package/dist/index.js.map +1 -1
- package/dist/install.ps1 +241 -0
- package/dist/locales/en_US.json +187 -0
- package/dist/locales/zh_CN.json +187 -0
- package/dist/plugins.d/_defaults.yml +4 -0
- package/dist/plugins.d/catlass-op-generator.yml +18 -0
- package/dist/plugins.d/model-infer-optimize.yml +19 -0
- package/dist/plugins.d/ops-code-reviewer.yml +14 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +18 -0
- package/dist/plugins.d/ops-direct-invoke.yml +20 -0
- package/dist/plugins.d/ops-registry-invoke.yml +24 -0
- package/dist/plugins.d/pypto-op-orchestrator.yml +18 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +20 -0
- package/dist/plugins.d/torch-compile.yml +14 -0
- package/dist/plugins.d/triton-op-generator.yml +14 -0
- package/package.json +17 -6
package/dist/index.js
CHANGED
|
@@ -5,410 +5,43 @@ import { Command } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import { select as select4, Separator as Separator3 } from "@inquirer/prompts";
|
|
8
|
-
import
|
|
8
|
+
import chalk7 from "chalk";
|
|
9
9
|
|
|
10
10
|
// src/ui/wizard.ts
|
|
11
|
-
import { select,
|
|
12
|
-
import
|
|
11
|
+
import { select, Separator } from "@inquirer/prompts";
|
|
12
|
+
import chalk3 from "chalk";
|
|
13
13
|
|
|
14
14
|
// src/core/registry.ts
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
displayName: "AscendC Kernel \u76F4\u8C03",
|
|
20
|
-
script: "init.sh",
|
|
21
|
-
aliases: ["ops-direct", "ascendc-direct", "direct", "kernel"],
|
|
22
|
-
skills: 16,
|
|
23
|
-
agents: 3,
|
|
24
|
-
description: "Ascend C Kernel <<<>>> \u76F4\u8C03\u7B97\u5B50\u5F00\u53D1\u5168\u6D41\u7A0B"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: "ops-direct-invoke-flash",
|
|
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"
|
|
115
|
-
}
|
|
116
|
-
];
|
|
117
|
-
function findPlugin(query) {
|
|
118
|
-
const normalized = query.toLowerCase().trim();
|
|
119
|
-
const exactMatch = PLUGIN_REGISTRY.find(
|
|
120
|
-
(p) => p.id === normalized || p.id === query.trim()
|
|
121
|
-
);
|
|
122
|
-
if (exactMatch) return exactMatch;
|
|
123
|
-
const aliasMatch = PLUGIN_REGISTRY.find(
|
|
124
|
-
(p) => p.aliases.some((a) => a === normalized)
|
|
125
|
-
);
|
|
126
|
-
if (aliasMatch) return aliasMatch;
|
|
127
|
-
const prefixMatch = PLUGIN_REGISTRY.find((p) => p.id.startsWith(normalized));
|
|
128
|
-
if (prefixMatch) return prefixMatch;
|
|
129
|
-
return void 0;
|
|
130
|
-
}
|
|
131
|
-
function getAllPlugins() {
|
|
132
|
-
return PLUGIN_REGISTRY;
|
|
133
|
-
}
|
|
134
|
-
function getPluginById(id) {
|
|
135
|
-
return PLUGIN_REGISTRY.find((p) => p.id === id);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// src/core/detector.ts
|
|
139
|
-
import { execa } from "execa";
|
|
140
|
-
import { existsSync as existsSync2 } from "fs";
|
|
141
|
-
import { homedir as homedir2 } from "os";
|
|
142
|
-
import { join as join2 } from "path";
|
|
15
|
+
import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
16
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
17
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18
|
+
import { parse as parseYaml } from "yaml";
|
|
143
19
|
|
|
144
|
-
// src/utils/
|
|
145
|
-
import
|
|
146
|
-
import
|
|
147
|
-
import { existsSync } from "fs";
|
|
148
|
-
function detectTraeVariant() {
|
|
149
|
-
const home = homedir();
|
|
150
|
-
if (existsSync(join(home, ".trae-cn"))) return "ide";
|
|
151
|
-
if (existsSync(join(home, ".marscode"))) return "plugin";
|
|
152
|
-
if (existsSync(join(home, ".traecli"))) return "cli";
|
|
153
|
-
return "unknown";
|
|
154
|
-
}
|
|
155
|
-
function getConfigRoot(tool, level, base) {
|
|
156
|
-
const home = homedir();
|
|
157
|
-
if (level === "global") {
|
|
158
|
-
switch (tool) {
|
|
159
|
-
case "opencode":
|
|
160
|
-
return join(home, ".config", "opencode");
|
|
161
|
-
case "claude":
|
|
162
|
-
return join(home, ".claude");
|
|
163
|
-
case "trae": {
|
|
164
|
-
const variant = detectTraeVariant();
|
|
165
|
-
switch (variant) {
|
|
166
|
-
case "plugin":
|
|
167
|
-
return join(home, ".marscode");
|
|
168
|
-
case "cli":
|
|
169
|
-
return join(home, ".traecli");
|
|
170
|
-
default:
|
|
171
|
-
return join(home, ".trae-cn");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
case "cursor":
|
|
175
|
-
return join(home, ".cursor");
|
|
176
|
-
case "copilot":
|
|
177
|
-
return join(home, ".copilot");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
const baseDir = base || process.cwd();
|
|
181
|
-
switch (tool) {
|
|
182
|
-
case "opencode":
|
|
183
|
-
return join(baseDir, ".opencode");
|
|
184
|
-
case "claude":
|
|
185
|
-
return join(baseDir, ".claude");
|
|
186
|
-
case "trae": {
|
|
187
|
-
const variant = detectTraeVariant();
|
|
188
|
-
switch (variant) {
|
|
189
|
-
case "plugin":
|
|
190
|
-
return join(baseDir, ".marscode");
|
|
191
|
-
case "cli":
|
|
192
|
-
return join(baseDir, ".traecli");
|
|
193
|
-
default:
|
|
194
|
-
return join(baseDir, ".trae");
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
case "cursor":
|
|
198
|
-
return join(baseDir, ".cursor");
|
|
199
|
-
case "copilot":
|
|
200
|
-
return join(baseDir, ".github");
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function getConfigFileName(tool) {
|
|
204
|
-
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
|
-
}
|
|
206
|
-
function getSkillsDir(configRoot) {
|
|
207
|
-
return join(configRoot, "skills");
|
|
208
|
-
}
|
|
209
|
-
function getAgentsDir(configRoot) {
|
|
210
|
-
return join(configRoot, "agents");
|
|
211
|
-
}
|
|
212
|
-
function getManifestPath(configRoot) {
|
|
213
|
-
return join(configRoot, "cannbot-manifest.json");
|
|
214
|
-
}
|
|
215
|
-
function getCannbotConfigDir() {
|
|
216
|
-
return join(homedir(), ".cannbot");
|
|
217
|
-
}
|
|
218
|
-
function getCannbotRepoPath() {
|
|
219
|
-
return join(getCannbotConfigDir(), "repo");
|
|
220
|
-
}
|
|
20
|
+
// src/utils/logger.ts
|
|
21
|
+
import chalk2 from "chalk";
|
|
22
|
+
import ora from "ora";
|
|
221
23
|
|
|
222
|
-
// src/
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const output = result.stdout.trim();
|
|
227
|
-
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
228
|
-
return match ? match[1] : output.split("\n")[0];
|
|
229
|
-
} catch {
|
|
230
|
-
return void 0;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
async function getCommandPath(cmd) {
|
|
234
|
-
try {
|
|
235
|
-
const result = await execa("sh", ["-c", `command -v ${cmd}`], { timeout: 5e3 });
|
|
236
|
-
return result.stdout.trim();
|
|
237
|
-
} catch {
|
|
238
|
-
return void 0;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
async function detectOpenCode() {
|
|
242
|
-
const path = await getCommandPath("opencode");
|
|
243
|
-
if (!path) return void 0;
|
|
244
|
-
const version = await getCommandVersion("opencode");
|
|
245
|
-
return { name: "opencode", version, path };
|
|
246
|
-
}
|
|
247
|
-
async function detectClaude() {
|
|
248
|
-
const path = await getCommandPath("claude");
|
|
249
|
-
if (!path) return void 0;
|
|
250
|
-
const version = await getCommandVersion("claude");
|
|
251
|
-
return { name: "claude", version, path };
|
|
252
|
-
}
|
|
253
|
-
async function detectTrae() {
|
|
254
|
-
const home = homedir2();
|
|
255
|
-
const variant = detectTraeVariant();
|
|
256
|
-
let path;
|
|
257
|
-
if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
|
|
258
|
-
path = join2(home, ".trae-cn");
|
|
259
|
-
} else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
|
|
260
|
-
path = join2(home, ".marscode");
|
|
261
|
-
} else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
|
|
262
|
-
path = join2(home, ".traecli");
|
|
263
|
-
} else if (variant === "unknown") {
|
|
264
|
-
const cmdPath = await getCommandPath("trae");
|
|
265
|
-
if (cmdPath) path = cmdPath;
|
|
266
|
-
}
|
|
267
|
-
if (!path) return void 0;
|
|
268
|
-
return { name: "trae", version: variant, path };
|
|
269
|
-
}
|
|
270
|
-
async function detectCursor() {
|
|
271
|
-
const path = await getCommandPath("cursor");
|
|
272
|
-
if (path) {
|
|
273
|
-
const version = await getCommandVersion("cursor");
|
|
274
|
-
return { name: "cursor", version, path };
|
|
275
|
-
}
|
|
276
|
-
if (process.platform === "darwin") {
|
|
277
|
-
const appPath = "/Applications/Cursor.app";
|
|
278
|
-
if (existsSync2(appPath)) {
|
|
279
|
-
return { name: "cursor", path: appPath };
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return void 0;
|
|
283
|
-
}
|
|
284
|
-
async function detectCopilot() {
|
|
285
|
-
const path = await getCommandPath("gh");
|
|
286
|
-
if (!path) return void 0;
|
|
287
|
-
try {
|
|
288
|
-
await execa("gh", ["extension", "list"], { timeout: 5e3 });
|
|
289
|
-
return { name: "copilot", path };
|
|
290
|
-
} catch {
|
|
291
|
-
return void 0;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
async function detectTools() {
|
|
295
|
-
const detectors = [
|
|
296
|
-
detectOpenCode,
|
|
297
|
-
detectClaude,
|
|
298
|
-
detectTrae,
|
|
299
|
-
detectCursor,
|
|
300
|
-
detectCopilot
|
|
301
|
-
];
|
|
302
|
-
const results = await Promise.allSettled(detectors.map((d) => d()));
|
|
303
|
-
const tools = [];
|
|
304
|
-
for (const result of results) {
|
|
305
|
-
if (result.status === "fulfilled" && result.value) {
|
|
306
|
-
tools.push(result.value);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return tools;
|
|
310
|
-
}
|
|
311
|
-
function getToolDisplayName(tool) {
|
|
312
|
-
const names = {
|
|
313
|
-
opencode: "OpenCode",
|
|
314
|
-
claude: "Claude Code",
|
|
315
|
-
trae: "Trae",
|
|
316
|
-
cursor: "Cursor",
|
|
317
|
-
copilot: "GitHub Copilot"
|
|
318
|
-
};
|
|
319
|
-
return names[tool];
|
|
320
|
-
}
|
|
321
|
-
function getAllTools() {
|
|
322
|
-
return ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
323
|
-
}
|
|
24
|
+
// src/utils/i18n.ts
|
|
25
|
+
import { readFileSync, existsSync } from "fs";
|
|
26
|
+
import { join, dirname } from "path";
|
|
27
|
+
import { fileURLToPath } from "url";
|
|
324
28
|
|
|
325
|
-
// src/
|
|
326
|
-
|
|
327
|
-
import { join as join3 } from "path";
|
|
328
|
-
function readManifest(configRoot) {
|
|
329
|
-
const manifestPath = getManifestPath(configRoot);
|
|
330
|
-
if (existsSync3(manifestPath)) {
|
|
331
|
-
try {
|
|
332
|
-
const content = readFileSync(manifestPath, "utf-8");
|
|
333
|
-
return JSON.parse(content);
|
|
334
|
-
} catch {
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
function readAllManifests(configRoot) {
|
|
340
|
-
const manifests = [];
|
|
341
|
-
const plugins = getAllPlugins();
|
|
342
|
-
const standardPath = getManifestPath(configRoot);
|
|
343
|
-
if (existsSync3(standardPath)) {
|
|
344
|
-
try {
|
|
345
|
-
const content = readFileSync(standardPath, "utf-8");
|
|
346
|
-
manifests.push(JSON.parse(content));
|
|
347
|
-
} catch {
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
|
|
352
|
-
if (existsSync3(pluginManifestPath)) {
|
|
353
|
-
try {
|
|
354
|
-
const content = readFileSync(pluginManifestPath, "utf-8");
|
|
355
|
-
manifests.push(JSON.parse(content));
|
|
356
|
-
} catch {
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return manifests;
|
|
361
|
-
}
|
|
362
|
-
function scanInstalled() {
|
|
363
|
-
const plugins = getAllPlugins();
|
|
364
|
-
const installed = [];
|
|
365
|
-
const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
366
|
-
const levels = ["project", "global"];
|
|
367
|
-
for (const tool of tools) {
|
|
368
|
-
for (const level of levels) {
|
|
369
|
-
const configRoot = getConfigRoot(tool, level);
|
|
370
|
-
const manifests = readAllManifests(configRoot);
|
|
371
|
-
for (const manifest of manifests) {
|
|
372
|
-
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
|
-
if (!plugin) continue;
|
|
374
|
-
const alreadyAdded = installed.some(
|
|
375
|
-
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
|
-
);
|
|
377
|
-
if (alreadyAdded) continue;
|
|
378
|
-
installed.push({
|
|
379
|
-
id: plugin.id,
|
|
380
|
-
displayName: plugin.displayName,
|
|
381
|
-
tool,
|
|
382
|
-
level,
|
|
383
|
-
skillsCount: manifest.installed_skills?.length || 0,
|
|
384
|
-
agentsCount: manifest.installed_agents?.length || 0,
|
|
385
|
-
installTime: manifest.install_time,
|
|
386
|
-
configRoot
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return installed;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// src/utils/i18n.ts
|
|
395
|
-
var zh_CN = {
|
|
29
|
+
// src/locales/zh_CN.json
|
|
30
|
+
var zh_CN_default = {
|
|
396
31
|
wizard_title: "CANNBot Install Helper",
|
|
397
32
|
wizard_detect: "\u6B63\u5728\u68C0\u6D4B\u5DF2\u5B89\u88C5\u7684 AI \u7F16\u7A0B\u5DE5\u5177...",
|
|
398
33
|
wizard_select_tool: "\u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
399
34
|
wizard_select_level: "\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
|
|
400
|
-
|
|
35
|
+
wizard_select_plugin: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
401
36
|
wizard_confirm: "\u786E\u8BA4\u5B89\u88C5",
|
|
402
37
|
wizard_detected_tool: "\u68C0\u6D4B\u5230",
|
|
403
38
|
wizard_confirm_tool: "\u4F7F\u7528",
|
|
404
39
|
wizard_last_used: "\u4E0A\u6B21\u4F7F\u7528",
|
|
405
40
|
wizard_already_installed: "\u5DF2\u5B89\u88C5",
|
|
406
|
-
install_cloning: "\u6B63\u5728\u514B\u9686 cannbot-skills \u4ED3\u5E93...",
|
|
407
41
|
install_repo_ready: "\u4ED3\u5E93\u5C31\u7EEA",
|
|
408
42
|
install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
409
43
|
install_done: "\u5B89\u88C5\u5B8C\u6210",
|
|
410
44
|
install_success: "\u6210\u529F",
|
|
411
|
-
install_failed: "\u5931\u8D25",
|
|
412
45
|
install_already_installed: "\u5DF2\u5B89\u88C5",
|
|
413
46
|
install_reinstall_confirm: "\u662F\u5426\u91CD\u65B0\u5B89\u88C5\uFF08\u8986\u76D6\uFF09",
|
|
414
47
|
install_skip: "\u8DF3\u8FC7",
|
|
@@ -418,7 +51,6 @@ var zh_CN = {
|
|
|
418
51
|
install_enhanced_more: "\u66F4\u591A",
|
|
419
52
|
install_enhanced_check: "\u68C0\u67E5",
|
|
420
53
|
install_enhanced_docs: "\u6587\u6863",
|
|
421
|
-
update_title: "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 Skills",
|
|
422
54
|
update_updating: "\u6B63\u5728\u66F4\u65B0",
|
|
423
55
|
update_done: "\u66F4\u65B0\u5B8C\u6210",
|
|
424
56
|
update_no_plugins: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
@@ -428,11 +60,8 @@ var zh_CN = {
|
|
|
428
60
|
doctor_links: "\u94FE\u63A5\u5B8C\u6574\u6027",
|
|
429
61
|
doctor_config: "\u914D\u7F6E\u6587\u4EF6",
|
|
430
62
|
doctor_result: "\u7ED3\u679C",
|
|
431
|
-
doctor_fix_title: "\u81EA\u52A8\u4FEE\u590D",
|
|
432
63
|
doctor_fix_cleaning: "\u6E05\u7406\u5931\u6548\u94FE\u63A5",
|
|
433
64
|
doctor_fix_rebuilding: "\u91CD\u5EFA\u76EE\u5F55",
|
|
434
|
-
doctor_fix_done: "\u4FEE\u590D\u5B8C\u6210",
|
|
435
|
-
info_title: "\u63D2\u4EF6\u8BE6\u60C5",
|
|
436
65
|
info_description: "\u63CF\u8FF0",
|
|
437
66
|
info_skills: "Skills",
|
|
438
67
|
info_agents: "Agents",
|
|
@@ -447,18 +76,29 @@ var zh_CN = {
|
|
|
447
76
|
list_id: "\u5E8F\u53F7",
|
|
448
77
|
list_name: "\u573A\u666F\u540D",
|
|
449
78
|
list_status: "\u72B6\u6001",
|
|
450
|
-
|
|
451
|
-
list_agents: "Agents",
|
|
79
|
+
list_description: "\u63CF\u8FF0",
|
|
452
80
|
status_installed: "\u5DF2\u88C5",
|
|
453
81
|
status_not_installed: "\u672A\u88C5",
|
|
454
82
|
error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
455
83
|
error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
84
|
+
error_invalid_tool: "\u6682\u4E0D\u652F\u6301\u7684 AI \u5DE5\u5177: {tool}\n\u652F\u6301\u7684\u5DE5\u5177: {tools}",
|
|
85
|
+
error_invalid_level: "\u6682\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u7EA7\u522B: {level}\n\u652F\u6301\u7684\u7EA7\u522B: {levels}",
|
|
86
|
+
error_skill_not_found: "\u672A\u5728\u6CE8\u518C\u8868\u4E2D\u627E\u5230 Skill",
|
|
87
|
+
error_source_not_found: "\u672A\u627E\u5230\u6E90\u6587\u4EF6: {path}",
|
|
88
|
+
error_script_not_found: "\u672A\u627E\u5230\u5B89\u88C5\u811A\u672C: {path}",
|
|
89
|
+
error_unknown: "\u672A\u77E5\u9519\u8BEF",
|
|
90
|
+
error_not_installed: "\u672A\u5B89\u88C5",
|
|
91
|
+
error_no_shell: "\u672A\u627E\u5230 bash \u6216 sh\uFF0C\u65E0\u6CD5\u6267\u884C\u5B89\u88C5\u811A\u672C\u3002\u8BF7\u5B89\u88C5 Git Bash \u6216 WSL",
|
|
92
|
+
error_symlink_windows: "\u521B\u5EFA\u7B26\u53F7\u94FE\u63A5\u5931\u8D25\uFF0C\u8BF7\u4EE5\u7BA1\u7406\u5458\u8EAB\u4EFD\u8FD0\u884C\u6216\u5F00\u542F Windows \u5F00\u53D1\u8005\u6A21\u5F0F",
|
|
459
93
|
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
460
94
|
skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
|
|
461
95
|
skill_already_installed: "\u5DF2\u5B89\u88C5",
|
|
96
|
+
skill_select_all: "\u5168\u9009\u6240\u6709 Skills\uFF08\u5171 {count} \u4E2A\uFF09",
|
|
97
|
+
skill_select_all_category: "\u5168\u9009\u5F53\u524D\u7C7B\u522B",
|
|
98
|
+
skill_continue_or_install: "\u5DF2\u9009 {count} \u4E2A Skills\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
99
|
+
skill_continue_select: "\u7EE7\u7EED\u9009\u62E9\u5176\u4ED6\u7C7B\u522B",
|
|
100
|
+
skill_confirm_install_count: "\u786E\u8BA4\u5B89\u88C5\uFF08\u5DF2\u9009 {count} \u4E2A Skills\uFF09",
|
|
101
|
+
skill_all_install_progress: "\u6B63\u5728\u5B89\u88C5\u5168\u90E8 {count} \u4E2A Skills...",
|
|
462
102
|
skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
463
103
|
skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
|
|
464
104
|
skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
|
|
@@ -489,7 +129,7 @@ var zh_CN = {
|
|
|
489
129
|
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
130
|
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
131
|
uninstall_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
492
|
-
|
|
132
|
+
uninstall_summary_format: "{name}\uFF08\u79FB\u9664 {files} \u4E2A\u6587\u4EF6\uFF0C{dirs} \u4E2A\u76EE\u5F55\uFF09",
|
|
493
133
|
uninstall_files_gone: "\u7684\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u5DF2\u6E05\u7406\u5B89\u88C5\u8BB0\u5F55",
|
|
494
134
|
uninstall_skills_in_progress: "\u6B63\u5728\u5378\u8F7D",
|
|
495
135
|
init_pkg_dir_warn: "\u5F53\u524D\u76EE\u5F55\u770B\u8D77\u6765\u662F install-helper \u5305\u76EE\u5F55",
|
|
@@ -508,25 +148,89 @@ var zh_CN = {
|
|
|
508
148
|
backup_only_uninstall: "\u4EC5\u5378\u8F7D\uFF08\u4E0D\u6062\u590D\uFF09",
|
|
509
149
|
backup_select_action: "\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
510
150
|
backup_restore_success: "\u5DF2\u6062\u590D\u914D\u7F6E",
|
|
511
|
-
backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD"
|
|
151
|
+
backup_created: "\u5DF2\u521B\u5EFA\u5907\u4EFD",
|
|
152
|
+
backup_failed: "\u5907\u4EFD\u521B\u5EFA\u5931\u8D25\uFF0C\u5B89\u88C5\u5DF2\u4E2D\u6B62\u4EE5\u4FDD\u62A4\u73B0\u6709\u914D\u7F6E",
|
|
153
|
+
loading_skills_list: "\u6B63\u5728\u52A0\u8F7D Skills \u5217\u8868...",
|
|
154
|
+
loading_skills_list_complete: "Skills \u5217\u8868\u52A0\u8F7D\u5B8C\u6210",
|
|
155
|
+
loading_plugin_data: "\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u6570\u636E...",
|
|
156
|
+
loading_plugin_data_complete: "\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210",
|
|
157
|
+
loading_skills_list_failed: "Skills \u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u6570\u636E",
|
|
158
|
+
repo_pull_failed: "\u65E0\u6CD5\u62C9\u53D6\u6700\u65B0\u5185\u5BB9\uFF0C\u4F7F\u7528\u7F13\u5B58\u6570\u636E",
|
|
159
|
+
repo_cloning: "\u9996\u6B21\u4F7F\u7528\uFF0C\u6B63\u5728\u514B\u9686 cannbot-skills \u4ED3\u5E93...",
|
|
160
|
+
config_corrupted: "\u914D\u7F6E\u6587\u4EF6\u5DF2\u635F\u574F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E",
|
|
161
|
+
record_corrupted: "\u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F\uFF1A{file}",
|
|
162
|
+
skill_record_corrupted: "Skill \u5B89\u88C5\u8BB0\u5F55\u5DF2\u635F\u574F",
|
|
163
|
+
plugin_yaml_error: "\u63D2\u4EF6\u914D\u7F6E\u89E3\u6790\u5931\u8D25\uFF1A{file}",
|
|
164
|
+
record_write_failed: "\u5B89\u88C5\u8BB0\u5F55\u5199\u5165\u5931\u8D25\uFF0C\u5378\u8F7D\u65F6\u53EF\u80FD\u65E0\u6CD5\u81EA\u52A8\u6E05\u7406",
|
|
165
|
+
install_config_failed: "\u914D\u7F6E\u6587\u4EF6\u5B89\u88C5\u5931\u8D25: {error}",
|
|
166
|
+
install_clone_failed: "git clone \u5931\u8D25: {url}\uFF0C\u8BF7\u624B\u52A8\u514B\u9686: git clone {url} {dir}",
|
|
167
|
+
install_link_config_failed: "\u94FE\u63A5 {name} \u5230\u914D\u7F6E\u76EE\u5F55\u5931\u8D25",
|
|
168
|
+
install_link_project_failed: "\u94FE\u63A5 {name} \u5230\u9879\u76EE\u76EE\u5F55\u5931\u8D25",
|
|
169
|
+
install_link_workflows_failed: "\u94FE\u63A5 workflows \u5230\u914D\u7F6E\u76EE\u5F55\u5931\u8D25",
|
|
170
|
+
install_skill_source_not_found: "Skill \u6E90\u76EE\u5F55\u672A\u627E\u5230: {dir}",
|
|
171
|
+
install_skill_not_found: "Skill \u672A\u627E\u5230: {name} (\u5728 {dir})",
|
|
172
|
+
install_skill_failed: "Skill \u5B89\u88C5\u5931\u8D25: {name}",
|
|
173
|
+
install_agent_not_found: "Agent \u672A\u627E\u5230: {name}",
|
|
174
|
+
install_agent_failed: "Agent \u5B89\u88C5\u5931\u8D25: {name}",
|
|
175
|
+
install_no_skill_selected: "\u672A\u9009\u62E9\u4EFB\u4F55 Skill",
|
|
176
|
+
install_to: "\u5B89\u88C5\u5230",
|
|
177
|
+
start_to_use: "\u542F\u52A8 {tool} \u5373\u53EF\u4F7F\u7528",
|
|
178
|
+
wizard_step_1_title: "[1/4] \u9009\u62E9 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
179
|
+
wizard_step_2_title: "[2/4] \u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E",
|
|
180
|
+
wizard_step_3_title: "[3/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
181
|
+
wizard_step_4_title: "[4/4] \u786E\u8BA4\u5B89\u88C5",
|
|
182
|
+
wizard_step_3_skill_title: "[3/4] \u9009\u62E9 Skill \u7C7B\u522B",
|
|
183
|
+
wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
|
|
184
|
+
install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
|
|
185
|
+
install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
|
|
186
|
+
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
187
|
+
skill_count_format: "{count} \u4E2A Skills",
|
|
188
|
+
result_success_format: "{count} \u6210\u529F",
|
|
189
|
+
result_failed_format: "{count} \u5931\u8D25",
|
|
190
|
+
uninstall_remove_label: "\u79FB\u9664",
|
|
191
|
+
uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
|
|
192
|
+
uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
|
|
193
|
+
available_plugins: "\u53EF\u7528\u63D2\u4EF6",
|
|
194
|
+
available_skills: "\u53EF\u7528 Skills",
|
|
195
|
+
skill_total_count: "\u5171 {count} \u4E2A Skills",
|
|
196
|
+
wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
|
|
197
|
+
wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
|
|
198
|
+
update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
|
|
199
|
+
hint_select_move: "\u79FB\u52A8",
|
|
200
|
+
hint_select_space: "\u7A7A\u683C",
|
|
201
|
+
hint_select_check: "\u52FE\u9009",
|
|
202
|
+
hint_select_confirm: "\u786E\u8BA4",
|
|
203
|
+
hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
|
|
204
|
+
doctor_not_installed: "\u672A\u5B89\u88C5",
|
|
205
|
+
doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
|
|
206
|
+
doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
207
|
+
doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
208
|
+
doctor_not_exist: "\u4E0D\u5B58\u5728",
|
|
209
|
+
doctor_created: "\u5DF2\u521B\u5EFA {path}",
|
|
210
|
+
doctor_config_exists: "\u5B58\u5728",
|
|
211
|
+
doctor_config_not_exist: "\u4E0D\u5B58\u5728",
|
|
212
|
+
doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {fixes} \u9879\u5DF2\u4FEE\u590D",
|
|
213
|
+
doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A",
|
|
214
|
+
count_suffix: "\u4E2A",
|
|
215
|
+
try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
|
|
512
216
|
};
|
|
513
|
-
|
|
217
|
+
|
|
218
|
+
// src/locales/en_US.json
|
|
219
|
+
var en_US_default = {
|
|
514
220
|
wizard_title: "CANNBot Install Helper",
|
|
515
221
|
wizard_detect: "Detecting installed AI coding tools...",
|
|
516
222
|
wizard_select_tool: "Select AI coding tool",
|
|
517
223
|
wizard_select_level: "Select install level",
|
|
518
|
-
|
|
224
|
+
wizard_select_plugin: "Select a scenario to install",
|
|
519
225
|
wizard_confirm: "Confirm installation",
|
|
520
226
|
wizard_detected_tool: "Detected",
|
|
521
227
|
wizard_confirm_tool: "Confirm using",
|
|
522
228
|
wizard_last_used: "last used",
|
|
523
229
|
wizard_already_installed: "installed",
|
|
524
|
-
install_cloning: "Cloning cannbot-skills repository...",
|
|
525
230
|
install_repo_ready: "Repository ready",
|
|
526
231
|
install_progress: "Installing",
|
|
527
232
|
install_done: "Installation complete",
|
|
528
233
|
install_success: "success",
|
|
529
|
-
install_failed: "failed",
|
|
530
234
|
install_already_installed: "already installed",
|
|
531
235
|
install_reinstall_confirm: "Reinstall (overwrite)",
|
|
532
236
|
install_skip: "skipped",
|
|
@@ -536,7 +240,6 @@ var en_US = {
|
|
|
536
240
|
install_enhanced_more: "More",
|
|
537
241
|
install_enhanced_check: "Check",
|
|
538
242
|
install_enhanced_docs: "Docs",
|
|
539
|
-
update_title: "Update installed Skills",
|
|
540
243
|
update_updating: "Updating",
|
|
541
244
|
update_done: "Update complete",
|
|
542
245
|
update_no_plugins: "No installed plugins",
|
|
@@ -546,11 +249,8 @@ var en_US = {
|
|
|
546
249
|
doctor_links: "Link Integrity",
|
|
547
250
|
doctor_config: "Configuration",
|
|
548
251
|
doctor_result: "Result",
|
|
549
|
-
doctor_fix_title: "Auto-fix",
|
|
550
252
|
doctor_fix_cleaning: "Cleaning broken links",
|
|
551
253
|
doctor_fix_rebuilding: "Rebuilding directories",
|
|
552
|
-
doctor_fix_done: "Fix complete",
|
|
553
|
-
info_title: "Plugin Details",
|
|
554
254
|
info_description: "Description",
|
|
555
255
|
info_skills: "Skills",
|
|
556
256
|
info_agents: "Agents",
|
|
@@ -565,18 +265,29 @@ var en_US = {
|
|
|
565
265
|
list_id: "#",
|
|
566
266
|
list_name: "Name",
|
|
567
267
|
list_status: "Status",
|
|
568
|
-
|
|
569
|
-
list_agents: "Agents",
|
|
268
|
+
list_description: "Description",
|
|
570
269
|
status_installed: "installed",
|
|
571
270
|
status_not_installed: "not installed",
|
|
572
271
|
error_tool_not_found: "No AI coding tool detected",
|
|
573
272
|
error_plugin_not_found: "Plugin not found",
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
273
|
+
error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
|
|
274
|
+
error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
|
|
275
|
+
error_skill_not_found: "Skill not found in registry",
|
|
276
|
+
error_source_not_found: "Source not found: {path}",
|
|
277
|
+
error_script_not_found: "Script not found: {path}",
|
|
278
|
+
error_unknown: "Unknown error",
|
|
279
|
+
error_not_installed: "Not installed",
|
|
280
|
+
error_no_shell: "bash or sh not found, cannot execute install script. Please install Git Bash or WSL",
|
|
281
|
+
error_symlink_windows: "Failed to create symlink, please run as administrator or enable Windows Developer Mode",
|
|
577
282
|
skill_select_category: "Select Skill category",
|
|
578
283
|
skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
|
|
579
284
|
skill_already_installed: "installed",
|
|
285
|
+
skill_select_all: "Select ALL Skills ({count} total)",
|
|
286
|
+
skill_select_all_category: "Select all in this category",
|
|
287
|
+
skill_continue_or_install: "{count} Skills selected, choose an action",
|
|
288
|
+
skill_continue_select: "Continue selecting from other categories",
|
|
289
|
+
skill_confirm_install_count: "Install now ({count} Skills selected)",
|
|
290
|
+
skill_all_install_progress: "Installing all {count} Skills...",
|
|
580
291
|
skill_install_progress: "Installing",
|
|
581
292
|
skill_install_done: "Skill installation complete",
|
|
582
293
|
skill_uninstall_done: "Skill uninstallation complete",
|
|
@@ -607,7 +318,7 @@ var en_US = {
|
|
|
607
318
|
uninstall_no_record: "No install record found, may not be installed or record is corrupted",
|
|
608
319
|
uninstall_no_record_hint: "For manual cleanup, delete related files in .opencode/ or .claude/ directory",
|
|
609
320
|
uninstall_in_progress: "Uninstalling",
|
|
610
|
-
|
|
321
|
+
uninstall_summary_format: "{name} (removed {files} files, {dirs} directories)",
|
|
611
322
|
uninstall_files_gone: " files no longer exist, install record cleaned",
|
|
612
323
|
uninstall_skills_in_progress: "Uninstalling",
|
|
613
324
|
init_pkg_dir_warn: "Current directory appears to be the install-helper package directory",
|
|
@@ -626,32 +337,200 @@ var en_US = {
|
|
|
626
337
|
backup_only_uninstall: "Uninstall only (no restore)",
|
|
627
338
|
backup_select_action: "Please select an action",
|
|
628
339
|
backup_restore_success: "Configuration restored",
|
|
629
|
-
backup_created: "Backup created"
|
|
340
|
+
backup_created: "Backup created",
|
|
341
|
+
backup_failed: "Backup creation failed, installation aborted to protect existing config",
|
|
342
|
+
loading_skills_list: "Loading Skills list...",
|
|
343
|
+
loading_skills_list_complete: "Skills list loaded",
|
|
344
|
+
loading_plugin_data: "Loading plugin data...",
|
|
345
|
+
loading_plugin_data_complete: "Plugin data loaded",
|
|
346
|
+
loading_skills_list_failed: "Failed to load Skills list, using built-in data",
|
|
347
|
+
repo_pull_failed: "Failed to pull latest updates, using cached data",
|
|
348
|
+
repo_cloning: "First-time setup: cloning cannbot-skills repository...",
|
|
349
|
+
config_corrupted: "Config file corrupted, using defaults",
|
|
350
|
+
record_corrupted: "Install record corrupted: {file}",
|
|
351
|
+
skill_record_corrupted: "Skill install record corrupted",
|
|
352
|
+
plugin_yaml_error: "Plugin config parse error: {file}",
|
|
353
|
+
record_write_failed: "Install record write failed, uninstall may not work properly",
|
|
354
|
+
install_config_failed: "Failed to install config file: {error}",
|
|
355
|
+
install_clone_failed: "git clone failed: {url}, clone manually: git clone {url} {dir}",
|
|
356
|
+
install_link_config_failed: "Failed to link {name} to config directory",
|
|
357
|
+
install_link_project_failed: "Failed to link {name} to project directory",
|
|
358
|
+
install_link_workflows_failed: "Failed to link workflows to config directory",
|
|
359
|
+
install_skill_source_not_found: "Skill source not found: {dir}",
|
|
360
|
+
install_skill_not_found: "Skill not found: {name} in {dir}",
|
|
361
|
+
install_skill_failed: "Failed to install skill: {name}",
|
|
362
|
+
install_agent_not_found: "Agent not found: {name}",
|
|
363
|
+
install_agent_failed: "Failed to install agent: {name}",
|
|
364
|
+
install_no_skill_selected: "No Skill selected",
|
|
365
|
+
install_to: "Install to",
|
|
366
|
+
start_to_use: "Start {tool} to use",
|
|
367
|
+
wizard_step_1_title: "[1/4] Select AI coding tool",
|
|
368
|
+
wizard_step_2_title: "[2/4] Select installation location",
|
|
369
|
+
wizard_step_3_title: "[3/4] Select plugins to install",
|
|
370
|
+
wizard_step_4_title: "[4/4] Confirm installation",
|
|
371
|
+
wizard_step_3_skill_title: "[3/4] Select Skill category",
|
|
372
|
+
wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
|
|
373
|
+
install_level_project: "Install to current directory (recommended, current project only)",
|
|
374
|
+
install_level_global: "Install globally (all projects)",
|
|
375
|
+
wizard_select_mode_title: "Select installation type",
|
|
376
|
+
skill_count_format: "{count} Skills",
|
|
377
|
+
result_success_format: "{count} succeeded",
|
|
378
|
+
result_failed_format: "{count} failed",
|
|
379
|
+
uninstall_remove_label: "Removed",
|
|
380
|
+
uninstall_clean_empty_dir: "Cleaned empty directory",
|
|
381
|
+
uninstall_skill_not_installed: "Not installed",
|
|
382
|
+
available_plugins: "Available plugins",
|
|
383
|
+
available_skills: "Available Skills",
|
|
384
|
+
skill_total_count: "Total {count} Skills",
|
|
385
|
+
wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
|
|
386
|
+
wizard_install_path: "Install path",
|
|
387
|
+
update_install_hint: "Use {cmd} to install plugins",
|
|
388
|
+
hint_select_move: "Navigate",
|
|
389
|
+
hint_select_space: "Space",
|
|
390
|
+
hint_select_check: "Toggle",
|
|
391
|
+
hint_select_confirm: "Confirm",
|
|
392
|
+
hint_checkbox_suffix: "Press enter without selection to go back",
|
|
393
|
+
doctor_not_installed: "Not installed",
|
|
394
|
+
doctor_links_valid: "{count} symlinks all valid",
|
|
395
|
+
doctor_broken_links: "{count} broken links",
|
|
396
|
+
doctor_fixed: "Cleaned {count} broken links",
|
|
397
|
+
doctor_not_exist: "Does not exist",
|
|
398
|
+
doctor_created: "Created {path}",
|
|
399
|
+
doctor_config_exists: "Exists",
|
|
400
|
+
doctor_config_not_exist: "Does not exist",
|
|
401
|
+
doctor_result_with_fix: "{warnings} warnings, {fixes} fixed",
|
|
402
|
+
doctor_result_without_fix: "{warnings} warnings",
|
|
403
|
+
count_suffix: "",
|
|
404
|
+
try_prompt: "help me develop an Abs operator"
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// src/utils/i18n.ts
|
|
408
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
409
|
+
var __dirname = dirname(__filename);
|
|
410
|
+
var EMBEDDED_LOCALES = {
|
|
411
|
+
zh_CN: zh_CN_default,
|
|
412
|
+
en_US: en_US_default
|
|
630
413
|
};
|
|
414
|
+
function loadLocale(locale) {
|
|
415
|
+
const embedded = EMBEDDED_LOCALES[locale];
|
|
416
|
+
if (embedded) return embedded;
|
|
417
|
+
const localePath = join(__dirname, "locales", `${locale}.json`);
|
|
418
|
+
if (existsSync(localePath)) {
|
|
419
|
+
try {
|
|
420
|
+
const content = readFileSync(localePath, "utf-8");
|
|
421
|
+
return JSON.parse(content);
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const fallbackPath = join(__dirname, "locales", "zh_CN.json");
|
|
426
|
+
if (existsSync(fallbackPath)) {
|
|
427
|
+
try {
|
|
428
|
+
const content = readFileSync(fallbackPath, "utf-8");
|
|
429
|
+
return JSON.parse(content);
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return EMBEDDED_LOCALES.zh_CN || {};
|
|
434
|
+
}
|
|
631
435
|
var currentLang = "zh_CN";
|
|
436
|
+
var messages = loadLocale("zh_CN");
|
|
632
437
|
function setLanguage(lang) {
|
|
633
438
|
currentLang = lang;
|
|
439
|
+
messages = loadLocale(lang);
|
|
634
440
|
}
|
|
635
441
|
function t(key) {
|
|
636
|
-
return
|
|
442
|
+
return messages[key] !== void 0 ? messages[key] : key;
|
|
637
443
|
}
|
|
638
444
|
|
|
639
|
-
// src/
|
|
445
|
+
// src/ui/theme.ts
|
|
640
446
|
import chalk from "chalk";
|
|
641
|
-
|
|
447
|
+
var selectTheme = {
|
|
448
|
+
helpMode: "never"
|
|
449
|
+
};
|
|
450
|
+
function isCJK(code) {
|
|
451
|
+
return code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65519 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983;
|
|
452
|
+
}
|
|
453
|
+
function isEmoji(code) {
|
|
454
|
+
return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
|
|
455
|
+
}
|
|
456
|
+
function getDisplayWidth(str) {
|
|
457
|
+
let width = 0;
|
|
458
|
+
for (const ch of str) {
|
|
459
|
+
const code = ch.codePointAt(0);
|
|
460
|
+
if (isCJK(code) || isEmoji(code)) {
|
|
461
|
+
width += 2;
|
|
462
|
+
} else {
|
|
463
|
+
width += 1;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return width;
|
|
467
|
+
}
|
|
468
|
+
function wrapDescription(text) {
|
|
469
|
+
const prefix = " \u{1F4A1} ";
|
|
470
|
+
const indent = " ";
|
|
471
|
+
const cols = process.stdout.columns || 120;
|
|
472
|
+
const maxLen = Math.max(20, cols - getDisplayWidth(prefix) - 2);
|
|
473
|
+
const tokens = [];
|
|
474
|
+
let i = 0;
|
|
475
|
+
while (i < text.length) {
|
|
476
|
+
const code = text[i].codePointAt(0);
|
|
477
|
+
if (isCJK(code)) {
|
|
478
|
+
tokens.push({ text: text[i], width: 2 });
|
|
479
|
+
i++;
|
|
480
|
+
} else {
|
|
481
|
+
let j = i;
|
|
482
|
+
while (j < text.length) {
|
|
483
|
+
const c = text[j].codePointAt(0);
|
|
484
|
+
if (isCJK(c)) break;
|
|
485
|
+
j++;
|
|
486
|
+
}
|
|
487
|
+
const word = text.slice(i, j);
|
|
488
|
+
tokens.push({ text: word, width: getDisplayWidth(word) });
|
|
489
|
+
i = j;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const lines = [];
|
|
493
|
+
let line = "";
|
|
494
|
+
let lineWidth = 0;
|
|
495
|
+
for (const token of tokens) {
|
|
496
|
+
if (lineWidth + token.width > maxLen && line) {
|
|
497
|
+
lines.push(line.trimEnd());
|
|
498
|
+
line = token.text;
|
|
499
|
+
lineWidth = token.width;
|
|
500
|
+
} else {
|
|
501
|
+
line += token.text;
|
|
502
|
+
lineWidth += token.width;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (line) lines.push(line.trimEnd());
|
|
506
|
+
return lines.map((l, i2) => chalk.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
|
|
507
|
+
}
|
|
508
|
+
var checkboxTheme = {
|
|
509
|
+
icon: {
|
|
510
|
+
checked: "\x1B[32m\u2611\x1B[22m",
|
|
511
|
+
unchecked: "\u2610",
|
|
512
|
+
cursor: "\u276F"
|
|
513
|
+
},
|
|
514
|
+
helpMode: "never",
|
|
515
|
+
style: {
|
|
516
|
+
description: (text) => wrapDescription(text)
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// src/utils/logger.ts
|
|
642
521
|
var logger = {
|
|
643
|
-
success: (msg) => console.log(
|
|
644
|
-
error: (msg) => console.log(
|
|
645
|
-
warn: (msg) => console.log(
|
|
646
|
-
info: (msg) => console.log(
|
|
647
|
-
step: (msg) => console.log(
|
|
522
|
+
success: (msg) => console.log(chalk2.green("\u2713") + " " + msg),
|
|
523
|
+
error: (msg) => console.log(chalk2.red("\u2717") + " " + msg),
|
|
524
|
+
warn: (msg) => console.log(chalk2.yellow("\u26A0") + " " + msg),
|
|
525
|
+
info: (msg) => console.log(chalk2.cyan("\u2192") + " " + msg),
|
|
526
|
+
step: (msg) => console.log(chalk2.dim(msg)),
|
|
648
527
|
blank: () => console.log()
|
|
649
528
|
};
|
|
650
529
|
function createSpinner(text) {
|
|
651
530
|
return ora({ text, color: "cyan" });
|
|
652
531
|
}
|
|
653
532
|
function printBanner(subtitle) {
|
|
654
|
-
console.log(
|
|
533
|
+
console.log(chalk2.cyan(`
|
|
655
534
|
____ _ _ _ _ _ ____ _
|
|
656
535
|
/ ___| / \\ | \\ | | \\ | | __ ) ___ | |_
|
|
657
536
|
| | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
|
|
@@ -659,66 +538,992 @@ function printBanner(subtitle) {
|
|
|
659
538
|
\\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
|
|
660
539
|
`));
|
|
661
540
|
if (subtitle) {
|
|
662
|
-
console.log(
|
|
541
|
+
console.log(chalk2.bold(` ${subtitle}`));
|
|
663
542
|
console.log();
|
|
664
543
|
}
|
|
665
544
|
}
|
|
666
|
-
function
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
545
|
+
function printBoxTitle(title, width = 65) {
|
|
546
|
+
const border = "\u2550".repeat(width);
|
|
547
|
+
const displayWidth = getDisplayWidth(title);
|
|
548
|
+
const totalPadding = width - displayWidth;
|
|
549
|
+
const leftPadding = Math.floor(totalPadding / 2);
|
|
550
|
+
const rightPadding = totalPadding - leftPadding;
|
|
551
|
+
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
552
|
+
console.log();
|
|
553
|
+
console.log(chalk2.cyan.bold(` \u2554${border}\u2557`));
|
|
554
|
+
console.log(chalk2.cyan.bold(` \u2551`) + chalk2.cyan.bold(paddedTitle) + chalk2.cyan.bold(`\u2551`));
|
|
555
|
+
console.log(chalk2.cyan.bold(` \u255A${border}\u255D`));
|
|
556
|
+
console.log();
|
|
557
|
+
}
|
|
558
|
+
function showOperationHints(isCheckbox = false) {
|
|
559
|
+
if (isCheckbox) {
|
|
560
|
+
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
|
|
561
|
+
`);
|
|
562
|
+
} else {
|
|
563
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
564
|
+
`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// src/utils/fs-helpers.ts
|
|
569
|
+
import { lstatSync, statSync } from "fs";
|
|
570
|
+
function isSymlink(path) {
|
|
571
|
+
try {
|
|
572
|
+
return lstatSync(path).isSymbolicLink();
|
|
573
|
+
} catch {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function isDirectory(path) {
|
|
578
|
+
try {
|
|
579
|
+
return statSync(path).isDirectory();
|
|
580
|
+
} catch {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/embedded-plugins.json
|
|
586
|
+
var embedded_plugins_default = [
|
|
587
|
+
{
|
|
588
|
+
id: "ops-direct-invoke",
|
|
589
|
+
dir: "plugins-official/ops-direct-invoke",
|
|
590
|
+
displayName: "AscendC Kernel \u76F4\u8C03",
|
|
591
|
+
script: "init.sh",
|
|
592
|
+
aliases: [
|
|
593
|
+
"ops-direct",
|
|
594
|
+
"ascendc-direct",
|
|
595
|
+
"direct",
|
|
596
|
+
"kernel"
|
|
597
|
+
],
|
|
598
|
+
skills: 16,
|
|
599
|
+
agents: 3,
|
|
600
|
+
description: "Ascend C \u76F4\u8C03\u5168\u6D41\u7A0B\uFF1A\u73AF\u5883\u2192\u8BBE\u8BA1\u2192\u5F00\u53D1\u2192\u5BA1\u67E5\u2192\u6027\u80FD\u9A8C\u6536",
|
|
601
|
+
version: "1.2.2",
|
|
602
|
+
configFile: "AGENTS.md",
|
|
603
|
+
configRootConfigLink: false,
|
|
604
|
+
installSkills: [
|
|
605
|
+
{
|
|
606
|
+
dir: "ops",
|
|
607
|
+
skills: [
|
|
608
|
+
"ascendc-tiling-design",
|
|
609
|
+
"npu-arch",
|
|
610
|
+
"ascendc-api-best-practices",
|
|
611
|
+
"ascendc-regbase-best-practice",
|
|
612
|
+
"ascendc-blaze-best-practice",
|
|
613
|
+
"ops-precision-standard",
|
|
614
|
+
"ascendc-docs-search",
|
|
615
|
+
"ascendc-env-check",
|
|
616
|
+
"ascendc-precision-debug",
|
|
617
|
+
"ops-profiling",
|
|
618
|
+
"ascendc-direct-invoke-template",
|
|
619
|
+
"torch-ascendc-op-extension",
|
|
620
|
+
"ascendc-runtime-debug",
|
|
621
|
+
"ascendc-crash-debug",
|
|
622
|
+
"ascendc-code-review"
|
|
623
|
+
]
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
dir: "infra",
|
|
627
|
+
skills: [
|
|
628
|
+
"gitcode-toolkit",
|
|
629
|
+
"gitcode-pr-handler",
|
|
630
|
+
"gitcode-issue-gen",
|
|
631
|
+
"gitcode-issue-handler"
|
|
632
|
+
]
|
|
633
|
+
}
|
|
634
|
+
],
|
|
635
|
+
installAgents: [
|
|
636
|
+
"ascendc-kernel-architect",
|
|
637
|
+
"ascendc-kernel-design-reviewer",
|
|
638
|
+
"ascendc-kernel-developer",
|
|
639
|
+
"ascendc-kernel-reviewer"
|
|
640
|
+
],
|
|
641
|
+
externalRepos: [
|
|
642
|
+
{
|
|
643
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
644
|
+
dir: "plugins-official/ops-direct-invoke/asc-devkit",
|
|
645
|
+
configRootLink: false
|
|
646
|
+
}
|
|
647
|
+
]
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
id: "ops-direct-invoke-flash",
|
|
651
|
+
dir: "plugins-official/ops-direct-invoke-flash",
|
|
652
|
+
displayName: "AscendC Kernel \u76F4\u8C03\uFF08\u5FEB\u901F\u7248\uFF09",
|
|
653
|
+
script: "init.sh",
|
|
654
|
+
aliases: [
|
|
655
|
+
"flash",
|
|
656
|
+
"kernel-flash"
|
|
657
|
+
],
|
|
658
|
+
skills: 1,
|
|
659
|
+
agents: 1,
|
|
660
|
+
description: "\u4ECE\u6570\u5B66\u516C\u5F0F\u6216\u4EE3\u7801\u7247\u6BB5\u5FEB\u901F\u6784\u5EFA\u5E76\u9A8C\u8BC1 Ascend NPU \u6838\u51FD\u6570",
|
|
661
|
+
version: "1.2.2",
|
|
662
|
+
configFile: "AGENTS.md",
|
|
663
|
+
configRootConfigLink: true,
|
|
664
|
+
installSkills: [
|
|
665
|
+
{
|
|
666
|
+
dir: "plugins-official/ops-direct-invoke-flash/skills",
|
|
667
|
+
skills: [
|
|
668
|
+
"ops-direct-invoke-flash"
|
|
669
|
+
]
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
dir: "ops",
|
|
673
|
+
skills: [
|
|
674
|
+
"ascendc-st-design",
|
|
675
|
+
"ascendc-whitebox-design"
|
|
676
|
+
]
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
dir: "infra",
|
|
680
|
+
skills: [
|
|
681
|
+
"gitcode-toolkit",
|
|
682
|
+
"gitcode-pr-handler",
|
|
683
|
+
"gitcode-issue-gen",
|
|
684
|
+
"gitcode-issue-handler"
|
|
685
|
+
]
|
|
686
|
+
}
|
|
687
|
+
],
|
|
688
|
+
installAgents: [
|
|
689
|
+
"ops-direct-invoke-flash-reviewer"
|
|
690
|
+
]
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
id: "ops-registry-invoke",
|
|
694
|
+
dir: "plugins-official/ops-registry-invoke",
|
|
695
|
+
displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
|
|
696
|
+
script: "init.sh",
|
|
697
|
+
aliases: [
|
|
698
|
+
"ops-registry",
|
|
699
|
+
"ascendc-registry",
|
|
700
|
+
"registry"
|
|
701
|
+
],
|
|
702
|
+
skills: 12,
|
|
703
|
+
agents: 4,
|
|
704
|
+
description: "\u751F\u4EA7\u7EA7 ACLNN \u6CE8\u518C\u7B97\u5B50\u5168\u6D41\u7A0B\uFF1A\u4EE3\u7801\u2192UT/ST\u2192\u4E0A\u5E93",
|
|
705
|
+
version: "1.2.2",
|
|
706
|
+
configFile: "AGENTS.md",
|
|
707
|
+
configRootConfigLink: false,
|
|
708
|
+
installSkills: [
|
|
709
|
+
{
|
|
710
|
+
dir: "ops",
|
|
711
|
+
skills: [
|
|
712
|
+
"ascendc-blaze-best-practice",
|
|
713
|
+
"ascendc-api-best-practices",
|
|
714
|
+
"ascendc-code-review",
|
|
715
|
+
"ascendc-crash-debug",
|
|
716
|
+
"ascendc-direct-invoke-template",
|
|
717
|
+
"ascendc-docs-gen",
|
|
718
|
+
"ascendc-docs-search",
|
|
719
|
+
"ascendc-env-check",
|
|
720
|
+
"npu-arch",
|
|
721
|
+
"ascendc-performance-best-practices",
|
|
722
|
+
"ascendc-precision-debug",
|
|
723
|
+
"ascendc-regbase-best-practice",
|
|
724
|
+
"ascendc-registry-invoke-template",
|
|
725
|
+
"ascendc-runtime-debug",
|
|
726
|
+
"ascendc-st-design",
|
|
727
|
+
"ascendc-tiling-design",
|
|
728
|
+
"ascendc-ut-develop",
|
|
729
|
+
"ascendc-whitebox-design",
|
|
730
|
+
"ops-precision-standard",
|
|
731
|
+
"ops-profiling",
|
|
732
|
+
"ops-spec-gen"
|
|
733
|
+
]
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
dir: "infra",
|
|
737
|
+
skills: [
|
|
738
|
+
"gitcode-toolkit",
|
|
739
|
+
"gitcode-pr-handler",
|
|
740
|
+
"gitcode-issue-gen",
|
|
741
|
+
"gitcode-issue-handler"
|
|
742
|
+
]
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
dir: "plugins-official/ops-registry-invoke",
|
|
746
|
+
skills: [
|
|
747
|
+
{
|
|
748
|
+
name: "workflow",
|
|
749
|
+
as: "ops-registry-invoke-workflow"
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
}
|
|
753
|
+
],
|
|
754
|
+
installAgents: [
|
|
755
|
+
"ascendc-ops-architect",
|
|
756
|
+
"ascendc-ops-developer",
|
|
757
|
+
"ascendc-ops-tester"
|
|
758
|
+
],
|
|
759
|
+
externalRepos: [
|
|
760
|
+
{
|
|
761
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
762
|
+
dir: "plugins-official/ops-registry-invoke/asc-devkit",
|
|
763
|
+
configRootLink: false
|
|
764
|
+
}
|
|
765
|
+
]
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
id: "catlass-op-generator",
|
|
769
|
+
dir: "plugins-official/catlass-op-generator",
|
|
770
|
+
displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
|
|
771
|
+
script: "init.sh",
|
|
772
|
+
aliases: [
|
|
773
|
+
"catlass"
|
|
774
|
+
],
|
|
775
|
+
skills: 10,
|
|
776
|
+
agents: 3,
|
|
777
|
+
description: "Catlass \u6A21\u677F\u62FC\u88C5 GEMM/Matmul/Epilogue \u7B97\u5B50\u5F00\u53D1",
|
|
778
|
+
version: "1.2.2",
|
|
779
|
+
configFile: "AGENTS.md",
|
|
780
|
+
configRootConfigLink: true,
|
|
781
|
+
installSkills: [
|
|
782
|
+
{
|
|
783
|
+
dir: "ops",
|
|
784
|
+
skills: [
|
|
785
|
+
"catlass-op-design",
|
|
786
|
+
"catlass-op-develop",
|
|
787
|
+
"catlass-op-perf-tune",
|
|
788
|
+
"ascendc-tiling-design",
|
|
789
|
+
"npu-arch",
|
|
790
|
+
"ascendc-api-best-practices",
|
|
791
|
+
"ops-precision-standard",
|
|
792
|
+
"ascendc-docs-search",
|
|
793
|
+
"ascendc-env-check",
|
|
794
|
+
"ascendc-precision-debug",
|
|
795
|
+
"ops-profiling",
|
|
796
|
+
"ascendc-runtime-debug",
|
|
797
|
+
"ascendc-code-review",
|
|
798
|
+
"torch-ascendc-op-extension"
|
|
799
|
+
]
|
|
800
|
+
}
|
|
801
|
+
],
|
|
802
|
+
installAgents: [
|
|
803
|
+
"catlass-op-architect",
|
|
804
|
+
"catlass-op-generator",
|
|
805
|
+
"catlass-op-reviewer"
|
|
806
|
+
],
|
|
807
|
+
externalRepos: [
|
|
808
|
+
{
|
|
809
|
+
url: "https://gitcode.com/cann/asc-devkit.git",
|
|
810
|
+
dir: "plugins-official/catlass-op-generator/asc-devkit",
|
|
811
|
+
configRootLink: false
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
id: "model-infer-optimize",
|
|
817
|
+
dir: "plugins-official/model-infer-optimize",
|
|
818
|
+
displayName: "NPU \u63A8\u7406\u4F18\u5316",
|
|
819
|
+
script: "init.sh",
|
|
820
|
+
aliases: [
|
|
821
|
+
"model-infer",
|
|
822
|
+
"infer",
|
|
823
|
+
"inference"
|
|
824
|
+
],
|
|
825
|
+
skills: 11,
|
|
826
|
+
agents: 3,
|
|
827
|
+
description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316\uFF08\u5E76\u884C/KVCache/\u878D\u5408/\u91CF\u5316/\u56FE\u6A21\u5F0F/\u591A\u6D41/SuperKernel\uFF09",
|
|
828
|
+
version: "1.2.2",
|
|
829
|
+
configFile: "AGENTS.md",
|
|
830
|
+
configRootConfigLink: true,
|
|
831
|
+
installSkills: [
|
|
832
|
+
{
|
|
833
|
+
dir: "model",
|
|
834
|
+
skills: [
|
|
835
|
+
"model-infer-migrator",
|
|
836
|
+
"model-infer-parallel-analysis",
|
|
837
|
+
"model-infer-parallel-impl",
|
|
838
|
+
"model-infer-kvcache",
|
|
839
|
+
"model-infer-fusion",
|
|
840
|
+
"model-infer-quantization",
|
|
841
|
+
"model-infer-graph-mode",
|
|
842
|
+
"model-infer-precision-debug",
|
|
843
|
+
"model-infer-runtime-debug",
|
|
844
|
+
"model-infer-multi-stream",
|
|
845
|
+
"model-infer-prefetch",
|
|
846
|
+
"model-infer-superkernel"
|
|
847
|
+
]
|
|
848
|
+
}
|
|
849
|
+
],
|
|
850
|
+
installAgents: [
|
|
851
|
+
"model-infer-analyzer",
|
|
852
|
+
"model-infer-implementer",
|
|
853
|
+
"model-infer-reviewer"
|
|
854
|
+
],
|
|
855
|
+
externalRepos: [
|
|
856
|
+
{
|
|
857
|
+
url: "https://gitcode.com/cann/cann-recipes-infer.git",
|
|
858
|
+
dir: "plugins-official/model-infer-optimize/cann-recipes-infer",
|
|
859
|
+
depth: 1,
|
|
860
|
+
configRootLink: true
|
|
861
|
+
}
|
|
862
|
+
]
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
id: "pypto-op-orchestrator",
|
|
866
|
+
dir: "plugins-official/pypto-op-orchestrator",
|
|
867
|
+
displayName: "PyPTO \u7B97\u5B50\u5F00\u53D1",
|
|
868
|
+
script: "init.sh",
|
|
869
|
+
aliases: [
|
|
870
|
+
"pypto",
|
|
871
|
+
"pytorch"
|
|
872
|
+
],
|
|
873
|
+
skills: 8,
|
|
874
|
+
agents: 3,
|
|
875
|
+
description: "PyPTO Python API \u7B97\u5B50\u5168\u6D41\u7A0B\u5F00\u53D1\uFF0C\u8986\u76D6\u9700\u6C42\u7406\u89E3\u3001API \u63A2\u7D22\u3001\u5B9E\u73B0\u4E0E\u8C03\u4F18",
|
|
876
|
+
version: "1.2.2",
|
|
877
|
+
configFile: "AGENTS.md",
|
|
878
|
+
configRootConfigLink: false,
|
|
879
|
+
installSkills: [
|
|
880
|
+
{
|
|
881
|
+
dir: "ops",
|
|
882
|
+
skills: [
|
|
883
|
+
"pypto-intent-understand",
|
|
884
|
+
"pypto-api-explore",
|
|
885
|
+
"pypto-op-design",
|
|
886
|
+
"pypto-op-develop",
|
|
887
|
+
"pypto-precision-debug",
|
|
888
|
+
"pypto-precision-compare",
|
|
889
|
+
"pypto-op-perf-tune",
|
|
890
|
+
"pypto-golden-generate"
|
|
891
|
+
]
|
|
892
|
+
}
|
|
893
|
+
],
|
|
894
|
+
installAgents: [
|
|
895
|
+
"pypto-op-analyst",
|
|
896
|
+
"pypto-op-developer",
|
|
897
|
+
"pypto-op-perf-tuner"
|
|
898
|
+
],
|
|
899
|
+
externalRepos: [
|
|
900
|
+
{
|
|
901
|
+
url: "https://gitcode.com/cann/pypto.git",
|
|
902
|
+
dir: "plugins-official/pypto-op-orchestrator/pypto",
|
|
903
|
+
configRootLink: false
|
|
904
|
+
}
|
|
905
|
+
]
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
id: "tilelang-op-orchestrator",
|
|
909
|
+
dir: "plugins-official/tilelang-op-orchestrator",
|
|
910
|
+
displayName: "TileLang \u7B97\u5B50\u5F00\u53D1",
|
|
911
|
+
script: "init.sh",
|
|
912
|
+
aliases: [
|
|
913
|
+
"tilelang"
|
|
914
|
+
],
|
|
915
|
+
skills: 9,
|
|
916
|
+
agents: 3,
|
|
917
|
+
description: "TileLang Python DSL + @tilelang.jit \u7B97\u5B50\u5F00\u53D1\uFF08\u652F\u6301 Developer/Expert \u53CC\u6A21\u5F0F\uFF09",
|
|
918
|
+
version: "1.2.2",
|
|
919
|
+
configFile: "AGENTS.md",
|
|
920
|
+
configRootConfigLink: false,
|
|
921
|
+
installSkills: [
|
|
922
|
+
{
|
|
923
|
+
dir: "ops",
|
|
924
|
+
skills: [
|
|
925
|
+
"tilelang-api-best-practices",
|
|
926
|
+
"tilelang-env-check",
|
|
927
|
+
"tilelang-op-design",
|
|
928
|
+
"tilelang-op-develop",
|
|
929
|
+
"tilelang-op-test-design",
|
|
930
|
+
"tilelang-perf-optimization",
|
|
931
|
+
"tilelang-programming-model-guide",
|
|
932
|
+
"tilelang-review",
|
|
933
|
+
"tilelang-submodule-pull"
|
|
934
|
+
]
|
|
935
|
+
}
|
|
936
|
+
],
|
|
937
|
+
installAgents: [
|
|
938
|
+
"tilelang-op-analyst",
|
|
939
|
+
"tilelang-op-developer",
|
|
940
|
+
"tilelang-op-perf-tuner"
|
|
941
|
+
],
|
|
942
|
+
externalRepos: [
|
|
943
|
+
{
|
|
944
|
+
url: "https://github.com/tile-ai/tilelang-ascend.git",
|
|
945
|
+
dir: "plugins-official/tilelang-op-orchestrator/tilelang-ascend",
|
|
946
|
+
depth: 1,
|
|
947
|
+
recursive: true,
|
|
948
|
+
configRootLink: false
|
|
949
|
+
}
|
|
950
|
+
]
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
id: "triton-op-generator",
|
|
954
|
+
dir: "plugins-official/triton-op-generator",
|
|
955
|
+
displayName: "Triton \u7B97\u5B50\u5F00\u53D1",
|
|
956
|
+
script: "init.sh",
|
|
957
|
+
aliases: [
|
|
958
|
+
"triton"
|
|
959
|
+
],
|
|
960
|
+
skills: 6,
|
|
961
|
+
agents: 1,
|
|
962
|
+
description: "Triton-Ascend \u7B97\u5B50 6 \u9636\u6BB5\u81EA\u52A8\u751F\u6210\u4E0E\u4F18\u5316\uFF08\u63CF\u8FF0\u2192\u4EE3\u7801\u2192\u9A8C\u8BC1\u2192\u4F18\u5316\uFF09",
|
|
963
|
+
version: "1.2.2",
|
|
964
|
+
configFile: "AGENTS.md",
|
|
965
|
+
configRootConfigLink: false,
|
|
966
|
+
installSkills: [
|
|
967
|
+
{
|
|
968
|
+
dir: "ops",
|
|
969
|
+
skills: [
|
|
970
|
+
"triton-task-extractor",
|
|
971
|
+
"triton-op-designer",
|
|
972
|
+
"triton-op-coding",
|
|
973
|
+
"triton-op-verifier",
|
|
974
|
+
"triton-latency-optimizer",
|
|
975
|
+
"npu-arch"
|
|
976
|
+
]
|
|
977
|
+
}
|
|
978
|
+
],
|
|
979
|
+
installAgents: []
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
id: "torch-compile",
|
|
983
|
+
dir: "plugins-official/torch-compile",
|
|
984
|
+
displayName: "torch.compile \u56FE\u6A21\u5F0F",
|
|
985
|
+
script: "init.sh",
|
|
986
|
+
aliases: [
|
|
987
|
+
"torch",
|
|
988
|
+
"compile",
|
|
989
|
+
"graph"
|
|
990
|
+
],
|
|
991
|
+
skills: 6,
|
|
992
|
+
agents: 1,
|
|
993
|
+
description: "NPU \u56FE\u6A21\u5F0F npugraph_ex \u914D\u7F6E\u3001\u811A\u672C\u8FC1\u79FB\u3001\u7F16\u8BD1\u8BCA\u65AD\u4E0E\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE",
|
|
994
|
+
version: "1.2.2",
|
|
995
|
+
configFile: "AGENTS.md",
|
|
996
|
+
configRootConfigLink: true,
|
|
997
|
+
installSkills: [
|
|
998
|
+
{
|
|
999
|
+
dir: "graph",
|
|
1000
|
+
skills: [
|
|
1001
|
+
"torch-npugraph-ex-knowledge",
|
|
1002
|
+
"torch-npugraph-ex-template",
|
|
1003
|
+
"torch-npugraph-ex-dfx-triage",
|
|
1004
|
+
"torch-npugraph-ex-compile-error-diagnosis",
|
|
1005
|
+
"torch-npugraph-ex-runtime-error-diagnosis",
|
|
1006
|
+
"torch-custom-ops-guide"
|
|
1007
|
+
]
|
|
1008
|
+
}
|
|
1009
|
+
],
|
|
1010
|
+
installAgents: [
|
|
1011
|
+
"torch-npugraph-ex"
|
|
1012
|
+
]
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
id: "ops-code-reviewer",
|
|
1016
|
+
dir: "plugins-official/ops-code-reviewer",
|
|
1017
|
+
displayName: "\u4EE3\u7801\u68C0\u89C6",
|
|
1018
|
+
script: "init.sh",
|
|
1019
|
+
aliases: [
|
|
1020
|
+
"code-review",
|
|
1021
|
+
"reviewer",
|
|
1022
|
+
"review"
|
|
1023
|
+
],
|
|
1024
|
+
skills: 5,
|
|
1025
|
+
agents: 1,
|
|
1026
|
+
description: "Ascend C \u7B97\u5B50\u4EE3\u7801 4 \u9636\u6BB5\u5168\u91CF\u68C0\u89C6\uFF08\u4E3B Agent \u7F16\u6392 + \u5B50 Agent \u5E76\u884C\u68C0\u89C6\uFF09",
|
|
1027
|
+
version: "1.2.2",
|
|
1028
|
+
configFile: "AGENTS.md",
|
|
1029
|
+
configRootConfigLink: false,
|
|
1030
|
+
installSkills: [
|
|
1031
|
+
{
|
|
1032
|
+
dir: "ops",
|
|
1033
|
+
skills: [
|
|
1034
|
+
"ascendc-code-review",
|
|
1035
|
+
"ascendc-docs-search",
|
|
1036
|
+
"ascendc-task-focus",
|
|
1037
|
+
"ascendc-api-best-practices",
|
|
1038
|
+
"npu-arch"
|
|
1039
|
+
]
|
|
1040
|
+
}
|
|
1041
|
+
],
|
|
1042
|
+
installAgents: [
|
|
1043
|
+
"ascendc-code-summarizer",
|
|
1044
|
+
"ascendc-ops-reviewer"
|
|
1045
|
+
]
|
|
1046
|
+
}
|
|
1047
|
+
];
|
|
1048
|
+
|
|
1049
|
+
// src/core/registry.ts
|
|
1050
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
1051
|
+
var __dirname2 = dirname2(__filename2);
|
|
1052
|
+
function findPluginsDir() {
|
|
1053
|
+
const candidates = [
|
|
1054
|
+
join2(__dirname2, "plugins.d"),
|
|
1055
|
+
join2(__dirname2, "..", "plugins.d"),
|
|
1056
|
+
join2(__dirname2, "..", "..", "plugins.d")
|
|
1057
|
+
];
|
|
1058
|
+
for (const dir of candidates) {
|
|
1059
|
+
if (existsSync2(dir)) return dir;
|
|
1060
|
+
}
|
|
1061
|
+
return candidates[0];
|
|
1062
|
+
}
|
|
1063
|
+
function loadPluginsFromYaml() {
|
|
1064
|
+
const pluginsDir = findPluginsDir();
|
|
1065
|
+
const defaultsPath = join2(pluginsDir, "_defaults.yml");
|
|
1066
|
+
let defaults = {};
|
|
1067
|
+
try {
|
|
1068
|
+
defaults = parseYaml(readFileSync2(defaultsPath, "utf-8")) || {};
|
|
1069
|
+
} catch {
|
|
1070
|
+
}
|
|
1071
|
+
const plugins = [];
|
|
1072
|
+
try {
|
|
1073
|
+
for (const file of readdirSync(pluginsDir)) {
|
|
1074
|
+
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
1075
|
+
try {
|
|
1076
|
+
const content = parseYaml(readFileSync2(join2(pluginsDir, file), "utf-8"));
|
|
1077
|
+
if (!content || !content.id) continue;
|
|
1078
|
+
plugins.push({
|
|
1079
|
+
id: content.id,
|
|
1080
|
+
dir: content.dir,
|
|
1081
|
+
displayName: content.displayName || content.id,
|
|
1082
|
+
script: content.script || defaults.script || "init.sh",
|
|
1083
|
+
aliases: content.aliases || [],
|
|
1084
|
+
skills: content.skills ?? defaults.skills ?? 0,
|
|
1085
|
+
agents: content.agents ?? defaults.agents ?? 0,
|
|
1086
|
+
description: content.description || "",
|
|
1087
|
+
version: content.version,
|
|
1088
|
+
configFile: content.configFile,
|
|
1089
|
+
configRootConfigLink: content.configRootConfigLink,
|
|
1090
|
+
installSkills: content.installSkills,
|
|
1091
|
+
installAgents: content.installAgents,
|
|
1092
|
+
externalRepos: content.externalRepos
|
|
1093
|
+
});
|
|
1094
|
+
} catch {
|
|
1095
|
+
logger.warn(t("plugin_yaml_error").replace("{file}", file));
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
} catch {
|
|
1099
|
+
}
|
|
1100
|
+
if (plugins.length === 0) {
|
|
1101
|
+
return [...embedded_plugins_default];
|
|
1102
|
+
}
|
|
1103
|
+
plugins.sort(
|
|
1104
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
1105
|
+
);
|
|
1106
|
+
return plugins;
|
|
1107
|
+
}
|
|
1108
|
+
var PLUGIN_REGISTRY = loadPluginsFromYaml();
|
|
1109
|
+
var PLUGIN_DIRS = ["plugins-official", "plugins-community"];
|
|
1110
|
+
function mergeDynamicPlugins(repoPath) {
|
|
1111
|
+
if (!existsSync2(repoPath)) return;
|
|
1112
|
+
const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
|
|
1113
|
+
const dynamic = [];
|
|
1114
|
+
for (const pluginDir of PLUGIN_DIRS) {
|
|
1115
|
+
const parent = join2(repoPath, pluginDir);
|
|
1116
|
+
if (!existsSync2(parent)) continue;
|
|
1117
|
+
let entries = [];
|
|
1118
|
+
try {
|
|
1119
|
+
entries = readdirSync(parent);
|
|
1120
|
+
} catch {
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
for (const entry of entries) {
|
|
1124
|
+
const pluginPath = join2(parent, entry);
|
|
1125
|
+
if (!isDirectory(pluginPath)) continue;
|
|
1126
|
+
if (entry === "install-helper") continue;
|
|
1127
|
+
const initSh = join2(pluginPath, "init.sh");
|
|
1128
|
+
if (!existsSync2(initSh)) continue;
|
|
1129
|
+
const pluginId = entry;
|
|
1130
|
+
if (existingIds.has(pluginId)) continue;
|
|
1131
|
+
dynamic.push({
|
|
1132
|
+
id: pluginId,
|
|
1133
|
+
dir: `${pluginDir}/${entry}`,
|
|
1134
|
+
displayName: entry,
|
|
1135
|
+
script: "init.sh",
|
|
1136
|
+
aliases: [],
|
|
1137
|
+
skills: 0,
|
|
1138
|
+
agents: 0,
|
|
1139
|
+
description: ""
|
|
1140
|
+
});
|
|
1141
|
+
existingIds.add(pluginId);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
if (dynamic.length > 0) {
|
|
1145
|
+
PLUGIN_REGISTRY = [...PLUGIN_REGISTRY, ...dynamic].sort(
|
|
1146
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
function findPlugin(query) {
|
|
1151
|
+
const normalized = query.toLowerCase().trim();
|
|
1152
|
+
const exactMatch = PLUGIN_REGISTRY.find(
|
|
1153
|
+
(p) => p.id === normalized || p.id === query.trim()
|
|
1154
|
+
);
|
|
1155
|
+
if (exactMatch) return exactMatch;
|
|
1156
|
+
const aliasMatch = PLUGIN_REGISTRY.find(
|
|
1157
|
+
(p) => p.aliases.some((a) => a === normalized)
|
|
1158
|
+
);
|
|
1159
|
+
if (aliasMatch) return aliasMatch;
|
|
1160
|
+
const prefixMatch = PLUGIN_REGISTRY.find((p) => p.id.startsWith(normalized));
|
|
1161
|
+
if (prefixMatch) return prefixMatch;
|
|
1162
|
+
return void 0;
|
|
1163
|
+
}
|
|
1164
|
+
function getAllPlugins() {
|
|
1165
|
+
return PLUGIN_REGISTRY;
|
|
1166
|
+
}
|
|
1167
|
+
function getPluginById(id) {
|
|
1168
|
+
return PLUGIN_REGISTRY.find((p) => p.id === id);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// src/core/detector.ts
|
|
1172
|
+
import { execa } from "execa";
|
|
1173
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1174
|
+
import { homedir as homedir2 } from "os";
|
|
1175
|
+
import { join as join4 } from "path";
|
|
1176
|
+
|
|
1177
|
+
// src/utils/paths.ts
|
|
1178
|
+
import { homedir } from "os";
|
|
1179
|
+
import { join as join3 } from "path";
|
|
1180
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1181
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
1182
|
+
var VALID_LEVELS = ["project", "global"];
|
|
1183
|
+
function validateTool(tool) {
|
|
1184
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
1185
|
+
throw new Error(
|
|
1186
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
return tool;
|
|
1190
|
+
}
|
|
1191
|
+
function validateLevel(level) {
|
|
1192
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
1193
|
+
throw new Error(
|
|
1194
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
return level;
|
|
1198
|
+
}
|
|
1199
|
+
function detectTraeVariant() {
|
|
1200
|
+
const home = homedir();
|
|
1201
|
+
if (existsSync3(join3(home, ".trae-cn"))) return "ide";
|
|
1202
|
+
if (existsSync3(join3(home, ".marscode"))) return "plugin";
|
|
1203
|
+
if (existsSync3(join3(home, ".traecli"))) return "cli";
|
|
1204
|
+
return "unknown";
|
|
1205
|
+
}
|
|
1206
|
+
function getConfigRoot(tool, level, base) {
|
|
1207
|
+
const home = homedir();
|
|
1208
|
+
if (level === "global") {
|
|
1209
|
+
switch (tool) {
|
|
1210
|
+
case "opencode":
|
|
1211
|
+
return join3(home, ".config", "opencode");
|
|
1212
|
+
case "claude":
|
|
1213
|
+
return join3(home, ".claude");
|
|
1214
|
+
case "trae": {
|
|
1215
|
+
const variant = detectTraeVariant();
|
|
1216
|
+
switch (variant) {
|
|
1217
|
+
case "plugin":
|
|
1218
|
+
return join3(home, ".marscode");
|
|
1219
|
+
case "cli":
|
|
1220
|
+
return join3(home, ".traecli");
|
|
1221
|
+
default:
|
|
1222
|
+
return join3(home, ".trae-cn");
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
case "cursor":
|
|
1226
|
+
return join3(home, ".cursor");
|
|
1227
|
+
case "copilot":
|
|
1228
|
+
return join3(home, ".copilot");
|
|
1229
|
+
default:
|
|
1230
|
+
throw new Error(
|
|
1231
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
const baseDir = base || process.cwd();
|
|
1236
|
+
switch (tool) {
|
|
1237
|
+
case "opencode":
|
|
1238
|
+
return join3(baseDir, ".opencode");
|
|
1239
|
+
case "claude":
|
|
1240
|
+
return join3(baseDir, ".claude");
|
|
1241
|
+
case "trae": {
|
|
1242
|
+
const variant = detectTraeVariant();
|
|
1243
|
+
switch (variant) {
|
|
1244
|
+
case "plugin":
|
|
1245
|
+
return join3(baseDir, ".marscode");
|
|
1246
|
+
case "cli":
|
|
1247
|
+
return join3(baseDir, ".traecli");
|
|
1248
|
+
default:
|
|
1249
|
+
return join3(baseDir, ".trae");
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
case "cursor":
|
|
1253
|
+
return join3(baseDir, ".cursor");
|
|
1254
|
+
case "copilot":
|
|
1255
|
+
return join3(baseDir, ".github");
|
|
1256
|
+
default:
|
|
1257
|
+
throw new Error(
|
|
1258
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function getConfigFileName(tool) {
|
|
1263
|
+
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1264
|
+
}
|
|
1265
|
+
function getAgentsFileName(tool) {
|
|
1266
|
+
return getConfigFileName(tool);
|
|
1267
|
+
}
|
|
1268
|
+
function getSkillsDir(configRoot) {
|
|
1269
|
+
return join3(configRoot, "skills");
|
|
1270
|
+
}
|
|
1271
|
+
function getAgentsDir(configRoot) {
|
|
1272
|
+
return join3(configRoot, "agents");
|
|
1273
|
+
}
|
|
1274
|
+
function getManifestPath(configRoot) {
|
|
1275
|
+
return join3(configRoot, "cannbot-manifest.json");
|
|
1276
|
+
}
|
|
1277
|
+
function getCannbotConfigDir() {
|
|
1278
|
+
return join3(homedir(), ".cannbot");
|
|
1279
|
+
}
|
|
1280
|
+
function getCannbotRepoPath() {
|
|
1281
|
+
return join3(getCannbotConfigDir(), "repo");
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/core/detector.ts
|
|
1285
|
+
async function getCommandVersion(cmd, args = ["--version"]) {
|
|
1286
|
+
try {
|
|
1287
|
+
const result = await execa(cmd, args, { timeout: 5e3 });
|
|
1288
|
+
const output = result.stdout.trim();
|
|
1289
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
1290
|
+
return match ? match[1] : output.split("\n")[0];
|
|
1291
|
+
} catch {
|
|
1292
|
+
return void 0;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
async function getCommandPath(cmd) {
|
|
1296
|
+
try {
|
|
1297
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1298
|
+
const result = await execa(whichCmd, [cmd], { timeout: 5e3 });
|
|
1299
|
+
return result.stdout.trim().split("\n")[0];
|
|
1300
|
+
} catch {
|
|
1301
|
+
return void 0;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
async function detectOpenCode() {
|
|
1305
|
+
const path = await getCommandPath("opencode");
|
|
1306
|
+
if (!path) return void 0;
|
|
1307
|
+
const version = await getCommandVersion("opencode");
|
|
1308
|
+
return { name: "opencode", version, path };
|
|
1309
|
+
}
|
|
1310
|
+
async function detectClaude() {
|
|
1311
|
+
const path = await getCommandPath("claude");
|
|
1312
|
+
if (!path) return void 0;
|
|
1313
|
+
const version = await getCommandVersion("claude");
|
|
1314
|
+
return { name: "claude", version, path };
|
|
1315
|
+
}
|
|
1316
|
+
async function detectTrae() {
|
|
1317
|
+
const home = homedir2();
|
|
1318
|
+
const variant = detectTraeVariant();
|
|
1319
|
+
let path;
|
|
1320
|
+
if (variant === "ide" && existsSync4(join4(home, ".trae-cn"))) {
|
|
1321
|
+
path = join4(home, ".trae-cn");
|
|
1322
|
+
} else if (variant === "plugin" && existsSync4(join4(home, ".marscode"))) {
|
|
1323
|
+
path = join4(home, ".marscode");
|
|
1324
|
+
} else if (variant === "cli" && existsSync4(join4(home, ".traecli"))) {
|
|
1325
|
+
path = join4(home, ".traecli");
|
|
1326
|
+
} else if (variant === "unknown") {
|
|
1327
|
+
const cmdPath = await getCommandPath("trae");
|
|
1328
|
+
if (cmdPath) path = cmdPath;
|
|
1329
|
+
}
|
|
1330
|
+
if (!path) return void 0;
|
|
1331
|
+
return { name: "trae", version: variant, path };
|
|
1332
|
+
}
|
|
1333
|
+
async function detectCursor() {
|
|
1334
|
+
const path = await getCommandPath("cursor");
|
|
1335
|
+
if (path) {
|
|
1336
|
+
const version = await getCommandVersion("cursor");
|
|
1337
|
+
return { name: "cursor", version, path };
|
|
1338
|
+
}
|
|
1339
|
+
if (process.platform === "darwin") {
|
|
1340
|
+
const appPath = "/Applications/Cursor.app";
|
|
1341
|
+
if (existsSync4(appPath)) {
|
|
1342
|
+
return { name: "cursor", path: appPath };
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
return void 0;
|
|
1346
|
+
}
|
|
1347
|
+
async function detectCopilot() {
|
|
1348
|
+
const path = await getCommandPath("gh");
|
|
1349
|
+
if (!path) return void 0;
|
|
1350
|
+
try {
|
|
1351
|
+
await execa("gh", ["extension", "list"], { timeout: 5e3 });
|
|
1352
|
+
return { name: "copilot", path };
|
|
1353
|
+
} catch {
|
|
1354
|
+
return void 0;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
async function detectTools() {
|
|
1358
|
+
const detectors = [
|
|
1359
|
+
detectOpenCode,
|
|
1360
|
+
detectClaude,
|
|
1361
|
+
detectTrae,
|
|
1362
|
+
detectCursor,
|
|
1363
|
+
detectCopilot
|
|
1364
|
+
];
|
|
1365
|
+
const results = await Promise.allSettled(detectors.map((d) => d()));
|
|
1366
|
+
const tools = [];
|
|
1367
|
+
for (const result of results) {
|
|
1368
|
+
if (result.status === "fulfilled" && result.value) {
|
|
1369
|
+
tools.push(result.value);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return tools;
|
|
1373
|
+
}
|
|
1374
|
+
function getToolDisplayName(tool) {
|
|
1375
|
+
const names = {
|
|
1376
|
+
opencode: "OpenCode",
|
|
1377
|
+
claude: "Claude Code",
|
|
1378
|
+
trae: "Trae",
|
|
1379
|
+
cursor: "Cursor",
|
|
1380
|
+
copilot: "GitHub Copilot"
|
|
1381
|
+
};
|
|
1382
|
+
return names[tool];
|
|
1383
|
+
}
|
|
1384
|
+
function getAllTools() {
|
|
1385
|
+
return VALID_TOOLS;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
// src/core/manifest.ts
|
|
1389
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
1390
|
+
import { join as join5 } from "path";
|
|
1391
|
+
function readManifest(configRoot) {
|
|
1392
|
+
const manifestPath = getManifestPath(configRoot);
|
|
1393
|
+
if (existsSync5(manifestPath)) {
|
|
1394
|
+
try {
|
|
1395
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
1396
|
+
return JSON.parse(content);
|
|
1397
|
+
} catch {
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
return null;
|
|
1401
|
+
}
|
|
1402
|
+
function readAllManifests(configRoot) {
|
|
1403
|
+
const manifests = [];
|
|
1404
|
+
const plugins = getAllPlugins();
|
|
1405
|
+
const standardPath = getManifestPath(configRoot);
|
|
1406
|
+
if (existsSync5(standardPath)) {
|
|
1407
|
+
try {
|
|
1408
|
+
const content = readFileSync3(standardPath, "utf-8");
|
|
1409
|
+
manifests.push(JSON.parse(content));
|
|
1410
|
+
} catch {
|
|
674
1411
|
}
|
|
675
1412
|
}
|
|
676
|
-
|
|
1413
|
+
for (const plugin of plugins) {
|
|
1414
|
+
const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
|
|
1415
|
+
if (existsSync5(pluginManifestPath)) {
|
|
1416
|
+
try {
|
|
1417
|
+
const content = readFileSync3(pluginManifestPath, "utf-8");
|
|
1418
|
+
manifests.push(JSON.parse(content));
|
|
1419
|
+
} catch {
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
return manifests;
|
|
677
1424
|
}
|
|
678
|
-
function
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
const
|
|
682
|
-
const
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1425
|
+
function scanInstalled() {
|
|
1426
|
+
const plugins = getAllPlugins();
|
|
1427
|
+
const installed = [];
|
|
1428
|
+
const tools = VALID_TOOLS;
|
|
1429
|
+
const levels = ["project", "global"];
|
|
1430
|
+
for (const tool of tools) {
|
|
1431
|
+
for (const level of levels) {
|
|
1432
|
+
const configRoot = getConfigRoot(tool, level);
|
|
1433
|
+
const manifests = readAllManifests(configRoot);
|
|
1434
|
+
for (const manifest of manifests) {
|
|
1435
|
+
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
1436
|
+
if (!plugin) continue;
|
|
1437
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
1438
|
+
continue;
|
|
1439
|
+
}
|
|
1440
|
+
const alreadyAdded = installed.some(
|
|
1441
|
+
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
1442
|
+
);
|
|
1443
|
+
if (alreadyAdded) continue;
|
|
1444
|
+
installed.push({
|
|
1445
|
+
id: plugin.id,
|
|
1446
|
+
displayName: plugin.displayName,
|
|
1447
|
+
tool,
|
|
1448
|
+
level,
|
|
1449
|
+
skillsCount: manifest.installed_skills?.length || 0,
|
|
1450
|
+
agentsCount: manifest.installed_agents?.length || 0,
|
|
1451
|
+
installTime: manifest.install_time,
|
|
1452
|
+
configRoot
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
return installed;
|
|
690
1458
|
}
|
|
691
|
-
function
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1459
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
1460
|
+
const skillsDir = join5(configRoot, "skills");
|
|
1461
|
+
const agentsDir = join5(configRoot, "agents");
|
|
1462
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
1463
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
1464
|
+
const skillPath = join5(skillsDir, skillId);
|
|
1465
|
+
return existsSync5(skillPath);
|
|
1466
|
+
});
|
|
1467
|
+
if (!hasAnySkill) return false;
|
|
1468
|
+
}
|
|
1469
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
1470
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
1471
|
+
const agentPath = join5(agentsDir, agentId);
|
|
1472
|
+
return existsSync5(agentPath);
|
|
1473
|
+
});
|
|
1474
|
+
if (!hasAnyAgent) return false;
|
|
1475
|
+
}
|
|
1476
|
+
let agentsMdPath;
|
|
1477
|
+
if (tool === "opencode" && level === "project") {
|
|
1478
|
+
agentsMdPath = join5(configRoot, "..", "AGENTS.md");
|
|
695
1479
|
} else {
|
|
696
|
-
|
|
697
|
-
`);
|
|
1480
|
+
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
698
1481
|
}
|
|
1482
|
+
if (!existsSync5(agentsMdPath)) return false;
|
|
1483
|
+
return true;
|
|
699
1484
|
}
|
|
700
1485
|
|
|
701
1486
|
// src/utils/config.ts
|
|
702
|
-
import { existsSync as
|
|
703
|
-
import { join as
|
|
704
|
-
import { homedir as homedir3 } from "os";
|
|
1487
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
1488
|
+
import { join as join6 } from "path";
|
|
705
1489
|
import { parse, stringify } from "yaml";
|
|
1490
|
+
|
|
1491
|
+
// src/utils/fs.ts
|
|
1492
|
+
import { writeFileSync, renameSync, existsSync as existsSync6, unlinkSync } from "fs";
|
|
1493
|
+
import { dirname as dirname3 } from "path";
|
|
1494
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
1495
|
+
const dir = dirname3(filePath);
|
|
1496
|
+
if (!existsSync6(dir)) {
|
|
1497
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
1498
|
+
}
|
|
1499
|
+
const tmpPath = `${filePath}.tmp`;
|
|
1500
|
+
writeFileSync(tmpPath, data, encoding);
|
|
1501
|
+
if (existsSync6(filePath)) {
|
|
1502
|
+
try {
|
|
1503
|
+
unlinkSync(filePath);
|
|
1504
|
+
} catch {
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
renameSync(tmpPath, filePath);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
// src/utils/config.ts
|
|
706
1511
|
function getConfigDir() {
|
|
707
|
-
return
|
|
1512
|
+
return getCannbotConfigDir();
|
|
708
1513
|
}
|
|
709
1514
|
function getConfigPath() {
|
|
710
|
-
return
|
|
1515
|
+
return join6(getConfigDir(), "config.yaml");
|
|
711
1516
|
}
|
|
712
1517
|
function readConfig() {
|
|
713
1518
|
const configPath = getConfigPath();
|
|
714
|
-
if (!
|
|
1519
|
+
if (!existsSync7(configPath)) {
|
|
715
1520
|
return {
|
|
716
1521
|
language: "zh_CN",
|
|
717
1522
|
installedPlugins: []
|
|
718
1523
|
};
|
|
719
1524
|
}
|
|
720
1525
|
try {
|
|
721
|
-
const content =
|
|
1526
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
722
1527
|
const parsed = parse(content);
|
|
723
1528
|
return {
|
|
724
1529
|
language: parsed.language || "zh_CN",
|
|
@@ -728,6 +1533,7 @@ function readConfig() {
|
|
|
728
1533
|
installedPlugins: parsed.installedPlugins || []
|
|
729
1534
|
};
|
|
730
1535
|
} catch {
|
|
1536
|
+
logger.warn(t("config_corrupted"));
|
|
731
1537
|
return {
|
|
732
1538
|
language: "zh_CN",
|
|
733
1539
|
installedPlugins: []
|
|
@@ -736,12 +1542,12 @@ function readConfig() {
|
|
|
736
1542
|
}
|
|
737
1543
|
function writeConfig(config) {
|
|
738
1544
|
const configDir = getConfigDir();
|
|
739
|
-
if (!
|
|
1545
|
+
if (!existsSync7(configDir)) {
|
|
740
1546
|
mkdirSync(configDir, { recursive: true });
|
|
741
1547
|
}
|
|
742
1548
|
const configPath = getConfigPath();
|
|
743
1549
|
const content = stringify(config);
|
|
744
|
-
|
|
1550
|
+
atomicWriteFileSync(configPath, content);
|
|
745
1551
|
}
|
|
746
1552
|
function updateConfig(updates) {
|
|
747
1553
|
const config = readConfig();
|
|
@@ -764,22 +1570,11 @@ function removeInstalledPlugin(pluginId) {
|
|
|
764
1570
|
writeConfig(config);
|
|
765
1571
|
}
|
|
766
1572
|
|
|
767
|
-
// src/
|
|
768
|
-
var selectTheme = {
|
|
769
|
-
helpMode: "never"
|
|
770
|
-
};
|
|
771
|
-
var checkboxTheme = {
|
|
772
|
-
icon: {
|
|
773
|
-
checked: "\x1B[32m\u2611\x1B[22m",
|
|
774
|
-
unchecked: "\u2610",
|
|
775
|
-
cursor: "\u276F"
|
|
776
|
-
},
|
|
777
|
-
helpMode: "never"
|
|
778
|
-
};
|
|
779
|
-
|
|
780
|
-
// src/ui/wizard.ts
|
|
1573
|
+
// src/utils/constants.ts
|
|
781
1574
|
var BACK = "__back__";
|
|
782
1575
|
var CANCEL = "__cancel__";
|
|
1576
|
+
|
|
1577
|
+
// src/ui/wizard.ts
|
|
783
1578
|
async function selectToolWithDetection() {
|
|
784
1579
|
const config = readConfig();
|
|
785
1580
|
logger.blank();
|
|
@@ -849,7 +1644,7 @@ async function runWizard() {
|
|
|
849
1644
|
step = 2;
|
|
850
1645
|
break;
|
|
851
1646
|
}
|
|
852
|
-
if (result === CANCEL
|
|
1647
|
+
if (result === CANCEL) {
|
|
853
1648
|
return { language: config.language || "zh_CN", tool: selectedTool, level, plugins: [], confirmed: false };
|
|
854
1649
|
}
|
|
855
1650
|
updateConfig({ lastTool: selectedTool, lastLevel: level });
|
|
@@ -859,11 +1654,11 @@ async function runWizard() {
|
|
|
859
1654
|
}
|
|
860
1655
|
}
|
|
861
1656
|
async function stepTool(detectedTools, lastTool) {
|
|
862
|
-
printBoxTitle("
|
|
1657
|
+
printBoxTitle(t("wizard_step_1_title"));
|
|
863
1658
|
if (detectedTools.length === 0) {
|
|
864
1659
|
logger.error(t("error_tool_not_found"));
|
|
865
1660
|
const choices2 = [
|
|
866
|
-
...
|
|
1661
|
+
...VALID_TOOLS.map((tool) => ({
|
|
867
1662
|
name: `> ${getToolDisplayName(tool)}`,
|
|
868
1663
|
value: tool
|
|
869
1664
|
})),
|
|
@@ -934,10 +1729,10 @@ async function stepToolManual(lastTool) {
|
|
|
934
1729
|
return result;
|
|
935
1730
|
}
|
|
936
1731
|
async function stepLevel(lastLevel) {
|
|
937
|
-
printBoxTitle("
|
|
1732
|
+
printBoxTitle(t("wizard_step_2_title"));
|
|
938
1733
|
const choices = [
|
|
939
|
-
{ name:
|
|
940
|
-
{ name:
|
|
1734
|
+
{ name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
|
|
1735
|
+
{ name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
|
|
941
1736
|
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
942
1737
|
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
943
1738
|
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
@@ -955,7 +1750,7 @@ async function stepLevel(lastLevel) {
|
|
|
955
1750
|
return result;
|
|
956
1751
|
}
|
|
957
1752
|
async function stepPlugins(tool, level) {
|
|
958
|
-
printBoxTitle("
|
|
1753
|
+
printBoxTitle(t("wizard_step_3_title"));
|
|
959
1754
|
const plugins = getAllPlugins();
|
|
960
1755
|
const configRoot = getConfigRoot(tool, level);
|
|
961
1756
|
const manifests = readAllManifests(configRoot);
|
|
@@ -964,49 +1759,38 @@ async function stepPlugins(tool, level) {
|
|
|
964
1759
|
const isInstalled = installedSet.has(p.id);
|
|
965
1760
|
const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
|
|
966
1761
|
return {
|
|
967
|
-
name:
|
|
1762
|
+
name: `> ${chalk3.cyan(p.displayName)}${suffix}`,
|
|
968
1763
|
value: p.id,
|
|
969
|
-
|
|
1764
|
+
description: p.description
|
|
970
1765
|
};
|
|
971
1766
|
});
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1767
|
+
choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1768
|
+
choices.push({ name: "<- " + t("wizard_back"), value: BACK });
|
|
1769
|
+
choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL });
|
|
1770
|
+
showOperationHints();
|
|
1771
|
+
const selected = await select({
|
|
1772
|
+
message: t("wizard_select_plugin"),
|
|
975
1773
|
choices,
|
|
976
1774
|
loop: false,
|
|
977
|
-
|
|
978
|
-
theme: checkboxTheme,
|
|
1775
|
+
theme: selectTheme,
|
|
979
1776
|
pageSize: 15
|
|
980
1777
|
});
|
|
981
|
-
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
message: t("wizard_no_selection"),
|
|
985
|
-
choices: [
|
|
986
|
-
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
987
|
-
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
988
|
-
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
989
|
-
],
|
|
990
|
-
loop: false,
|
|
991
|
-
theme: selectTheme
|
|
992
|
-
});
|
|
993
|
-
if (action === "back") return BACK;
|
|
994
|
-
return CANCEL;
|
|
995
|
-
}
|
|
996
|
-
return pluginIds;
|
|
1778
|
+
if (selected === BACK) return BACK;
|
|
1779
|
+
if (selected === CANCEL) return CANCEL;
|
|
1780
|
+
return [selected];
|
|
997
1781
|
}
|
|
998
1782
|
async function stepConfirm(tool, level, plugins) {
|
|
999
|
-
printBoxTitle("
|
|
1783
|
+
printBoxTitle(t("wizard_step_4_title"));
|
|
1000
1784
|
const allPlugins = getAllPlugins();
|
|
1001
1785
|
const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
|
|
1002
1786
|
const displayPath = getConfigRoot(tool, level);
|
|
1003
1787
|
const toolName = getToolDisplayName(tool);
|
|
1004
1788
|
const levelText = level === "project" ? "project" : "global";
|
|
1005
|
-
logger.info(
|
|
1006
|
-
logger.info(
|
|
1789
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk3.bold(String(plugins.length))).replace("{tool}", chalk3.green(toolName)).replace("{level}", chalk3.cyan(levelText)));
|
|
1790
|
+
logger.info(`${t("wizard_install_path")}: ${chalk3.cyan(displayPath)}`);
|
|
1007
1791
|
for (const plugin of selectedPlugins) {
|
|
1008
1792
|
if (plugin) {
|
|
1009
|
-
logger.step(` \u2022 ${
|
|
1793
|
+
logger.step(` \u2022 ${chalk3.bold(plugin.displayName)}`);
|
|
1010
1794
|
}
|
|
1011
1795
|
}
|
|
1012
1796
|
logger.blank();
|
|
@@ -1024,14 +1808,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
1024
1808
|
}
|
|
1025
1809
|
|
|
1026
1810
|
// src/core/repository.ts
|
|
1027
|
-
import { existsSync as
|
|
1028
|
-
import { join as
|
|
1811
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
1812
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
1813
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1029
1814
|
import { execa as execa2 } from "execa";
|
|
1815
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1030
1816
|
|
|
1031
1817
|
// src/core/scanner.ts
|
|
1032
|
-
import { existsSync as
|
|
1033
|
-
import { join as
|
|
1034
|
-
import {
|
|
1818
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
1819
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
1820
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1821
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1822
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
1823
|
+
var __dirname3 = dirname4(__filename3);
|
|
1035
1824
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
1036
1825
|
"node_modules",
|
|
1037
1826
|
".git",
|
|
@@ -1052,46 +1841,70 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
1052
1841
|
"docs",
|
|
1053
1842
|
"scripts"
|
|
1054
1843
|
]);
|
|
1055
|
-
|
|
1056
|
-
"
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
]
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1844
|
+
function loadScanConfig() {
|
|
1845
|
+
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
1846
|
+
try {
|
|
1847
|
+
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
1848
|
+
const ttlHours = config.scanCacheTtlHours || 24;
|
|
1849
|
+
return {
|
|
1850
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1851
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"],
|
|
1852
|
+
cacheTtlMs: ttlHours * 60 * 60 * 1e3
|
|
1853
|
+
};
|
|
1854
|
+
} catch {
|
|
1855
|
+
return {
|
|
1856
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
1857
|
+
pluginDirs: ["plugins-official", "plugins-community"],
|
|
1858
|
+
cacheTtlMs: 24 * 60 * 60 * 1e3
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
var scanConfig = loadScanConfig();
|
|
1863
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
1864
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1066
1865
|
function scanSkills(repoPath) {
|
|
1067
1866
|
const skills = [];
|
|
1068
1867
|
const seen = /* @__PURE__ */ new Set();
|
|
1069
1868
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1070
|
-
const fullPath =
|
|
1071
|
-
if (!
|
|
1869
|
+
const fullPath = join7(repoPath, dir);
|
|
1870
|
+
if (!existsSync8(fullPath)) continue;
|
|
1072
1871
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1073
1872
|
}
|
|
1074
1873
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1075
|
-
const pluginParent =
|
|
1076
|
-
if (!
|
|
1874
|
+
const pluginParent = join7(repoPath, pluginDir);
|
|
1875
|
+
if (!existsSync8(pluginParent)) continue;
|
|
1077
1876
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1078
|
-
const pluginPath =
|
|
1877
|
+
const pluginPath = join7(pluginParent, plugin);
|
|
1079
1878
|
if (!isDirectory(pluginPath)) continue;
|
|
1080
|
-
const skillsDir =
|
|
1081
|
-
if (
|
|
1879
|
+
const skillsDir = join7(pluginPath, "skills");
|
|
1880
|
+
if (existsSync8(skillsDir)) {
|
|
1082
1881
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1083
1882
|
}
|
|
1084
|
-
const skillDir =
|
|
1085
|
-
if (
|
|
1883
|
+
const skillDir = join7(pluginPath, "skill");
|
|
1884
|
+
if (existsSync8(skillDir)) {
|
|
1086
1885
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1087
1886
|
}
|
|
1887
|
+
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
1888
|
+
const initSh = join7(pluginPath, "init.sh");
|
|
1889
|
+
if (existsSync8(rootSkillMd) && !existsSync8(initSh)) {
|
|
1890
|
+
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
1891
|
+
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1892
|
+
seen.add(frontmatter.name);
|
|
1893
|
+
skills.push({
|
|
1894
|
+
id: frontmatter.name,
|
|
1895
|
+
description: frontmatter.description,
|
|
1896
|
+
source: `${pluginDir}/${plugin}`,
|
|
1897
|
+
filePath: rootSkillMd
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1088
1901
|
}
|
|
1089
1902
|
}
|
|
1090
1903
|
return skills;
|
|
1091
1904
|
}
|
|
1092
1905
|
function parseFrontmatter(filePath) {
|
|
1093
1906
|
try {
|
|
1094
|
-
const content =
|
|
1907
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1095
1908
|
const lines = content.split("\n");
|
|
1096
1909
|
if (lines.length < 3) return null;
|
|
1097
1910
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1104,11 +1917,15 @@ function parseFrontmatter(filePath) {
|
|
|
1104
1917
|
}
|
|
1105
1918
|
if (endIndex === -1) return null;
|
|
1106
1919
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1107
|
-
const parsed =
|
|
1920
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1108
1921
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1109
1922
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1923
|
+
const trimmedName = parsed.name.trim();
|
|
1924
|
+
if (!trimmedName || /[\/\\]|\.\./.test(trimmedName) || !/^[a-zA-Z0-9._-]+$/.test(trimmedName)) {
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1110
1927
|
return {
|
|
1111
|
-
name:
|
|
1928
|
+
name: trimmedName,
|
|
1112
1929
|
description: (parsed.description || "").trim()
|
|
1113
1930
|
};
|
|
1114
1931
|
} catch {
|
|
@@ -1117,17 +1934,17 @@ function parseFrontmatter(filePath) {
|
|
|
1117
1934
|
}
|
|
1118
1935
|
function getCurrentCommit(repoPath) {
|
|
1119
1936
|
try {
|
|
1120
|
-
const headFile =
|
|
1121
|
-
if (!
|
|
1122
|
-
const headContent =
|
|
1937
|
+
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1938
|
+
if (!existsSync8(headFile)) return "unknown";
|
|
1939
|
+
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1123
1940
|
if (headContent.startsWith("ref: ")) {
|
|
1124
|
-
const refPath =
|
|
1125
|
-
if (
|
|
1126
|
-
return
|
|
1941
|
+
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1942
|
+
if (existsSync8(refPath)) {
|
|
1943
|
+
return readFileSync5(refPath, "utf-8").trim();
|
|
1127
1944
|
}
|
|
1128
|
-
const packedRefsPath =
|
|
1129
|
-
if (
|
|
1130
|
-
const packedRefs =
|
|
1945
|
+
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1946
|
+
if (existsSync8(packedRefsPath)) {
|
|
1947
|
+
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1131
1948
|
const refName = headContent.slice(5);
|
|
1132
1949
|
const lines = packedRefs.split("\n");
|
|
1133
1950
|
for (const line of lines) {
|
|
@@ -1145,12 +1962,12 @@ function getCurrentCommit(repoPath) {
|
|
|
1145
1962
|
function readScanCache() {
|
|
1146
1963
|
try {
|
|
1147
1964
|
const cachePath = getCachePath();
|
|
1148
|
-
if (!
|
|
1149
|
-
const content =
|
|
1965
|
+
if (!existsSync8(cachePath)) return null;
|
|
1966
|
+
const content = readFileSync5(cachePath, "utf-8");
|
|
1150
1967
|
const cache = JSON.parse(content);
|
|
1151
1968
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1152
1969
|
const age = Date.now() - cache.timestamp;
|
|
1153
|
-
if (age >
|
|
1970
|
+
if (age > scanConfig.cacheTtlMs) return null;
|
|
1154
1971
|
return cache;
|
|
1155
1972
|
} catch {
|
|
1156
1973
|
return null;
|
|
@@ -1159,39 +1976,24 @@ function readScanCache() {
|
|
|
1159
1976
|
function writeScanCache(cache) {
|
|
1160
1977
|
try {
|
|
1161
1978
|
const cachePath = getCachePath();
|
|
1162
|
-
const cacheDir =
|
|
1163
|
-
if (!
|
|
1979
|
+
const cacheDir = dirname4(cachePath);
|
|
1980
|
+
if (!existsSync8(cacheDir)) {
|
|
1164
1981
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1165
1982
|
}
|
|
1166
|
-
|
|
1983
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1167
1984
|
} catch {
|
|
1168
1985
|
}
|
|
1169
1986
|
}
|
|
1170
1987
|
function getCachePath() {
|
|
1171
|
-
|
|
1172
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
1988
|
+
return join7(getCannbotConfigDir(), "scan-cache.json");
|
|
1173
1989
|
}
|
|
1174
1990
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1175
|
-
const selfSkillMd = join5(dirPath, "SKILL.md");
|
|
1176
|
-
if (existsSync5(selfSkillMd)) {
|
|
1177
|
-
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1178
|
-
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1179
|
-
seen.add(frontmatter.name);
|
|
1180
|
-
skills.push({
|
|
1181
|
-
id: frontmatter.name,
|
|
1182
|
-
description: frontmatter.description,
|
|
1183
|
-
source: sourcePrefix,
|
|
1184
|
-
filePath: selfSkillMd
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
1991
|
for (const entry of safeReaddir(dirPath)) {
|
|
1190
|
-
const fullPath =
|
|
1992
|
+
const fullPath = join7(dirPath, entry);
|
|
1191
1993
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1192
1994
|
if (!isDirectory(fullPath)) continue;
|
|
1193
|
-
const skillMd =
|
|
1194
|
-
if (
|
|
1995
|
+
const skillMd = join7(fullPath, "SKILL.md");
|
|
1996
|
+
if (existsSync8(skillMd)) {
|
|
1195
1997
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1196
1998
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1197
1999
|
seen.add(frontmatter.name);
|
|
@@ -1204,38 +2006,32 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1204
2006
|
}
|
|
1205
2007
|
continue;
|
|
1206
2008
|
}
|
|
1207
|
-
scanDirectory(fullPath, repoPath, sourcePrefix, skills, seen);
|
|
2009
|
+
scanDirectory(fullPath, repoPath, sourcePrefix + "/" + entry, skills, seen);
|
|
1208
2010
|
}
|
|
1209
2011
|
}
|
|
1210
2012
|
function safeReaddir(dir) {
|
|
1211
2013
|
try {
|
|
1212
|
-
return
|
|
2014
|
+
return readdirSync2(dir);
|
|
1213
2015
|
} catch {
|
|
1214
2016
|
return [];
|
|
1215
2017
|
}
|
|
1216
2018
|
}
|
|
1217
|
-
function isDirectory(path) {
|
|
1218
|
-
try {
|
|
1219
|
-
return statSync(path).isDirectory();
|
|
1220
|
-
} catch {
|
|
1221
|
-
return false;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
2019
|
|
|
1225
2020
|
// src/core/skill-registry.ts
|
|
1226
2021
|
var CATEGORY_DEFS = [
|
|
1227
|
-
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
|
|
1228
|
-
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
|
|
1229
|
-
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
|
|
1230
|
-
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
|
|
1231
|
-
{ id: "ascendc", name: "AscendC \u5F00\u53D1" },
|
|
1232
|
-
{ id: "pypto", name: "PyPTO \u5F00\u53D1" },
|
|
1233
|
-
{ id: "tilelang", name: "TileLang \u5F00\u53D1" },
|
|
1234
|
-
{ id: "triton", name: "Triton \u5F00\u53D1" },
|
|
1235
|
-
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
|
|
1236
|
-
{ id: "
|
|
1237
|
-
{ id: "
|
|
1238
|
-
{ id: "
|
|
2022
|
+
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
|
|
2023
|
+
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
|
|
2024
|
+
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
|
|
2025
|
+
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
|
|
2026
|
+
{ id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
|
|
2027
|
+
{ id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
|
|
2028
|
+
{ id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
|
|
2029
|
+
{ id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
|
|
2030
|
+
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
|
|
2031
|
+
{ id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
|
|
2032
|
+
{ id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
|
|
2033
|
+
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
|
|
2034
|
+
{ id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
|
|
1239
2035
|
];
|
|
1240
2036
|
var dynamicSkills = [];
|
|
1241
2037
|
var initialized = false;
|
|
@@ -1243,7 +2039,8 @@ function initFromScan(scanned) {
|
|
|
1243
2039
|
dynamicSkills = scanned.map((s) => ({
|
|
1244
2040
|
id: s.id,
|
|
1245
2041
|
description: s.description,
|
|
1246
|
-
source: s.source
|
|
2042
|
+
source: s.source,
|
|
2043
|
+
filePath: s.filePath
|
|
1247
2044
|
}));
|
|
1248
2045
|
initialized = true;
|
|
1249
2046
|
}
|
|
@@ -1319,9 +2116,7 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1319
2116
|
{ id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
|
|
1320
2117
|
{ id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
|
|
1321
2118
|
{ id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
|
|
1322
|
-
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1323
|
-
{ id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
|
|
1324
|
-
{ id: "ops-easyasc-dsl", description: "EasyASC DSL \u7B97\u5B50\u5F00\u53D1", source: "plugins-community/ops-easyasc-dsl/skill" }
|
|
2119
|
+
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1325
2120
|
]
|
|
1326
2121
|
},
|
|
1327
2122
|
{
|
|
@@ -1378,7 +2173,9 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1378
2173
|
{ id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
|
|
1379
2174
|
{ id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
|
|
1380
2175
|
{ id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
|
|
1381
|
-
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" }
|
|
2176
|
+
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
|
|
2177
|
+
{ id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
|
|
2178
|
+
{ id: "science-model-npu-migration", description: "NPU \u4EE3\u7801\u7EA7\u8FC1\u79FB\uFF08\u73AF\u5883\u95E8\u7981/\u811A\u672C\u9002\u914D/\u7CBE\u5EA6\u6027\u80FD\u5BF9\u6BD4\uFF09", source: "plugins-community/science-model-npu-migration", filePath: "plugins-community/science-model-npu-migration/SKILL.md" }
|
|
1382
2179
|
]
|
|
1383
2180
|
},
|
|
1384
2181
|
{
|
|
@@ -1394,6 +2191,13 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1394
2191
|
{ id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
|
|
1395
2192
|
]
|
|
1396
2193
|
},
|
|
2194
|
+
{
|
|
2195
|
+
id: "runtime",
|
|
2196
|
+
name: "Runtime \u8FC1\u79FB",
|
|
2197
|
+
skills: [
|
|
2198
|
+
{ id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
|
|
2199
|
+
]
|
|
2200
|
+
},
|
|
1397
2201
|
{
|
|
1398
2202
|
id: "platform",
|
|
1399
2203
|
name: "\u5E73\u53F0\u5DE5\u5177",
|
|
@@ -1437,9 +2241,10 @@ function getAllSkills() {
|
|
|
1437
2241
|
}
|
|
1438
2242
|
function getAllCategories() {
|
|
1439
2243
|
const skills = getActiveSkills();
|
|
2244
|
+
const isEn = t("list_name") === "Name";
|
|
1440
2245
|
return CATEGORY_DEFS.map((def) => ({
|
|
1441
2246
|
id: def.id,
|
|
1442
|
-
name: def.name,
|
|
2247
|
+
name: isEn ? def.nameEn : def.name,
|
|
1443
2248
|
skills: skills.filter((s) => {
|
|
1444
2249
|
const catId = CATEGORY_MAP[s.id];
|
|
1445
2250
|
if (catId) return catId === def.id;
|
|
@@ -1449,11 +2254,27 @@ function getAllCategories() {
|
|
|
1449
2254
|
}
|
|
1450
2255
|
|
|
1451
2256
|
// src/core/repository.ts
|
|
1452
|
-
var
|
|
2257
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
2258
|
+
var __dirname4 = dirname5(__filename4);
|
|
2259
|
+
function loadRepoUrl() {
|
|
2260
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
2261
|
+
return process.env.CANNBOT_REPO_URL;
|
|
2262
|
+
}
|
|
2263
|
+
const configPath = join8(__dirname4, "config", "repository.yaml");
|
|
2264
|
+
try {
|
|
2265
|
+
const config = parseYaml3(readFileSync6(configPath, "utf-8"));
|
|
2266
|
+
return config.repository?.url || "https://gitcode.com/cann/cannbot-skills.git";
|
|
2267
|
+
} catch {
|
|
2268
|
+
return "https://gitcode.com/cann/cannbot-skills.git";
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
var REPO_URL = loadRepoUrl();
|
|
1453
2272
|
var RepositoryManager = class {
|
|
1454
2273
|
repoPath;
|
|
2274
|
+
customPath;
|
|
1455
2275
|
constructor(customPath) {
|
|
1456
2276
|
this.repoPath = customPath;
|
|
2277
|
+
this.customPath = customPath;
|
|
1457
2278
|
}
|
|
1458
2279
|
async ensureRepo() {
|
|
1459
2280
|
if (this.repoPath && this.isValidRepo(this.repoPath)) {
|
|
@@ -1474,10 +2295,16 @@ var RepositoryManager = class {
|
|
|
1474
2295
|
this.repoPath = cachedPath;
|
|
1475
2296
|
return cachedPath;
|
|
1476
2297
|
}
|
|
1477
|
-
|
|
2298
|
+
const clonedPath = await this.cloneRepo();
|
|
2299
|
+
return clonedPath;
|
|
1478
2300
|
}
|
|
1479
2301
|
async ensureRepoAndScan() {
|
|
1480
2302
|
const repoPath = await this.ensureRepo();
|
|
2303
|
+
const isUserProvided = !!this.customPath || !!process.env.CANNBOT_REPO_PATH;
|
|
2304
|
+
if (!isUserProvided) {
|
|
2305
|
+
await this.updateRepo();
|
|
2306
|
+
}
|
|
2307
|
+
mergeDynamicPlugins(repoPath);
|
|
1481
2308
|
const cache = readScanCache();
|
|
1482
2309
|
if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
|
|
1483
2310
|
initFromScan(cache.skills);
|
|
@@ -1497,7 +2324,9 @@ var RepositoryManager = class {
|
|
|
1497
2324
|
try {
|
|
1498
2325
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1499
2326
|
} catch {
|
|
2327
|
+
logger.warn(t("repo_pull_failed"));
|
|
1500
2328
|
}
|
|
2329
|
+
mergeDynamicPlugins(repoPath);
|
|
1501
2330
|
}
|
|
1502
2331
|
getRepoPath() {
|
|
1503
2332
|
if (!this.repoPath) {
|
|
@@ -1506,24 +2335,25 @@ var RepositoryManager = class {
|
|
|
1506
2335
|
return this.repoPath;
|
|
1507
2336
|
}
|
|
1508
2337
|
isValidRepo(path) {
|
|
1509
|
-
if (!
|
|
1510
|
-
const gitDir =
|
|
1511
|
-
const pluginsDir =
|
|
1512
|
-
return
|
|
2338
|
+
if (!existsSync9(path)) return false;
|
|
2339
|
+
const gitDir = join8(path, ".git");
|
|
2340
|
+
const pluginsDir = join8(path, "plugins-official");
|
|
2341
|
+
return existsSync9(gitDir) && existsSync9(pluginsDir);
|
|
1513
2342
|
}
|
|
1514
2343
|
async cloneRepo() {
|
|
1515
2344
|
const configDir = getCannbotConfigDir();
|
|
1516
|
-
if (!
|
|
2345
|
+
if (!existsSync9(configDir)) {
|
|
1517
2346
|
mkdirSync3(configDir, { recursive: true });
|
|
1518
2347
|
}
|
|
1519
2348
|
const targetPath = getCannbotRepoPath();
|
|
1520
|
-
if (
|
|
2349
|
+
if (existsSync9(targetPath)) {
|
|
1521
2350
|
if (this.isValidRepo(targetPath)) {
|
|
1522
2351
|
this.repoPath = targetPath;
|
|
1523
2352
|
return targetPath;
|
|
1524
2353
|
}
|
|
1525
2354
|
}
|
|
1526
2355
|
try {
|
|
2356
|
+
logger.info(t("repo_cloning"));
|
|
1527
2357
|
await execa2(
|
|
1528
2358
|
"git",
|
|
1529
2359
|
["clone", "--depth", "1", REPO_URL, targetPath],
|
|
@@ -1533,7 +2363,7 @@ var RepositoryManager = class {
|
|
|
1533
2363
|
return targetPath;
|
|
1534
2364
|
} catch (error) {
|
|
1535
2365
|
throw new Error(
|
|
1536
|
-
`Failed to clone repository: ${error instanceof Error ? error.message : "
|
|
2366
|
+
`Failed to clone repository: ${error instanceof Error ? error.message : t("error_unknown")}`
|
|
1537
2367
|
);
|
|
1538
2368
|
}
|
|
1539
2369
|
}
|
|
@@ -1543,102 +2373,102 @@ function createRepositoryManager(customPath) {
|
|
|
1543
2373
|
}
|
|
1544
2374
|
|
|
1545
2375
|
// src/core/installer.ts
|
|
1546
|
-
import { existsSync as
|
|
1547
|
-
import { join as
|
|
1548
|
-
import { execa as
|
|
2376
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2377
|
+
import { join as join12, basename as basename2 } from "path";
|
|
2378
|
+
import { execa as execa4, execaSync } from "execa";
|
|
1549
2379
|
|
|
1550
2380
|
// src/core/record.ts
|
|
1551
|
-
import { existsSync as
|
|
1552
|
-
import { join as
|
|
2381
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
2382
|
+
import { join as join9 } from "path";
|
|
1553
2383
|
function getInstallsDir() {
|
|
1554
|
-
return
|
|
2384
|
+
return join9(getCannbotConfigDir(), "installs");
|
|
1555
2385
|
}
|
|
1556
2386
|
function getRecordPath(pluginId) {
|
|
1557
|
-
return
|
|
2387
|
+
return join9(getInstallsDir(), `${pluginId}.json`);
|
|
1558
2388
|
}
|
|
1559
2389
|
function readRecord(pluginId) {
|
|
1560
2390
|
const recordPath = getRecordPath(pluginId);
|
|
1561
|
-
if (!
|
|
2391
|
+
if (!existsSync10(recordPath)) {
|
|
1562
2392
|
return null;
|
|
1563
2393
|
}
|
|
1564
2394
|
try {
|
|
1565
|
-
const content =
|
|
2395
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1566
2396
|
return JSON.parse(content);
|
|
1567
2397
|
} catch {
|
|
2398
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1568
2399
|
return null;
|
|
1569
2400
|
}
|
|
1570
2401
|
}
|
|
1571
2402
|
function writeRecord(record) {
|
|
1572
2403
|
const installsDir = getInstallsDir();
|
|
1573
|
-
if (!
|
|
2404
|
+
if (!existsSync10(installsDir)) {
|
|
1574
2405
|
mkdirSync4(installsDir, { recursive: true });
|
|
1575
2406
|
}
|
|
1576
2407
|
const recordPath = getRecordPath(record.pluginId);
|
|
1577
|
-
|
|
2408
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1578
2409
|
}
|
|
1579
2410
|
function deleteRecord(pluginId) {
|
|
1580
2411
|
const recordPath = getRecordPath(pluginId);
|
|
1581
|
-
if (
|
|
1582
|
-
|
|
2412
|
+
if (existsSync10(recordPath)) {
|
|
2413
|
+
unlinkSync2(recordPath);
|
|
1583
2414
|
}
|
|
1584
2415
|
}
|
|
1585
|
-
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
2416
|
+
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames) {
|
|
1586
2417
|
const files = [];
|
|
1587
2418
|
const directories = [];
|
|
1588
2419
|
if (manifest) {
|
|
1589
|
-
const skillsDir =
|
|
1590
|
-
if (
|
|
2420
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2421
|
+
if (existsSync10(skillsDir)) {
|
|
1591
2422
|
directories.push(skillsDir);
|
|
1592
2423
|
for (const skillName of manifest.installed_skills || []) {
|
|
1593
|
-
const skillPath =
|
|
1594
|
-
if (
|
|
2424
|
+
const skillPath = join9(skillsDir, skillName);
|
|
2425
|
+
if (existsSync10(skillPath) || isSymlink(skillPath)) {
|
|
1595
2426
|
files.push(skillPath);
|
|
1596
2427
|
}
|
|
1597
2428
|
}
|
|
1598
2429
|
}
|
|
1599
|
-
const agentsDir =
|
|
1600
|
-
if (
|
|
2430
|
+
const agentsDir = join9(configRoot, "agents");
|
|
2431
|
+
if (existsSync10(agentsDir)) {
|
|
1601
2432
|
directories.push(agentsDir);
|
|
1602
2433
|
for (const agentName of manifest.installed_agents || []) {
|
|
1603
|
-
const agentPath =
|
|
1604
|
-
const agentPathMd =
|
|
1605
|
-
if (
|
|
2434
|
+
const agentPath = join9(agentsDir, agentName);
|
|
2435
|
+
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
2436
|
+
if (existsSync10(agentPath) || isSymlink(agentPath)) {
|
|
1606
2437
|
files.push(agentPath);
|
|
1607
|
-
} else if (
|
|
2438
|
+
} else if (existsSync10(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1608
2439
|
files.push(agentPathMd);
|
|
1609
2440
|
}
|
|
1610
2441
|
}
|
|
1611
2442
|
}
|
|
1612
2443
|
}
|
|
1613
|
-
const workflowsLink =
|
|
2444
|
+
const workflowsLink = join9(configRoot, "workflows");
|
|
1614
2445
|
if (isSymlink(workflowsLink)) {
|
|
1615
2446
|
files.push(workflowsLink);
|
|
1616
2447
|
}
|
|
1617
|
-
const manifestPath =
|
|
1618
|
-
if (
|
|
2448
|
+
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
2449
|
+
if (existsSync10(manifestPath)) {
|
|
1619
2450
|
files.push(manifestPath);
|
|
1620
2451
|
}
|
|
1621
|
-
const pluginManifestPath =
|
|
1622
|
-
if (
|
|
2452
|
+
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
2453
|
+
if (existsSync10(pluginManifestPath)) {
|
|
1623
2454
|
files.push(pluginManifestPath);
|
|
1624
2455
|
}
|
|
1625
2456
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1626
|
-
const configFilePath = level === "project" ?
|
|
1627
|
-
if (
|
|
2457
|
+
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
2458
|
+
if (existsSync10(configFilePath)) {
|
|
1628
2459
|
files.push(configFilePath);
|
|
1629
2460
|
}
|
|
1630
|
-
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
2461
|
+
const repoLinks = externalRepoNames && externalRepoNames.length > 0 ? externalRepoNames : ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1631
2462
|
for (const repoName of repoLinks) {
|
|
1632
|
-
const repoLinkPath =
|
|
2463
|
+
const repoLinkPath = join9(installPath, repoName);
|
|
1633
2464
|
if (isSymlink(repoLinkPath)) {
|
|
1634
2465
|
files.push(repoLinkPath);
|
|
1635
2466
|
}
|
|
1636
|
-
const repoLinkInConfig =
|
|
2467
|
+
const repoLinkInConfig = join9(configRoot, repoName);
|
|
1637
2468
|
if (isSymlink(repoLinkInConfig)) {
|
|
1638
2469
|
files.push(repoLinkInConfig);
|
|
1639
2470
|
}
|
|
1640
2471
|
}
|
|
1641
|
-
directories.push(configRoot);
|
|
1642
2472
|
return {
|
|
1643
2473
|
pluginId,
|
|
1644
2474
|
displayName,
|
|
@@ -1651,35 +2481,29 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1651
2481
|
directories
|
|
1652
2482
|
};
|
|
1653
2483
|
}
|
|
1654
|
-
function isSymlink(path) {
|
|
1655
|
-
try {
|
|
1656
|
-
return lstatSync(path).isSymbolicLink();
|
|
1657
|
-
} catch {
|
|
1658
|
-
return false;
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
2484
|
function getSkillRecordPath() {
|
|
1662
|
-
return
|
|
2485
|
+
return join9(getInstallsDir(), "skills.json");
|
|
1663
2486
|
}
|
|
1664
2487
|
function readSkillRecord() {
|
|
1665
2488
|
const recordPath = getSkillRecordPath();
|
|
1666
|
-
if (!
|
|
2489
|
+
if (!existsSync10(recordPath)) {
|
|
1667
2490
|
return {};
|
|
1668
2491
|
}
|
|
1669
2492
|
try {
|
|
1670
|
-
const content =
|
|
2493
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1671
2494
|
return JSON.parse(content);
|
|
1672
2495
|
} catch {
|
|
2496
|
+
logger.warn(t("skill_record_corrupted"));
|
|
1673
2497
|
return {};
|
|
1674
2498
|
}
|
|
1675
2499
|
}
|
|
1676
2500
|
function writeSkillRecord(record) {
|
|
1677
2501
|
const installsDir = getInstallsDir();
|
|
1678
|
-
if (!
|
|
2502
|
+
if (!existsSync10(installsDir)) {
|
|
1679
2503
|
mkdirSync4(installsDir, { recursive: true });
|
|
1680
2504
|
}
|
|
1681
2505
|
const recordPath = getSkillRecordPath();
|
|
1682
|
-
|
|
2506
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1683
2507
|
}
|
|
1684
2508
|
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
1685
2509
|
const record = readSkillRecord();
|
|
@@ -1715,15 +2539,18 @@ function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
|
1715
2539
|
}
|
|
1716
2540
|
function getInstalledSkills(tool, level, installPath) {
|
|
1717
2541
|
const record = readSkillRecord();
|
|
1718
|
-
|
|
2542
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
2543
|
+
const configRoot = getConfigRoot(tool, level);
|
|
2544
|
+
const skillsDir = join9(configRoot, "skills");
|
|
2545
|
+
return recordedSkills.filter((skillId) => {
|
|
2546
|
+
const skillPath = join9(skillsDir, skillId);
|
|
2547
|
+
return existsSync10(skillPath) || isSymlink(skillPath);
|
|
2548
|
+
});
|
|
1719
2549
|
}
|
|
1720
2550
|
|
|
1721
2551
|
// src/core/backup.ts
|
|
1722
|
-
import { existsSync as
|
|
1723
|
-
import { join as
|
|
1724
|
-
function getAgentsFileName(tool) {
|
|
1725
|
-
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1726
|
-
}
|
|
2552
|
+
import { existsSync as existsSync11, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2553
|
+
import { join as join10 } from "path";
|
|
1727
2554
|
function detectCurrentPlugin(configRoot, tool) {
|
|
1728
2555
|
const manifest = readManifest(configRoot);
|
|
1729
2556
|
if (manifest?.team) {
|
|
@@ -1735,8 +2562,8 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1735
2562
|
};
|
|
1736
2563
|
}
|
|
1737
2564
|
}
|
|
1738
|
-
const agentsFile =
|
|
1739
|
-
if (!
|
|
2565
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2566
|
+
if (!existsSync11(agentsFile)) {
|
|
1740
2567
|
return null;
|
|
1741
2568
|
}
|
|
1742
2569
|
const allRecords = readAllRecords();
|
|
@@ -1754,13 +2581,13 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1754
2581
|
return null;
|
|
1755
2582
|
}
|
|
1756
2583
|
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1757
|
-
const agentsFile =
|
|
1758
|
-
if (!
|
|
2584
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
2585
|
+
if (!existsSync11(agentsFile)) {
|
|
1759
2586
|
return null;
|
|
1760
2587
|
}
|
|
1761
2588
|
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
1762
2589
|
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
1763
|
-
const backupPath =
|
|
2590
|
+
const backupPath = join10(configRoot, backupFileName);
|
|
1764
2591
|
try {
|
|
1765
2592
|
copyFileSync(agentsFile, backupPath);
|
|
1766
2593
|
return {
|
|
@@ -1774,7 +2601,7 @@ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
|
1774
2601
|
}
|
|
1775
2602
|
}
|
|
1776
2603
|
function findBackups(configRoot) {
|
|
1777
|
-
if (!
|
|
2604
|
+
if (!existsSync11(configRoot)) {
|
|
1778
2605
|
return [];
|
|
1779
2606
|
}
|
|
1780
2607
|
const files = readdirSync3(configRoot);
|
|
@@ -1790,7 +2617,7 @@ function findBackups(configRoot) {
|
|
|
1790
2617
|
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
1791
2618
|
const plugin = findPlugin(pluginId);
|
|
1792
2619
|
backups.push({
|
|
1793
|
-
filePath:
|
|
2620
|
+
filePath: join10(configRoot, file),
|
|
1794
2621
|
pluginId,
|
|
1795
2622
|
pluginName: plugin?.displayName || pluginId,
|
|
1796
2623
|
backupTime: timestamp
|
|
@@ -1803,7 +2630,7 @@ function findBackups(configRoot) {
|
|
|
1803
2630
|
}
|
|
1804
2631
|
function restoreBackup(backupPath, configRoot, tool) {
|
|
1805
2632
|
try {
|
|
1806
|
-
const agentsFile =
|
|
2633
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1807
2634
|
copyFileSync(backupPath, agentsFile);
|
|
1808
2635
|
return true;
|
|
1809
2636
|
} catch {
|
|
@@ -1812,8 +2639,8 @@ function restoreBackup(backupPath, configRoot, tool) {
|
|
|
1812
2639
|
}
|
|
1813
2640
|
function deleteBackup(backupPath) {
|
|
1814
2641
|
try {
|
|
1815
|
-
if (
|
|
1816
|
-
|
|
2642
|
+
if (existsSync11(backupPath)) {
|
|
2643
|
+
unlinkSync3(backupPath);
|
|
1817
2644
|
return true;
|
|
1818
2645
|
}
|
|
1819
2646
|
return false;
|
|
@@ -1832,104 +2659,339 @@ function formatTimestamp(date) {
|
|
|
1832
2659
|
}
|
|
1833
2660
|
function readAllRecords() {
|
|
1834
2661
|
const records = [];
|
|
1835
|
-
const plugins =
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
"ops-registry-invoke",
|
|
1839
|
-
"pypto-op-orchestrator",
|
|
1840
|
-
"triton-op-generator",
|
|
1841
|
-
"tilelang-op-orchestrator",
|
|
1842
|
-
"model-infer-optimize",
|
|
1843
|
-
"catlass-op-generator",
|
|
1844
|
-
"ops-code-reviewer",
|
|
1845
|
-
"torch-compile"
|
|
1846
|
-
];
|
|
1847
|
-
for (const pluginId of plugins) {
|
|
1848
|
-
const record = readRecord(pluginId);
|
|
2662
|
+
const plugins = getAllPlugins();
|
|
2663
|
+
for (const plugin of plugins) {
|
|
2664
|
+
const record = readRecord(plugin.id);
|
|
1849
2665
|
if (record) {
|
|
1850
2666
|
records.push(record);
|
|
1851
2667
|
}
|
|
1852
2668
|
}
|
|
1853
|
-
return records;
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
|
-
// src/ui/backup-prompts.ts
|
|
1857
|
-
import { select as select2 } from "@inquirer/prompts";
|
|
1858
|
-
import
|
|
1859
|
-
async function showOverwriteWarning(currentPluginName, newPluginName) {
|
|
1860
|
-
console.log();
|
|
1861
|
-
console.log(
|
|
1862
|
-
console.log();
|
|
1863
|
-
console.log(` ${t("backup_current")}: ${
|
|
1864
|
-
console.log(` ${t("backup_installing")}: ${
|
|
1865
|
-
console.log();
|
|
1866
|
-
console.log(` ${t("backup_overwrite_warning")}`);
|
|
1867
|
-
console.log(` ${t("backup_auto_create")}`);
|
|
1868
|
-
console.log();
|
|
1869
|
-
const choice = await select2({
|
|
1870
|
-
message: t("backup_select_action"),
|
|
1871
|
-
choices: [
|
|
1872
|
-
{ name: t("backup_overwrite"), value: "overwrite" },
|
|
1873
|
-
{ name: t("backup_cancel"), value: "cancel" }
|
|
1874
|
-
]
|
|
1875
|
-
});
|
|
1876
|
-
return choice;
|
|
2669
|
+
return records;
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// src/ui/backup-prompts.ts
|
|
2673
|
+
import { select as select2 } from "@inquirer/prompts";
|
|
2674
|
+
import chalk4 from "chalk";
|
|
2675
|
+
async function showOverwriteWarning(currentPluginName, newPluginName) {
|
|
2676
|
+
console.log();
|
|
2677
|
+
console.log(chalk4.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
|
|
2678
|
+
console.log();
|
|
2679
|
+
console.log(` ${t("backup_current")}: ${chalk4.cyan(currentPluginName)}`);
|
|
2680
|
+
console.log(` ${t("backup_installing")}: ${chalk4.cyan(newPluginName)}`);
|
|
2681
|
+
console.log();
|
|
2682
|
+
console.log(` ${t("backup_overwrite_warning")}`);
|
|
2683
|
+
console.log(` ${t("backup_auto_create")}`);
|
|
2684
|
+
console.log();
|
|
2685
|
+
const choice = await select2({
|
|
2686
|
+
message: t("backup_select_action"),
|
|
2687
|
+
choices: [
|
|
2688
|
+
{ name: t("backup_overwrite"), value: "overwrite" },
|
|
2689
|
+
{ name: t("backup_cancel"), value: "cancel" }
|
|
2690
|
+
]
|
|
2691
|
+
});
|
|
2692
|
+
return choice;
|
|
2693
|
+
}
|
|
2694
|
+
async function showRestorePrompt(backups) {
|
|
2695
|
+
console.log();
|
|
2696
|
+
console.log(chalk4.cyan(` ${t("backup_restore_prompt")}`));
|
|
2697
|
+
console.log();
|
|
2698
|
+
const choices = backups.map((b) => ({
|
|
2699
|
+
name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
|
|
2700
|
+
value: b.filePath
|
|
2701
|
+
}));
|
|
2702
|
+
choices.push({ name: t("backup_only_uninstall"), value: "none" });
|
|
2703
|
+
const choice = await select2({
|
|
2704
|
+
message: t("backup_select_action"),
|
|
2705
|
+
choices
|
|
2706
|
+
});
|
|
2707
|
+
return choice;
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
// src/core/plugin-installer.ts
|
|
2711
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, realpathSync, copyFileSync as copyFileSync2, cpSync, writeFileSync as writeFileSync2, rmSync } from "fs";
|
|
2712
|
+
import { join as join11, basename } from "path";
|
|
2713
|
+
import { execa as execa3 } from "execa";
|
|
2714
|
+
function installLink(source, target) {
|
|
2715
|
+
try {
|
|
2716
|
+
const resolved = realpathSync(source);
|
|
2717
|
+
if (existsSync12(target) || isSymlink(target)) {
|
|
2718
|
+
unlinkSync4(target);
|
|
2719
|
+
}
|
|
2720
|
+
try {
|
|
2721
|
+
symlinkSync(resolved, target);
|
|
2722
|
+
return "symlink";
|
|
2723
|
+
} catch (e) {
|
|
2724
|
+
if (e.code === "EPERM") {
|
|
2725
|
+
cpSync(resolved, target, { recursive: true });
|
|
2726
|
+
return "copy";
|
|
2727
|
+
}
|
|
2728
|
+
throw e;
|
|
2729
|
+
}
|
|
2730
|
+
} catch {
|
|
2731
|
+
return "failed";
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
function installConfigFile(pluginDir, configFile, tool, level, configRoot, installPath, configRootConfigLink) {
|
|
2735
|
+
const sourcePath = join11(pluginDir, configFile);
|
|
2736
|
+
if (!existsSync12(sourcePath)) return;
|
|
2737
|
+
const agentsFileName = getAgentsFileName(tool);
|
|
2738
|
+
const isProjectLevel = level === "project";
|
|
2739
|
+
let primaryTarget;
|
|
2740
|
+
if (isProjectLevel) {
|
|
2741
|
+
primaryTarget = join11(installPath, agentsFileName);
|
|
2742
|
+
} else {
|
|
2743
|
+
primaryTarget = join11(configRoot, agentsFileName);
|
|
2744
|
+
}
|
|
2745
|
+
try {
|
|
2746
|
+
const resolvedSource = realpathSync(sourcePath);
|
|
2747
|
+
if (existsSync12(primaryTarget) || isSymlink(primaryTarget)) {
|
|
2748
|
+
unlinkSync4(primaryTarget);
|
|
2749
|
+
}
|
|
2750
|
+
try {
|
|
2751
|
+
symlinkSync(resolvedSource, primaryTarget);
|
|
2752
|
+
} catch (e) {
|
|
2753
|
+
if (e.code === "EPERM") {
|
|
2754
|
+
copyFileSync2(sourcePath, primaryTarget);
|
|
2755
|
+
} else {
|
|
2756
|
+
throw e;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
} catch (e) {
|
|
2760
|
+
logger.warn(t("install_config_failed").replace("{error}", e.message));
|
|
2761
|
+
}
|
|
2762
|
+
if (isProjectLevel && primaryTarget !== join11(configRoot, agentsFileName)) {
|
|
2763
|
+
if (configRootConfigLink === false) return;
|
|
2764
|
+
const configRootTarget = join11(configRoot, agentsFileName);
|
|
2765
|
+
if (existsSync12(configRootTarget) || isSymlink(configRootTarget)) {
|
|
2766
|
+
unlinkSync4(configRootTarget);
|
|
2767
|
+
}
|
|
2768
|
+
try {
|
|
2769
|
+
symlinkSync(realpathSync(sourcePath), configRootTarget);
|
|
2770
|
+
} catch (e) {
|
|
2771
|
+
if (e.code === "EPERM") {
|
|
2772
|
+
copyFileSync2(sourcePath, configRootTarget);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
async function installExternalRepos(repos, repoPath, configRoot, installPath, level) {
|
|
2778
|
+
if (!repos || repos.length === 0) return;
|
|
2779
|
+
for (const repo of repos) {
|
|
2780
|
+
const targetDir = join11(repoPath, repo.dir);
|
|
2781
|
+
if (!existsSync12(targetDir)) {
|
|
2782
|
+
const args = ["clone"];
|
|
2783
|
+
if (repo.depth) {
|
|
2784
|
+
args.push("--depth", String(repo.depth));
|
|
2785
|
+
}
|
|
2786
|
+
if (repo.recursive) {
|
|
2787
|
+
args.push("--recursive");
|
|
2788
|
+
}
|
|
2789
|
+
args.push(repo.url, targetDir);
|
|
2790
|
+
try {
|
|
2791
|
+
await execa3("git", args, { timeout: 12e4 });
|
|
2792
|
+
} catch {
|
|
2793
|
+
logger.warn(t("install_clone_failed").replace("{url}", repo.url).replace("{dir}", repo.dir));
|
|
2794
|
+
try {
|
|
2795
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
2796
|
+
} catch {
|
|
2797
|
+
}
|
|
2798
|
+
continue;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
const linkName = basename(repo.dir);
|
|
2802
|
+
const resolved = realpathSync(targetDir);
|
|
2803
|
+
const shouldConfigRootLink = repo.configRootLink !== false && (level === "global" || repo.configRootLink === true);
|
|
2804
|
+
if (shouldConfigRootLink) {
|
|
2805
|
+
const configRootLink = join11(configRoot, linkName);
|
|
2806
|
+
try {
|
|
2807
|
+
if (existsSync12(configRootLink) || isSymlink(configRootLink)) {
|
|
2808
|
+
unlinkSync4(configRootLink);
|
|
2809
|
+
}
|
|
2810
|
+
try {
|
|
2811
|
+
symlinkSync(resolved, configRootLink);
|
|
2812
|
+
} catch (e) {
|
|
2813
|
+
if (e.code === "EPERM") {
|
|
2814
|
+
cpSync(resolved, configRootLink, { recursive: true });
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
} catch {
|
|
2818
|
+
logger.warn(t("install_link_config_failed").replace("{name}", linkName));
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (level === "project") {
|
|
2822
|
+
const projectLink = join11(installPath, linkName);
|
|
2823
|
+
try {
|
|
2824
|
+
if (existsSync12(projectLink) || isSymlink(projectLink)) {
|
|
2825
|
+
unlinkSync4(projectLink);
|
|
2826
|
+
}
|
|
2827
|
+
try {
|
|
2828
|
+
symlinkSync(resolved, projectLink);
|
|
2829
|
+
} catch (e) {
|
|
2830
|
+
if (e.code === "EPERM") {
|
|
2831
|
+
cpSync(resolved, projectLink, { recursive: true });
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
} catch {
|
|
2835
|
+
logger.warn(t("install_link_project_failed").replace("{name}", linkName));
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
function installWorkflows(pluginDir, configRoot) {
|
|
2841
|
+
const workflowsDir = join11(pluginDir, "workflows");
|
|
2842
|
+
if (!existsSync12(workflowsDir)) return;
|
|
2843
|
+
const targetPath = join11(configRoot, "workflows");
|
|
2844
|
+
try {
|
|
2845
|
+
const resolved = realpathSync(workflowsDir);
|
|
2846
|
+
if (existsSync12(targetPath) || isSymlink(targetPath)) {
|
|
2847
|
+
unlinkSync4(targetPath);
|
|
2848
|
+
}
|
|
2849
|
+
try {
|
|
2850
|
+
symlinkSync(resolved, targetPath);
|
|
2851
|
+
} catch (e) {
|
|
2852
|
+
if (e.code === "EPERM") {
|
|
2853
|
+
cpSync(resolved, targetPath, { recursive: true });
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
} catch {
|
|
2857
|
+
logger.warn(t("install_link_workflows_failed"));
|
|
2858
|
+
}
|
|
1877
2859
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2860
|
+
function writeCannbotManifest(configRoot, pluginId, version, level, tool, skills, agents) {
|
|
2861
|
+
const manifest = {
|
|
2862
|
+
brand: "CANNBot",
|
|
2863
|
+
version,
|
|
2864
|
+
team: pluginId,
|
|
2865
|
+
level,
|
|
2866
|
+
tool,
|
|
2867
|
+
installed_skills: skills,
|
|
2868
|
+
installed_agents: agents,
|
|
2869
|
+
brand_dir: configRoot,
|
|
2870
|
+
install_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
2871
|
+
};
|
|
2872
|
+
const manifestPath = join11(configRoot, "cannbot-manifest.json");
|
|
2873
|
+
writeFileSync2(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
2874
|
+
return manifest;
|
|
1892
2875
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
const
|
|
1897
|
-
|
|
2876
|
+
async function installViaManifest(plugin, repoPath, tool, level, installPath) {
|
|
2877
|
+
const cwd = installPath || process.cwd();
|
|
2878
|
+
const configRoot = getConfigRoot(tool, level, installPath);
|
|
2879
|
+
const pluginDir = join11(repoPath, plugin.dir);
|
|
2880
|
+
try {
|
|
2881
|
+
const skillsDir = join11(configRoot, "skills");
|
|
2882
|
+
const agentsDir = join11(configRoot, "agents");
|
|
2883
|
+
mkdirSync5(skillsDir, { recursive: true });
|
|
2884
|
+
mkdirSync5(agentsDir, { recursive: true });
|
|
2885
|
+
const installedSkills = [];
|
|
2886
|
+
const installedAgents = [];
|
|
2887
|
+
for (const source of plugin.installSkills || []) {
|
|
2888
|
+
const sourceDir = join11(repoPath, source.dir);
|
|
2889
|
+
if (!existsSync12(sourceDir)) {
|
|
2890
|
+
logger.warn(t("install_skill_source_not_found").replace("{dir}", source.dir));
|
|
2891
|
+
continue;
|
|
2892
|
+
}
|
|
2893
|
+
for (const skillEntry of source.skills) {
|
|
2894
|
+
const skillName = typeof skillEntry === "string" ? skillEntry : skillEntry.name;
|
|
2895
|
+
const skillAlias = typeof skillEntry === "string" ? skillName : skillEntry.as || skillName;
|
|
2896
|
+
const skillPath = join11(sourceDir, skillName);
|
|
2897
|
+
if (!existsSync12(skillPath)) {
|
|
2898
|
+
logger.warn(t("install_skill_not_found").replace("{name}", skillName).replace("{dir}", source.dir));
|
|
2899
|
+
continue;
|
|
2900
|
+
}
|
|
2901
|
+
const targetPath = join11(skillsDir, skillAlias);
|
|
2902
|
+
const result = installLink(skillPath, targetPath);
|
|
2903
|
+
if (result !== "failed") {
|
|
2904
|
+
installedSkills.push(skillAlias);
|
|
2905
|
+
} else {
|
|
2906
|
+
logger.warn(t("install_skill_failed").replace("{name}", skillAlias));
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
for (const agentName of plugin.installAgents || []) {
|
|
2911
|
+
const agentFile = agentName.endsWith(".md") ? agentName : `${agentName}.md`;
|
|
2912
|
+
const agentPath = join11(pluginDir, "agents", agentFile);
|
|
2913
|
+
if (!existsSync12(agentPath)) {
|
|
2914
|
+
logger.warn(t("install_agent_not_found").replace("{name}", agentFile));
|
|
2915
|
+
continue;
|
|
2916
|
+
}
|
|
2917
|
+
const targetPath = join11(agentsDir, agentFile);
|
|
2918
|
+
const result = installLink(agentPath, targetPath);
|
|
2919
|
+
if (result !== "failed") {
|
|
2920
|
+
installedAgents.push(agentFile);
|
|
2921
|
+
} else {
|
|
2922
|
+
logger.warn(t("install_agent_failed").replace("{name}", agentFile));
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
installConfigFile(pluginDir, plugin.configFile || "AGENTS.md", tool, level, configRoot, cwd, plugin.configRootConfigLink);
|
|
2926
|
+
installWorkflows(pluginDir, configRoot);
|
|
2927
|
+
await installExternalRepos(plugin.externalRepos, repoPath, configRoot, cwd, level);
|
|
2928
|
+
const cannbotManifest = writeCannbotManifest(
|
|
2929
|
+
configRoot,
|
|
2930
|
+
plugin.id,
|
|
2931
|
+
plugin.version || "1.0.0",
|
|
2932
|
+
level,
|
|
2933
|
+
tool,
|
|
2934
|
+
installedSkills,
|
|
2935
|
+
installedAgents
|
|
2936
|
+
);
|
|
2937
|
+
return {
|
|
2938
|
+
success: true,
|
|
2939
|
+
skillsCount: installedSkills.length,
|
|
2940
|
+
agentsCount: installedAgents.length,
|
|
2941
|
+
errors: [],
|
|
2942
|
+
manifest: cannbotManifest
|
|
2943
|
+
};
|
|
2944
|
+
} catch (error) {
|
|
1898
2945
|
return {
|
|
1899
2946
|
success: false,
|
|
1900
|
-
pluginId: opts.pluginId,
|
|
1901
2947
|
skillsCount: 0,
|
|
1902
2948
|
agentsCount: 0,
|
|
1903
|
-
errors: [
|
|
1904
|
-
|
|
2949
|
+
errors: [error instanceof Error ? error.message : t("error_unknown")],
|
|
2950
|
+
manifest: null
|
|
1905
2951
|
};
|
|
1906
2952
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
// src/core/installer.ts
|
|
2956
|
+
function findShell() {
|
|
2957
|
+
const candidates = ["bash", "sh"];
|
|
2958
|
+
for (const cmd of candidates) {
|
|
2959
|
+
try {
|
|
2960
|
+
const detectCmd = process.platform === "win32" ? "where" : "which";
|
|
2961
|
+
execaSync(detectCmd, [cmd], { timeout: 3e3 });
|
|
2962
|
+
return cmd;
|
|
2963
|
+
} catch {
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
return null;
|
|
2967
|
+
}
|
|
2968
|
+
async function installPlugin(opts) {
|
|
2969
|
+
const plugin = getPluginById(opts.pluginId);
|
|
2970
|
+
if (!plugin) {
|
|
1909
2971
|
return {
|
|
1910
2972
|
success: false,
|
|
1911
2973
|
pluginId: opts.pluginId,
|
|
1912
2974
|
skillsCount: 0,
|
|
1913
2975
|
agentsCount: 0,
|
|
1914
|
-
errors: [
|
|
2976
|
+
errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
|
|
1915
2977
|
warnings: []
|
|
1916
2978
|
};
|
|
1917
2979
|
}
|
|
1918
|
-
const
|
|
1919
|
-
if (opts.installPath) {
|
|
1920
|
-
args.push(opts.installPath);
|
|
1921
|
-
}
|
|
2980
|
+
const pluginDir = join12(opts.repoPath, plugin.dir);
|
|
1922
2981
|
const cwd = opts.installPath || process.cwd();
|
|
1923
2982
|
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1924
|
-
const agentsFile =
|
|
2983
|
+
const agentsFile = join12(configRoot, getAgentsFileName(opts.tool));
|
|
1925
2984
|
let backupInfo = null;
|
|
1926
|
-
if (
|
|
2985
|
+
if (existsSync13(agentsFile)) {
|
|
1927
2986
|
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
1928
2987
|
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2988
|
+
let choice = "overwrite";
|
|
2989
|
+
if (!opts.yes) {
|
|
2990
|
+
choice = await showOverwriteWarning(
|
|
2991
|
+
currentPlugin.pluginName,
|
|
2992
|
+
plugin.displayName
|
|
2993
|
+
);
|
|
2994
|
+
}
|
|
1933
2995
|
if (choice === "cancel") {
|
|
1934
2996
|
logger.info(t("backup_cancel"));
|
|
1935
2997
|
return {
|
|
@@ -1947,17 +3009,105 @@ async function installPlugin(opts) {
|
|
|
1947
3009
|
currentPlugin.pluginId,
|
|
1948
3010
|
currentPlugin.pluginName
|
|
1949
3011
|
);
|
|
1950
|
-
if (backupInfo) {
|
|
1951
|
-
logger.
|
|
3012
|
+
if (!backupInfo) {
|
|
3013
|
+
logger.error(t("backup_failed"));
|
|
3014
|
+
return {
|
|
3015
|
+
success: false,
|
|
3016
|
+
pluginId: opts.pluginId,
|
|
3017
|
+
skillsCount: 0,
|
|
3018
|
+
agentsCount: 0,
|
|
3019
|
+
errors: [t("backup_failed")],
|
|
3020
|
+
warnings: []
|
|
3021
|
+
};
|
|
3022
|
+
}
|
|
3023
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
if (plugin.installSkills && plugin.installSkills.length > 0) {
|
|
3027
|
+
const result = await installViaManifest(
|
|
3028
|
+
plugin,
|
|
3029
|
+
opts.repoPath,
|
|
3030
|
+
opts.tool,
|
|
3031
|
+
opts.level,
|
|
3032
|
+
opts.installPath
|
|
3033
|
+
);
|
|
3034
|
+
if (result.success) {
|
|
3035
|
+
try {
|
|
3036
|
+
const record = scanInstalledFiles(
|
|
3037
|
+
opts.pluginId,
|
|
3038
|
+
plugin.displayName,
|
|
3039
|
+
opts.tool,
|
|
3040
|
+
opts.level,
|
|
3041
|
+
cwd,
|
|
3042
|
+
configRoot,
|
|
3043
|
+
result.manifest,
|
|
3044
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
3045
|
+
);
|
|
3046
|
+
if (backupInfo) {
|
|
3047
|
+
record.backup = {
|
|
3048
|
+
filePath: backupInfo.filePath,
|
|
3049
|
+
fromPluginId: backupInfo.pluginId,
|
|
3050
|
+
fromPluginName: backupInfo.pluginName,
|
|
3051
|
+
backupTime: backupInfo.backupTime
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
writeRecord(record);
|
|
3055
|
+
} catch {
|
|
3056
|
+
logger.warn(t("record_write_failed"));
|
|
1952
3057
|
}
|
|
3058
|
+
return {
|
|
3059
|
+
success: true,
|
|
3060
|
+
pluginId: opts.pluginId,
|
|
3061
|
+
skillsCount: result.skillsCount,
|
|
3062
|
+
agentsCount: result.agentsCount,
|
|
3063
|
+
errors: result.errors,
|
|
3064
|
+
warnings: []
|
|
3065
|
+
};
|
|
3066
|
+
} else {
|
|
3067
|
+
return {
|
|
3068
|
+
success: false,
|
|
3069
|
+
pluginId: opts.pluginId,
|
|
3070
|
+
skillsCount: 0,
|
|
3071
|
+
agentsCount: 0,
|
|
3072
|
+
errors: result.errors,
|
|
3073
|
+
warnings: []
|
|
3074
|
+
};
|
|
1953
3075
|
}
|
|
1954
3076
|
}
|
|
3077
|
+
const scriptPath = join12(pluginDir, plugin.script);
|
|
3078
|
+
if (!existsSync13(scriptPath)) {
|
|
3079
|
+
return {
|
|
3080
|
+
success: false,
|
|
3081
|
+
pluginId: opts.pluginId,
|
|
3082
|
+
skillsCount: 0,
|
|
3083
|
+
agentsCount: 0,
|
|
3084
|
+
errors: [t("error_script_not_found").replace("{path}", scriptPath)],
|
|
3085
|
+
warnings: []
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
const args = [opts.level, opts.tool];
|
|
3089
|
+
if (opts.installPath) {
|
|
3090
|
+
args.push(opts.installPath);
|
|
3091
|
+
}
|
|
1955
3092
|
try {
|
|
1956
|
-
|
|
3093
|
+
const shell = findShell();
|
|
3094
|
+
if (!shell) {
|
|
3095
|
+
return {
|
|
3096
|
+
success: false,
|
|
3097
|
+
pluginId: opts.pluginId,
|
|
3098
|
+
skillsCount: 0,
|
|
3099
|
+
agentsCount: 0,
|
|
3100
|
+
errors: [t("error_no_shell")],
|
|
3101
|
+
warnings: []
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
const execOptions = {
|
|
1957
3105
|
cwd,
|
|
1958
3106
|
timeout: 3e5,
|
|
1959
|
-
stdio: "pipe"
|
|
1960
|
-
|
|
3107
|
+
stdio: "pipe",
|
|
3108
|
+
input: opts.yes ? "y\n" : void 0
|
|
3109
|
+
};
|
|
3110
|
+
await execa4(shell, [scriptPath, ...args], execOptions);
|
|
1961
3111
|
const manifest = readManifest(configRoot);
|
|
1962
3112
|
let skillsCount = 0;
|
|
1963
3113
|
let agentsCount = 0;
|
|
@@ -1976,7 +3126,8 @@ async function installPlugin(opts) {
|
|
|
1976
3126
|
opts.level,
|
|
1977
3127
|
cwd,
|
|
1978
3128
|
configRoot,
|
|
1979
|
-
manifest
|
|
3129
|
+
manifest,
|
|
3130
|
+
plugin.externalRepos?.map((r) => basename2(r.dir))
|
|
1980
3131
|
);
|
|
1981
3132
|
if (backupInfo) {
|
|
1982
3133
|
record.backup = {
|
|
@@ -1988,6 +3139,7 @@ async function installPlugin(opts) {
|
|
|
1988
3139
|
}
|
|
1989
3140
|
writeRecord(record);
|
|
1990
3141
|
} catch {
|
|
3142
|
+
logger.warn(t("record_write_failed"));
|
|
1991
3143
|
}
|
|
1992
3144
|
return {
|
|
1993
3145
|
success: true,
|
|
@@ -1998,7 +3150,7 @@ async function installPlugin(opts) {
|
|
|
1998
3150
|
warnings: []
|
|
1999
3151
|
};
|
|
2000
3152
|
} catch (error) {
|
|
2001
|
-
const errorMessage = error instanceof Error ? error.message : "
|
|
3153
|
+
const errorMessage = error instanceof Error ? error.message : t("error_unknown");
|
|
2002
3154
|
return {
|
|
2003
3155
|
success: false,
|
|
2004
3156
|
pluginId: opts.pluginId,
|
|
@@ -2011,41 +3163,46 @@ async function installPlugin(opts) {
|
|
|
2011
3163
|
}
|
|
2012
3164
|
|
|
2013
3165
|
// src/core/skill-installer.ts
|
|
2014
|
-
import { existsSync as
|
|
2015
|
-
import { join as
|
|
2016
|
-
import { select as select3, checkbox
|
|
2017
|
-
import
|
|
3166
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, unlinkSync as unlinkSync5, readdirSync as readdirSync4, rmdirSync, realpathSync as realpathSync2 } from "fs";
|
|
3167
|
+
import { join as join13, dirname as dirname6 } from "path";
|
|
3168
|
+
import { select as select3, checkbox, Separator as Separator2 } from "@inquirer/prompts";
|
|
3169
|
+
import chalk5 from "chalk";
|
|
2018
3170
|
import Table from "cli-table3";
|
|
2019
3171
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
2020
3172
|
const configRoot = getConfigRoot(tool, level);
|
|
2021
|
-
const skillsDir =
|
|
3173
|
+
const skillsDir = join13(configRoot, "skills");
|
|
2022
3174
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
2023
|
-
if (!
|
|
2024
|
-
|
|
3175
|
+
if (!existsSync14(skillsDir)) {
|
|
3176
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
2025
3177
|
}
|
|
2026
3178
|
const results = [];
|
|
2027
3179
|
const installedIds = [];
|
|
2028
3180
|
for (const skillId of skillIds) {
|
|
2029
3181
|
const skill = findSkill(skillId);
|
|
2030
3182
|
if (!skill) {
|
|
2031
|
-
results.push({ skillId, success: false, error: "
|
|
3183
|
+
results.push({ skillId, success: false, error: t("error_skill_not_found") });
|
|
2032
3184
|
continue;
|
|
2033
3185
|
}
|
|
2034
|
-
const sourcePath =
|
|
2035
|
-
const targetPath =
|
|
2036
|
-
if (!
|
|
2037
|
-
results.push({ skillId, success: false, error:
|
|
3186
|
+
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
3187
|
+
const targetPath = join13(skillsDir, skillId);
|
|
3188
|
+
if (!existsSync14(sourcePath)) {
|
|
3189
|
+
results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
|
|
2038
3190
|
continue;
|
|
2039
3191
|
}
|
|
2040
3192
|
try {
|
|
2041
|
-
|
|
2042
|
-
|
|
3193
|
+
const resolvedSource = realpathSync2(sourcePath);
|
|
3194
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3195
|
+
unlinkSync5(targetPath);
|
|
2043
3196
|
}
|
|
2044
|
-
|
|
3197
|
+
symlinkSync2(resolvedSource, targetPath);
|
|
2045
3198
|
results.push({ skillId, success: true });
|
|
2046
3199
|
installedIds.push(skillId);
|
|
2047
3200
|
} catch (error) {
|
|
2048
|
-
|
|
3201
|
+
let msg = error instanceof Error ? error.message : t("error_unknown");
|
|
3202
|
+
if (process.platform === "win32" && msg.includes("EPERM")) {
|
|
3203
|
+
msg = t("error_symlink_windows");
|
|
3204
|
+
}
|
|
3205
|
+
results.push({ skillId, success: false, error: msg });
|
|
2049
3206
|
}
|
|
2050
3207
|
}
|
|
2051
3208
|
if (installedIds.length > 0) {
|
|
@@ -2055,69 +3212,69 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
2055
3212
|
}
|
|
2056
3213
|
async function uninstallSkills(skillIds, tool, level) {
|
|
2057
3214
|
const configRoot = getConfigRoot(tool, level);
|
|
2058
|
-
const skillsDir =
|
|
3215
|
+
const skillsDir = join13(configRoot, "skills");
|
|
2059
3216
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
2060
3217
|
const results = [];
|
|
2061
3218
|
const removedIds = [];
|
|
2062
3219
|
for (const skillId of skillIds) {
|
|
2063
|
-
const targetPath =
|
|
3220
|
+
const targetPath = join13(skillsDir, skillId);
|
|
2064
3221
|
try {
|
|
2065
|
-
if (
|
|
2066
|
-
|
|
3222
|
+
if (existsSync14(targetPath) || isSymlink(targetPath)) {
|
|
3223
|
+
unlinkSync5(targetPath);
|
|
2067
3224
|
results.push({ skillId, success: true });
|
|
2068
3225
|
removedIds.push(skillId);
|
|
2069
|
-
logger.step(`
|
|
3226
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
2070
3227
|
} else {
|
|
2071
|
-
results.push({ skillId, success: false, error: "
|
|
2072
|
-
logger.warn(`${skillId}
|
|
3228
|
+
results.push({ skillId, success: false, error: t("error_not_installed") });
|
|
3229
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
2073
3230
|
}
|
|
2074
3231
|
} catch (error) {
|
|
2075
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
3232
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
2076
3233
|
}
|
|
2077
3234
|
}
|
|
2078
3235
|
if (removedIds.length > 0) {
|
|
2079
3236
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
2080
3237
|
}
|
|
2081
|
-
if (
|
|
3238
|
+
if (existsSync14(skillsDir)) {
|
|
2082
3239
|
try {
|
|
2083
3240
|
const entries = readdirSync4(skillsDir);
|
|
2084
3241
|
if (entries.length === 0) {
|
|
2085
3242
|
rmdirSync(skillsDir);
|
|
2086
|
-
logger.step(`
|
|
2087
|
-
}
|
|
2088
|
-
} catch {
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
if (existsSync10(configRoot)) {
|
|
2092
|
-
try {
|
|
2093
|
-
const entries = readdirSync4(configRoot);
|
|
2094
|
-
if (entries.length === 0) {
|
|
2095
|
-
rmdirSync(configRoot);
|
|
2096
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
3243
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
2097
3244
|
}
|
|
2098
3245
|
} catch {
|
|
2099
3246
|
}
|
|
2100
3247
|
}
|
|
2101
3248
|
return results;
|
|
2102
3249
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
3250
|
+
var SELECT_ALL = "__select_all__";
|
|
3251
|
+
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
3252
|
+
var CONTINUE_SELECT = "__continue_select__";
|
|
3253
|
+
async function interactiveSkillSelect(tool, level) {
|
|
2106
3254
|
const categories = getAllCategories();
|
|
3255
|
+
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
3256
|
+
let accumulated = [];
|
|
2107
3257
|
let step = 0;
|
|
2108
3258
|
let selectedCategoryId = "";
|
|
2109
3259
|
while (true) {
|
|
2110
3260
|
switch (step) {
|
|
2111
3261
|
case 0: {
|
|
2112
|
-
|
|
3262
|
+
const titleSuffix = accumulated.length > 0 ? `${t("wizard_step_3_skill_title")}\uFF08${t("skill_count_format").replace("{count}", String(accumulated.length))}\uFF09` : t("wizard_step_3_skill_title");
|
|
3263
|
+
printBoxTitle(titleSuffix);
|
|
2113
3264
|
const categoryChoices = [
|
|
3265
|
+
{
|
|
3266
|
+
name: `>> ${t("skill_select_all").replace("{count}", String(allSkillIds.length))}`,
|
|
3267
|
+
value: SELECT_ALL
|
|
3268
|
+
},
|
|
3269
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2114
3270
|
...categories.map((cat) => ({
|
|
2115
3271
|
name: `> ${cat.name} (${cat.skills.length} skills)`,
|
|
2116
3272
|
value: cat.id
|
|
2117
3273
|
})),
|
|
2118
3274
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2119
|
-
{ name:
|
|
2120
|
-
{ name: "
|
|
3275
|
+
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
3276
|
+
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
3277
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2121
3278
|
];
|
|
2122
3279
|
showOperationHints();
|
|
2123
3280
|
selectedCategoryId = await select3({
|
|
@@ -2127,36 +3284,48 @@ async function interactiveSkillSelect() {
|
|
|
2127
3284
|
theme: selectTheme,
|
|
2128
3285
|
pageSize: 15
|
|
2129
3286
|
});
|
|
2130
|
-
if (selectedCategoryId ===
|
|
2131
|
-
if (selectedCategoryId ===
|
|
3287
|
+
if (selectedCategoryId === BACK) return "back";
|
|
3288
|
+
if (selectedCategoryId === CANCEL) return "cancel";
|
|
3289
|
+
if (selectedCategoryId === SELECT_ALL) {
|
|
3290
|
+
return allSkillIds;
|
|
3291
|
+
}
|
|
3292
|
+
if (selectedCategoryId === "install") {
|
|
3293
|
+
return accumulated;
|
|
3294
|
+
}
|
|
2132
3295
|
step = 1;
|
|
2133
3296
|
break;
|
|
2134
3297
|
}
|
|
2135
3298
|
case 1: {
|
|
2136
3299
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
2137
3300
|
if (!category) return [];
|
|
2138
|
-
printBoxTitle(
|
|
2139
|
-
const
|
|
2140
|
-
const
|
|
2141
|
-
const installedSkills = [];
|
|
2142
|
-
for (const tk of tools) {
|
|
2143
|
-
for (const l of levels) {
|
|
2144
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
2145
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
3301
|
+
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
3302
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
3303
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
2148
3304
|
const installedSet = new Set(installedSkills);
|
|
2149
|
-
const
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
name:
|
|
2154
|
-
value:
|
|
2155
|
-
checked:
|
|
2156
|
-
|
|
2157
|
-
|
|
3305
|
+
const categorySkillIds = new Set(category.skills.map((s) => s.id));
|
|
3306
|
+
const accumulatedInCategory = new Set(accumulated.filter((id) => categorySkillIds.has(id)));
|
|
3307
|
+
const skillChoices = [
|
|
3308
|
+
{
|
|
3309
|
+
name: `>> ${t("skill_select_all_category")}`,
|
|
3310
|
+
value: SELECT_ALL_CATEGORY,
|
|
3311
|
+
checked: accumulatedInCategory.size === category.skills.length,
|
|
3312
|
+
description: t("skill_select_all_category")
|
|
3313
|
+
},
|
|
3314
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
3315
|
+
...category.skills.map((skill) => {
|
|
3316
|
+
const isInstalled = installedSet.has(skill.id);
|
|
3317
|
+
const isAccumulated = accumulatedInCategory.has(skill.id);
|
|
3318
|
+
const suffix = isInstalled ? ` [${t("skill_already_installed")}]` : isAccumulated ? " [+]" : "";
|
|
3319
|
+
return {
|
|
3320
|
+
name: `${chalk5.cyan(skill.id)}${suffix}`,
|
|
3321
|
+
value: skill.id,
|
|
3322
|
+
checked: isAccumulated,
|
|
3323
|
+
description: skill.description
|
|
3324
|
+
};
|
|
3325
|
+
})
|
|
3326
|
+
];
|
|
2158
3327
|
showOperationHints(true);
|
|
2159
|
-
const selectedSkills = await
|
|
3328
|
+
const selectedSkills = await checkbox({
|
|
2160
3329
|
message: t("skill_select_items"),
|
|
2161
3330
|
choices: skillChoices,
|
|
2162
3331
|
loop: false,
|
|
@@ -2164,11 +3333,21 @@ async function interactiveSkillSelect() {
|
|
|
2164
3333
|
theme: checkboxTheme,
|
|
2165
3334
|
pageSize: 15
|
|
2166
3335
|
});
|
|
2167
|
-
|
|
3336
|
+
let skillIds;
|
|
3337
|
+
if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
|
|
3338
|
+
skillIds = category.skills.map((s) => s.id);
|
|
3339
|
+
} else {
|
|
3340
|
+
skillIds = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
|
|
3341
|
+
}
|
|
2168
3342
|
if (skillIds.length === 0) {
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
3343
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
3344
|
+
const action2 = await select3({
|
|
3345
|
+
message: accumulated.length > 0 ? t("skill_continue_or_install").replace("{count}", String(accumulated.length)) : t("wizard_no_selection"),
|
|
3346
|
+
choices: accumulated.length > 0 ? [
|
|
3347
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
3348
|
+
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
3349
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
3350
|
+
] : [
|
|
2172
3351
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2173
3352
|
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
2174
3353
|
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
@@ -2176,13 +3355,38 @@ async function interactiveSkillSelect() {
|
|
|
2176
3355
|
loop: false,
|
|
2177
3356
|
theme: selectTheme
|
|
2178
3357
|
});
|
|
2179
|
-
if (
|
|
3358
|
+
if (action2 === "install") return accumulated;
|
|
3359
|
+
if (action2 === "back") {
|
|
2180
3360
|
step = 0;
|
|
2181
3361
|
break;
|
|
2182
3362
|
}
|
|
2183
3363
|
return "cancel";
|
|
2184
3364
|
}
|
|
2185
|
-
|
|
3365
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
3366
|
+
for (const id of skillIds) {
|
|
3367
|
+
if (!accumulated.includes(id)) {
|
|
3368
|
+
accumulated.push(id);
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
const action = await select3({
|
|
3372
|
+
message: t("skill_continue_or_install").replace("{count}", String(accumulated.length)),
|
|
3373
|
+
choices: [
|
|
3374
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
3375
|
+
{ name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
|
|
3376
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
3377
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
3378
|
+
],
|
|
3379
|
+
loop: false,
|
|
3380
|
+
theme: selectTheme
|
|
3381
|
+
});
|
|
3382
|
+
if (action === "install") {
|
|
3383
|
+
return accumulated;
|
|
3384
|
+
}
|
|
3385
|
+
if (action === CONTINUE_SELECT) {
|
|
3386
|
+
step = 0;
|
|
3387
|
+
break;
|
|
3388
|
+
}
|
|
3389
|
+
return "cancel";
|
|
2186
3390
|
}
|
|
2187
3391
|
}
|
|
2188
3392
|
}
|
|
@@ -2190,11 +3394,11 @@ async function interactiveSkillSelect() {
|
|
|
2190
3394
|
function listAllSkills() {
|
|
2191
3395
|
const categories = getAllCategories();
|
|
2192
3396
|
console.log();
|
|
2193
|
-
console.log(
|
|
2194
|
-
console.log(
|
|
3397
|
+
console.log(chalk5.bold(` ${t("skill_list_title")}`));
|
|
3398
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
2195
3399
|
for (const category of categories) {
|
|
2196
3400
|
console.log();
|
|
2197
|
-
console.log(
|
|
3401
|
+
console.log(chalk5.bold(` ${category.name}`) + chalk5.dim(` (${category.skills.length} skills)`));
|
|
2198
3402
|
const table = new Table({
|
|
2199
3403
|
style: { head: [], border: [] },
|
|
2200
3404
|
colWidths: [35, 45],
|
|
@@ -2202,51 +3406,54 @@ function listAllSkills() {
|
|
|
2202
3406
|
});
|
|
2203
3407
|
for (const skill of category.skills) {
|
|
2204
3408
|
table.push([
|
|
2205
|
-
|
|
3409
|
+
chalk5.cyan(skill.id),
|
|
2206
3410
|
skill.description
|
|
2207
3411
|
]);
|
|
2208
3412
|
}
|
|
2209
3413
|
console.log(table.toString());
|
|
2210
3414
|
}
|
|
2211
3415
|
console.log();
|
|
2212
|
-
console.log(
|
|
3416
|
+
console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
2213
3417
|
console.log();
|
|
2214
3418
|
}
|
|
2215
|
-
function
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
3419
|
+
function resolveSkillSourcePath(skill, repoPath) {
|
|
3420
|
+
if (skill.filePath) {
|
|
3421
|
+
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join13(repoPath, skill.filePath);
|
|
3422
|
+
if (existsSync14(absPath)) {
|
|
3423
|
+
return dirname6(absPath);
|
|
3424
|
+
}
|
|
2220
3425
|
}
|
|
3426
|
+
return join13(repoPath, skill.source, skill.id);
|
|
2221
3427
|
}
|
|
2222
3428
|
|
|
2223
3429
|
// src/ui/display.ts
|
|
2224
|
-
import
|
|
3430
|
+
import chalk6 from "chalk";
|
|
2225
3431
|
import Table2 from "cli-table3";
|
|
2226
3432
|
function printPluginList(plugins, installed) {
|
|
2227
3433
|
const table = new Table2({
|
|
2228
3434
|
head: [
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
chalk5.cyan(t("list_agents"))
|
|
3435
|
+
chalk6.cyan(t("list_id")),
|
|
3436
|
+
chalk6.cyan(t("list_name")),
|
|
3437
|
+
chalk6.cyan(t("list_status")),
|
|
3438
|
+
chalk6.cyan(t("list_description"))
|
|
2234
3439
|
],
|
|
2235
|
-
style: { head: [], border: [] }
|
|
3440
|
+
style: { head: [], border: [] },
|
|
3441
|
+
colWidths: [6, 32, 8, 50],
|
|
3442
|
+
wordWrap: true
|
|
2236
3443
|
});
|
|
2237
3444
|
plugins.forEach((plugin, index) => {
|
|
2238
3445
|
const isInstalled = installed.has(plugin.id);
|
|
2239
|
-
const status = isInstalled ?
|
|
3446
|
+
const status = isInstalled ? chalk6.green(`\u2713 ${t("status_installed")}`) : chalk6.dim(`\u2014`);
|
|
2240
3447
|
table.push([
|
|
2241
3448
|
String(index + 1),
|
|
2242
3449
|
plugin.displayName,
|
|
2243
3450
|
status,
|
|
2244
|
-
|
|
2245
|
-
String(plugin.agents)
|
|
3451
|
+
plugin.description || chalk6.dim("\u2014")
|
|
2246
3452
|
]);
|
|
2247
3453
|
});
|
|
2248
3454
|
console.log();
|
|
2249
|
-
|
|
3455
|
+
const countSuffix = t("count_suffix");
|
|
3456
|
+
console.log(chalk6.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
2250
3457
|
console.log(table.toString());
|
|
2251
3458
|
console.log();
|
|
2252
3459
|
}
|
|
@@ -2255,7 +3462,7 @@ function printInstallSummary(results) {
|
|
|
2255
3462
|
const totalCount = results.length;
|
|
2256
3463
|
console.log();
|
|
2257
3464
|
console.log(
|
|
2258
|
-
|
|
3465
|
+
chalk6.bold(
|
|
2259
3466
|
` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
|
|
2260
3467
|
)
|
|
2261
3468
|
);
|
|
@@ -2263,10 +3470,10 @@ function printInstallSummary(results) {
|
|
|
2263
3470
|
for (const result of results) {
|
|
2264
3471
|
if (result.success) {
|
|
2265
3472
|
console.log(
|
|
2266
|
-
|
|
3473
|
+
chalk6.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
|
|
2267
3474
|
);
|
|
2268
3475
|
} else {
|
|
2269
|
-
console.log(
|
|
3476
|
+
console.log(chalk6.red(" \u2717") + ` ${result.displayName}`);
|
|
2270
3477
|
}
|
|
2271
3478
|
}
|
|
2272
3479
|
console.log();
|
|
@@ -2279,22 +3486,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2279
3486
|
return;
|
|
2280
3487
|
}
|
|
2281
3488
|
const toolName = getToolDisplayName(tool);
|
|
3489
|
+
const countSuffix = t("count_suffix");
|
|
2282
3490
|
const lines = [
|
|
2283
|
-
|
|
3491
|
+
chalk6.bold(` ${t("install_done")}!`),
|
|
2284
3492
|
"",
|
|
2285
|
-
` ${
|
|
2286
|
-
` ${
|
|
3493
|
+
` ${chalk6.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
3494
|
+
` ${chalk6.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk6.dim("|")} ${chalk6.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2287
3495
|
"",
|
|
2288
|
-
|
|
2289
|
-
` ${
|
|
2290
|
-
` ${
|
|
2291
|
-
` ${
|
|
2292
|
-
` ${
|
|
3496
|
+
chalk6.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
3497
|
+
` ${chalk6.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk6.green(toolName.toLowerCase())}`,
|
|
3498
|
+
` ${chalk6.cyan("2.")} ${t("install_enhanced_try")}: ${chalk6.green(t("try_prompt"))}`,
|
|
3499
|
+
` ${chalk6.cyan("3.")} ${t("install_enhanced_more")}: ${chalk6.green("install-helper list")}`,
|
|
3500
|
+
` ${chalk6.cyan("4.")} ${t("install_enhanced_check")}: ${chalk6.green("install-helper doctor")}`,
|
|
2293
3501
|
""
|
|
2294
3502
|
];
|
|
2295
3503
|
if (successResults.length === 1) {
|
|
3504
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
3505
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2296
3506
|
lines.push(
|
|
2297
|
-
` ${
|
|
3507
|
+
` ${chalk6.dim(t("install_enhanced_docs"))}: ${chalk6.dim(docsPath)}`
|
|
2298
3508
|
);
|
|
2299
3509
|
}
|
|
2300
3510
|
console.log();
|
|
@@ -2305,15 +3515,13 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2305
3515
|
}
|
|
2306
3516
|
|
|
2307
3517
|
// src/commands/init.ts
|
|
2308
|
-
import { existsSync as
|
|
2309
|
-
import { join as
|
|
2310
|
-
var BACK2 = "__back__";
|
|
2311
|
-
var CANCEL2 = "__cancel__";
|
|
3518
|
+
import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
|
|
3519
|
+
import { join as join14 } from "path";
|
|
2312
3520
|
async function initCommand() {
|
|
2313
3521
|
const cwd = process.cwd();
|
|
2314
|
-
if (
|
|
3522
|
+
if (existsSync15(join14(cwd, "package.json")) && (() => {
|
|
2315
3523
|
try {
|
|
2316
|
-
const pkg = JSON.parse(
|
|
3524
|
+
const pkg = JSON.parse(readFileSync8(join14(cwd, "package.json"), "utf-8"));
|
|
2317
3525
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2318
3526
|
} catch {
|
|
2319
3527
|
return false;
|
|
@@ -2325,14 +3533,14 @@ async function initCommand() {
|
|
|
2325
3533
|
return;
|
|
2326
3534
|
}
|
|
2327
3535
|
printBanner(t("wizard_title"));
|
|
2328
|
-
const spinner = createSpinner("
|
|
3536
|
+
const spinner = createSpinner(t("loading_skills_list"));
|
|
2329
3537
|
spinner.start();
|
|
2330
3538
|
try {
|
|
2331
3539
|
const repoManager = createRepositoryManager();
|
|
2332
3540
|
await repoManager.ensureRepoAndScan();
|
|
2333
|
-
spinner.succeed("
|
|
3541
|
+
spinner.succeed(t("loading_skills_list_complete"));
|
|
2334
3542
|
} catch {
|
|
2335
|
-
spinner.warn("
|
|
3543
|
+
spinner.warn(t("loading_skills_list_failed"));
|
|
2336
3544
|
}
|
|
2337
3545
|
let step = 0;
|
|
2338
3546
|
let mode = "plugin";
|
|
@@ -2366,8 +3574,7 @@ async function initCommand() {
|
|
|
2366
3574
|
}
|
|
2367
3575
|
}
|
|
2368
3576
|
async function selectMode() {
|
|
2369
|
-
printBoxTitle("
|
|
2370
|
-
const CANCEL3 = "__cancel__";
|
|
3577
|
+
printBoxTitle(t("wizard_select_mode_title"));
|
|
2371
3578
|
const choices = [
|
|
2372
3579
|
{
|
|
2373
3580
|
name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
|
|
@@ -2378,7 +3585,7 @@ async function selectMode() {
|
|
|
2378
3585
|
value: "skill"
|
|
2379
3586
|
},
|
|
2380
3587
|
new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2381
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
3588
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2382
3589
|
];
|
|
2383
3590
|
showOperationHints();
|
|
2384
3591
|
const result = await select4({
|
|
@@ -2387,7 +3594,7 @@ async function selectMode() {
|
|
|
2387
3594
|
loop: false,
|
|
2388
3595
|
theme: selectTheme
|
|
2389
3596
|
});
|
|
2390
|
-
if (result ===
|
|
3597
|
+
if (result === CANCEL) return null;
|
|
2391
3598
|
return result;
|
|
2392
3599
|
}
|
|
2393
3600
|
async function pluginInstallFlow() {
|
|
@@ -2398,10 +3605,10 @@ async function pluginInstallFlow() {
|
|
|
2398
3605
|
return "done";
|
|
2399
3606
|
}
|
|
2400
3607
|
const repoManager = createRepositoryManager();
|
|
2401
|
-
const spinner = createSpinner("
|
|
3608
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2402
3609
|
spinner.start();
|
|
2403
3610
|
const repoPath = await repoManager.ensureRepo();
|
|
2404
|
-
spinner.succeed("
|
|
3611
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2405
3612
|
const allPlugins = getAllPlugins();
|
|
2406
3613
|
const selectedPlugins = answers.plugins.map(
|
|
2407
3614
|
(id) => allPlugins.find((p) => p.id === id)
|
|
@@ -2464,11 +3671,11 @@ async function skillInstallFlow() {
|
|
|
2464
3671
|
}
|
|
2465
3672
|
case 1: {
|
|
2466
3673
|
const result = await stepLevel();
|
|
2467
|
-
if (result ===
|
|
3674
|
+
if (result === BACK) {
|
|
2468
3675
|
step = 0;
|
|
2469
3676
|
break;
|
|
2470
3677
|
}
|
|
2471
|
-
if (result ===
|
|
3678
|
+
if (result === CANCEL) {
|
|
2472
3679
|
logger.info(t("init_cancelled"));
|
|
2473
3680
|
return "done";
|
|
2474
3681
|
}
|
|
@@ -2477,7 +3684,7 @@ async function skillInstallFlow() {
|
|
|
2477
3684
|
break;
|
|
2478
3685
|
}
|
|
2479
3686
|
case 2: {
|
|
2480
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
3687
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2481
3688
|
if (selectedSkills === "back") {
|
|
2482
3689
|
step = 1;
|
|
2483
3690
|
break;
|
|
@@ -2487,15 +3694,15 @@ async function skillInstallFlow() {
|
|
|
2487
3694
|
return "done";
|
|
2488
3695
|
}
|
|
2489
3696
|
if (selectedSkills.length === 0) {
|
|
2490
|
-
logger.info("
|
|
3697
|
+
logger.info(t("install_no_skill_selected"));
|
|
2491
3698
|
return "done";
|
|
2492
3699
|
}
|
|
2493
3700
|
const repoManager = createRepositoryManager();
|
|
2494
|
-
const spinner = createSpinner("
|
|
3701
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2495
3702
|
spinner.start();
|
|
2496
3703
|
const repoPath = await repoManager.ensureRepo();
|
|
2497
|
-
spinner.succeed("
|
|
2498
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
3704
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3705
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk7.bold(String(selectedSkills.length)))}...`);
|
|
2499
3706
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2500
3707
|
let successCount = 0;
|
|
2501
3708
|
let failCount = 0;
|
|
@@ -2513,10 +3720,10 @@ async function skillInstallFlow() {
|
|
|
2513
3720
|
}
|
|
2514
3721
|
const configRoot = getConfigRoot(tool, level);
|
|
2515
3722
|
logger.blank();
|
|
2516
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
3723
|
+
logger.success(`${t("skill_install_done")}: ${chalk7.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk7.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk7.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2517
3724
|
logger.blank();
|
|
2518
|
-
logger.info(
|
|
2519
|
-
logger.info(
|
|
3725
|
+
logger.info(`${t("install_to")}: ${chalk7.cyan(join14(configRoot, "skills"))}`);
|
|
3726
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
|
|
2520
3727
|
logger.blank();
|
|
2521
3728
|
return "done";
|
|
2522
3729
|
}
|
|
@@ -2538,32 +3745,31 @@ async function listCommand() {
|
|
|
2538
3745
|
}
|
|
2539
3746
|
|
|
2540
3747
|
// src/commands/doctor.ts
|
|
2541
|
-
import
|
|
2542
|
-
import { existsSync as
|
|
2543
|
-
import { join as
|
|
3748
|
+
import chalk8 from "chalk";
|
|
3749
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
3750
|
+
import { join as join15 } from "path";
|
|
2544
3751
|
async function doctorCommand(options = {}) {
|
|
2545
3752
|
console.log();
|
|
2546
|
-
console.log(
|
|
2547
|
-
console.log(
|
|
2548
|
-
let errors = 0;
|
|
3753
|
+
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
3754
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2549
3755
|
let warnings = 0;
|
|
2550
3756
|
let fixes = 0;
|
|
2551
3757
|
console.log();
|
|
2552
|
-
console.log(
|
|
3758
|
+
console.log(chalk8.bold(` ${t("doctor_tools")}`));
|
|
2553
3759
|
const detectedTools = await detectTools();
|
|
2554
3760
|
const allTools = getAllTools();
|
|
2555
3761
|
for (const tool of allTools) {
|
|
2556
3762
|
const detected = detectedTools.find((d) => d.name === tool);
|
|
2557
3763
|
if (detected) {
|
|
2558
3764
|
console.log(
|
|
2559
|
-
|
|
3765
|
+
chalk8.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2560
3766
|
);
|
|
2561
3767
|
} else {
|
|
2562
|
-
console.log(
|
|
3768
|
+
console.log(chalk8.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2563
3769
|
}
|
|
2564
3770
|
}
|
|
2565
3771
|
console.log();
|
|
2566
|
-
console.log(
|
|
3772
|
+
console.log(chalk8.bold(` ${t("doctor_plugins")}`));
|
|
2567
3773
|
const plugins = getAllPlugins();
|
|
2568
3774
|
const installed = scanInstalled();
|
|
2569
3775
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
@@ -2571,80 +3777,80 @@ async function doctorCommand(options = {}) {
|
|
|
2571
3777
|
const inst = installedMap.get(plugin.id);
|
|
2572
3778
|
if (inst) {
|
|
2573
3779
|
console.log(
|
|
2574
|
-
|
|
3780
|
+
chalk8.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2575
3781
|
);
|
|
2576
3782
|
} else {
|
|
2577
|
-
console.log(
|
|
3783
|
+
console.log(chalk8.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2578
3784
|
}
|
|
2579
3785
|
}
|
|
2580
3786
|
console.log();
|
|
2581
|
-
console.log(
|
|
3787
|
+
console.log(chalk8.bold(` ${t("doctor_links")}`));
|
|
2582
3788
|
const primaryTool = detectedTools[0]?.name || "opencode";
|
|
2583
3789
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2584
3790
|
const skillsDir = getSkillsDir(configRoot);
|
|
2585
3791
|
const agentsDir = getAgentsDir(configRoot);
|
|
2586
|
-
if (
|
|
3792
|
+
if (existsSync16(skillsDir)) {
|
|
2587
3793
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2588
3794
|
if (brokenLinks === 0) {
|
|
2589
3795
|
const count = readdirSync5(skillsDir).length;
|
|
2590
|
-
console.log(
|
|
3796
|
+
console.log(chalk8.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2591
3797
|
} else {
|
|
2592
|
-
console.log(
|
|
3798
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2593
3799
|
warnings++;
|
|
2594
3800
|
if (options.fix) {
|
|
2595
3801
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2596
3802
|
fixes += fixed;
|
|
2597
|
-
console.log(
|
|
3803
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2598
3804
|
}
|
|
2599
3805
|
}
|
|
2600
3806
|
} else {
|
|
2601
|
-
console.log(
|
|
3807
|
+
console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2602
3808
|
if (options.fix) {
|
|
2603
|
-
|
|
3809
|
+
mkdirSync7(skillsDir, { recursive: true });
|
|
2604
3810
|
fixes++;
|
|
2605
|
-
console.log(
|
|
3811
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2606
3812
|
}
|
|
2607
3813
|
}
|
|
2608
|
-
if (
|
|
3814
|
+
if (existsSync16(agentsDir)) {
|
|
2609
3815
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2610
3816
|
if (brokenLinks === 0) {
|
|
2611
3817
|
const count = readdirSync5(agentsDir).length;
|
|
2612
|
-
console.log(
|
|
3818
|
+
console.log(chalk8.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2613
3819
|
} else {
|
|
2614
|
-
console.log(
|
|
3820
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2615
3821
|
warnings++;
|
|
2616
3822
|
if (options.fix) {
|
|
2617
3823
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2618
3824
|
fixes += fixed;
|
|
2619
|
-
console.log(
|
|
3825
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2620
3826
|
}
|
|
2621
3827
|
}
|
|
2622
3828
|
} else {
|
|
2623
|
-
console.log(
|
|
3829
|
+
console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2624
3830
|
if (options.fix) {
|
|
2625
|
-
|
|
3831
|
+
mkdirSync7(agentsDir, { recursive: true });
|
|
2626
3832
|
fixes++;
|
|
2627
|
-
console.log(
|
|
3833
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2628
3834
|
}
|
|
2629
3835
|
}
|
|
2630
3836
|
console.log();
|
|
2631
|
-
console.log(
|
|
3837
|
+
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2632
3838
|
const configFile = getConfigFileName(primaryTool);
|
|
2633
|
-
const configPath =
|
|
2634
|
-
if (
|
|
2635
|
-
console.log(
|
|
3839
|
+
const configPath = join15(process.cwd(), configFile);
|
|
3840
|
+
if (existsSync16(configPath)) {
|
|
3841
|
+
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2636
3842
|
} else {
|
|
2637
|
-
console.log(
|
|
3843
|
+
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2638
3844
|
}
|
|
2639
3845
|
console.log();
|
|
2640
|
-
console.log(
|
|
3846
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2641
3847
|
if (options.fix && fixes > 0) {
|
|
2642
3848
|
console.log(
|
|
2643
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3849
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
|
|
2644
3850
|
);
|
|
2645
3851
|
} else {
|
|
2646
3852
|
console.log(
|
|
2647
|
-
` ${t("doctor_result")}: ${warnings}
|
|
3853
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
|
|
2648
3854
|
);
|
|
2649
3855
|
}
|
|
2650
3856
|
console.log();
|
|
@@ -2654,11 +3860,11 @@ function checkBrokenLinks(dir) {
|
|
|
2654
3860
|
try {
|
|
2655
3861
|
const entries = readdirSync5(dir);
|
|
2656
3862
|
for (const entry of entries) {
|
|
2657
|
-
const fullPath =
|
|
3863
|
+
const fullPath = join15(dir, entry);
|
|
2658
3864
|
try {
|
|
2659
|
-
const stats =
|
|
3865
|
+
const stats = lstatSync2(fullPath);
|
|
2660
3866
|
if (stats.isSymbolicLink()) {
|
|
2661
|
-
if (!
|
|
3867
|
+
if (!existsSync16(fullPath)) {
|
|
2662
3868
|
broken++;
|
|
2663
3869
|
}
|
|
2664
3870
|
}
|
|
@@ -2675,12 +3881,12 @@ function fixBrokenLinks(dir) {
|
|
|
2675
3881
|
try {
|
|
2676
3882
|
const entries = readdirSync5(dir);
|
|
2677
3883
|
for (const entry of entries) {
|
|
2678
|
-
const fullPath =
|
|
3884
|
+
const fullPath = join15(dir, entry);
|
|
2679
3885
|
try {
|
|
2680
|
-
const stats =
|
|
3886
|
+
const stats = lstatSync2(fullPath);
|
|
2681
3887
|
if (stats.isSymbolicLink()) {
|
|
2682
|
-
if (!
|
|
2683
|
-
|
|
3888
|
+
if (!existsSync16(fullPath)) {
|
|
3889
|
+
unlinkSync6(fullPath);
|
|
2684
3890
|
fixed++;
|
|
2685
3891
|
}
|
|
2686
3892
|
}
|
|
@@ -2693,36 +3899,36 @@ function fixBrokenLinks(dir) {
|
|
|
2693
3899
|
}
|
|
2694
3900
|
|
|
2695
3901
|
// src/commands/status.ts
|
|
2696
|
-
import
|
|
3902
|
+
import chalk9 from "chalk";
|
|
2697
3903
|
async function statusCommand() {
|
|
2698
|
-
const plugins = getAllPlugins();
|
|
2699
3904
|
const installed = scanInstalled();
|
|
2700
3905
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2701
3906
|
console.log();
|
|
2702
|
-
console.log(
|
|
3907
|
+
console.log(chalk9.bold(` ${t("status_title")}`));
|
|
2703
3908
|
console.log();
|
|
2704
3909
|
if (installed.length === 0) {
|
|
2705
|
-
console.log(
|
|
2706
|
-
console.log(
|
|
3910
|
+
console.log(chalk9.dim(` ${t("status_none")}`));
|
|
3911
|
+
console.log(chalk9.dim(` ${t("status_hint")}`));
|
|
2707
3912
|
console.log();
|
|
2708
3913
|
return;
|
|
2709
3914
|
}
|
|
2710
3915
|
for (const inst of installed) {
|
|
2711
3916
|
console.log(
|
|
2712
|
-
|
|
3917
|
+
chalk9.green(" \u2713") + ` ${inst.displayName}` + chalk9.dim(` (${inst.tool}, ${inst.level})`)
|
|
2713
3918
|
);
|
|
2714
3919
|
console.log(
|
|
2715
|
-
|
|
3920
|
+
chalk9.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
|
|
2716
3921
|
);
|
|
2717
|
-
console.log(
|
|
2718
|
-
console.log(
|
|
3922
|
+
console.log(chalk9.dim(` ${inst.configRoot}`));
|
|
3923
|
+
console.log(chalk9.dim(` ${t("status_install_time")}: ${inst.installTime}`));
|
|
2719
3924
|
console.log();
|
|
2720
3925
|
}
|
|
2721
3926
|
}
|
|
2722
3927
|
|
|
2723
3928
|
// src/commands/install.ts
|
|
3929
|
+
import { join as join16 } from "path";
|
|
2724
3930
|
import { confirm } from "@inquirer/prompts";
|
|
2725
|
-
import
|
|
3931
|
+
import chalk10 from "chalk";
|
|
2726
3932
|
async function installCommand(names, options) {
|
|
2727
3933
|
if (options.list) {
|
|
2728
3934
|
try {
|
|
@@ -2733,47 +3939,115 @@ async function installCommand(names, options) {
|
|
|
2733
3939
|
listAllSkills();
|
|
2734
3940
|
return;
|
|
2735
3941
|
}
|
|
3942
|
+
if (options.all) {
|
|
3943
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
3944
|
+
scanSpinner2.start();
|
|
3945
|
+
try {
|
|
3946
|
+
const scanRepoManager = createRepositoryManager();
|
|
3947
|
+
await scanRepoManager.ensureRepoAndScan();
|
|
3948
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
3949
|
+
} catch {
|
|
3950
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
3951
|
+
}
|
|
3952
|
+
const allSkills = getAllSkills();
|
|
3953
|
+
const skillIds = allSkills.map((s) => s.id);
|
|
3954
|
+
if (skillIds.length === 0) {
|
|
3955
|
+
logger.error(t("install_no_skill_selected"));
|
|
3956
|
+
return;
|
|
3957
|
+
}
|
|
3958
|
+
let tool2;
|
|
3959
|
+
if (options.tool) {
|
|
3960
|
+
tool2 = validateTool(options.tool);
|
|
3961
|
+
} else {
|
|
3962
|
+
const detected = await selectToolWithDetection();
|
|
3963
|
+
if (detected === "back" || detected === "cancel") {
|
|
3964
|
+
logger.info(t("init_cancelled"));
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
3967
|
+
tool2 = detected;
|
|
3968
|
+
}
|
|
3969
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
3970
|
+
const repoManager = createRepositoryManager();
|
|
3971
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
3972
|
+
spinner.start();
|
|
3973
|
+
const repoPath = await repoManager.ensureRepo();
|
|
3974
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
3975
|
+
logger.info(`${t("skill_all_install_progress").replace("{count}", chalk10.bold(String(skillIds.length)))}`);
|
|
3976
|
+
const results = await installSkills(skillIds, tool2, level2, repoPath);
|
|
3977
|
+
let successCount = 0;
|
|
3978
|
+
let failCount = 0;
|
|
3979
|
+
const total = results.length;
|
|
3980
|
+
for (let i = 0; i < results.length; i++) {
|
|
3981
|
+
const result = results[i];
|
|
3982
|
+
const progress = `[${i + 1}/${total}]`;
|
|
3983
|
+
if (result.success) {
|
|
3984
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
3985
|
+
successCount++;
|
|
3986
|
+
} else {
|
|
3987
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
3988
|
+
failCount++;
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
const configRoot = getConfigRoot(tool2, level2);
|
|
3992
|
+
logger.blank();
|
|
3993
|
+
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
3994
|
+
logger.blank();
|
|
3995
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
|
|
3996
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
3997
|
+
logger.blank();
|
|
3998
|
+
return;
|
|
3999
|
+
}
|
|
2736
4000
|
if (names.length === 0) {
|
|
2737
|
-
const scanSpinner2 = createSpinner("
|
|
4001
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2738
4002
|
scanSpinner2.start();
|
|
2739
4003
|
try {
|
|
2740
4004
|
const scanRepoManager = createRepositoryManager();
|
|
2741
4005
|
await scanRepoManager.ensureRepoAndScan();
|
|
2742
|
-
scanSpinner2.succeed("
|
|
4006
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2743
4007
|
} catch {
|
|
2744
|
-
scanSpinner2.warn("
|
|
4008
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2745
4009
|
}
|
|
2746
4010
|
let tool2;
|
|
2747
4011
|
if (options.tool) {
|
|
2748
|
-
tool2 = options.tool;
|
|
4012
|
+
tool2 = validateTool(options.tool);
|
|
2749
4013
|
} else {
|
|
2750
4014
|
const detected = await selectToolWithDetection();
|
|
2751
|
-
if (detected === "back")
|
|
4015
|
+
if (detected === "back" || detected === "cancel") {
|
|
4016
|
+
logger.info(t("init_cancelled"));
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
2752
4019
|
tool2 = detected;
|
|
2753
4020
|
}
|
|
4021
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2754
4022
|
let step = 1;
|
|
2755
4023
|
while (true) {
|
|
2756
4024
|
switch (step) {
|
|
2757
4025
|
case 1: {
|
|
2758
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
4026
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2759
4027
|
if (selectedSkills === "back") {
|
|
2760
4028
|
const redetected = await selectToolWithDetection();
|
|
2761
|
-
if (redetected === "back")
|
|
4029
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
4030
|
+
logger.info(t("init_cancelled"));
|
|
4031
|
+
return;
|
|
4032
|
+
}
|
|
2762
4033
|
tool2 = redetected;
|
|
2763
4034
|
break;
|
|
2764
4035
|
}
|
|
4036
|
+
if (selectedSkills === "cancel") {
|
|
4037
|
+
logger.info(t("init_cancelled"));
|
|
4038
|
+
return;
|
|
4039
|
+
}
|
|
2765
4040
|
if (selectedSkills.length === 0) {
|
|
2766
|
-
logger.info("
|
|
4041
|
+
logger.info(t("install_no_skill_selected"));
|
|
2767
4042
|
return;
|
|
2768
4043
|
}
|
|
2769
4044
|
names = selectedSkills;
|
|
2770
4045
|
const repoManager = createRepositoryManager();
|
|
2771
|
-
const spinner = createSpinner("
|
|
4046
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2772
4047
|
spinner.start();
|
|
2773
4048
|
const repoPath = await repoManager.ensureRepo();
|
|
2774
|
-
spinner.succeed("
|
|
2775
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2776
|
-
const level2 = options.level || "project";
|
|
4049
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
4050
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(names.length)))}...`);
|
|
2777
4051
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2778
4052
|
let successCount = 0;
|
|
2779
4053
|
let failCount = 0;
|
|
@@ -2791,24 +4065,24 @@ async function installCommand(names, options) {
|
|
|
2791
4065
|
}
|
|
2792
4066
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2793
4067
|
logger.blank();
|
|
2794
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
4068
|
+
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2795
4069
|
logger.blank();
|
|
2796
|
-
logger.info(
|
|
2797
|
-
logger.info(
|
|
4070
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(configRoot, "skills"))}`);
|
|
4071
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2798
4072
|
logger.blank();
|
|
2799
4073
|
return;
|
|
2800
4074
|
}
|
|
2801
4075
|
}
|
|
2802
4076
|
}
|
|
2803
4077
|
}
|
|
2804
|
-
const scanSpinner = createSpinner("
|
|
4078
|
+
const scanSpinner = createSpinner(t("loading_skills_list"));
|
|
2805
4079
|
scanSpinner.start();
|
|
2806
4080
|
try {
|
|
2807
4081
|
const scanRepoManager = createRepositoryManager();
|
|
2808
4082
|
await scanRepoManager.ensureRepoAndScan();
|
|
2809
|
-
scanSpinner.succeed("
|
|
4083
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2810
4084
|
} catch {
|
|
2811
|
-
scanSpinner.warn("
|
|
4085
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2812
4086
|
}
|
|
2813
4087
|
const plugins = [];
|
|
2814
4088
|
const skills = [];
|
|
@@ -2825,22 +4099,25 @@ async function installCommand(names, options) {
|
|
|
2825
4099
|
}
|
|
2826
4100
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2827
4101
|
const allPlugins = getAllPlugins();
|
|
2828
|
-
logger.info("
|
|
4102
|
+
logger.info(t("available_plugins") + ":");
|
|
2829
4103
|
for (const p of allPlugins) {
|
|
2830
4104
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2831
4105
|
}
|
|
2832
|
-
logger.info(
|
|
4106
|
+
logger.info(`${t("available_skills")}: ${chalk10.cyan("install-helper install --list")}`);
|
|
2833
4107
|
return;
|
|
2834
4108
|
}
|
|
2835
4109
|
let tool;
|
|
2836
4110
|
if (options.tool) {
|
|
2837
|
-
tool = options.tool;
|
|
4111
|
+
tool = validateTool(options.tool);
|
|
2838
4112
|
} else {
|
|
2839
4113
|
const detected = await selectToolWithDetection();
|
|
2840
|
-
if (detected === "back")
|
|
4114
|
+
if (detected === "back" || detected === "cancel") {
|
|
4115
|
+
logger.info(t("init_cancelled"));
|
|
4116
|
+
return;
|
|
4117
|
+
}
|
|
2841
4118
|
tool = detected;
|
|
2842
4119
|
}
|
|
2843
|
-
const level = options.level
|
|
4120
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2844
4121
|
if (plugins.length > 0) {
|
|
2845
4122
|
const configRoot = getConfigRoot(tool, level);
|
|
2846
4123
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2868,16 +4145,18 @@ async function installCommand(names, options) {
|
|
|
2868
4145
|
}
|
|
2869
4146
|
if (pluginsToInstall.length > 0) {
|
|
2870
4147
|
const repoManager = createRepositoryManager();
|
|
2871
|
-
const spinner = createSpinner("
|
|
4148
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2872
4149
|
spinner.start();
|
|
2873
4150
|
const repoPath = await repoManager.ensureRepo();
|
|
2874
|
-
spinner.succeed("
|
|
4151
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2875
4152
|
const allPlugins = getAllPlugins();
|
|
2876
4153
|
const results = await installPlugins(
|
|
2877
4154
|
pluginsToInstall,
|
|
2878
4155
|
tool,
|
|
2879
4156
|
level,
|
|
2880
|
-
repoPath
|
|
4157
|
+
repoPath,
|
|
4158
|
+
void 0,
|
|
4159
|
+
options.yes
|
|
2881
4160
|
);
|
|
2882
4161
|
const summary = results.map((result) => {
|
|
2883
4162
|
const plugin = allPlugins.find((p) => p.id === result.pluginId);
|
|
@@ -2895,11 +4174,11 @@ async function installCommand(names, options) {
|
|
|
2895
4174
|
}
|
|
2896
4175
|
if (skills.length > 0) {
|
|
2897
4176
|
const repoManager = createRepositoryManager();
|
|
2898
|
-
const spinner = createSpinner("
|
|
4177
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2899
4178
|
spinner.start();
|
|
2900
4179
|
const repoPath = await repoManager.ensureRepo();
|
|
2901
|
-
spinner.succeed("
|
|
2902
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
4180
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
4181
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(skills.length)))}...`);
|
|
2903
4182
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2904
4183
|
let successCount = 0;
|
|
2905
4184
|
let failCount = 0;
|
|
@@ -2916,14 +4195,14 @@ async function installCommand(names, options) {
|
|
|
2916
4195
|
}
|
|
2917
4196
|
}
|
|
2918
4197
|
logger.blank();
|
|
2919
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
4198
|
+
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2920
4199
|
logger.blank();
|
|
2921
|
-
logger.info(
|
|
2922
|
-
logger.info(
|
|
4200
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(join16(getConfigRoot(tool, level), "skills"))}`);
|
|
4201
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
2923
4202
|
logger.blank();
|
|
2924
4203
|
}
|
|
2925
4204
|
}
|
|
2926
|
-
async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
4205
|
+
async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes) {
|
|
2927
4206
|
const results = [];
|
|
2928
4207
|
const total = pluginIds.length;
|
|
2929
4208
|
for (let i = 0; i < pluginIds.length; i++) {
|
|
@@ -2935,7 +4214,9 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2935
4214
|
pluginId,
|
|
2936
4215
|
tool,
|
|
2937
4216
|
level,
|
|
2938
|
-
repoPath
|
|
4217
|
+
repoPath,
|
|
4218
|
+
installPath,
|
|
4219
|
+
yes
|
|
2939
4220
|
});
|
|
2940
4221
|
if (result.success) {
|
|
2941
4222
|
spinner.succeed(
|
|
@@ -2951,11 +4232,12 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2951
4232
|
}
|
|
2952
4233
|
|
|
2953
4234
|
// src/commands/uninstall.ts
|
|
2954
|
-
import { existsSync as
|
|
2955
|
-
import
|
|
4235
|
+
import { existsSync as existsSync17, unlinkSync as unlinkSync7, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
|
|
4236
|
+
import { resolve, basename as basename3, sep } from "path";
|
|
4237
|
+
import chalk11 from "chalk";
|
|
2956
4238
|
async function uninstallCommand(names, options) {
|
|
2957
|
-
const tool = options.tool
|
|
2958
|
-
const level = options.level
|
|
4239
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
4240
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2959
4241
|
const plugins = [];
|
|
2960
4242
|
const skills = [];
|
|
2961
4243
|
for (const name of names) {
|
|
@@ -2981,28 +4263,38 @@ async function uninstallCommand(names, options) {
|
|
|
2981
4263
|
continue;
|
|
2982
4264
|
}
|
|
2983
4265
|
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
4266
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
4267
|
+
const allowedBases = [resolve(configRoot), resolve(record.installPath)];
|
|
4268
|
+
const isSafePath = (p) => {
|
|
4269
|
+
const resolved = resolve(p);
|
|
4270
|
+
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + sep));
|
|
4271
|
+
};
|
|
2984
4272
|
let removedFiles = 0;
|
|
2985
4273
|
let removedDirs = 0;
|
|
2986
4274
|
for (const filePath of record.files) {
|
|
4275
|
+
if (!isSafePath(filePath)) continue;
|
|
2987
4276
|
try {
|
|
2988
|
-
if (
|
|
2989
|
-
|
|
2990
|
-
const name = filePath
|
|
2991
|
-
logger.step(`
|
|
4277
|
+
if (existsSync17(filePath) || isSymlink(filePath)) {
|
|
4278
|
+
unlinkSync7(filePath);
|
|
4279
|
+
const name = basename3(filePath);
|
|
4280
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2992
4281
|
removedFiles++;
|
|
2993
4282
|
}
|
|
2994
4283
|
} catch {
|
|
2995
4284
|
}
|
|
2996
4285
|
}
|
|
2997
|
-
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
4286
|
+
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
2998
4287
|
for (const dirPath of sortedDirs) {
|
|
2999
4288
|
try {
|
|
3000
|
-
if (
|
|
4289
|
+
if (resolve(dirPath) === resolve(configRoot)) {
|
|
4290
|
+
continue;
|
|
4291
|
+
}
|
|
4292
|
+
if (existsSync17(dirPath)) {
|
|
3001
4293
|
const entries = readdirSync6(dirPath);
|
|
3002
4294
|
if (entries.length === 0) {
|
|
3003
4295
|
rmdirSync2(dirPath);
|
|
3004
|
-
const name = dirPath
|
|
3005
|
-
logger.step(`
|
|
4296
|
+
const name = basename3(dirPath);
|
|
4297
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
3006
4298
|
removedDirs++;
|
|
3007
4299
|
}
|
|
3008
4300
|
}
|
|
@@ -3011,7 +4303,6 @@ async function uninstallCommand(names, options) {
|
|
|
3011
4303
|
}
|
|
3012
4304
|
deleteRecord(plugin.id);
|
|
3013
4305
|
removeInstalledPlugin(plugin.id);
|
|
3014
|
-
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3015
4306
|
const backups = findBackups(configRoot);
|
|
3016
4307
|
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
3017
4308
|
if (otherBackups.length > 0) {
|
|
@@ -3031,13 +4322,13 @@ async function uninstallCommand(names, options) {
|
|
|
3031
4322
|
}
|
|
3032
4323
|
const totalRemoved = removedFiles + removedDirs;
|
|
3033
4324
|
if (totalRemoved > 0) {
|
|
3034
|
-
logger.success(
|
|
4325
|
+
logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
|
|
3035
4326
|
} else {
|
|
3036
4327
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3037
4328
|
}
|
|
3038
4329
|
}
|
|
3039
4330
|
if (skills.length > 0) {
|
|
3040
|
-
logger.info(`${t("uninstall_skills_in_progress")} ${skills.length}
|
|
4331
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
3041
4332
|
const results = await uninstallSkills(skills, tool, level);
|
|
3042
4333
|
let successCount = 0;
|
|
3043
4334
|
let failCount = 0;
|
|
@@ -3049,30 +4340,26 @@ async function uninstallCommand(names, options) {
|
|
|
3049
4340
|
}
|
|
3050
4341
|
}
|
|
3051
4342
|
logger.blank();
|
|
3052
|
-
logger.success(`${t("skill_uninstall_done")}: ${
|
|
4343
|
+
logger.success(`${t("skill_uninstall_done")}: ${chalk11.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk11.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk11.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
3053
4344
|
logger.blank();
|
|
3054
4345
|
}
|
|
3055
4346
|
}
|
|
3056
|
-
function isSymlink3(path) {
|
|
3057
|
-
try {
|
|
3058
|
-
return lstatSync4(path).isSymbolicLink();
|
|
3059
|
-
} catch {
|
|
3060
|
-
return false;
|
|
3061
|
-
}
|
|
3062
|
-
}
|
|
3063
4347
|
|
|
3064
4348
|
// src/commands/update.ts
|
|
3065
|
-
import
|
|
4349
|
+
import chalk12 from "chalk";
|
|
3066
4350
|
async function updateCommand(pluginNames, options) {
|
|
3067
4351
|
let tool;
|
|
3068
4352
|
if (options.tool) {
|
|
3069
|
-
tool = options.tool;
|
|
4353
|
+
tool = validateTool(options.tool);
|
|
3070
4354
|
} else {
|
|
3071
4355
|
const detected = await selectToolWithDetection();
|
|
3072
|
-
if (detected === "back")
|
|
4356
|
+
if (detected === "back" || detected === "cancel") {
|
|
4357
|
+
logger.info(t("init_cancelled"));
|
|
4358
|
+
return;
|
|
4359
|
+
}
|
|
3073
4360
|
tool = detected;
|
|
3074
4361
|
}
|
|
3075
|
-
const level = options.level
|
|
4362
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
3076
4363
|
let pluginsToUpdate = [];
|
|
3077
4364
|
if (pluginNames.length === 0) {
|
|
3078
4365
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -3080,7 +4367,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3080
4367
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
3081
4368
|
if (pluginsToUpdate.length === 0) {
|
|
3082
4369
|
logger.info(t("update_no_plugins"));
|
|
3083
|
-
logger.info(
|
|
4370
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk12.cyan("install-helper install <plugin>")));
|
|
3084
4371
|
return;
|
|
3085
4372
|
}
|
|
3086
4373
|
} else {
|
|
@@ -3096,9 +4383,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3096
4383
|
const repoManager = createRepositoryManager();
|
|
3097
4384
|
const updateSpinner = createSpinner(t("update_updating") + "...");
|
|
3098
4385
|
updateSpinner.start();
|
|
3099
|
-
await repoManager.
|
|
3100
|
-
const repoPath = repoManager.getRepoPath();
|
|
3101
|
-
await repoManager.ensureRepoAndScan();
|
|
4386
|
+
const repoPath = await repoManager.ensureRepoAndScan();
|
|
3102
4387
|
updateSpinner.succeed(t("install_repo_ready"));
|
|
3103
4388
|
const allPlugins = getAllPlugins();
|
|
3104
4389
|
const results = [];
|
|
@@ -3142,42 +4427,43 @@ async function updateCommand(pluginNames, options) {
|
|
|
3142
4427
|
}
|
|
3143
4428
|
|
|
3144
4429
|
// src/commands/info.ts
|
|
3145
|
-
import
|
|
3146
|
-
import { existsSync as
|
|
3147
|
-
import { join as
|
|
4430
|
+
import chalk13 from "chalk";
|
|
4431
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
|
|
4432
|
+
import { join as join17 } from "path";
|
|
3148
4433
|
async function infoCommand(pluginName) {
|
|
3149
4434
|
const plugin = findPlugin(pluginName);
|
|
3150
4435
|
if (!plugin) {
|
|
3151
|
-
console.log(
|
|
4436
|
+
console.log(chalk13.red(` ${t("info_not_found")}: ${pluginName}`));
|
|
3152
4437
|
return;
|
|
3153
4438
|
}
|
|
3154
4439
|
const installed = scanInstalled();
|
|
3155
|
-
const
|
|
3156
|
-
const
|
|
4440
|
+
const inst = installed.find((p) => p.id === plugin.id);
|
|
4441
|
+
const isInstalled = !!inst;
|
|
4442
|
+
const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
|
|
3157
4443
|
console.log();
|
|
3158
|
-
console.log(
|
|
3159
|
-
console.log(
|
|
4444
|
+
console.log(chalk13.bold(` ${plugin.displayName}`));
|
|
4445
|
+
console.log(chalk13.dim(" " + "\u2500".repeat(40)));
|
|
3160
4446
|
console.log();
|
|
3161
|
-
console.log(` ${
|
|
3162
|
-
console.log(` ${
|
|
3163
|
-
console.log(` ${
|
|
3164
|
-
console.log(` ${
|
|
3165
|
-
console.log(` ${
|
|
3166
|
-
console.log(` ${
|
|
4447
|
+
console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
4448
|
+
console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
|
|
4449
|
+
console.log(` ${chalk13.dim(t("info_status") + ":")} ${statusText}`);
|
|
4450
|
+
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${inst ? inst.skillsCount : plugin.skills}`);
|
|
4451
|
+
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${inst ? inst.agentsCount : plugin.agents}`);
|
|
4452
|
+
console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
3167
4453
|
console.log();
|
|
3168
4454
|
try {
|
|
3169
4455
|
const repoManager = createRepositoryManager();
|
|
3170
4456
|
const repoPath = await repoManager.ensureRepo();
|
|
3171
|
-
const quickstartPath =
|
|
3172
|
-
if (
|
|
3173
|
-
console.log(` ${
|
|
4457
|
+
const quickstartPath = join17(repoPath, plugin.dir, "quickstart.md");
|
|
4458
|
+
if (existsSync18(quickstartPath)) {
|
|
4459
|
+
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
3174
4460
|
}
|
|
3175
|
-
const agentsDir =
|
|
3176
|
-
if (
|
|
4461
|
+
const agentsDir = join17(repoPath, plugin.dir, "agents");
|
|
4462
|
+
if (existsSync18(agentsDir)) {
|
|
3177
4463
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3178
4464
|
if (agentFiles.length > 0) {
|
|
3179
4465
|
console.log();
|
|
3180
|
-
console.log(` ${
|
|
4466
|
+
console.log(` ${chalk13.bold("Agents:")}`);
|
|
3181
4467
|
for (const agent of agentFiles) {
|
|
3182
4468
|
console.log(` \u2022 ${agent.replace(".md", "")}`);
|
|
3183
4469
|
}
|
|
@@ -3189,49 +4475,80 @@ async function infoCommand(pluginName) {
|
|
|
3189
4475
|
}
|
|
3190
4476
|
|
|
3191
4477
|
// src/commands/lang.ts
|
|
3192
|
-
import
|
|
4478
|
+
import chalk14 from "chalk";
|
|
3193
4479
|
async function langCommand(action, value) {
|
|
3194
4480
|
const config = readConfig();
|
|
3195
4481
|
if (!action || action === "show") {
|
|
3196
4482
|
console.log();
|
|
3197
|
-
console.log(
|
|
3198
|
-
console.log(
|
|
4483
|
+
console.log(chalk14.bold(` ${t("lang_title")}`));
|
|
4484
|
+
console.log(chalk14.dim(" " + "\u2500".repeat(40)));
|
|
3199
4485
|
console.log();
|
|
3200
|
-
console.log(` ${
|
|
4486
|
+
console.log(` ${chalk14.dim(t("lang_current") + ":")} ${config.language}`);
|
|
3201
4487
|
console.log();
|
|
3202
|
-
console.log(
|
|
3203
|
-
console.log(
|
|
4488
|
+
console.log(chalk14.dim(` ${t("lang_supported")}`));
|
|
4489
|
+
console.log(chalk14.dim(` ${t("lang_usage")}`));
|
|
3204
4490
|
console.log();
|
|
3205
4491
|
return;
|
|
3206
4492
|
}
|
|
3207
4493
|
if (action === "set") {
|
|
3208
4494
|
if (!value) {
|
|
3209
|
-
console.log(
|
|
4495
|
+
console.log(chalk14.red(` ${t("lang_specify")}`));
|
|
3210
4496
|
return;
|
|
3211
4497
|
}
|
|
3212
4498
|
if (value !== "zh_CN" && value !== "en_US") {
|
|
3213
|
-
console.log(
|
|
4499
|
+
console.log(chalk14.red(` ${t("lang_invalid")}`));
|
|
3214
4500
|
return;
|
|
3215
4501
|
}
|
|
3216
4502
|
updateConfig({ language: value });
|
|
3217
4503
|
setLanguage(value);
|
|
3218
4504
|
console.log();
|
|
3219
|
-
console.log(
|
|
4505
|
+
console.log(chalk14.green(` \u2713 ${t("lang_set")} ${value}`));
|
|
3220
4506
|
console.log();
|
|
3221
4507
|
return;
|
|
3222
4508
|
}
|
|
3223
|
-
console.log(
|
|
3224
|
-
console.log(
|
|
4509
|
+
console.log(chalk14.red(` ${t("lang_unknown_action")}: ${action}`));
|
|
4510
|
+
console.log(chalk14.dim(` ${t("lang_supported_actions")}`));
|
|
3225
4511
|
}
|
|
3226
4512
|
|
|
3227
4513
|
// src/cli.ts
|
|
4514
|
+
import { readFileSync as readFileSync9, existsSync as existsSync19 } from "fs";
|
|
4515
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
4516
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
4517
|
+
function readVersion() {
|
|
4518
|
+
try {
|
|
4519
|
+
if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
|
|
4520
|
+
return INSTALL_HELPER_VERSION;
|
|
4521
|
+
}
|
|
4522
|
+
} catch {
|
|
4523
|
+
}
|
|
4524
|
+
try {
|
|
4525
|
+
const __filename5 = fileURLToPath5(import.meta.url);
|
|
4526
|
+
const __dirname5 = dirname7(__filename5);
|
|
4527
|
+
const candidates = [
|
|
4528
|
+
join18(__dirname5, "..", "package.json"),
|
|
4529
|
+
join18(__dirname5, "package.json"),
|
|
4530
|
+
join18(process.cwd(), "package.json")
|
|
4531
|
+
];
|
|
4532
|
+
for (const pkgPath of candidates) {
|
|
4533
|
+
if (existsSync19(pkgPath)) {
|
|
4534
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
4535
|
+
if (pkg.version && (pkg.name === "@cannbot-ai/install-helper" || pkg.name?.startsWith("@cannbot-ai/install-helper-"))) {
|
|
4536
|
+
return pkg.version;
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
return "0.0.0";
|
|
4541
|
+
} catch {
|
|
4542
|
+
return "0.0.0";
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
3228
4545
|
function createCLI() {
|
|
3229
4546
|
const config = readConfig();
|
|
3230
4547
|
if (config.language) {
|
|
3231
4548
|
setLanguage(config.language);
|
|
3232
4549
|
}
|
|
3233
4550
|
const program2 = new Command();
|
|
3234
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(
|
|
4551
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
|
|
3235
4552
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3236
4553
|
await initCommand();
|
|
3237
4554
|
});
|
|
@@ -3244,7 +4561,7 @@ function createCLI() {
|
|
|
3244
4561
|
program2.command("status").description("Show installed plugins").action(async () => {
|
|
3245
4562
|
await statusCommand();
|
|
3246
4563
|
});
|
|
3247
|
-
program2.command("install [names...]").description("Install plugins or skills (auto-detects type)").option("-t, --tool <tool>", "AI tool (opencode, claude, trae, cursor, copilot)").option("-l, --level <level>", "Install level (project, global)", "project").option("-y, --yes", "Skip confirmation prompts").option("--list", "List all available skills by category").action(async (names, options) => {
|
|
4564
|
+
program2.command("install [names...]").description("Install plugins or skills (auto-detects type)").option("-t, --tool <tool>", "AI tool (opencode, claude, trae, cursor, copilot)").option("-l, --level <level>", "Install level (project, global)", "project").option("-y, --yes", "Skip confirmation prompts").option("-a, --all", "Install ALL available skills").option("--list", "List all available skills by category").action(async (names, options) => {
|
|
3248
4565
|
await installCommand(names, options);
|
|
3249
4566
|
});
|
|
3250
4567
|
program2.command("uninstall <names...>").description("Uninstall plugins or skills (auto-detects type)").option("-t, --tool <tool>", "AI tool (opencode, claude, trae, cursor, copilot)").option("-l, --level <level>", "Install level (project, global)", "project").action(async (names, options) => {
|
|
@@ -3266,13 +4583,28 @@ function createCLI() {
|
|
|
3266
4583
|
}
|
|
3267
4584
|
|
|
3268
4585
|
// src/index.ts
|
|
3269
|
-
|
|
3270
|
-
if (error
|
|
3271
|
-
|
|
4586
|
+
function isExitError(error) {
|
|
4587
|
+
if (!error) return false;
|
|
4588
|
+
return error.name === "ExitPromptError" || error.message?.includes("force closed") || error.message?.includes("SIGINT") || error.constructor?.name === "ExitPromptError";
|
|
4589
|
+
}
|
|
4590
|
+
function handleExit(error) {
|
|
4591
|
+
if (isExitError(error)) {
|
|
4592
|
+
console.log("\n" + t("init_cancelled"));
|
|
3272
4593
|
process.exit(0);
|
|
3273
4594
|
}
|
|
4595
|
+
}
|
|
4596
|
+
process.on("SIGINT", () => {
|
|
4597
|
+
console.log("\n" + t("init_cancelled"));
|
|
4598
|
+
process.exit(130);
|
|
4599
|
+
});
|
|
4600
|
+
process.on("uncaughtException", (error) => {
|
|
4601
|
+
handleExit(error);
|
|
3274
4602
|
throw error;
|
|
3275
4603
|
});
|
|
4604
|
+
process.on("unhandledRejection", (error) => {
|
|
4605
|
+
handleExit(error);
|
|
4606
|
+
process.exit(1);
|
|
4607
|
+
});
|
|
3276
4608
|
var program = createCLI();
|
|
3277
4609
|
program.parse();
|
|
3278
4610
|
//# sourceMappingURL=index.js.map
|