@cannbot-ai/install-helper 0.0.2-beta.2 → 0.0.2-beta.21
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 +88 -7
- package/dist/config/repository.yaml +25 -0
- package/dist/index.js +895 -680
- package/dist/index.js.map +1 -1
- package/dist/locales/en_US.json +174 -0
- package/dist/locales/zh_CN.json +174 -0
- package/dist/plugins.d/_defaults.yml +4 -0
- package/dist/plugins.d/catlass-op-generator.yml +7 -0
- package/dist/plugins.d/model-infer-optimize.yml +7 -0
- package/dist/plugins.d/ops-code-reviewer.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke-flash.yml +7 -0
- package/dist/plugins.d/ops-direct-invoke.yml +7 -0
- package/dist/plugins.d/ops-registry-invoke.yml +7 -0
- package/dist/plugins.d/pypto-op-orchestrator.yml +7 -0
- package/dist/plugins.d/tilelang-op-orchestrator.yml +7 -0
- package/dist/plugins.d/torch-compile.yml +7 -0
- package/dist/plugins.d/triton-op-generator.yml +8 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -4,116 +4,53 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
|
-
import { select as
|
|
8
|
-
import
|
|
7
|
+
import { select as select4, Separator as Separator3 } from "@inquirer/prompts";
|
|
8
|
+
import chalk6 from "chalk";
|
|
9
9
|
|
|
10
10
|
// src/ui/wizard.ts
|
|
11
11
|
import { select, checkbox, Separator } from "@inquirer/prompts";
|
|
12
12
|
import chalk2 from "chalk";
|
|
13
13
|
|
|
14
14
|
// src/core/registry.ts
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
dir: "plugins-official/ops-direct-invoke-flash",
|
|
29
|
-
displayName: "AscendC Kernel \u4ECE\u96F6\u6784\u5EFA",
|
|
30
|
-
script: "init.sh",
|
|
31
|
-
aliases: ["flash", "kernel-flash"],
|
|
32
|
-
skills: 1,
|
|
33
|
-
agents: 1,
|
|
34
|
-
description: "Ascend C / Ascend950 Reg API \u6838\u51FD\u6570\u4ECE\u96F6\u6784\u5EFA"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
id: "ops-registry-invoke",
|
|
38
|
-
dir: "plugins-official/ops-registry-invoke",
|
|
39
|
-
displayName: "AscendC \u7B97\u5B50\u6CE8\u518C\u8C03\u7528",
|
|
40
|
-
script: "init.sh",
|
|
41
|
-
aliases: ["ops-registry", "ascendc-registry", "registry"],
|
|
42
|
-
skills: 12,
|
|
43
|
-
agents: 4,
|
|
44
|
-
description: "Ascend C \u7B97\u5B50\u6CE8\u518C\u8C03\u7528\u5F00\u53D1\u6D41\u7A0B"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: "pypto-op-orchestrator",
|
|
48
|
-
dir: "plugins-official/pypto-op-orchestrator",
|
|
49
|
-
displayName: "PyPTO \u7B97\u5B50",
|
|
50
|
-
script: "init.sh",
|
|
51
|
-
aliases: ["pypto", "pytorch"],
|
|
52
|
-
skills: 8,
|
|
53
|
-
agents: 3,
|
|
54
|
-
description: "PyPTO \u7B97\u5B50\u7AEF\u5230\u7AEF\u5F00\u53D1\u7F16\u6392"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: "triton-op-generator",
|
|
58
|
-
dir: "plugins-official/triton-op-generator",
|
|
59
|
-
displayName: "Triton \u7B97\u5B50\u751F\u6210",
|
|
60
|
-
script: "install.sh",
|
|
61
|
-
aliases: ["triton"],
|
|
62
|
-
skills: 6,
|
|
63
|
-
agents: 0,
|
|
64
|
-
description: "Triton-Ascend \u7B97\u5B50\u4EE3\u7801\u751F\u6210\u4E0E\u4F18\u5316"
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
id: "tilelang-op-orchestrator",
|
|
68
|
-
dir: "plugins-official/tilelang-op-orchestrator",
|
|
69
|
-
displayName: "TileLang \u7B97\u5B50",
|
|
70
|
-
script: "init.sh",
|
|
71
|
-
aliases: ["tilelang"],
|
|
72
|
-
skills: 9,
|
|
73
|
-
agents: 3,
|
|
74
|
-
description: "TileLang \u7B97\u5B50\u5F00\u53D1\u6D41\u7A0B"
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
id: "model-infer-optimize",
|
|
78
|
-
dir: "plugins-official/model-infer-optimize",
|
|
79
|
-
displayName: "NPU \u63A8\u7406\u4F18\u5316",
|
|
80
|
-
script: "init.sh",
|
|
81
|
-
aliases: ["model-infer", "infer", "inference"],
|
|
82
|
-
skills: 11,
|
|
83
|
-
agents: 3,
|
|
84
|
-
description: "NPU \u6A21\u578B\u63A8\u7406\u7AEF\u5230\u7AEF\u4F18\u5316"
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
id: "catlass-op-generator",
|
|
88
|
-
dir: "plugins-official/catlass-op-generator",
|
|
89
|
-
displayName: "Catlass \u7B97\u5B50\u76F4\u8C03",
|
|
90
|
-
script: "init.sh",
|
|
91
|
-
aliases: ["catlass"],
|
|
92
|
-
skills: 10,
|
|
93
|
-
agents: 3,
|
|
94
|
-
description: "Catlass \u7B97\u5B50\u76F4\u8C03\u5F00\u53D1"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
id: "ops-code-reviewer",
|
|
98
|
-
dir: "plugins-official/ops-code-reviewer",
|
|
99
|
-
displayName: "\u4EE3\u7801\u68C0\u89C6",
|
|
100
|
-
script: "init.sh",
|
|
101
|
-
aliases: ["code-review", "reviewer", "review"],
|
|
102
|
-
skills: 5,
|
|
103
|
-
agents: 1,
|
|
104
|
-
description: "\u4EE3\u7801\u68C0\u89C6\u4E0E\u89C4\u8303\u68C0\u67E5"
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
id: "torch-compile",
|
|
108
|
-
dir: "plugins-official/torch-compile",
|
|
109
|
-
displayName: "torch.compile \u56FE\u6A21\u5F0F",
|
|
110
|
-
script: "init.sh",
|
|
111
|
-
aliases: ["torch", "compile", "graph"],
|
|
112
|
-
skills: 6,
|
|
113
|
-
agents: 1,
|
|
114
|
-
description: "PyTorch torch.compile \u56FE\u6A21\u5F0F\u9002\u914D"
|
|
15
|
+
import { readdirSync, readFileSync } from "fs";
|
|
16
|
+
import { join, dirname } from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
import { parse as parseYaml } from "yaml";
|
|
19
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
var __dirname = dirname(__filename);
|
|
21
|
+
function loadPluginsFromYaml() {
|
|
22
|
+
const pluginsDir = join(__dirname, "plugins.d");
|
|
23
|
+
const defaultsPath = join(pluginsDir, "_defaults.yml");
|
|
24
|
+
let defaults = {};
|
|
25
|
+
try {
|
|
26
|
+
defaults = parseYaml(readFileSync(defaultsPath, "utf-8")) || {};
|
|
27
|
+
} catch {
|
|
115
28
|
}
|
|
116
|
-
];
|
|
29
|
+
const plugins = [];
|
|
30
|
+
try {
|
|
31
|
+
for (const file of readdirSync(pluginsDir)) {
|
|
32
|
+
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
33
|
+
try {
|
|
34
|
+
const content = parseYaml(readFileSync(join(pluginsDir, file), "utf-8"));
|
|
35
|
+
if (!content || !content.id) continue;
|
|
36
|
+
plugins.push({
|
|
37
|
+
id: content.id,
|
|
38
|
+
dir: content.dir,
|
|
39
|
+
displayName: content.displayName || content.id,
|
|
40
|
+
script: content.script || defaults.script || "init.sh",
|
|
41
|
+
aliases: content.aliases || [],
|
|
42
|
+
skills: content.skills ?? defaults.skills ?? 0,
|
|
43
|
+
agents: content.agents ?? defaults.agents ?? 0,
|
|
44
|
+
description: content.description || ""
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
return plugins;
|
|
52
|
+
}
|
|
53
|
+
var PLUGIN_REGISTRY = loadPluginsFromYaml();
|
|
117
54
|
function findPlugin(query) {
|
|
118
55
|
const normalized = query.toLowerCase().trim();
|
|
119
56
|
const exactMatch = PLUGIN_REGISTRY.find(
|
|
@@ -139,17 +76,68 @@ function getPluginById(id) {
|
|
|
139
76
|
import { execa } from "execa";
|
|
140
77
|
import { existsSync as existsSync2 } from "fs";
|
|
141
78
|
import { homedir as homedir2 } from "os";
|
|
142
|
-
import { join as
|
|
79
|
+
import { join as join4 } from "path";
|
|
143
80
|
|
|
144
81
|
// src/utils/paths.ts
|
|
145
82
|
import { homedir } from "os";
|
|
146
|
-
import { join } from "path";
|
|
83
|
+
import { join as join3 } from "path";
|
|
147
84
|
import { existsSync } from "fs";
|
|
85
|
+
|
|
86
|
+
// src/utils/i18n.ts
|
|
87
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
88
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
89
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
90
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
91
|
+
var __dirname2 = dirname2(__filename2);
|
|
92
|
+
function loadLocale(locale) {
|
|
93
|
+
const localePath = join2(__dirname2, "locales", `${locale}.json`);
|
|
94
|
+
try {
|
|
95
|
+
const content = readFileSync2(localePath, "utf-8");
|
|
96
|
+
return JSON.parse(content);
|
|
97
|
+
} catch {
|
|
98
|
+
const fallbackPath = join2(__dirname2, "locales", "zh_CN.json");
|
|
99
|
+
try {
|
|
100
|
+
const content = readFileSync2(fallbackPath, "utf-8");
|
|
101
|
+
return JSON.parse(content);
|
|
102
|
+
} catch {
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var currentLang = "zh_CN";
|
|
108
|
+
var messages = loadLocale("zh_CN");
|
|
109
|
+
function setLanguage(lang) {
|
|
110
|
+
currentLang = lang;
|
|
111
|
+
messages = loadLocale(lang);
|
|
112
|
+
}
|
|
113
|
+
function t(key) {
|
|
114
|
+
return messages[key] !== void 0 ? messages[key] : key;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/utils/paths.ts
|
|
118
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
119
|
+
var VALID_LEVELS = ["project", "global"];
|
|
120
|
+
function validateTool(tool) {
|
|
121
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return tool;
|
|
127
|
+
}
|
|
128
|
+
function validateLevel(level) {
|
|
129
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return level;
|
|
135
|
+
}
|
|
148
136
|
function detectTraeVariant() {
|
|
149
137
|
const home = homedir();
|
|
150
|
-
if (existsSync(
|
|
151
|
-
if (existsSync(
|
|
152
|
-
if (existsSync(
|
|
138
|
+
if (existsSync(join3(home, ".trae-cn"))) return "ide";
|
|
139
|
+
if (existsSync(join3(home, ".marscode"))) return "plugin";
|
|
140
|
+
if (existsSync(join3(home, ".traecli"))) return "cli";
|
|
153
141
|
return "unknown";
|
|
154
142
|
}
|
|
155
143
|
function getConfigRoot(tool, level, base) {
|
|
@@ -157,66 +145,74 @@ function getConfigRoot(tool, level, base) {
|
|
|
157
145
|
if (level === "global") {
|
|
158
146
|
switch (tool) {
|
|
159
147
|
case "opencode":
|
|
160
|
-
return
|
|
148
|
+
return join3(home, ".config", "opencode");
|
|
161
149
|
case "claude":
|
|
162
|
-
return
|
|
150
|
+
return join3(home, ".claude");
|
|
163
151
|
case "trae": {
|
|
164
152
|
const variant = detectTraeVariant();
|
|
165
153
|
switch (variant) {
|
|
166
154
|
case "plugin":
|
|
167
|
-
return
|
|
155
|
+
return join3(home, ".marscode");
|
|
168
156
|
case "cli":
|
|
169
|
-
return
|
|
157
|
+
return join3(home, ".traecli");
|
|
170
158
|
default:
|
|
171
|
-
return
|
|
159
|
+
return join3(home, ".trae-cn");
|
|
172
160
|
}
|
|
173
161
|
}
|
|
174
162
|
case "cursor":
|
|
175
|
-
return
|
|
163
|
+
return join3(home, ".cursor");
|
|
176
164
|
case "copilot":
|
|
177
|
-
return
|
|
165
|
+
return join3(home, ".copilot");
|
|
166
|
+
default:
|
|
167
|
+
throw new Error(
|
|
168
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
169
|
+
);
|
|
178
170
|
}
|
|
179
171
|
}
|
|
180
172
|
const baseDir = base || process.cwd();
|
|
181
173
|
switch (tool) {
|
|
182
174
|
case "opencode":
|
|
183
|
-
return
|
|
175
|
+
return join3(baseDir, ".opencode");
|
|
184
176
|
case "claude":
|
|
185
|
-
return
|
|
177
|
+
return join3(baseDir, ".claude");
|
|
186
178
|
case "trae": {
|
|
187
179
|
const variant = detectTraeVariant();
|
|
188
180
|
switch (variant) {
|
|
189
181
|
case "plugin":
|
|
190
|
-
return
|
|
182
|
+
return join3(baseDir, ".marscode");
|
|
191
183
|
case "cli":
|
|
192
|
-
return
|
|
184
|
+
return join3(baseDir, ".traecli");
|
|
193
185
|
default:
|
|
194
|
-
return
|
|
186
|
+
return join3(baseDir, ".trae");
|
|
195
187
|
}
|
|
196
188
|
}
|
|
197
189
|
case "cursor":
|
|
198
|
-
return
|
|
190
|
+
return join3(baseDir, ".cursor");
|
|
199
191
|
case "copilot":
|
|
200
|
-
return
|
|
192
|
+
return join3(baseDir, ".github");
|
|
193
|
+
default:
|
|
194
|
+
throw new Error(
|
|
195
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
196
|
+
);
|
|
201
197
|
}
|
|
202
198
|
}
|
|
203
199
|
function getConfigFileName(tool) {
|
|
204
200
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
201
|
}
|
|
206
202
|
function getSkillsDir(configRoot) {
|
|
207
|
-
return
|
|
203
|
+
return join3(configRoot, "skills");
|
|
208
204
|
}
|
|
209
205
|
function getAgentsDir(configRoot) {
|
|
210
|
-
return
|
|
206
|
+
return join3(configRoot, "agents");
|
|
211
207
|
}
|
|
212
208
|
function getManifestPath(configRoot) {
|
|
213
|
-
return
|
|
209
|
+
return join3(configRoot, "cannbot-manifest.json");
|
|
214
210
|
}
|
|
215
211
|
function getCannbotConfigDir() {
|
|
216
|
-
return
|
|
212
|
+
return join3(homedir(), ".cannbot");
|
|
217
213
|
}
|
|
218
214
|
function getCannbotRepoPath() {
|
|
219
|
-
return
|
|
215
|
+
return join3(getCannbotConfigDir(), "repo");
|
|
220
216
|
}
|
|
221
217
|
|
|
222
218
|
// src/core/detector.ts
|
|
@@ -232,7 +228,7 @@ async function getCommandVersion(cmd, args = ["--version"]) {
|
|
|
232
228
|
}
|
|
233
229
|
async function getCommandPath(cmd) {
|
|
234
230
|
try {
|
|
235
|
-
const result = await execa("
|
|
231
|
+
const result = await execa("which", [cmd], { timeout: 5e3 });
|
|
236
232
|
return result.stdout.trim();
|
|
237
233
|
} catch {
|
|
238
234
|
return void 0;
|
|
@@ -254,12 +250,12 @@ async function detectTrae() {
|
|
|
254
250
|
const home = homedir2();
|
|
255
251
|
const variant = detectTraeVariant();
|
|
256
252
|
let path;
|
|
257
|
-
if (variant === "ide" && existsSync2(
|
|
258
|
-
path =
|
|
259
|
-
} else if (variant === "plugin" && existsSync2(
|
|
260
|
-
path =
|
|
261
|
-
} else if (variant === "cli" && existsSync2(
|
|
262
|
-
path =
|
|
253
|
+
if (variant === "ide" && existsSync2(join4(home, ".trae-cn"))) {
|
|
254
|
+
path = join4(home, ".trae-cn");
|
|
255
|
+
} else if (variant === "plugin" && existsSync2(join4(home, ".marscode"))) {
|
|
256
|
+
path = join4(home, ".marscode");
|
|
257
|
+
} else if (variant === "cli" && existsSync2(join4(home, ".traecli"))) {
|
|
258
|
+
path = join4(home, ".traecli");
|
|
263
259
|
} else if (variant === "unknown") {
|
|
264
260
|
const cmdPath = await getCommandPath("trae");
|
|
265
261
|
if (cmdPath) path = cmdPath;
|
|
@@ -323,13 +319,13 @@ function getAllTools() {
|
|
|
323
319
|
}
|
|
324
320
|
|
|
325
321
|
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
327
|
-
import { join as
|
|
322
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
323
|
+
import { join as join5 } from "path";
|
|
328
324
|
function readManifest(configRoot) {
|
|
329
325
|
const manifestPath = getManifestPath(configRoot);
|
|
330
326
|
if (existsSync3(manifestPath)) {
|
|
331
327
|
try {
|
|
332
|
-
const content =
|
|
328
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
333
329
|
return JSON.parse(content);
|
|
334
330
|
} catch {
|
|
335
331
|
}
|
|
@@ -342,16 +338,16 @@ function readAllManifests(configRoot) {
|
|
|
342
338
|
const standardPath = getManifestPath(configRoot);
|
|
343
339
|
if (existsSync3(standardPath)) {
|
|
344
340
|
try {
|
|
345
|
-
const content =
|
|
341
|
+
const content = readFileSync3(standardPath, "utf-8");
|
|
346
342
|
manifests.push(JSON.parse(content));
|
|
347
343
|
} catch {
|
|
348
344
|
}
|
|
349
345
|
}
|
|
350
346
|
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath =
|
|
347
|
+
const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
|
|
352
348
|
if (existsSync3(pluginManifestPath)) {
|
|
353
349
|
try {
|
|
354
|
-
const content =
|
|
350
|
+
const content = readFileSync3(pluginManifestPath, "utf-8");
|
|
355
351
|
manifests.push(JSON.parse(content));
|
|
356
352
|
} catch {
|
|
357
353
|
}
|
|
@@ -371,6 +367,9 @@ function scanInstalled() {
|
|
|
371
367
|
for (const manifest of manifests) {
|
|
372
368
|
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
369
|
if (!plugin) continue;
|
|
370
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
374
373
|
const alreadyAdded = installed.some(
|
|
375
374
|
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
375
|
);
|
|
@@ -390,190 +389,31 @@ function scanInstalled() {
|
|
|
390
389
|
}
|
|
391
390
|
return installed;
|
|
392
391
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
install_enhanced_more: "\u66F4\u591A",
|
|
419
|
-
install_enhanced_check: "\u68C0\u67E5",
|
|
420
|
-
install_enhanced_docs: "\u6587\u6863",
|
|
421
|
-
update_title: "\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 Skills",
|
|
422
|
-
update_updating: "\u6B63\u5728\u66F4\u65B0",
|
|
423
|
-
update_done: "\u66F4\u65B0\u5B8C\u6210",
|
|
424
|
-
update_no_plugins: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
425
|
-
doctor_title: "CANNBot Doctor",
|
|
426
|
-
doctor_tools: "AI \u5DE5\u5177",
|
|
427
|
-
doctor_plugins: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
428
|
-
doctor_links: "\u94FE\u63A5\u5B8C\u6574\u6027",
|
|
429
|
-
doctor_config: "\u914D\u7F6E\u6587\u4EF6",
|
|
430
|
-
doctor_result: "\u7ED3\u679C",
|
|
431
|
-
doctor_fix_title: "\u81EA\u52A8\u4FEE\u590D",
|
|
432
|
-
doctor_fix_cleaning: "\u6E05\u7406\u5931\u6548\u94FE\u63A5",
|
|
433
|
-
doctor_fix_rebuilding: "\u91CD\u5EFA\u76EE\u5F55",
|
|
434
|
-
doctor_fix_done: "\u4FEE\u590D\u5B8C\u6210",
|
|
435
|
-
info_title: "\u63D2\u4EF6\u8BE6\u60C5",
|
|
436
|
-
info_description: "\u63CF\u8FF0",
|
|
437
|
-
info_skills: "Skills",
|
|
438
|
-
info_agents: "Agents",
|
|
439
|
-
info_status: "\u72B6\u6001",
|
|
440
|
-
info_quickstart: "\u5FEB\u901F\u5F00\u59CB",
|
|
441
|
-
info_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
442
|
-
lang_title: "\u8BED\u8A00\u8BBE\u7F6E",
|
|
443
|
-
lang_current: "\u5F53\u524D\u8BED\u8A00",
|
|
444
|
-
lang_set: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A",
|
|
445
|
-
lang_invalid: "\u65E0\u6548\u7684\u8BED\u8A00\uFF0C\u652F\u6301: zh_CN, en_US",
|
|
446
|
-
list_title: "\u53EF\u7528\u573A\u666F",
|
|
447
|
-
list_id: "\u5E8F\u53F7",
|
|
448
|
-
list_name: "\u573A\u666F\u540D",
|
|
449
|
-
list_status: "\u72B6\u6001",
|
|
450
|
-
list_skills: "Skills",
|
|
451
|
-
list_agents: "Agents",
|
|
452
|
-
status_installed: "\u5DF2\u88C5",
|
|
453
|
-
status_not_installed: "\u672A\u88C5",
|
|
454
|
-
error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
455
|
-
error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
456
|
-
error_repo_not_found: "\u4ED3\u5E93\u672A\u627E\u5230",
|
|
457
|
-
quick_start: "\u542F\u52A8\u547D\u4EE4",
|
|
458
|
-
quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
|
|
459
|
-
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
460
|
-
skill_select_items: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
|
|
461
|
-
skill_already_installed: "\u5DF2\u5B89\u88C5",
|
|
462
|
-
skill_install_progress: "\u6B63\u5728\u5B89\u88C5",
|
|
463
|
-
skill_install_done: "Skill \u5B89\u88C5\u5B8C\u6210",
|
|
464
|
-
skill_uninstall_done: "Skill \u5378\u8F7D\u5B8C\u6210",
|
|
465
|
-
skill_list_title: "\u53EF\u7528 Skills",
|
|
466
|
-
wizard_select_mode: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
467
|
-
wizard_mode_plugin: "\u5B89\u88C5 Plugin",
|
|
468
|
-
wizard_mode_plugin_desc: "\u5B8C\u6574\u5F00\u53D1\u5DE5\u4F5C\u6D41\uFF08Skills + Agents + \u914D\u7F6E\u6587\u4EF6\uFF09",
|
|
469
|
-
wizard_mode_skill: "\u5B89\u88C5 Skill",
|
|
470
|
-
wizard_mode_skill_desc: "\u4EC5\u5B89\u88C5\u9886\u57DF\u77E5\u8BC6\u6280\u80FD\uFF0C\u8BA9 AI \u638C\u63E1\u7279\u5B9A\u80FD\u529B",
|
|
471
|
-
wizard_back: "\u8FD4\u56DE",
|
|
472
|
-
wizard_cancel: "\u9000\u51FA",
|
|
473
|
-
wizard_no_selection: "\u672A\u9009\u62E9\u4EFB\u4F55\u9879\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
|
|
474
|
-
wizard_back_to_reselect: "\u8FD4\u56DE",
|
|
475
|
-
status_title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
476
|
-
status_none: "\u6682\u65E0\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6",
|
|
477
|
-
status_hint: "\u8FD0\u884C install-helper init \u5F00\u59CB\u5B89\u88C5",
|
|
478
|
-
status_install_time: "\u5B89\u88C5\u65F6\u95F4",
|
|
479
|
-
install_installed_to: "\u5B89\u88C5\u5230",
|
|
480
|
-
install_skills_count: "Skills",
|
|
481
|
-
install_agents_count: "Agents"
|
|
482
|
-
};
|
|
483
|
-
var en_US = {
|
|
484
|
-
wizard_title: "CANNBot Skills Setup Wizard",
|
|
485
|
-
wizard_detect: "Detecting installed AI coding tools...",
|
|
486
|
-
wizard_select_tool: "Select AI coding tool",
|
|
487
|
-
wizard_select_level: "Select install level",
|
|
488
|
-
wizard_select_plugins: "Select scenarios to install",
|
|
489
|
-
wizard_confirm: "Confirm installation",
|
|
490
|
-
wizard_detected_tool: "Detected",
|
|
491
|
-
wizard_confirm_tool: "Confirm using",
|
|
492
|
-
wizard_last_used: "last used",
|
|
493
|
-
wizard_already_installed: "installed",
|
|
494
|
-
install_cloning: "Cloning cannbot-skills repository...",
|
|
495
|
-
install_repo_ready: "Repository ready",
|
|
496
|
-
install_progress: "Installing",
|
|
497
|
-
install_done: "Installation complete",
|
|
498
|
-
install_success: "success",
|
|
499
|
-
install_failed: "failed",
|
|
500
|
-
install_already_installed: "already installed",
|
|
501
|
-
install_reinstall_confirm: "Reinstall (overwrite)",
|
|
502
|
-
install_skip: "skipped",
|
|
503
|
-
install_enhanced_next_steps: "Next steps",
|
|
504
|
-
install_enhanced_launch: "Launch",
|
|
505
|
-
install_enhanced_try: "Try",
|
|
506
|
-
install_enhanced_more: "More",
|
|
507
|
-
install_enhanced_check: "Check",
|
|
508
|
-
install_enhanced_docs: "Docs",
|
|
509
|
-
update_title: "Update installed Skills",
|
|
510
|
-
update_updating: "Updating",
|
|
511
|
-
update_done: "Update complete",
|
|
512
|
-
update_no_plugins: "No installed plugins",
|
|
513
|
-
doctor_title: "CANNBot Doctor",
|
|
514
|
-
doctor_tools: "AI Tools",
|
|
515
|
-
doctor_plugins: "Installed Plugins",
|
|
516
|
-
doctor_links: "Link Integrity",
|
|
517
|
-
doctor_config: "Configuration",
|
|
518
|
-
doctor_result: "Result",
|
|
519
|
-
doctor_fix_title: "Auto-fix",
|
|
520
|
-
doctor_fix_cleaning: "Cleaning broken links",
|
|
521
|
-
doctor_fix_rebuilding: "Rebuilding directories",
|
|
522
|
-
doctor_fix_done: "Fix complete",
|
|
523
|
-
info_title: "Plugin Details",
|
|
524
|
-
info_description: "Description",
|
|
525
|
-
info_skills: "Skills",
|
|
526
|
-
info_agents: "Agents",
|
|
527
|
-
info_status: "Status",
|
|
528
|
-
info_quickstart: "Quick Start",
|
|
529
|
-
info_not_found: "Plugin not found",
|
|
530
|
-
lang_title: "Language Settings",
|
|
531
|
-
lang_current: "Current language",
|
|
532
|
-
lang_set: "Language set to",
|
|
533
|
-
lang_invalid: "Invalid language, supported: zh_CN, en_US",
|
|
534
|
-
list_title: "Available Scenarios",
|
|
535
|
-
list_id: "#",
|
|
536
|
-
list_name: "Name",
|
|
537
|
-
list_status: "Status",
|
|
538
|
-
list_skills: "Skills",
|
|
539
|
-
list_agents: "Agents",
|
|
540
|
-
status_installed: "installed",
|
|
541
|
-
status_not_installed: "not installed",
|
|
542
|
-
error_tool_not_found: "No AI coding tool detected",
|
|
543
|
-
error_plugin_not_found: "Plugin not found",
|
|
544
|
-
error_repo_not_found: "Repository not found",
|
|
545
|
-
quick_start: "Launch command",
|
|
546
|
-
quick_start_hint: "Try: help me develop an Abs operator",
|
|
547
|
-
skill_select_category: "Select Skill category",
|
|
548
|
-
skill_select_items: "Select Skills to install (space to toggle, enter to confirm)",
|
|
549
|
-
skill_already_installed: "installed",
|
|
550
|
-
skill_install_progress: "Installing",
|
|
551
|
-
skill_install_done: "Skill installation complete",
|
|
552
|
-
skill_uninstall_done: "Skill uninstallation complete",
|
|
553
|
-
skill_list_title: "Available Skills",
|
|
554
|
-
wizard_select_mode: "Select installation mode",
|
|
555
|
-
wizard_mode_plugin: "Install Plugin",
|
|
556
|
-
wizard_mode_plugin_desc: "Full development workflow (Skills + Agents + config)",
|
|
557
|
-
wizard_mode_skill: "Install Skill",
|
|
558
|
-
wizard_mode_skill_desc: "Domain knowledge only, give AI specific capabilities",
|
|
559
|
-
wizard_back: "Go back",
|
|
560
|
-
wizard_cancel: "Cancel installation",
|
|
561
|
-
wizard_no_selection: "No items selected, please choose an action",
|
|
562
|
-
wizard_back_to_reselect: "Go back",
|
|
563
|
-
status_title: "Installed Plugins",
|
|
564
|
-
status_none: "No installed plugins",
|
|
565
|
-
status_hint: "Run install-helper init to start",
|
|
566
|
-
status_install_time: "Installed at",
|
|
567
|
-
install_installed_to: "Installed to",
|
|
568
|
-
install_skills_count: "Skills",
|
|
569
|
-
install_agents_count: "Agents"
|
|
570
|
-
};
|
|
571
|
-
var currentLang = "zh_CN";
|
|
572
|
-
function setLanguage(lang) {
|
|
573
|
-
currentLang = lang;
|
|
574
|
-
}
|
|
575
|
-
function t(key) {
|
|
576
|
-
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
392
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
393
|
+
const skillsDir = join5(configRoot, "skills");
|
|
394
|
+
const agentsDir = join5(configRoot, "agents");
|
|
395
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
396
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
397
|
+
const skillPath = join5(skillsDir, skillId);
|
|
398
|
+
return existsSync3(skillPath);
|
|
399
|
+
});
|
|
400
|
+
if (!hasAnySkill) return false;
|
|
401
|
+
}
|
|
402
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
403
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
404
|
+
const agentPath = join5(agentsDir, agentId);
|
|
405
|
+
return existsSync3(agentPath);
|
|
406
|
+
});
|
|
407
|
+
if (!hasAnyAgent) return false;
|
|
408
|
+
}
|
|
409
|
+
let agentsMdPath;
|
|
410
|
+
if (tool === "opencode" && level === "project") {
|
|
411
|
+
agentsMdPath = join5(configRoot, "..", "AGENTS.md");
|
|
412
|
+
} else {
|
|
413
|
+
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
414
|
+
}
|
|
415
|
+
if (!existsSync3(agentsMdPath)) return false;
|
|
416
|
+
return true;
|
|
577
417
|
}
|
|
578
418
|
|
|
579
419
|
// src/utils/logger.ts
|
|
@@ -630,24 +470,24 @@ function printBoxTitle(title, width = 65) {
|
|
|
630
470
|
}
|
|
631
471
|
function showOperationHints(isCheckbox = false) {
|
|
632
472
|
if (isCheckbox) {
|
|
633
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
473
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m${t("hint_select_space")}\x1B[0m ${t("hint_select_check")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}\uFF08${t("hint_checkbox_suffix")}\uFF09
|
|
634
474
|
`);
|
|
635
475
|
} else {
|
|
636
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
476
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
637
477
|
`);
|
|
638
478
|
}
|
|
639
479
|
}
|
|
640
480
|
|
|
641
481
|
// src/utils/config.ts
|
|
642
|
-
import { existsSync as existsSync4, mkdirSync, readFileSync as
|
|
643
|
-
import { join as
|
|
482
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
483
|
+
import { join as join6 } from "path";
|
|
644
484
|
import { homedir as homedir3 } from "os";
|
|
645
485
|
import { parse, stringify } from "yaml";
|
|
646
486
|
function getConfigDir() {
|
|
647
|
-
return
|
|
487
|
+
return join6(homedir3(), ".cannbot");
|
|
648
488
|
}
|
|
649
489
|
function getConfigPath() {
|
|
650
|
-
return
|
|
490
|
+
return join6(getConfigDir(), "config.yaml");
|
|
651
491
|
}
|
|
652
492
|
function readConfig() {
|
|
653
493
|
const configPath = getConfigPath();
|
|
@@ -658,7 +498,7 @@ function readConfig() {
|
|
|
658
498
|
};
|
|
659
499
|
}
|
|
660
500
|
try {
|
|
661
|
-
const content =
|
|
501
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
662
502
|
const parsed = parse(content);
|
|
663
503
|
return {
|
|
664
504
|
language: parsed.language || "zh_CN",
|
|
@@ -789,7 +629,7 @@ async function runWizard() {
|
|
|
789
629
|
step = 2;
|
|
790
630
|
break;
|
|
791
631
|
}
|
|
792
|
-
if (result === CANCEL
|
|
632
|
+
if (result === CANCEL) {
|
|
793
633
|
return { language: config.language || "zh_CN", tool: selectedTool, level, plugins: [], confirmed: false };
|
|
794
634
|
}
|
|
795
635
|
updateConfig({ lastTool: selectedTool, lastLevel: level });
|
|
@@ -799,7 +639,7 @@ async function runWizard() {
|
|
|
799
639
|
}
|
|
800
640
|
}
|
|
801
641
|
async function stepTool(detectedTools, lastTool) {
|
|
802
|
-
printBoxTitle("
|
|
642
|
+
printBoxTitle(t("wizard_step_1_title"));
|
|
803
643
|
if (detectedTools.length === 0) {
|
|
804
644
|
logger.error(t("error_tool_not_found"));
|
|
805
645
|
const choices2 = [
|
|
@@ -874,10 +714,10 @@ async function stepToolManual(lastTool) {
|
|
|
874
714
|
return result;
|
|
875
715
|
}
|
|
876
716
|
async function stepLevel(lastLevel) {
|
|
877
|
-
printBoxTitle("
|
|
717
|
+
printBoxTitle(t("wizard_step_2_title"));
|
|
878
718
|
const choices = [
|
|
879
|
-
{ name:
|
|
880
|
-
{ name:
|
|
719
|
+
{ name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
|
|
720
|
+
{ name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
|
|
881
721
|
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
882
722
|
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
883
723
|
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
@@ -895,7 +735,7 @@ async function stepLevel(lastLevel) {
|
|
|
895
735
|
return result;
|
|
896
736
|
}
|
|
897
737
|
async function stepPlugins(tool, level) {
|
|
898
|
-
printBoxTitle("
|
|
738
|
+
printBoxTitle(t("wizard_step_3_title"));
|
|
899
739
|
const plugins = getAllPlugins();
|
|
900
740
|
const configRoot = getConfigRoot(tool, level);
|
|
901
741
|
const manifests = readAllManifests(configRoot);
|
|
@@ -909,20 +749,16 @@ async function stepPlugins(tool, level) {
|
|
|
909
749
|
checked: false
|
|
910
750
|
};
|
|
911
751
|
});
|
|
912
|
-
choices.push(new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
913
|
-
choices.push({ name: "<- " + t("wizard_back"), value: BACK, checked: false });
|
|
914
|
-
choices.push({ name: "x " + t("wizard_cancel"), value: CANCEL, checked: false });
|
|
915
752
|
showOperationHints(true);
|
|
916
753
|
const selected = await checkbox({
|
|
917
754
|
message: t("wizard_select_plugins"),
|
|
918
755
|
choices,
|
|
919
756
|
loop: false,
|
|
920
757
|
instructions: false,
|
|
921
|
-
theme: checkboxTheme
|
|
758
|
+
theme: checkboxTheme,
|
|
759
|
+
pageSize: 15
|
|
922
760
|
});
|
|
923
|
-
|
|
924
|
-
if (selected.includes(CANCEL)) return CANCEL;
|
|
925
|
-
const pluginIds = selected.filter((v) => v !== BACK && v !== CANCEL);
|
|
761
|
+
const pluginIds = selected;
|
|
926
762
|
if (pluginIds.length === 0) {
|
|
927
763
|
const action = await select({
|
|
928
764
|
message: t("wizard_no_selection"),
|
|
@@ -940,14 +776,14 @@ async function stepPlugins(tool, level) {
|
|
|
940
776
|
return pluginIds;
|
|
941
777
|
}
|
|
942
778
|
async function stepConfirm(tool, level, plugins) {
|
|
943
|
-
printBoxTitle("
|
|
779
|
+
printBoxTitle(t("wizard_step_4_title"));
|
|
944
780
|
const allPlugins = getAllPlugins();
|
|
945
781
|
const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
|
|
946
782
|
const displayPath = getConfigRoot(tool, level);
|
|
947
783
|
const toolName = getToolDisplayName(tool);
|
|
948
784
|
const levelText = level === "project" ? "project" : "global";
|
|
949
|
-
logger.info(
|
|
950
|
-
logger.info(
|
|
785
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk2.bold(String(plugins.length))).replace("{tool}", chalk2.green(toolName)).replace("{level}", chalk2.cyan(levelText)));
|
|
786
|
+
logger.info(`${t("wizard_install_path")}: ${chalk2.cyan(displayPath)}`);
|
|
951
787
|
for (const plugin of selectedPlugins) {
|
|
952
788
|
if (plugin) {
|
|
953
789
|
logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
|
|
@@ -968,14 +804,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
968
804
|
}
|
|
969
805
|
|
|
970
806
|
// src/core/repository.ts
|
|
971
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
972
|
-
import { join as
|
|
807
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
808
|
+
import { join as join8, dirname as dirname4 } from "path";
|
|
809
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
973
810
|
import { execa as execa2 } from "execa";
|
|
811
|
+
import { parse as parseYaml3 } from "yaml";
|
|
974
812
|
|
|
975
813
|
// src/core/scanner.ts
|
|
976
|
-
import { existsSync as existsSync5, readdirSync, readFileSync as
|
|
977
|
-
import { join as
|
|
978
|
-
import {
|
|
814
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
815
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
816
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
817
|
+
import { parse as parseYaml2 } from "yaml";
|
|
818
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
819
|
+
var __dirname3 = dirname3(__filename3);
|
|
979
820
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
980
821
|
"node_modules",
|
|
981
822
|
".git",
|
|
@@ -996,36 +837,43 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
996
837
|
"docs",
|
|
997
838
|
"scripts"
|
|
998
839
|
]);
|
|
999
|
-
|
|
1000
|
-
"
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
]
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
]
|
|
840
|
+
function loadScanConfig() {
|
|
841
|
+
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
842
|
+
try {
|
|
843
|
+
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
844
|
+
return {
|
|
845
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
|
|
846
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
|
|
847
|
+
};
|
|
848
|
+
} catch {
|
|
849
|
+
return {
|
|
850
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
|
|
851
|
+
pluginDirs: ["plugins-official", "plugins-community"]
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
var scanConfig = loadScanConfig();
|
|
856
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
857
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1010
858
|
function scanSkills(repoPath) {
|
|
1011
859
|
const skills = [];
|
|
1012
860
|
const seen = /* @__PURE__ */ new Set();
|
|
1013
861
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1014
|
-
const fullPath =
|
|
862
|
+
const fullPath = join7(repoPath, dir);
|
|
1015
863
|
if (!existsSync5(fullPath)) continue;
|
|
1016
864
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1017
865
|
}
|
|
1018
866
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1019
|
-
const pluginParent =
|
|
867
|
+
const pluginParent = join7(repoPath, pluginDir);
|
|
1020
868
|
if (!existsSync5(pluginParent)) continue;
|
|
1021
869
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1022
|
-
const pluginPath =
|
|
870
|
+
const pluginPath = join7(pluginParent, plugin);
|
|
1023
871
|
if (!isDirectory(pluginPath)) continue;
|
|
1024
|
-
const skillsDir =
|
|
872
|
+
const skillsDir = join7(pluginPath, "skills");
|
|
1025
873
|
if (existsSync5(skillsDir)) {
|
|
1026
874
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1027
875
|
}
|
|
1028
|
-
const skillDir =
|
|
876
|
+
const skillDir = join7(pluginPath, "skill");
|
|
1029
877
|
if (existsSync5(skillDir)) {
|
|
1030
878
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1031
879
|
}
|
|
@@ -1035,7 +883,7 @@ function scanSkills(repoPath) {
|
|
|
1035
883
|
}
|
|
1036
884
|
function parseFrontmatter(filePath) {
|
|
1037
885
|
try {
|
|
1038
|
-
const content =
|
|
886
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1039
887
|
const lines = content.split("\n");
|
|
1040
888
|
if (lines.length < 3) return null;
|
|
1041
889
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1048,7 +896,7 @@ function parseFrontmatter(filePath) {
|
|
|
1048
896
|
}
|
|
1049
897
|
if (endIndex === -1) return null;
|
|
1050
898
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1051
|
-
const parsed =
|
|
899
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1052
900
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1053
901
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1054
902
|
return {
|
|
@@ -1061,17 +909,17 @@ function parseFrontmatter(filePath) {
|
|
|
1061
909
|
}
|
|
1062
910
|
function getCurrentCommit(repoPath) {
|
|
1063
911
|
try {
|
|
1064
|
-
const headFile =
|
|
912
|
+
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1065
913
|
if (!existsSync5(headFile)) return "unknown";
|
|
1066
|
-
const headContent =
|
|
914
|
+
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1067
915
|
if (headContent.startsWith("ref: ")) {
|
|
1068
|
-
const refPath =
|
|
916
|
+
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1069
917
|
if (existsSync5(refPath)) {
|
|
1070
|
-
return
|
|
918
|
+
return readFileSync5(refPath, "utf-8").trim();
|
|
1071
919
|
}
|
|
1072
|
-
const packedRefsPath =
|
|
920
|
+
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1073
921
|
if (existsSync5(packedRefsPath)) {
|
|
1074
|
-
const packedRefs =
|
|
922
|
+
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1075
923
|
const refName = headContent.slice(5);
|
|
1076
924
|
const lines = packedRefs.split("\n");
|
|
1077
925
|
for (const line of lines) {
|
|
@@ -1090,7 +938,7 @@ function readScanCache() {
|
|
|
1090
938
|
try {
|
|
1091
939
|
const cachePath = getCachePath();
|
|
1092
940
|
if (!existsSync5(cachePath)) return null;
|
|
1093
|
-
const content =
|
|
941
|
+
const content = readFileSync5(cachePath, "utf-8");
|
|
1094
942
|
const cache = JSON.parse(content);
|
|
1095
943
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1096
944
|
const age = Date.now() - cache.timestamp;
|
|
@@ -1103,7 +951,7 @@ function readScanCache() {
|
|
|
1103
951
|
function writeScanCache(cache) {
|
|
1104
952
|
try {
|
|
1105
953
|
const cachePath = getCachePath();
|
|
1106
|
-
const cacheDir =
|
|
954
|
+
const cacheDir = dirname3(cachePath);
|
|
1107
955
|
if (!existsSync5(cacheDir)) {
|
|
1108
956
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1109
957
|
}
|
|
@@ -1112,11 +960,10 @@ function writeScanCache(cache) {
|
|
|
1112
960
|
}
|
|
1113
961
|
}
|
|
1114
962
|
function getCachePath() {
|
|
1115
|
-
|
|
1116
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
963
|
+
return join7(getCannbotConfigDir(), "scan-cache.json");
|
|
1117
964
|
}
|
|
1118
965
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1119
|
-
const selfSkillMd =
|
|
966
|
+
const selfSkillMd = join7(dirPath, "SKILL.md");
|
|
1120
967
|
if (existsSync5(selfSkillMd)) {
|
|
1121
968
|
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1122
969
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
@@ -1131,10 +978,10 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1131
978
|
return;
|
|
1132
979
|
}
|
|
1133
980
|
for (const entry of safeReaddir(dirPath)) {
|
|
1134
|
-
const fullPath =
|
|
981
|
+
const fullPath = join7(dirPath, entry);
|
|
1135
982
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1136
983
|
if (!isDirectory(fullPath)) continue;
|
|
1137
|
-
const skillMd =
|
|
984
|
+
const skillMd = join7(fullPath, "SKILL.md");
|
|
1138
985
|
if (existsSync5(skillMd)) {
|
|
1139
986
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1140
987
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
@@ -1153,7 +1000,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1153
1000
|
}
|
|
1154
1001
|
function safeReaddir(dir) {
|
|
1155
1002
|
try {
|
|
1156
|
-
return
|
|
1003
|
+
return readdirSync2(dir);
|
|
1157
1004
|
} catch {
|
|
1158
1005
|
return [];
|
|
1159
1006
|
}
|
|
@@ -1393,7 +1240,21 @@ function getAllCategories() {
|
|
|
1393
1240
|
}
|
|
1394
1241
|
|
|
1395
1242
|
// src/core/repository.ts
|
|
1396
|
-
var
|
|
1243
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1244
|
+
var __dirname4 = dirname4(__filename4);
|
|
1245
|
+
function loadRepoUrl() {
|
|
1246
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
1247
|
+
return process.env.CANNBOT_REPO_URL;
|
|
1248
|
+
}
|
|
1249
|
+
const configPath = join8(__dirname4, "config", "repository.yaml");
|
|
1250
|
+
try {
|
|
1251
|
+
const config = parseYaml3(readFileSync6(configPath, "utf-8"));
|
|
1252
|
+
return config.repository?.url || "https://gitcode.com/cann/cannbot-skills.git";
|
|
1253
|
+
} catch {
|
|
1254
|
+
return "https://gitcode.com/cann/cannbot-skills.git";
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
var REPO_URL = loadRepoUrl();
|
|
1397
1258
|
var RepositoryManager = class {
|
|
1398
1259
|
repoPath;
|
|
1399
1260
|
constructor(customPath) {
|
|
@@ -1451,8 +1312,8 @@ var RepositoryManager = class {
|
|
|
1451
1312
|
}
|
|
1452
1313
|
isValidRepo(path) {
|
|
1453
1314
|
if (!existsSync6(path)) return false;
|
|
1454
|
-
const gitDir =
|
|
1455
|
-
const pluginsDir =
|
|
1315
|
+
const gitDir = join8(path, ".git");
|
|
1316
|
+
const pluginsDir = join8(path, "plugins-official");
|
|
1456
1317
|
return existsSync6(gitDir) && existsSync6(pluginsDir);
|
|
1457
1318
|
}
|
|
1458
1319
|
async cloneRepo() {
|
|
@@ -1487,18 +1348,18 @@ function createRepositoryManager(customPath) {
|
|
|
1487
1348
|
}
|
|
1488
1349
|
|
|
1489
1350
|
// src/core/installer.ts
|
|
1490
|
-
import { existsSync as
|
|
1491
|
-
import { join as
|
|
1351
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1352
|
+
import { join as join11 } from "path";
|
|
1492
1353
|
import { execa as execa3 } from "execa";
|
|
1493
1354
|
|
|
1494
1355
|
// src/core/record.ts
|
|
1495
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as
|
|
1496
|
-
import { join as
|
|
1356
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
|
|
1357
|
+
import { join as join9 } from "path";
|
|
1497
1358
|
function getInstallsDir() {
|
|
1498
|
-
return
|
|
1359
|
+
return join9(getCannbotConfigDir(), "installs");
|
|
1499
1360
|
}
|
|
1500
1361
|
function getRecordPath(pluginId) {
|
|
1501
|
-
return
|
|
1362
|
+
return join9(getInstallsDir(), `${pluginId}.json`);
|
|
1502
1363
|
}
|
|
1503
1364
|
function readRecord(pluginId) {
|
|
1504
1365
|
const recordPath = getRecordPath(pluginId);
|
|
@@ -1506,7 +1367,7 @@ function readRecord(pluginId) {
|
|
|
1506
1367
|
return null;
|
|
1507
1368
|
}
|
|
1508
1369
|
try {
|
|
1509
|
-
const content =
|
|
1370
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1510
1371
|
return JSON.parse(content);
|
|
1511
1372
|
} catch {
|
|
1512
1373
|
return null;
|
|
@@ -1530,22 +1391,22 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1530
1391
|
const files = [];
|
|
1531
1392
|
const directories = [];
|
|
1532
1393
|
if (manifest) {
|
|
1533
|
-
const skillsDir =
|
|
1394
|
+
const skillsDir = join9(configRoot, "skills");
|
|
1534
1395
|
if (existsSync7(skillsDir)) {
|
|
1535
1396
|
directories.push(skillsDir);
|
|
1536
1397
|
for (const skillName of manifest.installed_skills || []) {
|
|
1537
|
-
const skillPath =
|
|
1398
|
+
const skillPath = join9(skillsDir, skillName);
|
|
1538
1399
|
if (existsSync7(skillPath) || isSymlink(skillPath)) {
|
|
1539
1400
|
files.push(skillPath);
|
|
1540
1401
|
}
|
|
1541
1402
|
}
|
|
1542
1403
|
}
|
|
1543
|
-
const agentsDir =
|
|
1404
|
+
const agentsDir = join9(configRoot, "agents");
|
|
1544
1405
|
if (existsSync7(agentsDir)) {
|
|
1545
1406
|
directories.push(agentsDir);
|
|
1546
1407
|
for (const agentName of manifest.installed_agents || []) {
|
|
1547
|
-
const agentPath =
|
|
1548
|
-
const agentPathMd =
|
|
1408
|
+
const agentPath = join9(agentsDir, agentName);
|
|
1409
|
+
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
1549
1410
|
if (existsSync7(agentPath) || isSymlink(agentPath)) {
|
|
1550
1411
|
files.push(agentPath);
|
|
1551
1412
|
} else if (existsSync7(agentPathMd) || isSymlink(agentPathMd)) {
|
|
@@ -1554,35 +1415,34 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1554
1415
|
}
|
|
1555
1416
|
}
|
|
1556
1417
|
}
|
|
1557
|
-
const workflowsLink =
|
|
1418
|
+
const workflowsLink = join9(configRoot, "workflows");
|
|
1558
1419
|
if (isSymlink(workflowsLink)) {
|
|
1559
1420
|
files.push(workflowsLink);
|
|
1560
1421
|
}
|
|
1561
|
-
const manifestPath =
|
|
1422
|
+
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
1562
1423
|
if (existsSync7(manifestPath)) {
|
|
1563
1424
|
files.push(manifestPath);
|
|
1564
1425
|
}
|
|
1565
|
-
const pluginManifestPath =
|
|
1426
|
+
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
1566
1427
|
if (existsSync7(pluginManifestPath)) {
|
|
1567
1428
|
files.push(pluginManifestPath);
|
|
1568
1429
|
}
|
|
1569
1430
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1570
|
-
const configFilePath = level === "project" ?
|
|
1431
|
+
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
1571
1432
|
if (existsSync7(configFilePath)) {
|
|
1572
1433
|
files.push(configFilePath);
|
|
1573
1434
|
}
|
|
1574
1435
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1575
1436
|
for (const repoName of repoLinks) {
|
|
1576
|
-
const repoLinkPath =
|
|
1437
|
+
const repoLinkPath = join9(installPath, repoName);
|
|
1577
1438
|
if (isSymlink(repoLinkPath)) {
|
|
1578
1439
|
files.push(repoLinkPath);
|
|
1579
1440
|
}
|
|
1580
|
-
const repoLinkInConfig =
|
|
1441
|
+
const repoLinkInConfig = join9(configRoot, repoName);
|
|
1581
1442
|
if (isSymlink(repoLinkInConfig)) {
|
|
1582
1443
|
files.push(repoLinkInConfig);
|
|
1583
1444
|
}
|
|
1584
1445
|
}
|
|
1585
|
-
directories.push(configRoot);
|
|
1586
1446
|
return {
|
|
1587
1447
|
pluginId,
|
|
1588
1448
|
displayName,
|
|
@@ -1603,7 +1463,7 @@ function isSymlink(path) {
|
|
|
1603
1463
|
}
|
|
1604
1464
|
}
|
|
1605
1465
|
function getSkillRecordPath() {
|
|
1606
|
-
return
|
|
1466
|
+
return join9(getInstallsDir(), "skills.json");
|
|
1607
1467
|
}
|
|
1608
1468
|
function readSkillRecord() {
|
|
1609
1469
|
const recordPath = getSkillRecordPath();
|
|
@@ -1611,7 +1471,7 @@ function readSkillRecord() {
|
|
|
1611
1471
|
return {};
|
|
1612
1472
|
}
|
|
1613
1473
|
try {
|
|
1614
|
-
const content =
|
|
1474
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1615
1475
|
return JSON.parse(content);
|
|
1616
1476
|
} catch {
|
|
1617
1477
|
return {};
|
|
@@ -1659,7 +1519,186 @@ function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
|
1659
1519
|
}
|
|
1660
1520
|
function getInstalledSkills(tool, level, installPath) {
|
|
1661
1521
|
const record = readSkillRecord();
|
|
1662
|
-
|
|
1522
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1523
|
+
const configRoot = getConfigRoot(tool, level);
|
|
1524
|
+
const skillsDir = join9(configRoot, "skills");
|
|
1525
|
+
return recordedSkills.filter((skillId) => {
|
|
1526
|
+
const skillPath = join9(skillsDir, skillId);
|
|
1527
|
+
return existsSync7(skillPath) || isSymlink(skillPath);
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// src/core/backup.ts
|
|
1532
|
+
import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
1533
|
+
import { join as join10 } from "path";
|
|
1534
|
+
function getAgentsFileName(tool) {
|
|
1535
|
+
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1536
|
+
}
|
|
1537
|
+
function detectCurrentPlugin(configRoot, tool) {
|
|
1538
|
+
const manifest = readManifest(configRoot);
|
|
1539
|
+
if (manifest?.team) {
|
|
1540
|
+
const plugin = findPlugin(manifest.team);
|
|
1541
|
+
if (plugin) {
|
|
1542
|
+
return {
|
|
1543
|
+
pluginId: plugin.id,
|
|
1544
|
+
pluginName: plugin.displayName
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1549
|
+
if (!existsSync8(agentsFile)) {
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
const allRecords = readAllRecords();
|
|
1553
|
+
for (const record of allRecords) {
|
|
1554
|
+
if (record.configRoot === configRoot && record.tool === tool) {
|
|
1555
|
+
const plugin = findPlugin(record.pluginId);
|
|
1556
|
+
if (plugin) {
|
|
1557
|
+
return {
|
|
1558
|
+
pluginId: plugin.id,
|
|
1559
|
+
pluginName: plugin.displayName
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1567
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1568
|
+
if (!existsSync8(agentsFile)) {
|
|
1569
|
+
return null;
|
|
1570
|
+
}
|
|
1571
|
+
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
1572
|
+
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
1573
|
+
const backupPath = join10(configRoot, backupFileName);
|
|
1574
|
+
try {
|
|
1575
|
+
copyFileSync(agentsFile, backupPath);
|
|
1576
|
+
return {
|
|
1577
|
+
filePath: backupPath,
|
|
1578
|
+
pluginId: fromPluginId,
|
|
1579
|
+
pluginName: fromPluginName,
|
|
1580
|
+
backupTime: timestamp
|
|
1581
|
+
};
|
|
1582
|
+
} catch {
|
|
1583
|
+
return null;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
function findBackups(configRoot) {
|
|
1587
|
+
if (!existsSync8(configRoot)) {
|
|
1588
|
+
return [];
|
|
1589
|
+
}
|
|
1590
|
+
const files = readdirSync3(configRoot);
|
|
1591
|
+
const backups = [];
|
|
1592
|
+
for (const file of files) {
|
|
1593
|
+
if (file.includes(".cannbot-backup.")) {
|
|
1594
|
+
const parts = file.split(".cannbot-backup.");
|
|
1595
|
+
if (parts.length === 2) {
|
|
1596
|
+
const pluginIdAndTime = parts[1];
|
|
1597
|
+
const lastDotIndex = pluginIdAndTime.lastIndexOf(".");
|
|
1598
|
+
if (lastDotIndex > 0) {
|
|
1599
|
+
const pluginId = pluginIdAndTime.substring(0, lastDotIndex);
|
|
1600
|
+
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
1601
|
+
const plugin = findPlugin(pluginId);
|
|
1602
|
+
backups.push({
|
|
1603
|
+
filePath: join10(configRoot, file),
|
|
1604
|
+
pluginId,
|
|
1605
|
+
pluginName: plugin?.displayName || pluginId,
|
|
1606
|
+
backupTime: timestamp
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
return backups.sort((a, b) => b.backupTime.localeCompare(a.backupTime));
|
|
1613
|
+
}
|
|
1614
|
+
function restoreBackup(backupPath, configRoot, tool) {
|
|
1615
|
+
try {
|
|
1616
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1617
|
+
copyFileSync(backupPath, agentsFile);
|
|
1618
|
+
return true;
|
|
1619
|
+
} catch {
|
|
1620
|
+
return false;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
function deleteBackup(backupPath) {
|
|
1624
|
+
try {
|
|
1625
|
+
if (existsSync8(backupPath)) {
|
|
1626
|
+
unlinkSync2(backupPath);
|
|
1627
|
+
return true;
|
|
1628
|
+
}
|
|
1629
|
+
return false;
|
|
1630
|
+
} catch {
|
|
1631
|
+
return false;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
function formatTimestamp(date) {
|
|
1635
|
+
const year = date.getFullYear();
|
|
1636
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
1637
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
1638
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1639
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1640
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
1641
|
+
return `${year}${month}${day}-${hours}${minutes}${seconds}`;
|
|
1642
|
+
}
|
|
1643
|
+
function readAllRecords() {
|
|
1644
|
+
const records = [];
|
|
1645
|
+
const plugins = [
|
|
1646
|
+
"ops-direct-invoke",
|
|
1647
|
+
"ops-direct-invoke-flash",
|
|
1648
|
+
"ops-registry-invoke",
|
|
1649
|
+
"pypto-op-orchestrator",
|
|
1650
|
+
"triton-op-generator",
|
|
1651
|
+
"tilelang-op-orchestrator",
|
|
1652
|
+
"model-infer-optimize",
|
|
1653
|
+
"catlass-op-generator",
|
|
1654
|
+
"ops-code-reviewer",
|
|
1655
|
+
"torch-compile"
|
|
1656
|
+
];
|
|
1657
|
+
for (const pluginId of plugins) {
|
|
1658
|
+
const record = readRecord(pluginId);
|
|
1659
|
+
if (record) {
|
|
1660
|
+
records.push(record);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
return records;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// src/ui/backup-prompts.ts
|
|
1667
|
+
import { select as select2 } from "@inquirer/prompts";
|
|
1668
|
+
import chalk3 from "chalk";
|
|
1669
|
+
async function showOverwriteWarning(currentPluginName, newPluginName) {
|
|
1670
|
+
console.log();
|
|
1671
|
+
console.log(chalk3.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
|
|
1672
|
+
console.log();
|
|
1673
|
+
console.log(` ${t("backup_current")}: ${chalk3.cyan(currentPluginName)}`);
|
|
1674
|
+
console.log(` ${t("backup_installing")}: ${chalk3.cyan(newPluginName)}`);
|
|
1675
|
+
console.log();
|
|
1676
|
+
console.log(` ${t("backup_overwrite_warning")}`);
|
|
1677
|
+
console.log(` ${t("backup_auto_create")}`);
|
|
1678
|
+
console.log();
|
|
1679
|
+
const choice = await select2({
|
|
1680
|
+
message: t("backup_select_action"),
|
|
1681
|
+
choices: [
|
|
1682
|
+
{ name: t("backup_overwrite"), value: "overwrite" },
|
|
1683
|
+
{ name: t("backup_cancel"), value: "cancel" }
|
|
1684
|
+
]
|
|
1685
|
+
});
|
|
1686
|
+
return choice;
|
|
1687
|
+
}
|
|
1688
|
+
async function showRestorePrompt(backups) {
|
|
1689
|
+
console.log();
|
|
1690
|
+
console.log(chalk3.cyan(` ${t("backup_restore_prompt")}`));
|
|
1691
|
+
console.log();
|
|
1692
|
+
const choices = backups.map((b) => ({
|
|
1693
|
+
name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
|
|
1694
|
+
value: b.filePath
|
|
1695
|
+
}));
|
|
1696
|
+
choices.push({ name: t("backup_only_uninstall"), value: "none" });
|
|
1697
|
+
const choice = await select2({
|
|
1698
|
+
message: t("backup_select_action"),
|
|
1699
|
+
choices
|
|
1700
|
+
});
|
|
1701
|
+
return choice;
|
|
1663
1702
|
}
|
|
1664
1703
|
|
|
1665
1704
|
// src/core/installer.ts
|
|
@@ -1675,8 +1714,8 @@ async function installPlugin(opts) {
|
|
|
1675
1714
|
warnings: []
|
|
1676
1715
|
};
|
|
1677
1716
|
}
|
|
1678
|
-
const scriptPath =
|
|
1679
|
-
if (!
|
|
1717
|
+
const scriptPath = join11(opts.repoPath, plugin.dir, plugin.script);
|
|
1718
|
+
if (!existsSync9(scriptPath)) {
|
|
1680
1719
|
return {
|
|
1681
1720
|
success: false,
|
|
1682
1721
|
pluginId: opts.pluginId,
|
|
@@ -1691,13 +1730,49 @@ async function installPlugin(opts) {
|
|
|
1691
1730
|
args.push(opts.installPath);
|
|
1692
1731
|
}
|
|
1693
1732
|
const cwd = opts.installPath || process.cwd();
|
|
1733
|
+
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1734
|
+
const agentsFile = join11(configRoot, getAgentsFileName(opts.tool));
|
|
1735
|
+
let backupInfo = null;
|
|
1736
|
+
if (existsSync9(agentsFile)) {
|
|
1737
|
+
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
1738
|
+
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
1739
|
+
let choice = "overwrite";
|
|
1740
|
+
if (!opts.yes) {
|
|
1741
|
+
choice = await showOverwriteWarning(
|
|
1742
|
+
currentPlugin.pluginName,
|
|
1743
|
+
plugin.displayName
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
if (choice === "cancel") {
|
|
1747
|
+
logger.info(t("backup_cancel"));
|
|
1748
|
+
return {
|
|
1749
|
+
success: false,
|
|
1750
|
+
pluginId: opts.pluginId,
|
|
1751
|
+
skillsCount: 0,
|
|
1752
|
+
agentsCount: 0,
|
|
1753
|
+
errors: [t("backup_cancel")],
|
|
1754
|
+
warnings: []
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
backupInfo = createBackup(
|
|
1758
|
+
configRoot,
|
|
1759
|
+
opts.tool,
|
|
1760
|
+
currentPlugin.pluginId,
|
|
1761
|
+
currentPlugin.pluginName
|
|
1762
|
+
);
|
|
1763
|
+
if (backupInfo) {
|
|
1764
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1694
1768
|
try {
|
|
1695
|
-
|
|
1769
|
+
const execOptions = {
|
|
1696
1770
|
cwd,
|
|
1697
1771
|
timeout: 3e5,
|
|
1698
|
-
stdio: "pipe"
|
|
1699
|
-
|
|
1700
|
-
|
|
1772
|
+
stdio: "pipe",
|
|
1773
|
+
input: opts.yes ? "y\n" : void 0
|
|
1774
|
+
};
|
|
1775
|
+
await execa3("bash", [scriptPath, ...args], execOptions);
|
|
1701
1776
|
const manifest = readManifest(configRoot);
|
|
1702
1777
|
let skillsCount = 0;
|
|
1703
1778
|
let agentsCount = 0;
|
|
@@ -1718,6 +1793,14 @@ async function installPlugin(opts) {
|
|
|
1718
1793
|
configRoot,
|
|
1719
1794
|
manifest
|
|
1720
1795
|
);
|
|
1796
|
+
if (backupInfo) {
|
|
1797
|
+
record.backup = {
|
|
1798
|
+
filePath: backupInfo.filePath,
|
|
1799
|
+
fromPluginId: backupInfo.pluginId,
|
|
1800
|
+
fromPluginName: backupInfo.pluginName,
|
|
1801
|
+
backupTime: backupInfo.backupTime
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1721
1804
|
writeRecord(record);
|
|
1722
1805
|
} catch {
|
|
1723
1806
|
}
|
|
@@ -1743,16 +1826,16 @@ async function installPlugin(opts) {
|
|
|
1743
1826
|
}
|
|
1744
1827
|
|
|
1745
1828
|
// src/core/skill-installer.ts
|
|
1746
|
-
import { existsSync as
|
|
1747
|
-
import { join as
|
|
1748
|
-
import { select as
|
|
1749
|
-
import
|
|
1829
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
|
|
1830
|
+
import { join as join12 } from "path";
|
|
1831
|
+
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
1832
|
+
import chalk4 from "chalk";
|
|
1750
1833
|
import Table from "cli-table3";
|
|
1751
1834
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
1752
1835
|
const configRoot = getConfigRoot(tool, level);
|
|
1753
|
-
const skillsDir =
|
|
1836
|
+
const skillsDir = join12(configRoot, "skills");
|
|
1754
1837
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1755
|
-
if (!
|
|
1838
|
+
if (!existsSync10(skillsDir)) {
|
|
1756
1839
|
mkdirSync5(skillsDir, { recursive: true });
|
|
1757
1840
|
}
|
|
1758
1841
|
const results = [];
|
|
@@ -1763,15 +1846,15 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1763
1846
|
results.push({ skillId, success: false, error: "Skill not found in registry" });
|
|
1764
1847
|
continue;
|
|
1765
1848
|
}
|
|
1766
|
-
const sourcePath =
|
|
1767
|
-
const targetPath =
|
|
1768
|
-
if (!
|
|
1849
|
+
const sourcePath = join12(repoPath, skill.source, skill.id);
|
|
1850
|
+
const targetPath = join12(skillsDir, skillId);
|
|
1851
|
+
if (!existsSync10(sourcePath)) {
|
|
1769
1852
|
results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
|
|
1770
1853
|
continue;
|
|
1771
1854
|
}
|
|
1772
1855
|
try {
|
|
1773
|
-
if (
|
|
1774
|
-
|
|
1856
|
+
if (existsSync10(targetPath) || isSymlink2(targetPath)) {
|
|
1857
|
+
unlinkSync3(targetPath);
|
|
1775
1858
|
}
|
|
1776
1859
|
symlinkSync(realpathSync(sourcePath), targetPath);
|
|
1777
1860
|
results.push({ skillId, success: true });
|
|
@@ -1787,21 +1870,21 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1787
1870
|
}
|
|
1788
1871
|
async function uninstallSkills(skillIds, tool, level) {
|
|
1789
1872
|
const configRoot = getConfigRoot(tool, level);
|
|
1790
|
-
const skillsDir =
|
|
1873
|
+
const skillsDir = join12(configRoot, "skills");
|
|
1791
1874
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1792
1875
|
const results = [];
|
|
1793
1876
|
const removedIds = [];
|
|
1794
1877
|
for (const skillId of skillIds) {
|
|
1795
|
-
const targetPath =
|
|
1878
|
+
const targetPath = join12(skillsDir, skillId);
|
|
1796
1879
|
try {
|
|
1797
|
-
if (
|
|
1798
|
-
|
|
1880
|
+
if (existsSync10(targetPath) || isSymlink2(targetPath)) {
|
|
1881
|
+
unlinkSync3(targetPath);
|
|
1799
1882
|
results.push({ skillId, success: true });
|
|
1800
1883
|
removedIds.push(skillId);
|
|
1801
|
-
logger.step(`
|
|
1884
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
1802
1885
|
} else {
|
|
1803
1886
|
results.push({ skillId, success: false, error: "Not installed" });
|
|
1804
|
-
logger.warn(`${skillId}
|
|
1887
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
1805
1888
|
}
|
|
1806
1889
|
} catch (error) {
|
|
1807
1890
|
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
|
|
@@ -1810,103 +1893,119 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
1810
1893
|
if (removedIds.length > 0) {
|
|
1811
1894
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
1812
1895
|
}
|
|
1813
|
-
if (
|
|
1896
|
+
if (existsSync10(skillsDir)) {
|
|
1814
1897
|
try {
|
|
1815
|
-
const entries =
|
|
1898
|
+
const entries = readdirSync4(skillsDir);
|
|
1816
1899
|
if (entries.length === 0) {
|
|
1817
1900
|
rmdirSync(skillsDir);
|
|
1818
|
-
logger.step(`
|
|
1819
|
-
}
|
|
1820
|
-
} catch {
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
if (existsSync9(configRoot)) {
|
|
1824
|
-
try {
|
|
1825
|
-
const entries = readdirSync3(configRoot);
|
|
1826
|
-
if (entries.length === 0) {
|
|
1827
|
-
rmdirSync(configRoot);
|
|
1828
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
1901
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
1829
1902
|
}
|
|
1830
1903
|
} catch {
|
|
1831
1904
|
}
|
|
1832
1905
|
}
|
|
1833
1906
|
return results;
|
|
1834
1907
|
}
|
|
1835
|
-
|
|
1908
|
+
var SELECT_ALL = "__select_all__";
|
|
1909
|
+
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
1910
|
+
var CONTINUE_SELECT = "__continue_select__";
|
|
1911
|
+
async function interactiveSkillSelect(tool, level) {
|
|
1836
1912
|
const BACK3 = "__back__";
|
|
1837
1913
|
const CANCEL3 = "__cancel__";
|
|
1838
1914
|
const categories = getAllCategories();
|
|
1915
|
+
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
1916
|
+
let accumulated = [];
|
|
1839
1917
|
let step = 0;
|
|
1840
1918
|
let selectedCategoryId = "";
|
|
1841
1919
|
while (true) {
|
|
1842
1920
|
switch (step) {
|
|
1843
1921
|
case 0: {
|
|
1844
|
-
|
|
1922
|
+
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");
|
|
1923
|
+
printBoxTitle(titleSuffix);
|
|
1845
1924
|
const categoryChoices = [
|
|
1925
|
+
{
|
|
1926
|
+
name: `>> ${t("skill_select_all").replace("{count}", String(allSkillIds.length))}`,
|
|
1927
|
+
value: SELECT_ALL
|
|
1928
|
+
},
|
|
1929
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1846
1930
|
...categories.map((cat) => ({
|
|
1847
1931
|
name: `> ${cat.name} (${cat.skills.length} skills)`,
|
|
1848
1932
|
value: cat.id
|
|
1849
1933
|
})),
|
|
1850
1934
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1935
|
+
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
1851
1936
|
{ name: "<- " + t("wizard_back"), value: BACK3 },
|
|
1852
1937
|
{ name: "x " + t("wizard_cancel"), value: CANCEL3 }
|
|
1853
1938
|
];
|
|
1854
1939
|
showOperationHints();
|
|
1855
|
-
selectedCategoryId = await
|
|
1940
|
+
selectedCategoryId = await select3({
|
|
1856
1941
|
message: t("skill_select_category"),
|
|
1857
1942
|
choices: categoryChoices,
|
|
1858
1943
|
loop: false,
|
|
1859
|
-
theme: selectTheme
|
|
1944
|
+
theme: selectTheme,
|
|
1945
|
+
pageSize: 15
|
|
1860
1946
|
});
|
|
1861
1947
|
if (selectedCategoryId === BACK3) return "back";
|
|
1862
1948
|
if (selectedCategoryId === CANCEL3) return "cancel";
|
|
1949
|
+
if (selectedCategoryId === SELECT_ALL) {
|
|
1950
|
+
return allSkillIds;
|
|
1951
|
+
}
|
|
1952
|
+
if (selectedCategoryId === "install") {
|
|
1953
|
+
return accumulated;
|
|
1954
|
+
}
|
|
1863
1955
|
step = 1;
|
|
1864
1956
|
break;
|
|
1865
1957
|
}
|
|
1866
1958
|
case 1: {
|
|
1867
1959
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
1868
1960
|
if (!category) return [];
|
|
1869
|
-
printBoxTitle(
|
|
1870
|
-
const
|
|
1871
|
-
const
|
|
1872
|
-
const installedSkills = [];
|
|
1873
|
-
for (const tk of tools) {
|
|
1874
|
-
for (const l of levels) {
|
|
1875
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
1876
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1961
|
+
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
1962
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
1963
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
1879
1964
|
const installedSet = new Set(installedSkills);
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
name:
|
|
1885
|
-
value:
|
|
1886
|
-
checked:
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1965
|
+
const categorySkillIds = new Set(category.skills.map((s) => s.id));
|
|
1966
|
+
const accumulatedInCategory = new Set(accumulated.filter((id) => categorySkillIds.has(id)));
|
|
1967
|
+
const skillChoices = [
|
|
1968
|
+
{
|
|
1969
|
+
name: `>> ${t("skill_select_all_category")}`,
|
|
1970
|
+
value: SELECT_ALL_CATEGORY,
|
|
1971
|
+
checked: accumulatedInCategory.size === category.skills.length
|
|
1972
|
+
},
|
|
1973
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1974
|
+
...category.skills.map((skill) => {
|
|
1975
|
+
const isInstalled = installedSet.has(skill.id);
|
|
1976
|
+
const isAccumulated = accumulatedInCategory.has(skill.id);
|
|
1977
|
+
const suffix = isInstalled ? ` [${t("skill_already_installed")}]` : isAccumulated ? " [+]" : "";
|
|
1978
|
+
return {
|
|
1979
|
+
name: `${skill.id}${suffix} \u2014 ${skill.description}`,
|
|
1980
|
+
value: skill.id,
|
|
1981
|
+
checked: isAccumulated
|
|
1982
|
+
};
|
|
1983
|
+
})
|
|
1984
|
+
];
|
|
1892
1985
|
showOperationHints(true);
|
|
1893
1986
|
const selectedSkills = await checkbox2({
|
|
1894
1987
|
message: t("skill_select_items"),
|
|
1895
1988
|
choices: skillChoices,
|
|
1896
1989
|
loop: false,
|
|
1897
1990
|
instructions: false,
|
|
1898
|
-
theme: checkboxTheme
|
|
1991
|
+
theme: checkboxTheme,
|
|
1992
|
+
pageSize: 15
|
|
1899
1993
|
});
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1994
|
+
let skillIds;
|
|
1995
|
+
if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
|
|
1996
|
+
skillIds = category.skills.map((s) => s.id);
|
|
1997
|
+
} else {
|
|
1998
|
+
skillIds = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
|
|
1903
1999
|
}
|
|
1904
|
-
if (selectedSkills.includes(CANCEL3)) return "cancel";
|
|
1905
|
-
const skillIds = selectedSkills.filter((v) => v !== BACK3 && v !== CANCEL3);
|
|
1906
2000
|
if (skillIds.length === 0) {
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2001
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
2002
|
+
const action2 = await select3({
|
|
2003
|
+
message: accumulated.length > 0 ? t("skill_continue_or_install").replace("{count}", String(accumulated.length)) : t("wizard_no_selection"),
|
|
2004
|
+
choices: accumulated.length > 0 ? [
|
|
2005
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
2006
|
+
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
2007
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
2008
|
+
] : [
|
|
1910
2009
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1911
2010
|
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
1912
2011
|
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
@@ -1914,13 +2013,38 @@ async function interactiveSkillSelect() {
|
|
|
1914
2013
|
loop: false,
|
|
1915
2014
|
theme: selectTheme
|
|
1916
2015
|
});
|
|
1917
|
-
if (
|
|
2016
|
+
if (action2 === "install") return accumulated;
|
|
2017
|
+
if (action2 === "back") {
|
|
1918
2018
|
step = 0;
|
|
1919
2019
|
break;
|
|
1920
2020
|
}
|
|
1921
2021
|
return "cancel";
|
|
1922
2022
|
}
|
|
1923
|
-
|
|
2023
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
2024
|
+
for (const id of skillIds) {
|
|
2025
|
+
if (!accumulated.includes(id)) {
|
|
2026
|
+
accumulated.push(id);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
const action = await select3({
|
|
2030
|
+
message: t("skill_continue_or_install").replace("{count}", String(accumulated.length)),
|
|
2031
|
+
choices: [
|
|
2032
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
2033
|
+
{ name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
|
|
2034
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2035
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
2036
|
+
],
|
|
2037
|
+
loop: false,
|
|
2038
|
+
theme: selectTheme
|
|
2039
|
+
});
|
|
2040
|
+
if (action === "install") {
|
|
2041
|
+
return accumulated;
|
|
2042
|
+
}
|
|
2043
|
+
if (action === CONTINUE_SELECT) {
|
|
2044
|
+
step = 0;
|
|
2045
|
+
break;
|
|
2046
|
+
}
|
|
2047
|
+
return "cancel";
|
|
1924
2048
|
}
|
|
1925
2049
|
}
|
|
1926
2050
|
}
|
|
@@ -1928,11 +2052,11 @@ async function interactiveSkillSelect() {
|
|
|
1928
2052
|
function listAllSkills() {
|
|
1929
2053
|
const categories = getAllCategories();
|
|
1930
2054
|
console.log();
|
|
1931
|
-
console.log(
|
|
1932
|
-
console.log(
|
|
2055
|
+
console.log(chalk4.bold(` ${t("skill_list_title")}`));
|
|
2056
|
+
console.log(chalk4.dim(" " + "\u2500".repeat(60)));
|
|
1933
2057
|
for (const category of categories) {
|
|
1934
2058
|
console.log();
|
|
1935
|
-
console.log(
|
|
2059
|
+
console.log(chalk4.bold(` ${category.name}`) + chalk4.dim(` (${category.skills.length} skills)`));
|
|
1936
2060
|
const table = new Table({
|
|
1937
2061
|
style: { head: [], border: [] },
|
|
1938
2062
|
colWidths: [35, 45],
|
|
@@ -1940,14 +2064,14 @@ function listAllSkills() {
|
|
|
1940
2064
|
});
|
|
1941
2065
|
for (const skill of category.skills) {
|
|
1942
2066
|
table.push([
|
|
1943
|
-
|
|
2067
|
+
chalk4.cyan(skill.id),
|
|
1944
2068
|
skill.description
|
|
1945
2069
|
]);
|
|
1946
2070
|
}
|
|
1947
2071
|
console.log(table.toString());
|
|
1948
2072
|
}
|
|
1949
2073
|
console.log();
|
|
1950
|
-
console.log(
|
|
2074
|
+
console.log(chalk4.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
1951
2075
|
console.log();
|
|
1952
2076
|
}
|
|
1953
2077
|
function isSymlink2(path) {
|
|
@@ -1959,22 +2083,22 @@ function isSymlink2(path) {
|
|
|
1959
2083
|
}
|
|
1960
2084
|
|
|
1961
2085
|
// src/ui/display.ts
|
|
1962
|
-
import
|
|
2086
|
+
import chalk5 from "chalk";
|
|
1963
2087
|
import Table2 from "cli-table3";
|
|
1964
2088
|
function printPluginList(plugins, installed) {
|
|
1965
2089
|
const table = new Table2({
|
|
1966
2090
|
head: [
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2091
|
+
chalk5.cyan(t("list_id")),
|
|
2092
|
+
chalk5.cyan(t("list_name")),
|
|
2093
|
+
chalk5.cyan(t("list_status")),
|
|
2094
|
+
chalk5.cyan(t("list_skills")),
|
|
2095
|
+
chalk5.cyan(t("list_agents"))
|
|
1972
2096
|
],
|
|
1973
2097
|
style: { head: [], border: [] }
|
|
1974
2098
|
});
|
|
1975
2099
|
plugins.forEach((plugin, index) => {
|
|
1976
2100
|
const isInstalled = installed.has(plugin.id);
|
|
1977
|
-
const status = isInstalled ?
|
|
2101
|
+
const status = isInstalled ? chalk5.green(`\u2713 ${t("status_installed")}`) : chalk5.dim(`\u2014`);
|
|
1978
2102
|
table.push([
|
|
1979
2103
|
String(index + 1),
|
|
1980
2104
|
plugin.displayName,
|
|
@@ -1984,7 +2108,8 @@ function printPluginList(plugins, installed) {
|
|
|
1984
2108
|
]);
|
|
1985
2109
|
});
|
|
1986
2110
|
console.log();
|
|
1987
|
-
|
|
2111
|
+
const countSuffix = t("count_suffix");
|
|
2112
|
+
console.log(chalk5.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
1988
2113
|
console.log(table.toString());
|
|
1989
2114
|
console.log();
|
|
1990
2115
|
}
|
|
@@ -1993,7 +2118,7 @@ function printInstallSummary(results) {
|
|
|
1993
2118
|
const totalCount = results.length;
|
|
1994
2119
|
console.log();
|
|
1995
2120
|
console.log(
|
|
1996
|
-
|
|
2121
|
+
chalk5.bold(
|
|
1997
2122
|
` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
|
|
1998
2123
|
)
|
|
1999
2124
|
);
|
|
@@ -2001,10 +2126,10 @@ function printInstallSummary(results) {
|
|
|
2001
2126
|
for (const result of results) {
|
|
2002
2127
|
if (result.success) {
|
|
2003
2128
|
console.log(
|
|
2004
|
-
|
|
2129
|
+
chalk5.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
|
|
2005
2130
|
);
|
|
2006
2131
|
} else {
|
|
2007
|
-
console.log(
|
|
2132
|
+
console.log(chalk5.red(" \u2717") + ` ${result.displayName}`);
|
|
2008
2133
|
}
|
|
2009
2134
|
}
|
|
2010
2135
|
console.log();
|
|
@@ -2017,22 +2142,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2017
2142
|
return;
|
|
2018
2143
|
}
|
|
2019
2144
|
const toolName = getToolDisplayName(tool);
|
|
2145
|
+
const countSuffix = t("count_suffix");
|
|
2020
2146
|
const lines = [
|
|
2021
|
-
|
|
2147
|
+
chalk5.bold(` ${t("install_done")}!`),
|
|
2022
2148
|
"",
|
|
2023
|
-
` ${
|
|
2024
|
-
` ${
|
|
2149
|
+
` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
2150
|
+
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2025
2151
|
"",
|
|
2026
|
-
|
|
2027
|
-
` ${
|
|
2028
|
-
` ${
|
|
2029
|
-
` ${
|
|
2030
|
-
` ${
|
|
2152
|
+
chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
2153
|
+
` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
|
|
2154
|
+
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green(t("try_prompt"))}`,
|
|
2155
|
+
` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
|
|
2156
|
+
` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
|
|
2031
2157
|
""
|
|
2032
2158
|
];
|
|
2033
2159
|
if (successResults.length === 1) {
|
|
2160
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
2161
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2034
2162
|
lines.push(
|
|
2035
|
-
` ${
|
|
2163
|
+
` ${chalk5.dim(t("install_enhanced_docs"))}: ${chalk5.dim(docsPath)}`
|
|
2036
2164
|
);
|
|
2037
2165
|
}
|
|
2038
2166
|
console.log();
|
|
@@ -2043,34 +2171,34 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2043
2171
|
}
|
|
2044
2172
|
|
|
2045
2173
|
// src/commands/init.ts
|
|
2046
|
-
import { existsSync as
|
|
2047
|
-
import { join as
|
|
2174
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
2175
|
+
import { join as join13 } from "path";
|
|
2048
2176
|
var BACK2 = "__back__";
|
|
2049
2177
|
var CANCEL2 = "__cancel__";
|
|
2050
2178
|
async function initCommand() {
|
|
2051
2179
|
const cwd = process.cwd();
|
|
2052
|
-
if (
|
|
2180
|
+
if (existsSync11(join13(cwd, "package.json")) && (() => {
|
|
2053
2181
|
try {
|
|
2054
|
-
const pkg = JSON.parse(
|
|
2182
|
+
const pkg = JSON.parse(readFileSync8(join13(cwd, "package.json"), "utf-8"));
|
|
2055
2183
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2056
2184
|
} catch {
|
|
2057
2185
|
return false;
|
|
2058
2186
|
}
|
|
2059
2187
|
})()) {
|
|
2060
|
-
logger.warn("
|
|
2061
|
-
logger.info("
|
|
2062
|
-
logger.info("
|
|
2188
|
+
logger.warn(t("init_pkg_dir_warn"));
|
|
2189
|
+
logger.info(t("init_pkg_dir_hint"));
|
|
2190
|
+
logger.info(t("init_pkg_dir_example"));
|
|
2063
2191
|
return;
|
|
2064
2192
|
}
|
|
2065
2193
|
printBanner(t("wizard_title"));
|
|
2066
|
-
const spinner = createSpinner("
|
|
2194
|
+
const spinner = createSpinner(t("loading_skills_list"));
|
|
2067
2195
|
spinner.start();
|
|
2068
2196
|
try {
|
|
2069
2197
|
const repoManager = createRepositoryManager();
|
|
2070
2198
|
await repoManager.ensureRepoAndScan();
|
|
2071
|
-
spinner.succeed("
|
|
2199
|
+
spinner.succeed(t("loading_skills_list_complete"));
|
|
2072
2200
|
} catch {
|
|
2073
|
-
spinner.warn("
|
|
2201
|
+
spinner.warn(t("loading_skills_list_failed"));
|
|
2074
2202
|
}
|
|
2075
2203
|
let step = 0;
|
|
2076
2204
|
let mode = "plugin";
|
|
@@ -2104,7 +2232,7 @@ async function initCommand() {
|
|
|
2104
2232
|
}
|
|
2105
2233
|
}
|
|
2106
2234
|
async function selectMode() {
|
|
2107
|
-
printBoxTitle("
|
|
2235
|
+
printBoxTitle(t("wizard_select_mode_title"));
|
|
2108
2236
|
const CANCEL3 = "__cancel__";
|
|
2109
2237
|
const choices = [
|
|
2110
2238
|
{
|
|
@@ -2119,7 +2247,7 @@ async function selectMode() {
|
|
|
2119
2247
|
{ name: "x " + t("wizard_cancel"), value: CANCEL3 }
|
|
2120
2248
|
];
|
|
2121
2249
|
showOperationHints();
|
|
2122
|
-
const result = await
|
|
2250
|
+
const result = await select4({
|
|
2123
2251
|
message: t("wizard_select_mode"),
|
|
2124
2252
|
choices,
|
|
2125
2253
|
loop: false,
|
|
@@ -2132,14 +2260,14 @@ async function pluginInstallFlow() {
|
|
|
2132
2260
|
const answers = await runWizard();
|
|
2133
2261
|
if (!answers.confirmed) {
|
|
2134
2262
|
if (answers.back) return "back";
|
|
2135
|
-
logger.info("
|
|
2263
|
+
logger.info(t("init_cancelled"));
|
|
2136
2264
|
return "done";
|
|
2137
2265
|
}
|
|
2138
2266
|
const repoManager = createRepositoryManager();
|
|
2139
|
-
const spinner = createSpinner("
|
|
2267
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2140
2268
|
spinner.start();
|
|
2141
2269
|
const repoPath = await repoManager.ensureRepo();
|
|
2142
|
-
spinner.succeed("
|
|
2270
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2143
2271
|
const allPlugins = getAllPlugins();
|
|
2144
2272
|
const selectedPlugins = answers.plugins.map(
|
|
2145
2273
|
(id) => allPlugins.find((p) => p.id === id)
|
|
@@ -2193,7 +2321,7 @@ async function skillInstallFlow() {
|
|
|
2193
2321
|
const result = await selectToolWithDetection();
|
|
2194
2322
|
if (result === "back") return "back";
|
|
2195
2323
|
if (result === "cancel") {
|
|
2196
|
-
logger.info("
|
|
2324
|
+
logger.info(t("init_cancelled"));
|
|
2197
2325
|
return "done";
|
|
2198
2326
|
}
|
|
2199
2327
|
tool = result;
|
|
@@ -2207,7 +2335,7 @@ async function skillInstallFlow() {
|
|
|
2207
2335
|
break;
|
|
2208
2336
|
}
|
|
2209
2337
|
if (result === CANCEL2) {
|
|
2210
|
-
logger.info("
|
|
2338
|
+
logger.info(t("init_cancelled"));
|
|
2211
2339
|
return "done";
|
|
2212
2340
|
}
|
|
2213
2341
|
level = result;
|
|
@@ -2215,25 +2343,25 @@ async function skillInstallFlow() {
|
|
|
2215
2343
|
break;
|
|
2216
2344
|
}
|
|
2217
2345
|
case 2: {
|
|
2218
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2346
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2219
2347
|
if (selectedSkills === "back") {
|
|
2220
2348
|
step = 1;
|
|
2221
2349
|
break;
|
|
2222
2350
|
}
|
|
2223
2351
|
if (selectedSkills === "cancel") {
|
|
2224
|
-
logger.info("
|
|
2352
|
+
logger.info(t("init_cancelled"));
|
|
2225
2353
|
return "done";
|
|
2226
2354
|
}
|
|
2227
2355
|
if (selectedSkills.length === 0) {
|
|
2228
|
-
logger.info("
|
|
2356
|
+
logger.info(t("install_no_skill_selected"));
|
|
2229
2357
|
return "done";
|
|
2230
2358
|
}
|
|
2231
2359
|
const repoManager = createRepositoryManager();
|
|
2232
|
-
const spinner = createSpinner("
|
|
2360
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2233
2361
|
spinner.start();
|
|
2234
2362
|
const repoPath = await repoManager.ensureRepo();
|
|
2235
|
-
spinner.succeed("
|
|
2236
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2363
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2364
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk6.bold(String(selectedSkills.length)))}...`);
|
|
2237
2365
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2238
2366
|
let successCount = 0;
|
|
2239
2367
|
let failCount = 0;
|
|
@@ -2251,10 +2379,10 @@ async function skillInstallFlow() {
|
|
|
2251
2379
|
}
|
|
2252
2380
|
const configRoot = getConfigRoot(tool, level);
|
|
2253
2381
|
logger.blank();
|
|
2254
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
2382
|
+
logger.success(`${t("skill_install_done")}: ${chalk6.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk6.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk6.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2255
2383
|
logger.blank();
|
|
2256
|
-
logger.info(
|
|
2257
|
-
logger.info(
|
|
2384
|
+
logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
|
|
2385
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
|
|
2258
2386
|
logger.blank();
|
|
2259
2387
|
return "done";
|
|
2260
2388
|
}
|
|
@@ -2276,32 +2404,32 @@ async function listCommand() {
|
|
|
2276
2404
|
}
|
|
2277
2405
|
|
|
2278
2406
|
// src/commands/doctor.ts
|
|
2279
|
-
import
|
|
2280
|
-
import { existsSync as
|
|
2281
|
-
import { join as
|
|
2407
|
+
import chalk7 from "chalk";
|
|
2408
|
+
import { existsSync as existsSync12, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
2409
|
+
import { join as join14 } from "path";
|
|
2282
2410
|
async function doctorCommand(options = {}) {
|
|
2283
2411
|
console.log();
|
|
2284
|
-
console.log(
|
|
2285
|
-
console.log(
|
|
2412
|
+
console.log(chalk7.bold(` ${t("doctor_title")}`));
|
|
2413
|
+
console.log(chalk7.dim(" " + "\u2500".repeat(46)));
|
|
2286
2414
|
let errors = 0;
|
|
2287
2415
|
let warnings = 0;
|
|
2288
2416
|
let fixes = 0;
|
|
2289
2417
|
console.log();
|
|
2290
|
-
console.log(
|
|
2418
|
+
console.log(chalk7.bold(` ${t("doctor_tools")}`));
|
|
2291
2419
|
const detectedTools = await detectTools();
|
|
2292
2420
|
const allTools = getAllTools();
|
|
2293
2421
|
for (const tool of allTools) {
|
|
2294
2422
|
const detected = detectedTools.find((d) => d.name === tool);
|
|
2295
2423
|
if (detected) {
|
|
2296
2424
|
console.log(
|
|
2297
|
-
|
|
2425
|
+
chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2298
2426
|
);
|
|
2299
2427
|
} else {
|
|
2300
|
-
console.log(
|
|
2428
|
+
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2301
2429
|
}
|
|
2302
2430
|
}
|
|
2303
2431
|
console.log();
|
|
2304
|
-
console.log(
|
|
2432
|
+
console.log(chalk7.bold(` ${t("doctor_plugins")}`));
|
|
2305
2433
|
const plugins = getAllPlugins();
|
|
2306
2434
|
const installed = scanInstalled();
|
|
2307
2435
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
@@ -2309,80 +2437,80 @@ async function doctorCommand(options = {}) {
|
|
|
2309
2437
|
const inst = installedMap.get(plugin.id);
|
|
2310
2438
|
if (inst) {
|
|
2311
2439
|
console.log(
|
|
2312
|
-
|
|
2440
|
+
chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2313
2441
|
);
|
|
2314
2442
|
} else {
|
|
2315
|
-
console.log(
|
|
2443
|
+
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2316
2444
|
}
|
|
2317
2445
|
}
|
|
2318
2446
|
console.log();
|
|
2319
|
-
console.log(
|
|
2447
|
+
console.log(chalk7.bold(` ${t("doctor_links")}`));
|
|
2320
2448
|
const primaryTool = detectedTools[0]?.name || "opencode";
|
|
2321
2449
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2322
2450
|
const skillsDir = getSkillsDir(configRoot);
|
|
2323
2451
|
const agentsDir = getAgentsDir(configRoot);
|
|
2324
|
-
if (
|
|
2452
|
+
if (existsSync12(skillsDir)) {
|
|
2325
2453
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2326
2454
|
if (brokenLinks === 0) {
|
|
2327
|
-
const count =
|
|
2328
|
-
console.log(
|
|
2455
|
+
const count = readdirSync5(skillsDir).length;
|
|
2456
|
+
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2329
2457
|
} else {
|
|
2330
|
-
console.log(
|
|
2458
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2331
2459
|
warnings++;
|
|
2332
2460
|
if (options.fix) {
|
|
2333
2461
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2334
2462
|
fixes += fixed;
|
|
2335
|
-
console.log(
|
|
2463
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2336
2464
|
}
|
|
2337
2465
|
}
|
|
2338
2466
|
} else {
|
|
2339
|
-
console.log(
|
|
2467
|
+
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2340
2468
|
if (options.fix) {
|
|
2341
2469
|
mkdirSync6(skillsDir, { recursive: true });
|
|
2342
2470
|
fixes++;
|
|
2343
|
-
console.log(
|
|
2471
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2344
2472
|
}
|
|
2345
2473
|
}
|
|
2346
|
-
if (
|
|
2474
|
+
if (existsSync12(agentsDir)) {
|
|
2347
2475
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2348
2476
|
if (brokenLinks === 0) {
|
|
2349
|
-
const count =
|
|
2350
|
-
console.log(
|
|
2477
|
+
const count = readdirSync5(agentsDir).length;
|
|
2478
|
+
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2351
2479
|
} else {
|
|
2352
|
-
console.log(
|
|
2480
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2353
2481
|
warnings++;
|
|
2354
2482
|
if (options.fix) {
|
|
2355
2483
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2356
2484
|
fixes += fixed;
|
|
2357
|
-
console.log(
|
|
2485
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2358
2486
|
}
|
|
2359
2487
|
}
|
|
2360
2488
|
} else {
|
|
2361
|
-
console.log(
|
|
2489
|
+
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2362
2490
|
if (options.fix) {
|
|
2363
2491
|
mkdirSync6(agentsDir, { recursive: true });
|
|
2364
2492
|
fixes++;
|
|
2365
|
-
console.log(
|
|
2493
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2366
2494
|
}
|
|
2367
2495
|
}
|
|
2368
2496
|
console.log();
|
|
2369
|
-
console.log(
|
|
2497
|
+
console.log(chalk7.bold(` ${t("doctor_config")}`));
|
|
2370
2498
|
const configFile = getConfigFileName(primaryTool);
|
|
2371
|
-
const configPath =
|
|
2372
|
-
if (
|
|
2373
|
-
console.log(
|
|
2499
|
+
const configPath = join14(process.cwd(), configFile);
|
|
2500
|
+
if (existsSync12(configPath)) {
|
|
2501
|
+
console.log(chalk7.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2374
2502
|
} else {
|
|
2375
|
-
console.log(
|
|
2503
|
+
console.log(chalk7.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2376
2504
|
}
|
|
2377
2505
|
console.log();
|
|
2378
|
-
console.log(
|
|
2506
|
+
console.log(chalk7.dim(" " + "\u2500".repeat(46)));
|
|
2379
2507
|
if (options.fix && fixes > 0) {
|
|
2380
2508
|
console.log(
|
|
2381
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2509
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
|
|
2382
2510
|
);
|
|
2383
2511
|
} else {
|
|
2384
2512
|
console.log(
|
|
2385
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2513
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
|
|
2386
2514
|
);
|
|
2387
2515
|
}
|
|
2388
2516
|
console.log();
|
|
@@ -2390,13 +2518,13 @@ async function doctorCommand(options = {}) {
|
|
|
2390
2518
|
function checkBrokenLinks(dir) {
|
|
2391
2519
|
let broken = 0;
|
|
2392
2520
|
try {
|
|
2393
|
-
const entries =
|
|
2521
|
+
const entries = readdirSync5(dir);
|
|
2394
2522
|
for (const entry of entries) {
|
|
2395
|
-
const fullPath =
|
|
2523
|
+
const fullPath = join14(dir, entry);
|
|
2396
2524
|
try {
|
|
2397
2525
|
const stats = lstatSync3(fullPath);
|
|
2398
2526
|
if (stats.isSymbolicLink()) {
|
|
2399
|
-
if (!
|
|
2527
|
+
if (!existsSync12(fullPath)) {
|
|
2400
2528
|
broken++;
|
|
2401
2529
|
}
|
|
2402
2530
|
}
|
|
@@ -2411,14 +2539,14 @@ function checkBrokenLinks(dir) {
|
|
|
2411
2539
|
function fixBrokenLinks(dir) {
|
|
2412
2540
|
let fixed = 0;
|
|
2413
2541
|
try {
|
|
2414
|
-
const entries =
|
|
2542
|
+
const entries = readdirSync5(dir);
|
|
2415
2543
|
for (const entry of entries) {
|
|
2416
|
-
const fullPath =
|
|
2544
|
+
const fullPath = join14(dir, entry);
|
|
2417
2545
|
try {
|
|
2418
2546
|
const stats = lstatSync3(fullPath);
|
|
2419
2547
|
if (stats.isSymbolicLink()) {
|
|
2420
|
-
if (!
|
|
2421
|
-
|
|
2548
|
+
if (!existsSync12(fullPath)) {
|
|
2549
|
+
unlinkSync4(fullPath);
|
|
2422
2550
|
fixed++;
|
|
2423
2551
|
}
|
|
2424
2552
|
}
|
|
@@ -2431,36 +2559,36 @@ function fixBrokenLinks(dir) {
|
|
|
2431
2559
|
}
|
|
2432
2560
|
|
|
2433
2561
|
// src/commands/status.ts
|
|
2434
|
-
import
|
|
2562
|
+
import chalk8 from "chalk";
|
|
2435
2563
|
async function statusCommand() {
|
|
2436
2564
|
const plugins = getAllPlugins();
|
|
2437
2565
|
const installed = scanInstalled();
|
|
2438
2566
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2439
2567
|
console.log();
|
|
2440
|
-
console.log(
|
|
2568
|
+
console.log(chalk8.bold(` ${t("status_title")}`));
|
|
2441
2569
|
console.log();
|
|
2442
2570
|
if (installed.length === 0) {
|
|
2443
|
-
console.log(
|
|
2444
|
-
console.log(
|
|
2571
|
+
console.log(chalk8.dim(` ${t("status_none")}`));
|
|
2572
|
+
console.log(chalk8.dim(` ${t("status_hint")}`));
|
|
2445
2573
|
console.log();
|
|
2446
2574
|
return;
|
|
2447
2575
|
}
|
|
2448
2576
|
for (const inst of installed) {
|
|
2449
2577
|
console.log(
|
|
2450
|
-
|
|
2578
|
+
chalk8.green(" \u2713") + ` ${inst.displayName}` + chalk8.dim(` (${inst.tool}, ${inst.level})`)
|
|
2451
2579
|
);
|
|
2452
2580
|
console.log(
|
|
2453
|
-
|
|
2581
|
+
chalk8.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
|
|
2454
2582
|
);
|
|
2455
|
-
console.log(
|
|
2456
|
-
console.log(
|
|
2583
|
+
console.log(chalk8.dim(` ${inst.configRoot}`));
|
|
2584
|
+
console.log(chalk8.dim(` ${t("status_install_time")}: ${inst.installTime}`));
|
|
2457
2585
|
console.log();
|
|
2458
2586
|
}
|
|
2459
2587
|
}
|
|
2460
2588
|
|
|
2461
2589
|
// src/commands/install.ts
|
|
2462
2590
|
import { confirm } from "@inquirer/prompts";
|
|
2463
|
-
import
|
|
2591
|
+
import chalk9 from "chalk";
|
|
2464
2592
|
async function installCommand(names, options) {
|
|
2465
2593
|
if (options.list) {
|
|
2466
2594
|
try {
|
|
@@ -2471,79 +2599,138 @@ async function installCommand(names, options) {
|
|
|
2471
2599
|
listAllSkills();
|
|
2472
2600
|
return;
|
|
2473
2601
|
}
|
|
2602
|
+
if (options.all) {
|
|
2603
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2604
|
+
scanSpinner2.start();
|
|
2605
|
+
try {
|
|
2606
|
+
const scanRepoManager = createRepositoryManager();
|
|
2607
|
+
await scanRepoManager.ensureRepoAndScan();
|
|
2608
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2609
|
+
} catch {
|
|
2610
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2611
|
+
}
|
|
2612
|
+
const allSkills = getAllSkills();
|
|
2613
|
+
const skillIds = allSkills.map((s) => s.id);
|
|
2614
|
+
if (skillIds.length === 0) {
|
|
2615
|
+
logger.error(t("install_no_skill_selected"));
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
let tool2;
|
|
2619
|
+
if (options.tool) {
|
|
2620
|
+
tool2 = validateTool(options.tool);
|
|
2621
|
+
} else {
|
|
2622
|
+
const detected = await selectToolWithDetection();
|
|
2623
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2624
|
+
tool2 = detected;
|
|
2625
|
+
}
|
|
2626
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2627
|
+
const repoManager = createRepositoryManager();
|
|
2628
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2629
|
+
spinner.start();
|
|
2630
|
+
const repoPath = await repoManager.ensureRepo();
|
|
2631
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2632
|
+
logger.info(`${t("skill_all_install_progress").replace("{count}", chalk9.bold(String(skillIds.length)))}`);
|
|
2633
|
+
const results = await installSkills(skillIds, tool2, level2, repoPath);
|
|
2634
|
+
let successCount = 0;
|
|
2635
|
+
let failCount = 0;
|
|
2636
|
+
const total = results.length;
|
|
2637
|
+
for (let i = 0; i < results.length; i++) {
|
|
2638
|
+
const result = results[i];
|
|
2639
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2640
|
+
if (result.success) {
|
|
2641
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2642
|
+
successCount++;
|
|
2643
|
+
} else {
|
|
2644
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2645
|
+
failCount++;
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
const configRoot = getConfigRoot(tool2, level2);
|
|
2649
|
+
logger.blank();
|
|
2650
|
+
logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2651
|
+
logger.blank();
|
|
2652
|
+
logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
|
|
2653
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
|
|
2654
|
+
logger.blank();
|
|
2655
|
+
return;
|
|
2656
|
+
}
|
|
2474
2657
|
if (names.length === 0) {
|
|
2475
|
-
const scanSpinner2 = createSpinner("
|
|
2658
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2476
2659
|
scanSpinner2.start();
|
|
2477
2660
|
try {
|
|
2478
2661
|
const scanRepoManager = createRepositoryManager();
|
|
2479
2662
|
await scanRepoManager.ensureRepoAndScan();
|
|
2480
|
-
scanSpinner2.succeed("
|
|
2663
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2481
2664
|
} catch {
|
|
2482
|
-
scanSpinner2.warn("
|
|
2665
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2483
2666
|
}
|
|
2484
2667
|
let tool2;
|
|
2485
2668
|
if (options.tool) {
|
|
2486
|
-
tool2 = options.tool;
|
|
2669
|
+
tool2 = validateTool(options.tool);
|
|
2487
2670
|
} else {
|
|
2488
2671
|
const detected = await selectToolWithDetection();
|
|
2489
|
-
if (detected === "back") return;
|
|
2672
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2490
2673
|
tool2 = detected;
|
|
2491
2674
|
}
|
|
2675
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2492
2676
|
let step = 1;
|
|
2493
2677
|
while (true) {
|
|
2494
2678
|
switch (step) {
|
|
2495
2679
|
case 1: {
|
|
2496
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2680
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2497
2681
|
if (selectedSkills === "back") {
|
|
2498
2682
|
const redetected = await selectToolWithDetection();
|
|
2499
|
-
if (redetected === "back") return;
|
|
2683
|
+
if (redetected === "back" || redetected === "cancel") return;
|
|
2500
2684
|
tool2 = redetected;
|
|
2501
2685
|
break;
|
|
2502
2686
|
}
|
|
2687
|
+
if (selectedSkills === "cancel") return;
|
|
2503
2688
|
if (selectedSkills.length === 0) {
|
|
2504
|
-
logger.info("
|
|
2689
|
+
logger.info(t("install_no_skill_selected"));
|
|
2505
2690
|
return;
|
|
2506
2691
|
}
|
|
2507
2692
|
names = selectedSkills;
|
|
2508
2693
|
const repoManager = createRepositoryManager();
|
|
2509
|
-
const spinner = createSpinner(t("
|
|
2694
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2510
2695
|
spinner.start();
|
|
2511
2696
|
const repoPath = await repoManager.ensureRepo();
|
|
2512
|
-
spinner.succeed(t("
|
|
2513
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2514
|
-
const level2 = options.level || "project";
|
|
2697
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2698
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(names.length)))}...`);
|
|
2515
2699
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2516
2700
|
let successCount = 0;
|
|
2517
2701
|
let failCount = 0;
|
|
2518
|
-
|
|
2702
|
+
const total = results.length;
|
|
2703
|
+
for (let i = 0; i < results.length; i++) {
|
|
2704
|
+
const result = results[i];
|
|
2705
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2519
2706
|
if (result.success) {
|
|
2520
|
-
logger.success(`${result.skillId}`);
|
|
2707
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2521
2708
|
successCount++;
|
|
2522
2709
|
} else {
|
|
2523
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
2710
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2524
2711
|
failCount++;
|
|
2525
2712
|
}
|
|
2526
2713
|
}
|
|
2527
2714
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2528
2715
|
logger.blank();
|
|
2529
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
2716
|
+
logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2530
2717
|
logger.blank();
|
|
2531
|
-
logger.info(
|
|
2532
|
-
logger.info(
|
|
2718
|
+
logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
|
|
2719
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
|
|
2533
2720
|
logger.blank();
|
|
2534
2721
|
return;
|
|
2535
2722
|
}
|
|
2536
2723
|
}
|
|
2537
2724
|
}
|
|
2538
2725
|
}
|
|
2539
|
-
const scanSpinner = createSpinner("
|
|
2726
|
+
const scanSpinner = createSpinner(t("loading_skills_list"));
|
|
2540
2727
|
scanSpinner.start();
|
|
2541
2728
|
try {
|
|
2542
2729
|
const scanRepoManager = createRepositoryManager();
|
|
2543
2730
|
await scanRepoManager.ensureRepoAndScan();
|
|
2544
|
-
scanSpinner.succeed("
|
|
2731
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2545
2732
|
} catch {
|
|
2546
|
-
scanSpinner.warn("
|
|
2733
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2547
2734
|
}
|
|
2548
2735
|
const plugins = [];
|
|
2549
2736
|
const skills = [];
|
|
@@ -2560,22 +2747,22 @@ async function installCommand(names, options) {
|
|
|
2560
2747
|
}
|
|
2561
2748
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2562
2749
|
const allPlugins = getAllPlugins();
|
|
2563
|
-
logger.info("
|
|
2750
|
+
logger.info(t("available_plugins") + ":");
|
|
2564
2751
|
for (const p of allPlugins) {
|
|
2565
2752
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2566
2753
|
}
|
|
2567
|
-
logger.info(
|
|
2754
|
+
logger.info(`${t("available_skills")}: ${chalk9.cyan("install-helper install --list")}`);
|
|
2568
2755
|
return;
|
|
2569
2756
|
}
|
|
2570
2757
|
let tool;
|
|
2571
2758
|
if (options.tool) {
|
|
2572
|
-
tool = options.tool;
|
|
2759
|
+
tool = validateTool(options.tool);
|
|
2573
2760
|
} else {
|
|
2574
2761
|
const detected = await selectToolWithDetection();
|
|
2575
|
-
if (detected === "back") return;
|
|
2762
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2576
2763
|
tool = detected;
|
|
2577
2764
|
}
|
|
2578
|
-
const level = options.level
|
|
2765
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2579
2766
|
if (plugins.length > 0) {
|
|
2580
2767
|
const configRoot = getConfigRoot(tool, level);
|
|
2581
2768
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2603,16 +2790,18 @@ async function installCommand(names, options) {
|
|
|
2603
2790
|
}
|
|
2604
2791
|
if (pluginsToInstall.length > 0) {
|
|
2605
2792
|
const repoManager = createRepositoryManager();
|
|
2606
|
-
const spinner = createSpinner(t("
|
|
2793
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2607
2794
|
spinner.start();
|
|
2608
2795
|
const repoPath = await repoManager.ensureRepo();
|
|
2609
|
-
spinner.succeed(t("
|
|
2796
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2610
2797
|
const allPlugins = getAllPlugins();
|
|
2611
2798
|
const results = await installPlugins(
|
|
2612
2799
|
pluginsToInstall,
|
|
2613
2800
|
tool,
|
|
2614
2801
|
level,
|
|
2615
|
-
repoPath
|
|
2802
|
+
repoPath,
|
|
2803
|
+
void 0,
|
|
2804
|
+
options.yes
|
|
2616
2805
|
);
|
|
2617
2806
|
const summary = results.map((result) => {
|
|
2618
2807
|
const plugin = allPlugins.find((p) => p.id === result.pluginId);
|
|
@@ -2630,32 +2819,35 @@ async function installCommand(names, options) {
|
|
|
2630
2819
|
}
|
|
2631
2820
|
if (skills.length > 0) {
|
|
2632
2821
|
const repoManager = createRepositoryManager();
|
|
2633
|
-
const spinner = createSpinner(t("
|
|
2822
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2634
2823
|
spinner.start();
|
|
2635
2824
|
const repoPath = await repoManager.ensureRepo();
|
|
2636
|
-
spinner.succeed(t("
|
|
2637
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2825
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2826
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(skills.length)))}...`);
|
|
2638
2827
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2639
2828
|
let successCount = 0;
|
|
2640
2829
|
let failCount = 0;
|
|
2641
|
-
|
|
2830
|
+
const total = results.length;
|
|
2831
|
+
for (let i = 0; i < results.length; i++) {
|
|
2832
|
+
const result = results[i];
|
|
2833
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2642
2834
|
if (result.success) {
|
|
2643
|
-
logger.success(`${result.skillId}`);
|
|
2835
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2644
2836
|
successCount++;
|
|
2645
2837
|
} else {
|
|
2646
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
2838
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2647
2839
|
failCount++;
|
|
2648
2840
|
}
|
|
2649
2841
|
}
|
|
2650
2842
|
logger.blank();
|
|
2651
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
2843
|
+
logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2652
2844
|
logger.blank();
|
|
2653
|
-
logger.info(
|
|
2654
|
-
logger.info(
|
|
2845
|
+
logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
2846
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
|
|
2655
2847
|
logger.blank();
|
|
2656
2848
|
}
|
|
2657
2849
|
}
|
|
2658
|
-
async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
2850
|
+
async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes) {
|
|
2659
2851
|
const results = [];
|
|
2660
2852
|
const total = pluginIds.length;
|
|
2661
2853
|
for (let i = 0; i < pluginIds.length; i++) {
|
|
@@ -2667,7 +2859,9 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2667
2859
|
pluginId,
|
|
2668
2860
|
tool,
|
|
2669
2861
|
level,
|
|
2670
|
-
repoPath
|
|
2862
|
+
repoPath,
|
|
2863
|
+
installPath,
|
|
2864
|
+
yes
|
|
2671
2865
|
});
|
|
2672
2866
|
if (result.success) {
|
|
2673
2867
|
spinner.succeed(
|
|
@@ -2683,11 +2877,11 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2683
2877
|
}
|
|
2684
2878
|
|
|
2685
2879
|
// src/commands/uninstall.ts
|
|
2686
|
-
import { existsSync as
|
|
2687
|
-
import
|
|
2880
|
+
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
|
|
2881
|
+
import chalk10 from "chalk";
|
|
2688
2882
|
async function uninstallCommand(names, options) {
|
|
2689
|
-
const tool = options.tool
|
|
2690
|
-
const level = options.level
|
|
2883
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
2884
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2691
2885
|
const plugins = [];
|
|
2692
2886
|
const skills = [];
|
|
2693
2887
|
for (const name of names) {
|
|
@@ -2701,40 +2895,44 @@ async function uninstallCommand(names, options) {
|
|
|
2701
2895
|
skills.push(skill.id);
|
|
2702
2896
|
continue;
|
|
2703
2897
|
}
|
|
2704
|
-
logger.error(
|
|
2898
|
+
logger.error(`${t("uninstall_not_found")}: ${name}`);
|
|
2705
2899
|
}
|
|
2706
2900
|
for (const pluginId of plugins) {
|
|
2707
2901
|
const plugin = findPlugin(pluginId);
|
|
2708
2902
|
if (!plugin) continue;
|
|
2709
2903
|
const record = readRecord(plugin.id);
|
|
2710
2904
|
if (!record) {
|
|
2711
|
-
logger.warn(`${plugin.displayName}
|
|
2712
|
-
logger.info(
|
|
2905
|
+
logger.warn(`${plugin.displayName} ${t("uninstall_no_record")}`);
|
|
2906
|
+
logger.info(`${t("uninstall_no_record_hint")}`);
|
|
2713
2907
|
continue;
|
|
2714
2908
|
}
|
|
2715
|
-
logger.info(
|
|
2909
|
+
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
2716
2910
|
let removedFiles = 0;
|
|
2717
2911
|
let removedDirs = 0;
|
|
2718
2912
|
for (const filePath of record.files) {
|
|
2719
2913
|
try {
|
|
2720
|
-
if (
|
|
2721
|
-
|
|
2914
|
+
if (existsSync13(filePath) || isSymlink3(filePath)) {
|
|
2915
|
+
unlinkSync5(filePath);
|
|
2722
2916
|
const name = filePath.split("/").pop() || filePath;
|
|
2723
|
-
logger.step(`
|
|
2917
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2724
2918
|
removedFiles++;
|
|
2725
2919
|
}
|
|
2726
2920
|
} catch {
|
|
2727
2921
|
}
|
|
2728
2922
|
}
|
|
2923
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
2729
2924
|
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
2730
2925
|
for (const dirPath of sortedDirs) {
|
|
2731
2926
|
try {
|
|
2732
|
-
if (
|
|
2733
|
-
|
|
2927
|
+
if (dirPath === configRoot) {
|
|
2928
|
+
continue;
|
|
2929
|
+
}
|
|
2930
|
+
if (existsSync13(dirPath)) {
|
|
2931
|
+
const entries = readdirSync6(dirPath);
|
|
2734
2932
|
if (entries.length === 0) {
|
|
2735
2933
|
rmdirSync2(dirPath);
|
|
2736
2934
|
const name = dirPath.split("/").pop() || dirPath;
|
|
2737
|
-
logger.step(`
|
|
2935
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
2738
2936
|
removedDirs++;
|
|
2739
2937
|
}
|
|
2740
2938
|
}
|
|
@@ -2743,15 +2941,32 @@ async function uninstallCommand(names, options) {
|
|
|
2743
2941
|
}
|
|
2744
2942
|
deleteRecord(plugin.id);
|
|
2745
2943
|
removeInstalledPlugin(plugin.id);
|
|
2944
|
+
const backups = findBackups(configRoot);
|
|
2945
|
+
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
2946
|
+
if (otherBackups.length > 0) {
|
|
2947
|
+
const choice = await showRestorePrompt(otherBackups);
|
|
2948
|
+
if (choice !== "none") {
|
|
2949
|
+
const restored = restoreBackup(choice, configRoot, record.tool);
|
|
2950
|
+
if (restored) {
|
|
2951
|
+
const backupInfo = otherBackups.find((b) => b.filePath === choice);
|
|
2952
|
+
logger.success(`${t("backup_restore_success")}: ${backupInfo?.pluginName || ""}`);
|
|
2953
|
+
deleteBackup(choice);
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
const currentBackups = backups.filter((b) => b.pluginId === pluginId);
|
|
2958
|
+
for (const backup of currentBackups) {
|
|
2959
|
+
deleteBackup(backup.filePath);
|
|
2960
|
+
}
|
|
2746
2961
|
const totalRemoved = removedFiles + removedDirs;
|
|
2747
2962
|
if (totalRemoved > 0) {
|
|
2748
|
-
logger.success(
|
|
2963
|
+
logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08${t("uninstall_remove_file_format").replace("{count}", String(removedFiles))}\uFF0C${t("uninstall_remove_dir_format").replace("{count}", String(removedDirs))}\uFF09`);
|
|
2749
2964
|
} else {
|
|
2750
|
-
logger.warn(`${plugin.displayName}
|
|
2965
|
+
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
2751
2966
|
}
|
|
2752
2967
|
}
|
|
2753
2968
|
if (skills.length > 0) {
|
|
2754
|
-
logger.info(
|
|
2969
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
2755
2970
|
const results = await uninstallSkills(skills, tool, level);
|
|
2756
2971
|
let successCount = 0;
|
|
2757
2972
|
let failCount = 0;
|
|
@@ -2763,7 +2978,7 @@ async function uninstallCommand(names, options) {
|
|
|
2763
2978
|
}
|
|
2764
2979
|
}
|
|
2765
2980
|
logger.blank();
|
|
2766
|
-
logger.success(`${t("skill_uninstall_done")}: ${
|
|
2981
|
+
logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2767
2982
|
logger.blank();
|
|
2768
2983
|
}
|
|
2769
2984
|
}
|
|
@@ -2776,17 +2991,17 @@ function isSymlink3(path) {
|
|
|
2776
2991
|
}
|
|
2777
2992
|
|
|
2778
2993
|
// src/commands/update.ts
|
|
2779
|
-
import
|
|
2994
|
+
import chalk11 from "chalk";
|
|
2780
2995
|
async function updateCommand(pluginNames, options) {
|
|
2781
2996
|
let tool;
|
|
2782
2997
|
if (options.tool) {
|
|
2783
|
-
tool = options.tool;
|
|
2998
|
+
tool = validateTool(options.tool);
|
|
2784
2999
|
} else {
|
|
2785
3000
|
const detected = await selectToolWithDetection();
|
|
2786
|
-
if (detected === "back") return;
|
|
3001
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2787
3002
|
tool = detected;
|
|
2788
3003
|
}
|
|
2789
|
-
const level = options.level
|
|
3004
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2790
3005
|
let pluginsToUpdate = [];
|
|
2791
3006
|
if (pluginNames.length === 0) {
|
|
2792
3007
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -2794,7 +3009,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2794
3009
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
2795
3010
|
if (pluginsToUpdate.length === 0) {
|
|
2796
3011
|
logger.info(t("update_no_plugins"));
|
|
2797
|
-
logger.info(
|
|
3012
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk11.cyan("install-helper install <plugin>")));
|
|
2798
3013
|
return;
|
|
2799
3014
|
}
|
|
2800
3015
|
} else {
|
|
@@ -2856,42 +3071,42 @@ async function updateCommand(pluginNames, options) {
|
|
|
2856
3071
|
}
|
|
2857
3072
|
|
|
2858
3073
|
// src/commands/info.ts
|
|
2859
|
-
import
|
|
2860
|
-
import { existsSync as
|
|
2861
|
-
import { join as
|
|
3074
|
+
import chalk12 from "chalk";
|
|
3075
|
+
import { existsSync as existsSync14, readdirSync as readdirSync7 } from "fs";
|
|
3076
|
+
import { join as join15 } from "path";
|
|
2862
3077
|
async function infoCommand(pluginName) {
|
|
2863
3078
|
const plugin = findPlugin(pluginName);
|
|
2864
3079
|
if (!plugin) {
|
|
2865
|
-
console.log(
|
|
3080
|
+
console.log(chalk12.red(` ${t("info_not_found")}: ${pluginName}`));
|
|
2866
3081
|
return;
|
|
2867
3082
|
}
|
|
2868
3083
|
const installed = scanInstalled();
|
|
2869
3084
|
const isInstalled = installed.some((p) => p.id === plugin.id);
|
|
2870
|
-
const statusText = isInstalled ?
|
|
3085
|
+
const statusText = isInstalled ? chalk12.green(`\u2713 ${t("status_installed")}`) : chalk12.dim(`\u2014 ${t("status_not_installed")}`);
|
|
2871
3086
|
console.log();
|
|
2872
|
-
console.log(
|
|
2873
|
-
console.log(
|
|
3087
|
+
console.log(chalk12.bold(` ${plugin.displayName}`));
|
|
3088
|
+
console.log(chalk12.dim(" " + "\u2500".repeat(40)));
|
|
2874
3089
|
console.log();
|
|
2875
|
-
console.log(` ${
|
|
2876
|
-
console.log(` ${
|
|
2877
|
-
console.log(` ${
|
|
2878
|
-
console.log(` ${
|
|
2879
|
-
console.log(` ${
|
|
2880
|
-
console.log(` ${
|
|
3090
|
+
console.log(` ${chalk12.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
3091
|
+
console.log(` ${chalk12.dim("ID:")} ${plugin.id}`);
|
|
3092
|
+
console.log(` ${chalk12.dim(t("info_status") + ":")} ${statusText}`);
|
|
3093
|
+
console.log(` ${chalk12.dim(t("info_skills") + ":")} ${plugin.skills}`);
|
|
3094
|
+
console.log(` ${chalk12.dim(t("info_agents") + ":")} ${plugin.agents}`);
|
|
3095
|
+
console.log(` ${chalk12.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
2881
3096
|
console.log();
|
|
2882
3097
|
try {
|
|
2883
3098
|
const repoManager = createRepositoryManager();
|
|
2884
3099
|
const repoPath = await repoManager.ensureRepo();
|
|
2885
|
-
const quickstartPath =
|
|
2886
|
-
if (
|
|
2887
|
-
console.log(` ${
|
|
3100
|
+
const quickstartPath = join15(repoPath, plugin.dir, "quickstart.md");
|
|
3101
|
+
if (existsSync14(quickstartPath)) {
|
|
3102
|
+
console.log(` ${chalk12.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
2888
3103
|
}
|
|
2889
|
-
const agentsDir =
|
|
2890
|
-
if (
|
|
2891
|
-
const agentFiles =
|
|
3104
|
+
const agentsDir = join15(repoPath, plugin.dir, "agents");
|
|
3105
|
+
if (existsSync14(agentsDir)) {
|
|
3106
|
+
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
2892
3107
|
if (agentFiles.length > 0) {
|
|
2893
3108
|
console.log();
|
|
2894
|
-
console.log(` ${
|
|
3109
|
+
console.log(` ${chalk12.bold("Agents:")}`);
|
|
2895
3110
|
for (const agent of agentFiles) {
|
|
2896
3111
|
console.log(` \u2022 ${agent.replace(".md", "")}`);
|
|
2897
3112
|
}
|
|
@@ -2903,39 +3118,39 @@ async function infoCommand(pluginName) {
|
|
|
2903
3118
|
}
|
|
2904
3119
|
|
|
2905
3120
|
// src/commands/lang.ts
|
|
2906
|
-
import
|
|
3121
|
+
import chalk13 from "chalk";
|
|
2907
3122
|
async function langCommand(action, value) {
|
|
2908
3123
|
const config = readConfig();
|
|
2909
3124
|
if (!action || action === "show") {
|
|
2910
3125
|
console.log();
|
|
2911
|
-
console.log(
|
|
2912
|
-
console.log(
|
|
3126
|
+
console.log(chalk13.bold(` ${t("lang_title")}`));
|
|
3127
|
+
console.log(chalk13.dim(" " + "\u2500".repeat(40)));
|
|
2913
3128
|
console.log();
|
|
2914
|
-
console.log(` ${
|
|
3129
|
+
console.log(` ${chalk13.dim(t("lang_current") + ":")} ${config.language}`);
|
|
2915
3130
|
console.log();
|
|
2916
|
-
console.log(
|
|
2917
|
-
console.log(
|
|
3131
|
+
console.log(chalk13.dim(` ${t("lang_supported")}`));
|
|
3132
|
+
console.log(chalk13.dim(` ${t("lang_usage")}`));
|
|
2918
3133
|
console.log();
|
|
2919
3134
|
return;
|
|
2920
3135
|
}
|
|
2921
3136
|
if (action === "set") {
|
|
2922
3137
|
if (!value) {
|
|
2923
|
-
console.log(
|
|
3138
|
+
console.log(chalk13.red(` ${t("lang_specify")}`));
|
|
2924
3139
|
return;
|
|
2925
3140
|
}
|
|
2926
3141
|
if (value !== "zh_CN" && value !== "en_US") {
|
|
2927
|
-
console.log(
|
|
3142
|
+
console.log(chalk13.red(` ${t("lang_invalid")}`));
|
|
2928
3143
|
return;
|
|
2929
3144
|
}
|
|
2930
3145
|
updateConfig({ language: value });
|
|
2931
3146
|
setLanguage(value);
|
|
2932
3147
|
console.log();
|
|
2933
|
-
console.log(
|
|
3148
|
+
console.log(chalk13.green(` \u2713 ${t("lang_set")} ${value}`));
|
|
2934
3149
|
console.log();
|
|
2935
3150
|
return;
|
|
2936
3151
|
}
|
|
2937
|
-
console.log(
|
|
2938
|
-
console.log(
|
|
3152
|
+
console.log(chalk13.red(` ${t("lang_unknown_action")}: ${action}`));
|
|
3153
|
+
console.log(chalk13.dim(` ${t("lang_supported_actions")}`));
|
|
2939
3154
|
}
|
|
2940
3155
|
|
|
2941
3156
|
// src/cli.ts
|
|
@@ -2945,7 +3160,7 @@ function createCLI() {
|
|
|
2945
3160
|
setLanguage(config.language);
|
|
2946
3161
|
}
|
|
2947
3162
|
const program2 = new Command();
|
|
2948
|
-
program2.name("install-helper").description("Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.
|
|
3163
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.21");
|
|
2949
3164
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
2950
3165
|
await initCommand();
|
|
2951
3166
|
});
|
|
@@ -2958,7 +3173,7 @@ function createCLI() {
|
|
|
2958
3173
|
program2.command("status").description("Show installed plugins").action(async () => {
|
|
2959
3174
|
await statusCommand();
|
|
2960
3175
|
});
|
|
2961
|
-
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) => {
|
|
3176
|
+
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) => {
|
|
2962
3177
|
await installCommand(names, options);
|
|
2963
3178
|
});
|
|
2964
3179
|
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) => {
|
|
@@ -2982,7 +3197,7 @@ function createCLI() {
|
|
|
2982
3197
|
// src/index.ts
|
|
2983
3198
|
process.on("uncaughtException", (error) => {
|
|
2984
3199
|
if (error.name === "ExitPromptError") {
|
|
2985
|
-
console.log("\n
|
|
3200
|
+
console.log("\n" + t("init_cancelled"));
|
|
2986
3201
|
process.exit(0);
|
|
2987
3202
|
}
|
|
2988
3203
|
throw error;
|