@cannbot-ai/install-helper 0.0.2-beta.3 → 0.0.2-beta.31
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 +122 -31
- package/dist/config/repository.yaml +26 -0
- package/dist/index.js +1097 -744
- 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 +7 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,116 +4,104 @@
|
|
|
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 chalk7 from "chalk";
|
|
9
9
|
|
|
10
10
|
// src/ui/wizard.ts
|
|
11
11
|
import { select, checkbox, Separator } from "@inquirer/prompts";
|
|
12
|
-
import
|
|
12
|
+
import chalk3 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, existsSync, statSync } 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
|
+
plugins.sort(
|
|
52
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
53
|
+
);
|
|
54
|
+
return plugins;
|
|
55
|
+
}
|
|
56
|
+
var PLUGIN_REGISTRY = loadPluginsFromYaml();
|
|
57
|
+
var PLUGIN_DIRS = ["plugins-official", "plugins-community"];
|
|
58
|
+
function mergeDynamicPlugins(repoPath) {
|
|
59
|
+
if (!existsSync(repoPath)) return;
|
|
60
|
+
const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
|
|
61
|
+
const dynamic = [];
|
|
62
|
+
for (const pluginDir of PLUGIN_DIRS) {
|
|
63
|
+
const parent = join(repoPath, pluginDir);
|
|
64
|
+
if (!existsSync(parent)) continue;
|
|
65
|
+
let entries = [];
|
|
66
|
+
try {
|
|
67
|
+
entries = readdirSync(parent);
|
|
68
|
+
} catch {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const pluginPath = join(parent, entry);
|
|
73
|
+
if (!isDirectory(pluginPath)) continue;
|
|
74
|
+
if (entry === "install-helper") continue;
|
|
75
|
+
const initSh = join(pluginPath, "init.sh");
|
|
76
|
+
if (!existsSync(initSh)) continue;
|
|
77
|
+
const pluginId = entry;
|
|
78
|
+
if (existingIds.has(pluginId)) continue;
|
|
79
|
+
dynamic.push({
|
|
80
|
+
id: pluginId,
|
|
81
|
+
dir: `${pluginDir}/${entry}`,
|
|
82
|
+
displayName: entry,
|
|
83
|
+
script: "init.sh",
|
|
84
|
+
aliases: [],
|
|
85
|
+
skills: 0,
|
|
86
|
+
agents: 0,
|
|
87
|
+
description: ""
|
|
88
|
+
});
|
|
89
|
+
existingIds.add(pluginId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (dynamic.length > 0) {
|
|
93
|
+
PLUGIN_REGISTRY = [...PLUGIN_REGISTRY, ...dynamic].sort(
|
|
94
|
+
(a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function isDirectory(path) {
|
|
99
|
+
try {
|
|
100
|
+
return statSync(path).isDirectory();
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
117
105
|
function findPlugin(query) {
|
|
118
106
|
const normalized = query.toLowerCase().trim();
|
|
119
107
|
const exactMatch = PLUGIN_REGISTRY.find(
|
|
@@ -137,19 +125,70 @@ function getPluginById(id) {
|
|
|
137
125
|
|
|
138
126
|
// src/core/detector.ts
|
|
139
127
|
import { execa } from "execa";
|
|
140
|
-
import { existsSync as
|
|
128
|
+
import { existsSync as existsSync3 } from "fs";
|
|
141
129
|
import { homedir as homedir2 } from "os";
|
|
142
|
-
import { join as
|
|
130
|
+
import { join as join4 } from "path";
|
|
143
131
|
|
|
144
132
|
// src/utils/paths.ts
|
|
145
133
|
import { homedir } from "os";
|
|
146
|
-
import { join } from "path";
|
|
147
|
-
import { existsSync } from "fs";
|
|
134
|
+
import { join as join3 } from "path";
|
|
135
|
+
import { existsSync as existsSync2 } from "fs";
|
|
136
|
+
|
|
137
|
+
// src/utils/i18n.ts
|
|
138
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
139
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
140
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
141
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
142
|
+
var __dirname2 = dirname2(__filename2);
|
|
143
|
+
function loadLocale(locale) {
|
|
144
|
+
const localePath = join2(__dirname2, "locales", `${locale}.json`);
|
|
145
|
+
try {
|
|
146
|
+
const content = readFileSync2(localePath, "utf-8");
|
|
147
|
+
return JSON.parse(content);
|
|
148
|
+
} catch {
|
|
149
|
+
const fallbackPath = join2(__dirname2, "locales", "zh_CN.json");
|
|
150
|
+
try {
|
|
151
|
+
const content = readFileSync2(fallbackPath, "utf-8");
|
|
152
|
+
return JSON.parse(content);
|
|
153
|
+
} catch {
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
var currentLang = "zh_CN";
|
|
159
|
+
var messages = loadLocale("zh_CN");
|
|
160
|
+
function setLanguage(lang) {
|
|
161
|
+
currentLang = lang;
|
|
162
|
+
messages = loadLocale(lang);
|
|
163
|
+
}
|
|
164
|
+
function t(key) {
|
|
165
|
+
return messages[key] !== void 0 ? messages[key] : key;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/utils/paths.ts
|
|
169
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
170
|
+
var VALID_LEVELS = ["project", "global"];
|
|
171
|
+
function validateTool(tool) {
|
|
172
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return tool;
|
|
178
|
+
}
|
|
179
|
+
function validateLevel(level) {
|
|
180
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return level;
|
|
186
|
+
}
|
|
148
187
|
function detectTraeVariant() {
|
|
149
188
|
const home = homedir();
|
|
150
|
-
if (
|
|
151
|
-
if (
|
|
152
|
-
if (
|
|
189
|
+
if (existsSync2(join3(home, ".trae-cn"))) return "ide";
|
|
190
|
+
if (existsSync2(join3(home, ".marscode"))) return "plugin";
|
|
191
|
+
if (existsSync2(join3(home, ".traecli"))) return "cli";
|
|
153
192
|
return "unknown";
|
|
154
193
|
}
|
|
155
194
|
function getConfigRoot(tool, level, base) {
|
|
@@ -157,66 +196,74 @@ function getConfigRoot(tool, level, base) {
|
|
|
157
196
|
if (level === "global") {
|
|
158
197
|
switch (tool) {
|
|
159
198
|
case "opencode":
|
|
160
|
-
return
|
|
199
|
+
return join3(home, ".config", "opencode");
|
|
161
200
|
case "claude":
|
|
162
|
-
return
|
|
201
|
+
return join3(home, ".claude");
|
|
163
202
|
case "trae": {
|
|
164
203
|
const variant = detectTraeVariant();
|
|
165
204
|
switch (variant) {
|
|
166
205
|
case "plugin":
|
|
167
|
-
return
|
|
206
|
+
return join3(home, ".marscode");
|
|
168
207
|
case "cli":
|
|
169
|
-
return
|
|
208
|
+
return join3(home, ".traecli");
|
|
170
209
|
default:
|
|
171
|
-
return
|
|
210
|
+
return join3(home, ".trae-cn");
|
|
172
211
|
}
|
|
173
212
|
}
|
|
174
213
|
case "cursor":
|
|
175
|
-
return
|
|
214
|
+
return join3(home, ".cursor");
|
|
176
215
|
case "copilot":
|
|
177
|
-
return
|
|
216
|
+
return join3(home, ".copilot");
|
|
217
|
+
default:
|
|
218
|
+
throw new Error(
|
|
219
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
220
|
+
);
|
|
178
221
|
}
|
|
179
222
|
}
|
|
180
223
|
const baseDir = base || process.cwd();
|
|
181
224
|
switch (tool) {
|
|
182
225
|
case "opencode":
|
|
183
|
-
return
|
|
226
|
+
return join3(baseDir, ".opencode");
|
|
184
227
|
case "claude":
|
|
185
|
-
return
|
|
228
|
+
return join3(baseDir, ".claude");
|
|
186
229
|
case "trae": {
|
|
187
230
|
const variant = detectTraeVariant();
|
|
188
231
|
switch (variant) {
|
|
189
232
|
case "plugin":
|
|
190
|
-
return
|
|
233
|
+
return join3(baseDir, ".marscode");
|
|
191
234
|
case "cli":
|
|
192
|
-
return
|
|
235
|
+
return join3(baseDir, ".traecli");
|
|
193
236
|
default:
|
|
194
|
-
return
|
|
237
|
+
return join3(baseDir, ".trae");
|
|
195
238
|
}
|
|
196
239
|
}
|
|
197
240
|
case "cursor":
|
|
198
|
-
return
|
|
241
|
+
return join3(baseDir, ".cursor");
|
|
199
242
|
case "copilot":
|
|
200
|
-
return
|
|
243
|
+
return join3(baseDir, ".github");
|
|
244
|
+
default:
|
|
245
|
+
throw new Error(
|
|
246
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
247
|
+
);
|
|
201
248
|
}
|
|
202
249
|
}
|
|
203
250
|
function getConfigFileName(tool) {
|
|
204
251
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
252
|
}
|
|
206
253
|
function getSkillsDir(configRoot) {
|
|
207
|
-
return
|
|
254
|
+
return join3(configRoot, "skills");
|
|
208
255
|
}
|
|
209
256
|
function getAgentsDir(configRoot) {
|
|
210
|
-
return
|
|
257
|
+
return join3(configRoot, "agents");
|
|
211
258
|
}
|
|
212
259
|
function getManifestPath(configRoot) {
|
|
213
|
-
return
|
|
260
|
+
return join3(configRoot, "cannbot-manifest.json");
|
|
214
261
|
}
|
|
215
262
|
function getCannbotConfigDir() {
|
|
216
|
-
return
|
|
263
|
+
return join3(homedir(), ".cannbot");
|
|
217
264
|
}
|
|
218
265
|
function getCannbotRepoPath() {
|
|
219
|
-
return
|
|
266
|
+
return join3(getCannbotConfigDir(), "repo");
|
|
220
267
|
}
|
|
221
268
|
|
|
222
269
|
// src/core/detector.ts
|
|
@@ -232,7 +279,7 @@ async function getCommandVersion(cmd, args = ["--version"]) {
|
|
|
232
279
|
}
|
|
233
280
|
async function getCommandPath(cmd) {
|
|
234
281
|
try {
|
|
235
|
-
const result = await execa("
|
|
282
|
+
const result = await execa("which", [cmd], { timeout: 5e3 });
|
|
236
283
|
return result.stdout.trim();
|
|
237
284
|
} catch {
|
|
238
285
|
return void 0;
|
|
@@ -254,12 +301,12 @@ async function detectTrae() {
|
|
|
254
301
|
const home = homedir2();
|
|
255
302
|
const variant = detectTraeVariant();
|
|
256
303
|
let path;
|
|
257
|
-
if (variant === "ide" &&
|
|
258
|
-
path =
|
|
259
|
-
} else if (variant === "plugin" &&
|
|
260
|
-
path =
|
|
261
|
-
} else if (variant === "cli" &&
|
|
262
|
-
path =
|
|
304
|
+
if (variant === "ide" && existsSync3(join4(home, ".trae-cn"))) {
|
|
305
|
+
path = join4(home, ".trae-cn");
|
|
306
|
+
} else if (variant === "plugin" && existsSync3(join4(home, ".marscode"))) {
|
|
307
|
+
path = join4(home, ".marscode");
|
|
308
|
+
} else if (variant === "cli" && existsSync3(join4(home, ".traecli"))) {
|
|
309
|
+
path = join4(home, ".traecli");
|
|
263
310
|
} else if (variant === "unknown") {
|
|
264
311
|
const cmdPath = await getCommandPath("trae");
|
|
265
312
|
if (cmdPath) path = cmdPath;
|
|
@@ -275,7 +322,7 @@ async function detectCursor() {
|
|
|
275
322
|
}
|
|
276
323
|
if (process.platform === "darwin") {
|
|
277
324
|
const appPath = "/Applications/Cursor.app";
|
|
278
|
-
if (
|
|
325
|
+
if (existsSync3(appPath)) {
|
|
279
326
|
return { name: "cursor", path: appPath };
|
|
280
327
|
}
|
|
281
328
|
}
|
|
@@ -323,13 +370,13 @@ function getAllTools() {
|
|
|
323
370
|
}
|
|
324
371
|
|
|
325
372
|
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as
|
|
327
|
-
import { join as
|
|
373
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
374
|
+
import { join as join5 } from "path";
|
|
328
375
|
function readManifest(configRoot) {
|
|
329
376
|
const manifestPath = getManifestPath(configRoot);
|
|
330
|
-
if (
|
|
377
|
+
if (existsSync4(manifestPath)) {
|
|
331
378
|
try {
|
|
332
|
-
const content =
|
|
379
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
333
380
|
return JSON.parse(content);
|
|
334
381
|
} catch {
|
|
335
382
|
}
|
|
@@ -340,18 +387,18 @@ function readAllManifests(configRoot) {
|
|
|
340
387
|
const manifests = [];
|
|
341
388
|
const plugins = getAllPlugins();
|
|
342
389
|
const standardPath = getManifestPath(configRoot);
|
|
343
|
-
if (
|
|
390
|
+
if (existsSync4(standardPath)) {
|
|
344
391
|
try {
|
|
345
|
-
const content =
|
|
392
|
+
const content = readFileSync3(standardPath, "utf-8");
|
|
346
393
|
manifests.push(JSON.parse(content));
|
|
347
394
|
} catch {
|
|
348
395
|
}
|
|
349
396
|
}
|
|
350
397
|
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath =
|
|
352
|
-
if (
|
|
398
|
+
const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
|
|
399
|
+
if (existsSync4(pluginManifestPath)) {
|
|
353
400
|
try {
|
|
354
|
-
const content =
|
|
401
|
+
const content = readFileSync3(pluginManifestPath, "utf-8");
|
|
355
402
|
manifests.push(JSON.parse(content));
|
|
356
403
|
} catch {
|
|
357
404
|
}
|
|
@@ -371,6 +418,9 @@ function scanInstalled() {
|
|
|
371
418
|
for (const manifest of manifests) {
|
|
372
419
|
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
420
|
if (!plugin) continue;
|
|
421
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
374
424
|
const alreadyAdded = installed.some(
|
|
375
425
|
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
426
|
);
|
|
@@ -390,190 +440,31 @@ function scanInstalled() {
|
|
|
390
440
|
}
|
|
391
441
|
return installed;
|
|
392
442
|
}
|
|
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];
|
|
443
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
444
|
+
const skillsDir = join5(configRoot, "skills");
|
|
445
|
+
const agentsDir = join5(configRoot, "agents");
|
|
446
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
447
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
448
|
+
const skillPath = join5(skillsDir, skillId);
|
|
449
|
+
return existsSync4(skillPath);
|
|
450
|
+
});
|
|
451
|
+
if (!hasAnySkill) return false;
|
|
452
|
+
}
|
|
453
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
454
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
455
|
+
const agentPath = join5(agentsDir, agentId);
|
|
456
|
+
return existsSync4(agentPath);
|
|
457
|
+
});
|
|
458
|
+
if (!hasAnyAgent) return false;
|
|
459
|
+
}
|
|
460
|
+
let agentsMdPath;
|
|
461
|
+
if (tool === "opencode" && level === "project") {
|
|
462
|
+
agentsMdPath = join5(configRoot, "..", "AGENTS.md");
|
|
463
|
+
} else {
|
|
464
|
+
agentsMdPath = join5(configRoot, "AGENTS.md");
|
|
465
|
+
}
|
|
466
|
+
if (!existsSync4(agentsMdPath)) return false;
|
|
467
|
+
return true;
|
|
577
468
|
}
|
|
578
469
|
|
|
579
470
|
// src/utils/logger.ts
|
|
@@ -630,35 +521,35 @@ function printBoxTitle(title, width = 65) {
|
|
|
630
521
|
}
|
|
631
522
|
function showOperationHints(isCheckbox = false) {
|
|
632
523
|
if (isCheckbox) {
|
|
633
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
524
|
+
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
525
|
`);
|
|
635
526
|
} else {
|
|
636
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
527
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
637
528
|
`);
|
|
638
529
|
}
|
|
639
530
|
}
|
|
640
531
|
|
|
641
532
|
// src/utils/config.ts
|
|
642
|
-
import { existsSync as
|
|
643
|
-
import { join as
|
|
533
|
+
import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
534
|
+
import { join as join6 } from "path";
|
|
644
535
|
import { homedir as homedir3 } from "os";
|
|
645
536
|
import { parse, stringify } from "yaml";
|
|
646
537
|
function getConfigDir() {
|
|
647
|
-
return
|
|
538
|
+
return join6(homedir3(), ".cannbot");
|
|
648
539
|
}
|
|
649
540
|
function getConfigPath() {
|
|
650
|
-
return
|
|
541
|
+
return join6(getConfigDir(), "config.yaml");
|
|
651
542
|
}
|
|
652
543
|
function readConfig() {
|
|
653
544
|
const configPath = getConfigPath();
|
|
654
|
-
if (!
|
|
545
|
+
if (!existsSync5(configPath)) {
|
|
655
546
|
return {
|
|
656
547
|
language: "zh_CN",
|
|
657
548
|
installedPlugins: []
|
|
658
549
|
};
|
|
659
550
|
}
|
|
660
551
|
try {
|
|
661
|
-
const content =
|
|
552
|
+
const content = readFileSync4(configPath, "utf-8");
|
|
662
553
|
const parsed = parse(content);
|
|
663
554
|
return {
|
|
664
555
|
language: parsed.language || "zh_CN",
|
|
@@ -676,7 +567,7 @@ function readConfig() {
|
|
|
676
567
|
}
|
|
677
568
|
function writeConfig(config) {
|
|
678
569
|
const configDir = getConfigDir();
|
|
679
|
-
if (!
|
|
570
|
+
if (!existsSync5(configDir)) {
|
|
680
571
|
mkdirSync(configDir, { recursive: true });
|
|
681
572
|
}
|
|
682
573
|
const configPath = getConfigPath();
|
|
@@ -705,16 +596,78 @@ function removeInstalledPlugin(pluginId) {
|
|
|
705
596
|
}
|
|
706
597
|
|
|
707
598
|
// src/ui/theme.ts
|
|
599
|
+
import chalk2 from "chalk";
|
|
708
600
|
var selectTheme = {
|
|
709
601
|
helpMode: "never"
|
|
710
602
|
};
|
|
603
|
+
function isCJK(code) {
|
|
604
|
+
return code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65519 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 173824 && code <= 177983 || code >= 177984 && code <= 178207 || code >= 178208 && code <= 183983;
|
|
605
|
+
}
|
|
606
|
+
function isEmoji(code) {
|
|
607
|
+
return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
|
|
608
|
+
}
|
|
609
|
+
function getDisplayWidth2(str) {
|
|
610
|
+
let width = 0;
|
|
611
|
+
for (const ch of str) {
|
|
612
|
+
const code = ch.codePointAt(0);
|
|
613
|
+
if (isCJK(code) || isEmoji(code)) {
|
|
614
|
+
width += 2;
|
|
615
|
+
} else {
|
|
616
|
+
width += 1;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return width;
|
|
620
|
+
}
|
|
621
|
+
function wrapDescription(text) {
|
|
622
|
+
const prefix = " \u{1F4A1} ";
|
|
623
|
+
const indent = " ";
|
|
624
|
+
const cols = process.stdout.columns || 120;
|
|
625
|
+
const maxLen = Math.max(20, cols - getDisplayWidth2(prefix) - 2);
|
|
626
|
+
const tokens = [];
|
|
627
|
+
let i = 0;
|
|
628
|
+
while (i < text.length) {
|
|
629
|
+
const code = text[i].codePointAt(0);
|
|
630
|
+
if (isCJK(code)) {
|
|
631
|
+
tokens.push({ text: text[i], width: 2 });
|
|
632
|
+
i++;
|
|
633
|
+
} else {
|
|
634
|
+
let j = i;
|
|
635
|
+
while (j < text.length) {
|
|
636
|
+
const c = text[j].codePointAt(0);
|
|
637
|
+
if (isCJK(c)) break;
|
|
638
|
+
j++;
|
|
639
|
+
}
|
|
640
|
+
const word = text.slice(i, j);
|
|
641
|
+
tokens.push({ text: word, width: getDisplayWidth2(word) });
|
|
642
|
+
i = j;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const lines = [];
|
|
646
|
+
let line = "";
|
|
647
|
+
let lineWidth = 0;
|
|
648
|
+
for (const token of tokens) {
|
|
649
|
+
if (lineWidth + token.width > maxLen && line) {
|
|
650
|
+
lines.push(line.trimEnd());
|
|
651
|
+
line = token.text;
|
|
652
|
+
lineWidth = token.width;
|
|
653
|
+
} else {
|
|
654
|
+
line += token.text;
|
|
655
|
+
lineWidth += token.width;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (line) lines.push(line.trimEnd());
|
|
659
|
+
return lines.map((l, i2) => chalk2.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
|
|
660
|
+
}
|
|
711
661
|
var checkboxTheme = {
|
|
712
662
|
icon: {
|
|
713
663
|
checked: "\x1B[32m\u2611\x1B[22m",
|
|
714
664
|
unchecked: "\u2610",
|
|
715
665
|
cursor: "\u276F"
|
|
716
666
|
},
|
|
717
|
-
helpMode: "never"
|
|
667
|
+
helpMode: "never",
|
|
668
|
+
style: {
|
|
669
|
+
description: (text) => wrapDescription(text)
|
|
670
|
+
}
|
|
718
671
|
};
|
|
719
672
|
|
|
720
673
|
// src/ui/wizard.ts
|
|
@@ -789,7 +742,7 @@ async function runWizard() {
|
|
|
789
742
|
step = 2;
|
|
790
743
|
break;
|
|
791
744
|
}
|
|
792
|
-
if (result === CANCEL
|
|
745
|
+
if (result === CANCEL) {
|
|
793
746
|
return { language: config.language || "zh_CN", tool: selectedTool, level, plugins: [], confirmed: false };
|
|
794
747
|
}
|
|
795
748
|
updateConfig({ lastTool: selectedTool, lastLevel: level });
|
|
@@ -799,7 +752,7 @@ async function runWizard() {
|
|
|
799
752
|
}
|
|
800
753
|
}
|
|
801
754
|
async function stepTool(detectedTools, lastTool) {
|
|
802
|
-
printBoxTitle("
|
|
755
|
+
printBoxTitle(t("wizard_step_1_title"));
|
|
803
756
|
if (detectedTools.length === 0) {
|
|
804
757
|
logger.error(t("error_tool_not_found"));
|
|
805
758
|
const choices2 = [
|
|
@@ -874,10 +827,10 @@ async function stepToolManual(lastTool) {
|
|
|
874
827
|
return result;
|
|
875
828
|
}
|
|
876
829
|
async function stepLevel(lastLevel) {
|
|
877
|
-
printBoxTitle("
|
|
830
|
+
printBoxTitle(t("wizard_step_2_title"));
|
|
878
831
|
const choices = [
|
|
879
|
-
{ name:
|
|
880
|
-
{ name:
|
|
832
|
+
{ name: `> project \u2014 ${t("install_level_project")}`, value: "project" },
|
|
833
|
+
{ name: `> global \u2014 ${t("install_level_global")}`, value: "global" },
|
|
881
834
|
new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
882
835
|
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
883
836
|
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
@@ -895,7 +848,7 @@ async function stepLevel(lastLevel) {
|
|
|
895
848
|
return result;
|
|
896
849
|
}
|
|
897
850
|
async function stepPlugins(tool, level) {
|
|
898
|
-
printBoxTitle("
|
|
851
|
+
printBoxTitle(t("wizard_step_3_title"));
|
|
899
852
|
const plugins = getAllPlugins();
|
|
900
853
|
const configRoot = getConfigRoot(tool, level);
|
|
901
854
|
const manifests = readAllManifests(configRoot);
|
|
@@ -904,25 +857,22 @@ async function stepPlugins(tool, level) {
|
|
|
904
857
|
const isInstalled = installedSet.has(p.id);
|
|
905
858
|
const suffix = isInstalled ? ` [${t("wizard_already_installed")}]` : "";
|
|
906
859
|
return {
|
|
907
|
-
name: `${p.displayName}${suffix}
|
|
860
|
+
name: `${chalk3.cyan(p.displayName)}${suffix}`,
|
|
908
861
|
value: p.id,
|
|
909
|
-
checked: false
|
|
862
|
+
checked: false,
|
|
863
|
+
description: p.description
|
|
910
864
|
};
|
|
911
865
|
});
|
|
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
866
|
showOperationHints(true);
|
|
916
867
|
const selected = await checkbox({
|
|
917
868
|
message: t("wizard_select_plugins"),
|
|
918
869
|
choices,
|
|
919
870
|
loop: false,
|
|
920
871
|
instructions: false,
|
|
921
|
-
theme: checkboxTheme
|
|
872
|
+
theme: checkboxTheme,
|
|
873
|
+
pageSize: 15
|
|
922
874
|
});
|
|
923
|
-
|
|
924
|
-
if (selected.includes(CANCEL)) return CANCEL;
|
|
925
|
-
const pluginIds = selected.filter((v) => v !== BACK && v !== CANCEL);
|
|
875
|
+
const pluginIds = selected;
|
|
926
876
|
if (pluginIds.length === 0) {
|
|
927
877
|
const action = await select({
|
|
928
878
|
message: t("wizard_no_selection"),
|
|
@@ -940,17 +890,17 @@ async function stepPlugins(tool, level) {
|
|
|
940
890
|
return pluginIds;
|
|
941
891
|
}
|
|
942
892
|
async function stepConfirm(tool, level, plugins) {
|
|
943
|
-
printBoxTitle("
|
|
893
|
+
printBoxTitle(t("wizard_step_4_title"));
|
|
944
894
|
const allPlugins = getAllPlugins();
|
|
945
895
|
const selectedPlugins = plugins.map((id) => allPlugins.find((p) => p.id === id));
|
|
946
896
|
const displayPath = getConfigRoot(tool, level);
|
|
947
897
|
const toolName = getToolDisplayName(tool);
|
|
948
898
|
const levelText = level === "project" ? "project" : "global";
|
|
949
|
-
logger.info(
|
|
950
|
-
logger.info(
|
|
899
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk3.bold(String(plugins.length))).replace("{tool}", chalk3.green(toolName)).replace("{level}", chalk3.cyan(levelText)));
|
|
900
|
+
logger.info(`${t("wizard_install_path")}: ${chalk3.cyan(displayPath)}`);
|
|
951
901
|
for (const plugin of selectedPlugins) {
|
|
952
902
|
if (plugin) {
|
|
953
|
-
logger.step(` \u2022 ${
|
|
903
|
+
logger.step(` \u2022 ${chalk3.bold(plugin.displayName)}`);
|
|
954
904
|
}
|
|
955
905
|
}
|
|
956
906
|
logger.blank();
|
|
@@ -968,14 +918,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
968
918
|
}
|
|
969
919
|
|
|
970
920
|
// src/core/repository.ts
|
|
971
|
-
import { existsSync as
|
|
972
|
-
import { join as
|
|
921
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
922
|
+
import { join as join8, dirname as dirname4 } from "path";
|
|
923
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
973
924
|
import { execa as execa2 } from "execa";
|
|
925
|
+
import { parse as parseYaml3 } from "yaml";
|
|
974
926
|
|
|
975
927
|
// src/core/scanner.ts
|
|
976
|
-
import { existsSync as
|
|
977
|
-
import { join as
|
|
978
|
-
import {
|
|
928
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
929
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
930
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
931
|
+
import { parse as parseYaml2 } from "yaml";
|
|
932
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
933
|
+
var __dirname3 = dirname3(__filename3);
|
|
979
934
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
980
935
|
"node_modules",
|
|
981
936
|
".git",
|
|
@@ -996,46 +951,67 @@ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
|
996
951
|
"docs",
|
|
997
952
|
"scripts"
|
|
998
953
|
]);
|
|
999
|
-
|
|
1000
|
-
"
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
]
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
]
|
|
954
|
+
function loadScanConfig() {
|
|
955
|
+
const configPath = join7(__dirname3, "config", "repository.yaml");
|
|
956
|
+
try {
|
|
957
|
+
const config = parseYaml2(readFileSync5(configPath, "utf-8"));
|
|
958
|
+
return {
|
|
959
|
+
skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
960
|
+
pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
|
|
961
|
+
};
|
|
962
|
+
} catch {
|
|
963
|
+
return {
|
|
964
|
+
skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
|
|
965
|
+
pluginDirs: ["plugins-official", "plugins-community"]
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
var scanConfig = loadScanConfig();
|
|
970
|
+
var SKILL_SCAN_DIRS = scanConfig.skillDirs;
|
|
971
|
+
var PLUGIN_SCAN_DIRS = scanConfig.pluginDirs;
|
|
1010
972
|
function scanSkills(repoPath) {
|
|
1011
973
|
const skills = [];
|
|
1012
974
|
const seen = /* @__PURE__ */ new Set();
|
|
1013
975
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
1014
|
-
const fullPath =
|
|
1015
|
-
if (!
|
|
976
|
+
const fullPath = join7(repoPath, dir);
|
|
977
|
+
if (!existsSync6(fullPath)) continue;
|
|
1016
978
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
1017
979
|
}
|
|
1018
980
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
1019
|
-
const pluginParent =
|
|
1020
|
-
if (!
|
|
981
|
+
const pluginParent = join7(repoPath, pluginDir);
|
|
982
|
+
if (!existsSync6(pluginParent)) continue;
|
|
1021
983
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
1022
|
-
const pluginPath =
|
|
1023
|
-
if (!
|
|
1024
|
-
const skillsDir =
|
|
1025
|
-
if (
|
|
984
|
+
const pluginPath = join7(pluginParent, plugin);
|
|
985
|
+
if (!isDirectory2(pluginPath)) continue;
|
|
986
|
+
const skillsDir = join7(pluginPath, "skills");
|
|
987
|
+
if (existsSync6(skillsDir)) {
|
|
1026
988
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
1027
989
|
}
|
|
1028
|
-
const skillDir =
|
|
1029
|
-
if (
|
|
990
|
+
const skillDir = join7(pluginPath, "skill");
|
|
991
|
+
if (existsSync6(skillDir)) {
|
|
1030
992
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
1031
993
|
}
|
|
994
|
+
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
995
|
+
const initSh = join7(pluginPath, "init.sh");
|
|
996
|
+
if (existsSync6(rootSkillMd) && !existsSync6(initSh)) {
|
|
997
|
+
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
998
|
+
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
999
|
+
seen.add(frontmatter.name);
|
|
1000
|
+
skills.push({
|
|
1001
|
+
id: frontmatter.name,
|
|
1002
|
+
description: frontmatter.description,
|
|
1003
|
+
source: `${pluginDir}/${plugin}`,
|
|
1004
|
+
filePath: rootSkillMd
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1032
1008
|
}
|
|
1033
1009
|
}
|
|
1034
1010
|
return skills;
|
|
1035
1011
|
}
|
|
1036
1012
|
function parseFrontmatter(filePath) {
|
|
1037
1013
|
try {
|
|
1038
|
-
const content =
|
|
1014
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
1039
1015
|
const lines = content.split("\n");
|
|
1040
1016
|
if (lines.length < 3) return null;
|
|
1041
1017
|
if (lines[0].trim() !== "---") return null;
|
|
@@ -1048,7 +1024,7 @@ function parseFrontmatter(filePath) {
|
|
|
1048
1024
|
}
|
|
1049
1025
|
if (endIndex === -1) return null;
|
|
1050
1026
|
const yamlBlock = lines.slice(1, endIndex).map((l) => l.replace(/\r$/, "")).join("\n");
|
|
1051
|
-
const parsed =
|
|
1027
|
+
const parsed = parseYaml2(yamlBlock);
|
|
1052
1028
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1053
1029
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1054
1030
|
return {
|
|
@@ -1061,17 +1037,17 @@ function parseFrontmatter(filePath) {
|
|
|
1061
1037
|
}
|
|
1062
1038
|
function getCurrentCommit(repoPath) {
|
|
1063
1039
|
try {
|
|
1064
|
-
const headFile =
|
|
1065
|
-
if (!
|
|
1066
|
-
const headContent =
|
|
1040
|
+
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1041
|
+
if (!existsSync6(headFile)) return "unknown";
|
|
1042
|
+
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1067
1043
|
if (headContent.startsWith("ref: ")) {
|
|
1068
|
-
const refPath =
|
|
1069
|
-
if (
|
|
1070
|
-
return
|
|
1044
|
+
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1045
|
+
if (existsSync6(refPath)) {
|
|
1046
|
+
return readFileSync5(refPath, "utf-8").trim();
|
|
1071
1047
|
}
|
|
1072
|
-
const packedRefsPath =
|
|
1073
|
-
if (
|
|
1074
|
-
const packedRefs =
|
|
1048
|
+
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1049
|
+
if (existsSync6(packedRefsPath)) {
|
|
1050
|
+
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1075
1051
|
const refName = headContent.slice(5);
|
|
1076
1052
|
const lines = packedRefs.split("\n");
|
|
1077
1053
|
for (const line of lines) {
|
|
@@ -1089,8 +1065,8 @@ function getCurrentCommit(repoPath) {
|
|
|
1089
1065
|
function readScanCache() {
|
|
1090
1066
|
try {
|
|
1091
1067
|
const cachePath = getCachePath();
|
|
1092
|
-
if (!
|
|
1093
|
-
const content =
|
|
1068
|
+
if (!existsSync6(cachePath)) return null;
|
|
1069
|
+
const content = readFileSync5(cachePath, "utf-8");
|
|
1094
1070
|
const cache = JSON.parse(content);
|
|
1095
1071
|
if (!cache.skills || !cache.timestamp) return null;
|
|
1096
1072
|
const age = Date.now() - cache.timestamp;
|
|
@@ -1103,8 +1079,8 @@ function readScanCache() {
|
|
|
1103
1079
|
function writeScanCache(cache) {
|
|
1104
1080
|
try {
|
|
1105
1081
|
const cachePath = getCachePath();
|
|
1106
|
-
const cacheDir =
|
|
1107
|
-
if (!
|
|
1082
|
+
const cacheDir = dirname3(cachePath);
|
|
1083
|
+
if (!existsSync6(cacheDir)) {
|
|
1108
1084
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1109
1085
|
}
|
|
1110
1086
|
writeFileSync2(cachePath, JSON.stringify(cache, null, 2));
|
|
@@ -1112,30 +1088,15 @@ function writeScanCache(cache) {
|
|
|
1112
1088
|
}
|
|
1113
1089
|
}
|
|
1114
1090
|
function getCachePath() {
|
|
1115
|
-
|
|
1116
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
1091
|
+
return join7(getCannbotConfigDir(), "scan-cache.json");
|
|
1117
1092
|
}
|
|
1118
1093
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1119
|
-
const selfSkillMd = join5(dirPath, "SKILL.md");
|
|
1120
|
-
if (existsSync5(selfSkillMd)) {
|
|
1121
|
-
const frontmatter = parseFrontmatter(selfSkillMd);
|
|
1122
|
-
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1123
|
-
seen.add(frontmatter.name);
|
|
1124
|
-
skills.push({
|
|
1125
|
-
id: frontmatter.name,
|
|
1126
|
-
description: frontmatter.description,
|
|
1127
|
-
source: sourcePrefix,
|
|
1128
|
-
filePath: selfSkillMd
|
|
1129
|
-
});
|
|
1130
|
-
}
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
1094
|
for (const entry of safeReaddir(dirPath)) {
|
|
1134
|
-
const fullPath =
|
|
1095
|
+
const fullPath = join7(dirPath, entry);
|
|
1135
1096
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1136
|
-
if (!
|
|
1137
|
-
const skillMd =
|
|
1138
|
-
if (
|
|
1097
|
+
if (!isDirectory2(fullPath)) continue;
|
|
1098
|
+
const skillMd = join7(fullPath, "SKILL.md");
|
|
1099
|
+
if (existsSync6(skillMd)) {
|
|
1139
1100
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1140
1101
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1141
1102
|
seen.add(frontmatter.name);
|
|
@@ -1148,19 +1109,19 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1148
1109
|
}
|
|
1149
1110
|
continue;
|
|
1150
1111
|
}
|
|
1151
|
-
scanDirectory(fullPath, repoPath, sourcePrefix, skills, seen);
|
|
1112
|
+
scanDirectory(fullPath, repoPath, sourcePrefix + "/" + entry, skills, seen);
|
|
1152
1113
|
}
|
|
1153
1114
|
}
|
|
1154
1115
|
function safeReaddir(dir) {
|
|
1155
1116
|
try {
|
|
1156
|
-
return
|
|
1117
|
+
return readdirSync2(dir);
|
|
1157
1118
|
} catch {
|
|
1158
1119
|
return [];
|
|
1159
1120
|
}
|
|
1160
1121
|
}
|
|
1161
|
-
function
|
|
1122
|
+
function isDirectory2(path) {
|
|
1162
1123
|
try {
|
|
1163
|
-
return
|
|
1124
|
+
return statSync2(path).isDirectory();
|
|
1164
1125
|
} catch {
|
|
1165
1126
|
return false;
|
|
1166
1127
|
}
|
|
@@ -1177,6 +1138,7 @@ var CATEGORY_DEFS = [
|
|
|
1177
1138
|
{ id: "tilelang", name: "TileLang \u5F00\u53D1" },
|
|
1178
1139
|
{ id: "triton", name: "Triton \u5F00\u53D1" },
|
|
1179
1140
|
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
|
|
1141
|
+
{ id: "runtime", name: "Runtime \u8FC1\u79FB" },
|
|
1180
1142
|
{ id: "graph", name: "\u56FE\u6A21\u5F0F" },
|
|
1181
1143
|
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177" },
|
|
1182
1144
|
{ id: "other", name: "\u5176\u4ED6 Skills" }
|
|
@@ -1187,7 +1149,8 @@ function initFromScan(scanned) {
|
|
|
1187
1149
|
dynamicSkills = scanned.map((s) => ({
|
|
1188
1150
|
id: s.id,
|
|
1189
1151
|
description: s.description,
|
|
1190
|
-
source: s.source
|
|
1152
|
+
source: s.source,
|
|
1153
|
+
filePath: s.filePath
|
|
1191
1154
|
}));
|
|
1192
1155
|
initialized = true;
|
|
1193
1156
|
}
|
|
@@ -1263,9 +1226,7 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1263
1226
|
{ id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
|
|
1264
1227
|
{ id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
|
|
1265
1228
|
{ id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
|
|
1266
|
-
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1267
|
-
{ id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
|
|
1268
|
-
{ id: "ops-easyasc-dsl", description: "EasyASC DSL \u7B97\u5B50\u5F00\u53D1", source: "plugins-community/ops-easyasc-dsl/skill" }
|
|
1229
|
+
{ id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
|
|
1269
1230
|
]
|
|
1270
1231
|
},
|
|
1271
1232
|
{
|
|
@@ -1322,7 +1283,9 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1322
1283
|
{ id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
|
|
1323
1284
|
{ id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
|
|
1324
1285
|
{ id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
|
|
1325
|
-
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" }
|
|
1286
|
+
{ id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
|
|
1287
|
+
{ id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
|
|
1288
|
+
{ id: "science-model-npu-migration", description: "NPU \u4EE3\u7801\u7EA7\u8FC1\u79FB\uFF08\u73AF\u5883\u95E8\u7981/\u811A\u672C\u9002\u914D/\u7CBE\u5EA6\u6027\u80FD\u5BF9\u6BD4\uFF09", source: "plugins-community/science-model-npu-migration", filePath: "plugins-community/science-model-npu-migration/SKILL.md" }
|
|
1326
1289
|
]
|
|
1327
1290
|
},
|
|
1328
1291
|
{
|
|
@@ -1338,6 +1301,13 @@ var STATIC_SKILL_CATEGORIES = [
|
|
|
1338
1301
|
{ id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
|
|
1339
1302
|
]
|
|
1340
1303
|
},
|
|
1304
|
+
{
|
|
1305
|
+
id: "runtime",
|
|
1306
|
+
name: "Runtime \u8FC1\u79FB",
|
|
1307
|
+
skills: [
|
|
1308
|
+
{ id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
|
|
1309
|
+
]
|
|
1310
|
+
},
|
|
1341
1311
|
{
|
|
1342
1312
|
id: "platform",
|
|
1343
1313
|
name: "\u5E73\u53F0\u5DE5\u5177",
|
|
@@ -1393,7 +1363,21 @@ function getAllCategories() {
|
|
|
1393
1363
|
}
|
|
1394
1364
|
|
|
1395
1365
|
// src/core/repository.ts
|
|
1396
|
-
var
|
|
1366
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1367
|
+
var __dirname4 = dirname4(__filename4);
|
|
1368
|
+
function loadRepoUrl() {
|
|
1369
|
+
if (process.env.CANNBOT_REPO_URL) {
|
|
1370
|
+
return process.env.CANNBOT_REPO_URL;
|
|
1371
|
+
}
|
|
1372
|
+
const configPath = join8(__dirname4, "config", "repository.yaml");
|
|
1373
|
+
try {
|
|
1374
|
+
const config = parseYaml3(readFileSync6(configPath, "utf-8"));
|
|
1375
|
+
return config.repository?.url || "https://gitcode.com/cann/cannbot-skills.git";
|
|
1376
|
+
} catch {
|
|
1377
|
+
return "https://gitcode.com/cann/cannbot-skills.git";
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
var REPO_URL = loadRepoUrl();
|
|
1397
1381
|
var RepositoryManager = class {
|
|
1398
1382
|
repoPath;
|
|
1399
1383
|
constructor(customPath) {
|
|
@@ -1418,10 +1402,13 @@ var RepositoryManager = class {
|
|
|
1418
1402
|
this.repoPath = cachedPath;
|
|
1419
1403
|
return cachedPath;
|
|
1420
1404
|
}
|
|
1421
|
-
|
|
1405
|
+
const clonedPath = await this.cloneRepo();
|
|
1406
|
+
return clonedPath;
|
|
1422
1407
|
}
|
|
1423
1408
|
async ensureRepoAndScan() {
|
|
1424
1409
|
const repoPath = await this.ensureRepo();
|
|
1410
|
+
await this.updateRepo();
|
|
1411
|
+
mergeDynamicPlugins(repoPath);
|
|
1425
1412
|
const cache = readScanCache();
|
|
1426
1413
|
if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
|
|
1427
1414
|
initFromScan(cache.skills);
|
|
@@ -1442,6 +1429,7 @@ var RepositoryManager = class {
|
|
|
1442
1429
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1443
1430
|
} catch {
|
|
1444
1431
|
}
|
|
1432
|
+
mergeDynamicPlugins(repoPath);
|
|
1445
1433
|
}
|
|
1446
1434
|
getRepoPath() {
|
|
1447
1435
|
if (!this.repoPath) {
|
|
@@ -1450,18 +1438,18 @@ var RepositoryManager = class {
|
|
|
1450
1438
|
return this.repoPath;
|
|
1451
1439
|
}
|
|
1452
1440
|
isValidRepo(path) {
|
|
1453
|
-
if (!
|
|
1454
|
-
const gitDir =
|
|
1455
|
-
const pluginsDir =
|
|
1456
|
-
return
|
|
1441
|
+
if (!existsSync7(path)) return false;
|
|
1442
|
+
const gitDir = join8(path, ".git");
|
|
1443
|
+
const pluginsDir = join8(path, "plugins-official");
|
|
1444
|
+
return existsSync7(gitDir) && existsSync7(pluginsDir);
|
|
1457
1445
|
}
|
|
1458
1446
|
async cloneRepo() {
|
|
1459
1447
|
const configDir = getCannbotConfigDir();
|
|
1460
|
-
if (!
|
|
1448
|
+
if (!existsSync7(configDir)) {
|
|
1461
1449
|
mkdirSync3(configDir, { recursive: true });
|
|
1462
1450
|
}
|
|
1463
1451
|
const targetPath = getCannbotRepoPath();
|
|
1464
|
-
if (
|
|
1452
|
+
if (existsSync7(targetPath)) {
|
|
1465
1453
|
if (this.isValidRepo(targetPath)) {
|
|
1466
1454
|
this.repoPath = targetPath;
|
|
1467
1455
|
return targetPath;
|
|
@@ -1487,26 +1475,26 @@ function createRepositoryManager(customPath) {
|
|
|
1487
1475
|
}
|
|
1488
1476
|
|
|
1489
1477
|
// src/core/installer.ts
|
|
1490
|
-
import { existsSync as
|
|
1491
|
-
import { join as
|
|
1478
|
+
import { existsSync as existsSync10 } from "fs";
|
|
1479
|
+
import { join as join11 } from "path";
|
|
1492
1480
|
import { execa as execa3 } from "execa";
|
|
1493
1481
|
|
|
1494
1482
|
// src/core/record.ts
|
|
1495
|
-
import { existsSync as
|
|
1496
|
-
import { join as
|
|
1483
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
|
|
1484
|
+
import { join as join9 } from "path";
|
|
1497
1485
|
function getInstallsDir() {
|
|
1498
|
-
return
|
|
1486
|
+
return join9(getCannbotConfigDir(), "installs");
|
|
1499
1487
|
}
|
|
1500
1488
|
function getRecordPath(pluginId) {
|
|
1501
|
-
return
|
|
1489
|
+
return join9(getInstallsDir(), `${pluginId}.json`);
|
|
1502
1490
|
}
|
|
1503
1491
|
function readRecord(pluginId) {
|
|
1504
1492
|
const recordPath = getRecordPath(pluginId);
|
|
1505
|
-
if (!
|
|
1493
|
+
if (!existsSync8(recordPath)) {
|
|
1506
1494
|
return null;
|
|
1507
1495
|
}
|
|
1508
1496
|
try {
|
|
1509
|
-
const content =
|
|
1497
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1510
1498
|
return JSON.parse(content);
|
|
1511
1499
|
} catch {
|
|
1512
1500
|
return null;
|
|
@@ -1514,7 +1502,7 @@ function readRecord(pluginId) {
|
|
|
1514
1502
|
}
|
|
1515
1503
|
function writeRecord(record) {
|
|
1516
1504
|
const installsDir = getInstallsDir();
|
|
1517
|
-
if (!
|
|
1505
|
+
if (!existsSync8(installsDir)) {
|
|
1518
1506
|
mkdirSync4(installsDir, { recursive: true });
|
|
1519
1507
|
}
|
|
1520
1508
|
const recordPath = getRecordPath(record.pluginId);
|
|
@@ -1522,7 +1510,7 @@ function writeRecord(record) {
|
|
|
1522
1510
|
}
|
|
1523
1511
|
function deleteRecord(pluginId) {
|
|
1524
1512
|
const recordPath = getRecordPath(pluginId);
|
|
1525
|
-
if (
|
|
1513
|
+
if (existsSync8(recordPath)) {
|
|
1526
1514
|
unlinkSync(recordPath);
|
|
1527
1515
|
}
|
|
1528
1516
|
}
|
|
@@ -1530,59 +1518,58 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1530
1518
|
const files = [];
|
|
1531
1519
|
const directories = [];
|
|
1532
1520
|
if (manifest) {
|
|
1533
|
-
const skillsDir =
|
|
1534
|
-
if (
|
|
1521
|
+
const skillsDir = join9(configRoot, "skills");
|
|
1522
|
+
if (existsSync8(skillsDir)) {
|
|
1535
1523
|
directories.push(skillsDir);
|
|
1536
1524
|
for (const skillName of manifest.installed_skills || []) {
|
|
1537
|
-
const skillPath =
|
|
1538
|
-
if (
|
|
1525
|
+
const skillPath = join9(skillsDir, skillName);
|
|
1526
|
+
if (existsSync8(skillPath) || isSymlink(skillPath)) {
|
|
1539
1527
|
files.push(skillPath);
|
|
1540
1528
|
}
|
|
1541
1529
|
}
|
|
1542
1530
|
}
|
|
1543
|
-
const agentsDir =
|
|
1544
|
-
if (
|
|
1531
|
+
const agentsDir = join9(configRoot, "agents");
|
|
1532
|
+
if (existsSync8(agentsDir)) {
|
|
1545
1533
|
directories.push(agentsDir);
|
|
1546
1534
|
for (const agentName of manifest.installed_agents || []) {
|
|
1547
|
-
const agentPath =
|
|
1548
|
-
const agentPathMd =
|
|
1549
|
-
if (
|
|
1535
|
+
const agentPath = join9(agentsDir, agentName);
|
|
1536
|
+
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
1537
|
+
if (existsSync8(agentPath) || isSymlink(agentPath)) {
|
|
1550
1538
|
files.push(agentPath);
|
|
1551
|
-
} else if (
|
|
1539
|
+
} else if (existsSync8(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1552
1540
|
files.push(agentPathMd);
|
|
1553
1541
|
}
|
|
1554
1542
|
}
|
|
1555
1543
|
}
|
|
1556
1544
|
}
|
|
1557
|
-
const workflowsLink =
|
|
1545
|
+
const workflowsLink = join9(configRoot, "workflows");
|
|
1558
1546
|
if (isSymlink(workflowsLink)) {
|
|
1559
1547
|
files.push(workflowsLink);
|
|
1560
1548
|
}
|
|
1561
|
-
const manifestPath =
|
|
1562
|
-
if (
|
|
1549
|
+
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
1550
|
+
if (existsSync8(manifestPath)) {
|
|
1563
1551
|
files.push(manifestPath);
|
|
1564
1552
|
}
|
|
1565
|
-
const pluginManifestPath =
|
|
1566
|
-
if (
|
|
1553
|
+
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
1554
|
+
if (existsSync8(pluginManifestPath)) {
|
|
1567
1555
|
files.push(pluginManifestPath);
|
|
1568
1556
|
}
|
|
1569
1557
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1570
|
-
const configFilePath = level === "project" ?
|
|
1571
|
-
if (
|
|
1558
|
+
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
1559
|
+
if (existsSync8(configFilePath)) {
|
|
1572
1560
|
files.push(configFilePath);
|
|
1573
1561
|
}
|
|
1574
1562
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
1575
1563
|
for (const repoName of repoLinks) {
|
|
1576
|
-
const repoLinkPath =
|
|
1564
|
+
const repoLinkPath = join9(installPath, repoName);
|
|
1577
1565
|
if (isSymlink(repoLinkPath)) {
|
|
1578
1566
|
files.push(repoLinkPath);
|
|
1579
1567
|
}
|
|
1580
|
-
const repoLinkInConfig =
|
|
1568
|
+
const repoLinkInConfig = join9(configRoot, repoName);
|
|
1581
1569
|
if (isSymlink(repoLinkInConfig)) {
|
|
1582
1570
|
files.push(repoLinkInConfig);
|
|
1583
1571
|
}
|
|
1584
1572
|
}
|
|
1585
|
-
directories.push(configRoot);
|
|
1586
1573
|
return {
|
|
1587
1574
|
pluginId,
|
|
1588
1575
|
displayName,
|
|
@@ -1603,15 +1590,15 @@ function isSymlink(path) {
|
|
|
1603
1590
|
}
|
|
1604
1591
|
}
|
|
1605
1592
|
function getSkillRecordPath() {
|
|
1606
|
-
return
|
|
1593
|
+
return join9(getInstallsDir(), "skills.json");
|
|
1607
1594
|
}
|
|
1608
1595
|
function readSkillRecord() {
|
|
1609
1596
|
const recordPath = getSkillRecordPath();
|
|
1610
|
-
if (!
|
|
1597
|
+
if (!existsSync8(recordPath)) {
|
|
1611
1598
|
return {};
|
|
1612
1599
|
}
|
|
1613
1600
|
try {
|
|
1614
|
-
const content =
|
|
1601
|
+
const content = readFileSync7(recordPath, "utf-8");
|
|
1615
1602
|
return JSON.parse(content);
|
|
1616
1603
|
} catch {
|
|
1617
1604
|
return {};
|
|
@@ -1619,7 +1606,7 @@ function readSkillRecord() {
|
|
|
1619
1606
|
}
|
|
1620
1607
|
function writeSkillRecord(record) {
|
|
1621
1608
|
const installsDir = getInstallsDir();
|
|
1622
|
-
if (!
|
|
1609
|
+
if (!existsSync8(installsDir)) {
|
|
1623
1610
|
mkdirSync4(installsDir, { recursive: true });
|
|
1624
1611
|
}
|
|
1625
1612
|
const recordPath = getSkillRecordPath();
|
|
@@ -1659,7 +1646,186 @@ function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
|
1659
1646
|
}
|
|
1660
1647
|
function getInstalledSkills(tool, level, installPath) {
|
|
1661
1648
|
const record = readSkillRecord();
|
|
1662
|
-
|
|
1649
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1650
|
+
const configRoot = getConfigRoot(tool, level);
|
|
1651
|
+
const skillsDir = join9(configRoot, "skills");
|
|
1652
|
+
return recordedSkills.filter((skillId) => {
|
|
1653
|
+
const skillPath = join9(skillsDir, skillId);
|
|
1654
|
+
return existsSync8(skillPath) || isSymlink(skillPath);
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// src/core/backup.ts
|
|
1659
|
+
import { existsSync as existsSync9, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
1660
|
+
import { join as join10 } from "path";
|
|
1661
|
+
function getAgentsFileName(tool) {
|
|
1662
|
+
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1663
|
+
}
|
|
1664
|
+
function detectCurrentPlugin(configRoot, tool) {
|
|
1665
|
+
const manifest = readManifest(configRoot);
|
|
1666
|
+
if (manifest?.team) {
|
|
1667
|
+
const plugin = findPlugin(manifest.team);
|
|
1668
|
+
if (plugin) {
|
|
1669
|
+
return {
|
|
1670
|
+
pluginId: plugin.id,
|
|
1671
|
+
pluginName: plugin.displayName
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1676
|
+
if (!existsSync9(agentsFile)) {
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
const allRecords = readAllRecords();
|
|
1680
|
+
for (const record of allRecords) {
|
|
1681
|
+
if (record.configRoot === configRoot && record.tool === tool) {
|
|
1682
|
+
const plugin = findPlugin(record.pluginId);
|
|
1683
|
+
if (plugin) {
|
|
1684
|
+
return {
|
|
1685
|
+
pluginId: plugin.id,
|
|
1686
|
+
pluginName: plugin.displayName
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1693
|
+
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1694
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1695
|
+
if (!existsSync9(agentsFile)) {
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1698
|
+
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
1699
|
+
const backupFileName = `${getAgentsFileName(tool)}.cannbot-backup.${fromPluginId}.${timestamp}`;
|
|
1700
|
+
const backupPath = join10(configRoot, backupFileName);
|
|
1701
|
+
try {
|
|
1702
|
+
copyFileSync(agentsFile, backupPath);
|
|
1703
|
+
return {
|
|
1704
|
+
filePath: backupPath,
|
|
1705
|
+
pluginId: fromPluginId,
|
|
1706
|
+
pluginName: fromPluginName,
|
|
1707
|
+
backupTime: timestamp
|
|
1708
|
+
};
|
|
1709
|
+
} catch {
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
function findBackups(configRoot) {
|
|
1714
|
+
if (!existsSync9(configRoot)) {
|
|
1715
|
+
return [];
|
|
1716
|
+
}
|
|
1717
|
+
const files = readdirSync3(configRoot);
|
|
1718
|
+
const backups = [];
|
|
1719
|
+
for (const file of files) {
|
|
1720
|
+
if (file.includes(".cannbot-backup.")) {
|
|
1721
|
+
const parts = file.split(".cannbot-backup.");
|
|
1722
|
+
if (parts.length === 2) {
|
|
1723
|
+
const pluginIdAndTime = parts[1];
|
|
1724
|
+
const lastDotIndex = pluginIdAndTime.lastIndexOf(".");
|
|
1725
|
+
if (lastDotIndex > 0) {
|
|
1726
|
+
const pluginId = pluginIdAndTime.substring(0, lastDotIndex);
|
|
1727
|
+
const timestamp = pluginIdAndTime.substring(lastDotIndex + 1);
|
|
1728
|
+
const plugin = findPlugin(pluginId);
|
|
1729
|
+
backups.push({
|
|
1730
|
+
filePath: join10(configRoot, file),
|
|
1731
|
+
pluginId,
|
|
1732
|
+
pluginName: plugin?.displayName || pluginId,
|
|
1733
|
+
backupTime: timestamp
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
return backups.sort((a, b) => b.backupTime.localeCompare(a.backupTime));
|
|
1740
|
+
}
|
|
1741
|
+
function restoreBackup(backupPath, configRoot, tool) {
|
|
1742
|
+
try {
|
|
1743
|
+
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1744
|
+
copyFileSync(backupPath, agentsFile);
|
|
1745
|
+
return true;
|
|
1746
|
+
} catch {
|
|
1747
|
+
return false;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
function deleteBackup(backupPath) {
|
|
1751
|
+
try {
|
|
1752
|
+
if (existsSync9(backupPath)) {
|
|
1753
|
+
unlinkSync2(backupPath);
|
|
1754
|
+
return true;
|
|
1755
|
+
}
|
|
1756
|
+
return false;
|
|
1757
|
+
} catch {
|
|
1758
|
+
return false;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
function formatTimestamp(date) {
|
|
1762
|
+
const year = date.getFullYear();
|
|
1763
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
1764
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
1765
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1766
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1767
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
1768
|
+
return `${year}${month}${day}-${hours}${minutes}${seconds}`;
|
|
1769
|
+
}
|
|
1770
|
+
function readAllRecords() {
|
|
1771
|
+
const records = [];
|
|
1772
|
+
const plugins = [
|
|
1773
|
+
"ops-direct-invoke",
|
|
1774
|
+
"ops-direct-invoke-flash",
|
|
1775
|
+
"ops-registry-invoke",
|
|
1776
|
+
"pypto-op-orchestrator",
|
|
1777
|
+
"triton-op-generator",
|
|
1778
|
+
"tilelang-op-orchestrator",
|
|
1779
|
+
"model-infer-optimize",
|
|
1780
|
+
"catlass-op-generator",
|
|
1781
|
+
"ops-code-reviewer",
|
|
1782
|
+
"torch-compile"
|
|
1783
|
+
];
|
|
1784
|
+
for (const pluginId of plugins) {
|
|
1785
|
+
const record = readRecord(pluginId);
|
|
1786
|
+
if (record) {
|
|
1787
|
+
records.push(record);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
return records;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
// src/ui/backup-prompts.ts
|
|
1794
|
+
import { select as select2 } from "@inquirer/prompts";
|
|
1795
|
+
import chalk4 from "chalk";
|
|
1796
|
+
async function showOverwriteWarning(currentPluginName, newPluginName) {
|
|
1797
|
+
console.log();
|
|
1798
|
+
console.log(chalk4.yellow(` \u26A0\uFE0F ${t("backup_detected")}`));
|
|
1799
|
+
console.log();
|
|
1800
|
+
console.log(` ${t("backup_current")}: ${chalk4.cyan(currentPluginName)}`);
|
|
1801
|
+
console.log(` ${t("backup_installing")}: ${chalk4.cyan(newPluginName)}`);
|
|
1802
|
+
console.log();
|
|
1803
|
+
console.log(` ${t("backup_overwrite_warning")}`);
|
|
1804
|
+
console.log(` ${t("backup_auto_create")}`);
|
|
1805
|
+
console.log();
|
|
1806
|
+
const choice = await select2({
|
|
1807
|
+
message: t("backup_select_action"),
|
|
1808
|
+
choices: [
|
|
1809
|
+
{ name: t("backup_overwrite"), value: "overwrite" },
|
|
1810
|
+
{ name: t("backup_cancel"), value: "cancel" }
|
|
1811
|
+
]
|
|
1812
|
+
});
|
|
1813
|
+
return choice;
|
|
1814
|
+
}
|
|
1815
|
+
async function showRestorePrompt(backups) {
|
|
1816
|
+
console.log();
|
|
1817
|
+
console.log(chalk4.cyan(` ${t("backup_restore_prompt")}`));
|
|
1818
|
+
console.log();
|
|
1819
|
+
const choices = backups.map((b) => ({
|
|
1820
|
+
name: `${t("backup_restore")} ${b.pluginName} (${b.backupTime})`,
|
|
1821
|
+
value: b.filePath
|
|
1822
|
+
}));
|
|
1823
|
+
choices.push({ name: t("backup_only_uninstall"), value: "none" });
|
|
1824
|
+
const choice = await select2({
|
|
1825
|
+
message: t("backup_select_action"),
|
|
1826
|
+
choices
|
|
1827
|
+
});
|
|
1828
|
+
return choice;
|
|
1663
1829
|
}
|
|
1664
1830
|
|
|
1665
1831
|
// src/core/installer.ts
|
|
@@ -1675,8 +1841,8 @@ async function installPlugin(opts) {
|
|
|
1675
1841
|
warnings: []
|
|
1676
1842
|
};
|
|
1677
1843
|
}
|
|
1678
|
-
const scriptPath =
|
|
1679
|
-
if (!
|
|
1844
|
+
const scriptPath = join11(opts.repoPath, plugin.dir, plugin.script);
|
|
1845
|
+
if (!existsSync10(scriptPath)) {
|
|
1680
1846
|
return {
|
|
1681
1847
|
success: false,
|
|
1682
1848
|
pluginId: opts.pluginId,
|
|
@@ -1691,13 +1857,49 @@ async function installPlugin(opts) {
|
|
|
1691
1857
|
args.push(opts.installPath);
|
|
1692
1858
|
}
|
|
1693
1859
|
const cwd = opts.installPath || process.cwd();
|
|
1860
|
+
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1861
|
+
const agentsFile = join11(configRoot, getAgentsFileName(opts.tool));
|
|
1862
|
+
let backupInfo = null;
|
|
1863
|
+
if (existsSync10(agentsFile)) {
|
|
1864
|
+
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
1865
|
+
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
1866
|
+
let choice = "overwrite";
|
|
1867
|
+
if (!opts.yes) {
|
|
1868
|
+
choice = await showOverwriteWarning(
|
|
1869
|
+
currentPlugin.pluginName,
|
|
1870
|
+
plugin.displayName
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
if (choice === "cancel") {
|
|
1874
|
+
logger.info(t("backup_cancel"));
|
|
1875
|
+
return {
|
|
1876
|
+
success: false,
|
|
1877
|
+
pluginId: opts.pluginId,
|
|
1878
|
+
skillsCount: 0,
|
|
1879
|
+
agentsCount: 0,
|
|
1880
|
+
errors: [t("backup_cancel")],
|
|
1881
|
+
warnings: []
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
backupInfo = createBackup(
|
|
1885
|
+
configRoot,
|
|
1886
|
+
opts.tool,
|
|
1887
|
+
currentPlugin.pluginId,
|
|
1888
|
+
currentPlugin.pluginName
|
|
1889
|
+
);
|
|
1890
|
+
if (backupInfo) {
|
|
1891
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1694
1895
|
try {
|
|
1695
|
-
|
|
1896
|
+
const execOptions = {
|
|
1696
1897
|
cwd,
|
|
1697
1898
|
timeout: 3e5,
|
|
1698
|
-
stdio: "pipe"
|
|
1699
|
-
|
|
1700
|
-
|
|
1899
|
+
stdio: "pipe",
|
|
1900
|
+
input: opts.yes ? "y\n" : void 0
|
|
1901
|
+
};
|
|
1902
|
+
await execa3("bash", [scriptPath, ...args], execOptions);
|
|
1701
1903
|
const manifest = readManifest(configRoot);
|
|
1702
1904
|
let skillsCount = 0;
|
|
1703
1905
|
let agentsCount = 0;
|
|
@@ -1718,6 +1920,14 @@ async function installPlugin(opts) {
|
|
|
1718
1920
|
configRoot,
|
|
1719
1921
|
manifest
|
|
1720
1922
|
);
|
|
1923
|
+
if (backupInfo) {
|
|
1924
|
+
record.backup = {
|
|
1925
|
+
filePath: backupInfo.filePath,
|
|
1926
|
+
fromPluginId: backupInfo.pluginId,
|
|
1927
|
+
fromPluginName: backupInfo.pluginName,
|
|
1928
|
+
backupTime: backupInfo.backupTime
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1721
1931
|
writeRecord(record);
|
|
1722
1932
|
} catch {
|
|
1723
1933
|
}
|
|
@@ -1743,16 +1953,16 @@ async function installPlugin(opts) {
|
|
|
1743
1953
|
}
|
|
1744
1954
|
|
|
1745
1955
|
// src/core/skill-installer.ts
|
|
1746
|
-
import { existsSync as
|
|
1747
|
-
import { join as
|
|
1748
|
-
import { select as
|
|
1749
|
-
import
|
|
1956
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
|
|
1957
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
1958
|
+
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
1959
|
+
import chalk5 from "chalk";
|
|
1750
1960
|
import Table from "cli-table3";
|
|
1751
1961
|
async function installSkills(skillIds, tool, level, repoPath) {
|
|
1752
1962
|
const configRoot = getConfigRoot(tool, level);
|
|
1753
|
-
const skillsDir =
|
|
1963
|
+
const skillsDir = join12(configRoot, "skills");
|
|
1754
1964
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1755
|
-
if (!
|
|
1965
|
+
if (!existsSync11(skillsDir)) {
|
|
1756
1966
|
mkdirSync5(skillsDir, { recursive: true });
|
|
1757
1967
|
}
|
|
1758
1968
|
const results = [];
|
|
@@ -1763,15 +1973,15 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1763
1973
|
results.push({ skillId, success: false, error: "Skill not found in registry" });
|
|
1764
1974
|
continue;
|
|
1765
1975
|
}
|
|
1766
|
-
const sourcePath =
|
|
1767
|
-
const targetPath =
|
|
1768
|
-
if (!
|
|
1976
|
+
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
1977
|
+
const targetPath = join12(skillsDir, skillId);
|
|
1978
|
+
if (!existsSync11(sourcePath)) {
|
|
1769
1979
|
results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
|
|
1770
1980
|
continue;
|
|
1771
1981
|
}
|
|
1772
1982
|
try {
|
|
1773
|
-
if (
|
|
1774
|
-
|
|
1983
|
+
if (existsSync11(targetPath) || isSymlink2(targetPath)) {
|
|
1984
|
+
unlinkSync3(targetPath);
|
|
1775
1985
|
}
|
|
1776
1986
|
symlinkSync(realpathSync(sourcePath), targetPath);
|
|
1777
1987
|
results.push({ skillId, success: true });
|
|
@@ -1787,21 +1997,21 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1787
1997
|
}
|
|
1788
1998
|
async function uninstallSkills(skillIds, tool, level) {
|
|
1789
1999
|
const configRoot = getConfigRoot(tool, level);
|
|
1790
|
-
const skillsDir =
|
|
2000
|
+
const skillsDir = join12(configRoot, "skills");
|
|
1791
2001
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1792
2002
|
const results = [];
|
|
1793
2003
|
const removedIds = [];
|
|
1794
2004
|
for (const skillId of skillIds) {
|
|
1795
|
-
const targetPath =
|
|
2005
|
+
const targetPath = join12(skillsDir, skillId);
|
|
1796
2006
|
try {
|
|
1797
|
-
if (
|
|
1798
|
-
|
|
2007
|
+
if (existsSync11(targetPath) || isSymlink2(targetPath)) {
|
|
2008
|
+
unlinkSync3(targetPath);
|
|
1799
2009
|
results.push({ skillId, success: true });
|
|
1800
2010
|
removedIds.push(skillId);
|
|
1801
|
-
logger.step(`
|
|
2011
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
1802
2012
|
} else {
|
|
1803
2013
|
results.push({ skillId, success: false, error: "Not installed" });
|
|
1804
|
-
logger.warn(`${skillId}
|
|
2014
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
1805
2015
|
}
|
|
1806
2016
|
} catch (error) {
|
|
1807
2017
|
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
|
|
@@ -1810,103 +2020,121 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
1810
2020
|
if (removedIds.length > 0) {
|
|
1811
2021
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
1812
2022
|
}
|
|
1813
|
-
if (
|
|
2023
|
+
if (existsSync11(skillsDir)) {
|
|
1814
2024
|
try {
|
|
1815
|
-
const entries =
|
|
2025
|
+
const entries = readdirSync4(skillsDir);
|
|
1816
2026
|
if (entries.length === 0) {
|
|
1817
2027
|
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()}/`);
|
|
2028
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
1829
2029
|
}
|
|
1830
2030
|
} catch {
|
|
1831
2031
|
}
|
|
1832
2032
|
}
|
|
1833
2033
|
return results;
|
|
1834
2034
|
}
|
|
1835
|
-
|
|
2035
|
+
var SELECT_ALL = "__select_all__";
|
|
2036
|
+
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
2037
|
+
var CONTINUE_SELECT = "__continue_select__";
|
|
2038
|
+
async function interactiveSkillSelect(tool, level) {
|
|
1836
2039
|
const BACK3 = "__back__";
|
|
1837
2040
|
const CANCEL3 = "__cancel__";
|
|
1838
2041
|
const categories = getAllCategories();
|
|
2042
|
+
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
2043
|
+
let accumulated = [];
|
|
1839
2044
|
let step = 0;
|
|
1840
2045
|
let selectedCategoryId = "";
|
|
1841
2046
|
while (true) {
|
|
1842
2047
|
switch (step) {
|
|
1843
2048
|
case 0: {
|
|
1844
|
-
|
|
2049
|
+
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");
|
|
2050
|
+
printBoxTitle(titleSuffix);
|
|
1845
2051
|
const categoryChoices = [
|
|
2052
|
+
{
|
|
2053
|
+
name: `>> ${t("skill_select_all").replace("{count}", String(allSkillIds.length))}`,
|
|
2054
|
+
value: SELECT_ALL
|
|
2055
|
+
},
|
|
2056
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1846
2057
|
...categories.map((cat) => ({
|
|
1847
2058
|
name: `> ${cat.name} (${cat.skills.length} skills)`,
|
|
1848
2059
|
value: cat.id
|
|
1849
2060
|
})),
|
|
1850
2061
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2062
|
+
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
1851
2063
|
{ name: "<- " + t("wizard_back"), value: BACK3 },
|
|
1852
2064
|
{ name: "x " + t("wizard_cancel"), value: CANCEL3 }
|
|
1853
2065
|
];
|
|
1854
2066
|
showOperationHints();
|
|
1855
|
-
selectedCategoryId = await
|
|
2067
|
+
selectedCategoryId = await select3({
|
|
1856
2068
|
message: t("skill_select_category"),
|
|
1857
2069
|
choices: categoryChoices,
|
|
1858
2070
|
loop: false,
|
|
1859
|
-
theme: selectTheme
|
|
2071
|
+
theme: selectTheme,
|
|
2072
|
+
pageSize: 15
|
|
1860
2073
|
});
|
|
1861
2074
|
if (selectedCategoryId === BACK3) return "back";
|
|
1862
2075
|
if (selectedCategoryId === CANCEL3) return "cancel";
|
|
2076
|
+
if (selectedCategoryId === SELECT_ALL) {
|
|
2077
|
+
return allSkillIds;
|
|
2078
|
+
}
|
|
2079
|
+
if (selectedCategoryId === "install") {
|
|
2080
|
+
return accumulated;
|
|
2081
|
+
}
|
|
1863
2082
|
step = 1;
|
|
1864
2083
|
break;
|
|
1865
2084
|
}
|
|
1866
2085
|
case 1: {
|
|
1867
2086
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
1868
2087
|
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
|
-
}
|
|
2088
|
+
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
2089
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
2090
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
1879
2091
|
const installedSet = new Set(installedSkills);
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
name:
|
|
1885
|
-
value:
|
|
1886
|
-
checked:
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2092
|
+
const categorySkillIds = new Set(category.skills.map((s) => s.id));
|
|
2093
|
+
const accumulatedInCategory = new Set(accumulated.filter((id) => categorySkillIds.has(id)));
|
|
2094
|
+
const skillChoices = [
|
|
2095
|
+
{
|
|
2096
|
+
name: `>> ${t("skill_select_all_category")}`,
|
|
2097
|
+
value: SELECT_ALL_CATEGORY,
|
|
2098
|
+
checked: accumulatedInCategory.size === category.skills.length,
|
|
2099
|
+
description: t("skill_select_all_category")
|
|
2100
|
+
},
|
|
2101
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2102
|
+
...category.skills.map((skill) => {
|
|
2103
|
+
const isInstalled = installedSet.has(skill.id);
|
|
2104
|
+
const isAccumulated = accumulatedInCategory.has(skill.id);
|
|
2105
|
+
const suffix = isInstalled ? ` [${t("skill_already_installed")}]` : isAccumulated ? " [+]" : "";
|
|
2106
|
+
return {
|
|
2107
|
+
name: `${chalk5.cyan(skill.id)}${suffix}`,
|
|
2108
|
+
value: skill.id,
|
|
2109
|
+
checked: isAccumulated,
|
|
2110
|
+
description: skill.description
|
|
2111
|
+
};
|
|
2112
|
+
})
|
|
2113
|
+
];
|
|
1892
2114
|
showOperationHints(true);
|
|
1893
2115
|
const selectedSkills = await checkbox2({
|
|
1894
2116
|
message: t("skill_select_items"),
|
|
1895
2117
|
choices: skillChoices,
|
|
1896
2118
|
loop: false,
|
|
1897
2119
|
instructions: false,
|
|
1898
|
-
theme: checkboxTheme
|
|
2120
|
+
theme: checkboxTheme,
|
|
2121
|
+
pageSize: 15
|
|
1899
2122
|
});
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2123
|
+
let skillIds;
|
|
2124
|
+
if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
|
|
2125
|
+
skillIds = category.skills.map((s) => s.id);
|
|
2126
|
+
} else {
|
|
2127
|
+
skillIds = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
|
|
1903
2128
|
}
|
|
1904
|
-
if (selectedSkills.includes(CANCEL3)) return "cancel";
|
|
1905
|
-
const skillIds = selectedSkills.filter((v) => v !== BACK3 && v !== CANCEL3);
|
|
1906
2129
|
if (skillIds.length === 0) {
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2130
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
2131
|
+
const action2 = await select3({
|
|
2132
|
+
message: accumulated.length > 0 ? t("skill_continue_or_install").replace("{count}", String(accumulated.length)) : t("wizard_no_selection"),
|
|
2133
|
+
choices: accumulated.length > 0 ? [
|
|
2134
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
2135
|
+
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
2136
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
2137
|
+
] : [
|
|
1910
2138
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
1911
2139
|
{ name: "<- " + t("wizard_back_to_reselect"), value: "back" },
|
|
1912
2140
|
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
@@ -1914,13 +2142,38 @@ async function interactiveSkillSelect() {
|
|
|
1914
2142
|
loop: false,
|
|
1915
2143
|
theme: selectTheme
|
|
1916
2144
|
});
|
|
1917
|
-
if (
|
|
2145
|
+
if (action2 === "install") return accumulated;
|
|
2146
|
+
if (action2 === "back") {
|
|
1918
2147
|
step = 0;
|
|
1919
2148
|
break;
|
|
1920
2149
|
}
|
|
1921
2150
|
return "cancel";
|
|
1922
2151
|
}
|
|
1923
|
-
|
|
2152
|
+
accumulated = accumulated.filter((id) => !categorySkillIds.has(id));
|
|
2153
|
+
for (const id of skillIds) {
|
|
2154
|
+
if (!accumulated.includes(id)) {
|
|
2155
|
+
accumulated.push(id);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
const action = await select3({
|
|
2159
|
+
message: t("skill_continue_or_install").replace("{count}", String(accumulated.length)),
|
|
2160
|
+
choices: [
|
|
2161
|
+
{ name: ">> " + t("skill_confirm_install_count").replace("{count}", String(accumulated.length)), value: "install" },
|
|
2162
|
+
{ name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
|
|
2163
|
+
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2164
|
+
{ name: "x " + t("wizard_cancel"), value: "cancel" }
|
|
2165
|
+
],
|
|
2166
|
+
loop: false,
|
|
2167
|
+
theme: selectTheme
|
|
2168
|
+
});
|
|
2169
|
+
if (action === "install") {
|
|
2170
|
+
return accumulated;
|
|
2171
|
+
}
|
|
2172
|
+
if (action === CONTINUE_SELECT) {
|
|
2173
|
+
step = 0;
|
|
2174
|
+
break;
|
|
2175
|
+
}
|
|
2176
|
+
return "cancel";
|
|
1924
2177
|
}
|
|
1925
2178
|
}
|
|
1926
2179
|
}
|
|
@@ -1928,11 +2181,11 @@ async function interactiveSkillSelect() {
|
|
|
1928
2181
|
function listAllSkills() {
|
|
1929
2182
|
const categories = getAllCategories();
|
|
1930
2183
|
console.log();
|
|
1931
|
-
console.log(
|
|
1932
|
-
console.log(
|
|
2184
|
+
console.log(chalk5.bold(` ${t("skill_list_title")}`));
|
|
2185
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
1933
2186
|
for (const category of categories) {
|
|
1934
2187
|
console.log();
|
|
1935
|
-
console.log(
|
|
2188
|
+
console.log(chalk5.bold(` ${category.name}`) + chalk5.dim(` (${category.skills.length} skills)`));
|
|
1936
2189
|
const table = new Table({
|
|
1937
2190
|
style: { head: [], border: [] },
|
|
1938
2191
|
colWidths: [35, 45],
|
|
@@ -1940,16 +2193,25 @@ function listAllSkills() {
|
|
|
1940
2193
|
});
|
|
1941
2194
|
for (const skill of category.skills) {
|
|
1942
2195
|
table.push([
|
|
1943
|
-
|
|
2196
|
+
chalk5.cyan(skill.id),
|
|
1944
2197
|
skill.description
|
|
1945
2198
|
]);
|
|
1946
2199
|
}
|
|
1947
2200
|
console.log(table.toString());
|
|
1948
2201
|
}
|
|
1949
2202
|
console.log();
|
|
1950
|
-
console.log(
|
|
2203
|
+
console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
1951
2204
|
console.log();
|
|
1952
2205
|
}
|
|
2206
|
+
function resolveSkillSourcePath(skill, repoPath) {
|
|
2207
|
+
if (skill.filePath) {
|
|
2208
|
+
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join12(repoPath, skill.filePath);
|
|
2209
|
+
if (existsSync11(absPath)) {
|
|
2210
|
+
return dirname5(absPath);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
return join12(repoPath, skill.source, skill.id);
|
|
2214
|
+
}
|
|
1953
2215
|
function isSymlink2(path) {
|
|
1954
2216
|
try {
|
|
1955
2217
|
return lstatSync2(path).isSymbolicLink();
|
|
@@ -1959,22 +2221,22 @@ function isSymlink2(path) {
|
|
|
1959
2221
|
}
|
|
1960
2222
|
|
|
1961
2223
|
// src/ui/display.ts
|
|
1962
|
-
import
|
|
2224
|
+
import chalk6 from "chalk";
|
|
1963
2225
|
import Table2 from "cli-table3";
|
|
1964
2226
|
function printPluginList(plugins, installed) {
|
|
1965
2227
|
const table = new Table2({
|
|
1966
2228
|
head: [
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2229
|
+
chalk6.cyan(t("list_id")),
|
|
2230
|
+
chalk6.cyan(t("list_name")),
|
|
2231
|
+
chalk6.cyan(t("list_status")),
|
|
2232
|
+
chalk6.cyan(t("list_skills")),
|
|
2233
|
+
chalk6.cyan(t("list_agents"))
|
|
1972
2234
|
],
|
|
1973
2235
|
style: { head: [], border: [] }
|
|
1974
2236
|
});
|
|
1975
2237
|
plugins.forEach((plugin, index) => {
|
|
1976
2238
|
const isInstalled = installed.has(plugin.id);
|
|
1977
|
-
const status = isInstalled ?
|
|
2239
|
+
const status = isInstalled ? chalk6.green(`\u2713 ${t("status_installed")}`) : chalk6.dim(`\u2014`);
|
|
1978
2240
|
table.push([
|
|
1979
2241
|
String(index + 1),
|
|
1980
2242
|
plugin.displayName,
|
|
@@ -1984,7 +2246,8 @@ function printPluginList(plugins, installed) {
|
|
|
1984
2246
|
]);
|
|
1985
2247
|
});
|
|
1986
2248
|
console.log();
|
|
1987
|
-
|
|
2249
|
+
const countSuffix = t("count_suffix");
|
|
2250
|
+
console.log(chalk6.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
1988
2251
|
console.log(table.toString());
|
|
1989
2252
|
console.log();
|
|
1990
2253
|
}
|
|
@@ -1993,7 +2256,7 @@ function printInstallSummary(results) {
|
|
|
1993
2256
|
const totalCount = results.length;
|
|
1994
2257
|
console.log();
|
|
1995
2258
|
console.log(
|
|
1996
|
-
|
|
2259
|
+
chalk6.bold(
|
|
1997
2260
|
` ${t("install_done")}! ${successCount}/${totalCount} ${t("install_success")}`
|
|
1998
2261
|
)
|
|
1999
2262
|
);
|
|
@@ -2001,10 +2264,10 @@ function printInstallSummary(results) {
|
|
|
2001
2264
|
for (const result of results) {
|
|
2002
2265
|
if (result.success) {
|
|
2003
2266
|
console.log(
|
|
2004
|
-
|
|
2267
|
+
chalk6.green(" \u2713") + ` ${result.displayName} (${result.skillsCount} skills, ${result.agentsCount} agents)`
|
|
2005
2268
|
);
|
|
2006
2269
|
} else {
|
|
2007
|
-
console.log(
|
|
2270
|
+
console.log(chalk6.red(" \u2717") + ` ${result.displayName}`);
|
|
2008
2271
|
}
|
|
2009
2272
|
}
|
|
2010
2273
|
console.log();
|
|
@@ -2017,22 +2280,25 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2017
2280
|
return;
|
|
2018
2281
|
}
|
|
2019
2282
|
const toolName = getToolDisplayName(tool);
|
|
2283
|
+
const countSuffix = t("count_suffix");
|
|
2020
2284
|
const lines = [
|
|
2021
|
-
|
|
2285
|
+
chalk6.bold(` ${t("install_done")}!`),
|
|
2022
2286
|
"",
|
|
2023
|
-
` ${
|
|
2024
|
-
` ${
|
|
2287
|
+
` ${chalk6.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
2288
|
+
` ${chalk6.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk6.dim("|")} ${chalk6.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2025
2289
|
"",
|
|
2026
|
-
|
|
2027
|
-
` ${
|
|
2028
|
-
` ${
|
|
2029
|
-
` ${
|
|
2030
|
-
` ${
|
|
2290
|
+
chalk6.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
2291
|
+
` ${chalk6.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk6.green(toolName.toLowerCase())}`,
|
|
2292
|
+
` ${chalk6.cyan("2.")} ${t("install_enhanced_try")}: ${chalk6.green(t("try_prompt"))}`,
|
|
2293
|
+
` ${chalk6.cyan("3.")} ${t("install_enhanced_more")}: ${chalk6.green("install-helper list")}`,
|
|
2294
|
+
` ${chalk6.cyan("4.")} ${t("install_enhanced_check")}: ${chalk6.green("install-helper doctor")}`,
|
|
2031
2295
|
""
|
|
2032
2296
|
];
|
|
2033
2297
|
if (successResults.length === 1) {
|
|
2298
|
+
const plugin = findPlugin(successResults[0].pluginId);
|
|
2299
|
+
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
2034
2300
|
lines.push(
|
|
2035
|
-
` ${
|
|
2301
|
+
` ${chalk6.dim(t("install_enhanced_docs"))}: ${chalk6.dim(docsPath)}`
|
|
2036
2302
|
);
|
|
2037
2303
|
}
|
|
2038
2304
|
console.log();
|
|
@@ -2043,34 +2309,34 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2043
2309
|
}
|
|
2044
2310
|
|
|
2045
2311
|
// src/commands/init.ts
|
|
2046
|
-
import { existsSync as
|
|
2047
|
-
import { join as
|
|
2312
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
|
|
2313
|
+
import { join as join13 } from "path";
|
|
2048
2314
|
var BACK2 = "__back__";
|
|
2049
2315
|
var CANCEL2 = "__cancel__";
|
|
2050
2316
|
async function initCommand() {
|
|
2051
2317
|
const cwd = process.cwd();
|
|
2052
|
-
if (
|
|
2318
|
+
if (existsSync12(join13(cwd, "package.json")) && (() => {
|
|
2053
2319
|
try {
|
|
2054
|
-
const pkg = JSON.parse(
|
|
2320
|
+
const pkg = JSON.parse(readFileSync8(join13(cwd, "package.json"), "utf-8"));
|
|
2055
2321
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
2056
2322
|
} catch {
|
|
2057
2323
|
return false;
|
|
2058
2324
|
}
|
|
2059
2325
|
})()) {
|
|
2060
|
-
logger.warn("
|
|
2061
|
-
logger.info("
|
|
2062
|
-
logger.info("
|
|
2326
|
+
logger.warn(t("init_pkg_dir_warn"));
|
|
2327
|
+
logger.info(t("init_pkg_dir_hint"));
|
|
2328
|
+
logger.info(t("init_pkg_dir_example"));
|
|
2063
2329
|
return;
|
|
2064
2330
|
}
|
|
2065
2331
|
printBanner(t("wizard_title"));
|
|
2066
|
-
const spinner = createSpinner("
|
|
2332
|
+
const spinner = createSpinner(t("loading_skills_list"));
|
|
2067
2333
|
spinner.start();
|
|
2068
2334
|
try {
|
|
2069
2335
|
const repoManager = createRepositoryManager();
|
|
2070
2336
|
await repoManager.ensureRepoAndScan();
|
|
2071
|
-
spinner.succeed("
|
|
2337
|
+
spinner.succeed(t("loading_skills_list_complete"));
|
|
2072
2338
|
} catch {
|
|
2073
|
-
spinner.warn("
|
|
2339
|
+
spinner.warn(t("loading_skills_list_failed"));
|
|
2074
2340
|
}
|
|
2075
2341
|
let step = 0;
|
|
2076
2342
|
let mode = "plugin";
|
|
@@ -2104,7 +2370,7 @@ async function initCommand() {
|
|
|
2104
2370
|
}
|
|
2105
2371
|
}
|
|
2106
2372
|
async function selectMode() {
|
|
2107
|
-
printBoxTitle("
|
|
2373
|
+
printBoxTitle(t("wizard_select_mode_title"));
|
|
2108
2374
|
const CANCEL3 = "__cancel__";
|
|
2109
2375
|
const choices = [
|
|
2110
2376
|
{
|
|
@@ -2119,7 +2385,7 @@ async function selectMode() {
|
|
|
2119
2385
|
{ name: "x " + t("wizard_cancel"), value: CANCEL3 }
|
|
2120
2386
|
];
|
|
2121
2387
|
showOperationHints();
|
|
2122
|
-
const result = await
|
|
2388
|
+
const result = await select4({
|
|
2123
2389
|
message: t("wizard_select_mode"),
|
|
2124
2390
|
choices,
|
|
2125
2391
|
loop: false,
|
|
@@ -2132,14 +2398,14 @@ async function pluginInstallFlow() {
|
|
|
2132
2398
|
const answers = await runWizard();
|
|
2133
2399
|
if (!answers.confirmed) {
|
|
2134
2400
|
if (answers.back) return "back";
|
|
2135
|
-
logger.info("
|
|
2401
|
+
logger.info(t("init_cancelled"));
|
|
2136
2402
|
return "done";
|
|
2137
2403
|
}
|
|
2138
2404
|
const repoManager = createRepositoryManager();
|
|
2139
|
-
const spinner = createSpinner("
|
|
2405
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2140
2406
|
spinner.start();
|
|
2141
2407
|
const repoPath = await repoManager.ensureRepo();
|
|
2142
|
-
spinner.succeed("
|
|
2408
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2143
2409
|
const allPlugins = getAllPlugins();
|
|
2144
2410
|
const selectedPlugins = answers.plugins.map(
|
|
2145
2411
|
(id) => allPlugins.find((p) => p.id === id)
|
|
@@ -2193,7 +2459,7 @@ async function skillInstallFlow() {
|
|
|
2193
2459
|
const result = await selectToolWithDetection();
|
|
2194
2460
|
if (result === "back") return "back";
|
|
2195
2461
|
if (result === "cancel") {
|
|
2196
|
-
logger.info("
|
|
2462
|
+
logger.info(t("init_cancelled"));
|
|
2197
2463
|
return "done";
|
|
2198
2464
|
}
|
|
2199
2465
|
tool = result;
|
|
@@ -2207,7 +2473,7 @@ async function skillInstallFlow() {
|
|
|
2207
2473
|
break;
|
|
2208
2474
|
}
|
|
2209
2475
|
if (result === CANCEL2) {
|
|
2210
|
-
logger.info("
|
|
2476
|
+
logger.info(t("init_cancelled"));
|
|
2211
2477
|
return "done";
|
|
2212
2478
|
}
|
|
2213
2479
|
level = result;
|
|
@@ -2215,25 +2481,25 @@ async function skillInstallFlow() {
|
|
|
2215
2481
|
break;
|
|
2216
2482
|
}
|
|
2217
2483
|
case 2: {
|
|
2218
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2484
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2219
2485
|
if (selectedSkills === "back") {
|
|
2220
2486
|
step = 1;
|
|
2221
2487
|
break;
|
|
2222
2488
|
}
|
|
2223
2489
|
if (selectedSkills === "cancel") {
|
|
2224
|
-
logger.info("
|
|
2490
|
+
logger.info(t("init_cancelled"));
|
|
2225
2491
|
return "done";
|
|
2226
2492
|
}
|
|
2227
2493
|
if (selectedSkills.length === 0) {
|
|
2228
|
-
logger.info("
|
|
2494
|
+
logger.info(t("install_no_skill_selected"));
|
|
2229
2495
|
return "done";
|
|
2230
2496
|
}
|
|
2231
2497
|
const repoManager = createRepositoryManager();
|
|
2232
|
-
const spinner = createSpinner("
|
|
2498
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2233
2499
|
spinner.start();
|
|
2234
2500
|
const repoPath = await repoManager.ensureRepo();
|
|
2235
|
-
spinner.succeed("
|
|
2236
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2501
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2502
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk7.bold(String(selectedSkills.length)))}...`);
|
|
2237
2503
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2238
2504
|
let successCount = 0;
|
|
2239
2505
|
let failCount = 0;
|
|
@@ -2251,10 +2517,10 @@ async function skillInstallFlow() {
|
|
|
2251
2517
|
}
|
|
2252
2518
|
const configRoot = getConfigRoot(tool, level);
|
|
2253
2519
|
logger.blank();
|
|
2254
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
2520
|
+
logger.success(`${t("skill_install_done")}: ${chalk7.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk7.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk7.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2255
2521
|
logger.blank();
|
|
2256
|
-
logger.info(
|
|
2257
|
-
logger.info(
|
|
2522
|
+
logger.info(`${t("install_to")}: ${chalk7.cyan(configRoot + "/skills/")}`);
|
|
2523
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk7.green(tool))}`);
|
|
2258
2524
|
logger.blank();
|
|
2259
2525
|
return "done";
|
|
2260
2526
|
}
|
|
@@ -2276,32 +2542,32 @@ async function listCommand() {
|
|
|
2276
2542
|
}
|
|
2277
2543
|
|
|
2278
2544
|
// src/commands/doctor.ts
|
|
2279
|
-
import
|
|
2280
|
-
import { existsSync as
|
|
2281
|
-
import { join as
|
|
2545
|
+
import chalk8 from "chalk";
|
|
2546
|
+
import { existsSync as existsSync13, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
2547
|
+
import { join as join14 } from "path";
|
|
2282
2548
|
async function doctorCommand(options = {}) {
|
|
2283
2549
|
console.log();
|
|
2284
|
-
console.log(
|
|
2285
|
-
console.log(
|
|
2550
|
+
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
2551
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2286
2552
|
let errors = 0;
|
|
2287
2553
|
let warnings = 0;
|
|
2288
2554
|
let fixes = 0;
|
|
2289
2555
|
console.log();
|
|
2290
|
-
console.log(
|
|
2556
|
+
console.log(chalk8.bold(` ${t("doctor_tools")}`));
|
|
2291
2557
|
const detectedTools = await detectTools();
|
|
2292
2558
|
const allTools = getAllTools();
|
|
2293
2559
|
for (const tool of allTools) {
|
|
2294
2560
|
const detected = detectedTools.find((d) => d.name === tool);
|
|
2295
2561
|
if (detected) {
|
|
2296
2562
|
console.log(
|
|
2297
|
-
|
|
2563
|
+
chalk8.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2298
2564
|
);
|
|
2299
2565
|
} else {
|
|
2300
|
-
console.log(
|
|
2566
|
+
console.log(chalk8.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2301
2567
|
}
|
|
2302
2568
|
}
|
|
2303
2569
|
console.log();
|
|
2304
|
-
console.log(
|
|
2570
|
+
console.log(chalk8.bold(` ${t("doctor_plugins")}`));
|
|
2305
2571
|
const plugins = getAllPlugins();
|
|
2306
2572
|
const installed = scanInstalled();
|
|
2307
2573
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
@@ -2309,80 +2575,80 @@ async function doctorCommand(options = {}) {
|
|
|
2309
2575
|
const inst = installedMap.get(plugin.id);
|
|
2310
2576
|
if (inst) {
|
|
2311
2577
|
console.log(
|
|
2312
|
-
|
|
2578
|
+
chalk8.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2313
2579
|
);
|
|
2314
2580
|
} else {
|
|
2315
|
-
console.log(
|
|
2581
|
+
console.log(chalk8.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2316
2582
|
}
|
|
2317
2583
|
}
|
|
2318
2584
|
console.log();
|
|
2319
|
-
console.log(
|
|
2585
|
+
console.log(chalk8.bold(` ${t("doctor_links")}`));
|
|
2320
2586
|
const primaryTool = detectedTools[0]?.name || "opencode";
|
|
2321
2587
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2322
2588
|
const skillsDir = getSkillsDir(configRoot);
|
|
2323
2589
|
const agentsDir = getAgentsDir(configRoot);
|
|
2324
|
-
if (
|
|
2590
|
+
if (existsSync13(skillsDir)) {
|
|
2325
2591
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2326
2592
|
if (brokenLinks === 0) {
|
|
2327
|
-
const count =
|
|
2328
|
-
console.log(
|
|
2593
|
+
const count = readdirSync5(skillsDir).length;
|
|
2594
|
+
console.log(chalk8.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2329
2595
|
} else {
|
|
2330
|
-
console.log(
|
|
2596
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2331
2597
|
warnings++;
|
|
2332
2598
|
if (options.fix) {
|
|
2333
2599
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2334
2600
|
fixes += fixed;
|
|
2335
|
-
console.log(
|
|
2601
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2336
2602
|
}
|
|
2337
2603
|
}
|
|
2338
2604
|
} else {
|
|
2339
|
-
console.log(
|
|
2605
|
+
console.log(chalk8.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2340
2606
|
if (options.fix) {
|
|
2341
2607
|
mkdirSync6(skillsDir, { recursive: true });
|
|
2342
2608
|
fixes++;
|
|
2343
|
-
console.log(
|
|
2609
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2344
2610
|
}
|
|
2345
2611
|
}
|
|
2346
|
-
if (
|
|
2612
|
+
if (existsSync13(agentsDir)) {
|
|
2347
2613
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2348
2614
|
if (brokenLinks === 0) {
|
|
2349
|
-
const count =
|
|
2350
|
-
console.log(
|
|
2615
|
+
const count = readdirSync5(agentsDir).length;
|
|
2616
|
+
console.log(chalk8.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2351
2617
|
} else {
|
|
2352
|
-
console.log(
|
|
2618
|
+
console.log(chalk8.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2353
2619
|
warnings++;
|
|
2354
2620
|
if (options.fix) {
|
|
2355
2621
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2356
2622
|
fixes += fixed;
|
|
2357
|
-
console.log(
|
|
2623
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2358
2624
|
}
|
|
2359
2625
|
}
|
|
2360
2626
|
} else {
|
|
2361
|
-
console.log(
|
|
2627
|
+
console.log(chalk8.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2362
2628
|
if (options.fix) {
|
|
2363
2629
|
mkdirSync6(agentsDir, { recursive: true });
|
|
2364
2630
|
fixes++;
|
|
2365
|
-
console.log(
|
|
2631
|
+
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2366
2632
|
}
|
|
2367
2633
|
}
|
|
2368
2634
|
console.log();
|
|
2369
|
-
console.log(
|
|
2635
|
+
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2370
2636
|
const configFile = getConfigFileName(primaryTool);
|
|
2371
|
-
const configPath =
|
|
2372
|
-
if (
|
|
2373
|
-
console.log(
|
|
2637
|
+
const configPath = join14(process.cwd(), configFile);
|
|
2638
|
+
if (existsSync13(configPath)) {
|
|
2639
|
+
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2374
2640
|
} else {
|
|
2375
|
-
console.log(
|
|
2641
|
+
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2376
2642
|
}
|
|
2377
2643
|
console.log();
|
|
2378
|
-
console.log(
|
|
2644
|
+
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2379
2645
|
if (options.fix && fixes > 0) {
|
|
2380
2646
|
console.log(
|
|
2381
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2647
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
|
|
2382
2648
|
);
|
|
2383
2649
|
} else {
|
|
2384
2650
|
console.log(
|
|
2385
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2651
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
|
|
2386
2652
|
);
|
|
2387
2653
|
}
|
|
2388
2654
|
console.log();
|
|
@@ -2390,13 +2656,13 @@ async function doctorCommand(options = {}) {
|
|
|
2390
2656
|
function checkBrokenLinks(dir) {
|
|
2391
2657
|
let broken = 0;
|
|
2392
2658
|
try {
|
|
2393
|
-
const entries =
|
|
2659
|
+
const entries = readdirSync5(dir);
|
|
2394
2660
|
for (const entry of entries) {
|
|
2395
|
-
const fullPath =
|
|
2661
|
+
const fullPath = join14(dir, entry);
|
|
2396
2662
|
try {
|
|
2397
2663
|
const stats = lstatSync3(fullPath);
|
|
2398
2664
|
if (stats.isSymbolicLink()) {
|
|
2399
|
-
if (!
|
|
2665
|
+
if (!existsSync13(fullPath)) {
|
|
2400
2666
|
broken++;
|
|
2401
2667
|
}
|
|
2402
2668
|
}
|
|
@@ -2411,14 +2677,14 @@ function checkBrokenLinks(dir) {
|
|
|
2411
2677
|
function fixBrokenLinks(dir) {
|
|
2412
2678
|
let fixed = 0;
|
|
2413
2679
|
try {
|
|
2414
|
-
const entries =
|
|
2680
|
+
const entries = readdirSync5(dir);
|
|
2415
2681
|
for (const entry of entries) {
|
|
2416
|
-
const fullPath =
|
|
2682
|
+
const fullPath = join14(dir, entry);
|
|
2417
2683
|
try {
|
|
2418
2684
|
const stats = lstatSync3(fullPath);
|
|
2419
2685
|
if (stats.isSymbolicLink()) {
|
|
2420
|
-
if (!
|
|
2421
|
-
|
|
2686
|
+
if (!existsSync13(fullPath)) {
|
|
2687
|
+
unlinkSync4(fullPath);
|
|
2422
2688
|
fixed++;
|
|
2423
2689
|
}
|
|
2424
2690
|
}
|
|
@@ -2431,36 +2697,36 @@ function fixBrokenLinks(dir) {
|
|
|
2431
2697
|
}
|
|
2432
2698
|
|
|
2433
2699
|
// src/commands/status.ts
|
|
2434
|
-
import
|
|
2700
|
+
import chalk9 from "chalk";
|
|
2435
2701
|
async function statusCommand() {
|
|
2436
2702
|
const plugins = getAllPlugins();
|
|
2437
2703
|
const installed = scanInstalled();
|
|
2438
2704
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2439
2705
|
console.log();
|
|
2440
|
-
console.log(
|
|
2706
|
+
console.log(chalk9.bold(` ${t("status_title")}`));
|
|
2441
2707
|
console.log();
|
|
2442
2708
|
if (installed.length === 0) {
|
|
2443
|
-
console.log(
|
|
2444
|
-
console.log(
|
|
2709
|
+
console.log(chalk9.dim(` ${t("status_none")}`));
|
|
2710
|
+
console.log(chalk9.dim(` ${t("status_hint")}`));
|
|
2445
2711
|
console.log();
|
|
2446
2712
|
return;
|
|
2447
2713
|
}
|
|
2448
2714
|
for (const inst of installed) {
|
|
2449
2715
|
console.log(
|
|
2450
|
-
|
|
2716
|
+
chalk9.green(" \u2713") + ` ${inst.displayName}` + chalk9.dim(` (${inst.tool}, ${inst.level})`)
|
|
2451
2717
|
);
|
|
2452
2718
|
console.log(
|
|
2453
|
-
|
|
2719
|
+
chalk9.dim(` ${inst.skillsCount} skills, ${inst.agentsCount} agents`)
|
|
2454
2720
|
);
|
|
2455
|
-
console.log(
|
|
2456
|
-
console.log(
|
|
2721
|
+
console.log(chalk9.dim(` ${inst.configRoot}`));
|
|
2722
|
+
console.log(chalk9.dim(` ${t("status_install_time")}: ${inst.installTime}`));
|
|
2457
2723
|
console.log();
|
|
2458
2724
|
}
|
|
2459
2725
|
}
|
|
2460
2726
|
|
|
2461
2727
|
// src/commands/install.ts
|
|
2462
2728
|
import { confirm } from "@inquirer/prompts";
|
|
2463
|
-
import
|
|
2729
|
+
import chalk10 from "chalk";
|
|
2464
2730
|
async function installCommand(names, options) {
|
|
2465
2731
|
if (options.list) {
|
|
2466
2732
|
try {
|
|
@@ -2471,79 +2737,138 @@ async function installCommand(names, options) {
|
|
|
2471
2737
|
listAllSkills();
|
|
2472
2738
|
return;
|
|
2473
2739
|
}
|
|
2740
|
+
if (options.all) {
|
|
2741
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2742
|
+
scanSpinner2.start();
|
|
2743
|
+
try {
|
|
2744
|
+
const scanRepoManager = createRepositoryManager();
|
|
2745
|
+
await scanRepoManager.ensureRepoAndScan();
|
|
2746
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2747
|
+
} catch {
|
|
2748
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2749
|
+
}
|
|
2750
|
+
const allSkills = getAllSkills();
|
|
2751
|
+
const skillIds = allSkills.map((s) => s.id);
|
|
2752
|
+
if (skillIds.length === 0) {
|
|
2753
|
+
logger.error(t("install_no_skill_selected"));
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
let tool2;
|
|
2757
|
+
if (options.tool) {
|
|
2758
|
+
tool2 = validateTool(options.tool);
|
|
2759
|
+
} else {
|
|
2760
|
+
const detected = await selectToolWithDetection();
|
|
2761
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2762
|
+
tool2 = detected;
|
|
2763
|
+
}
|
|
2764
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2765
|
+
const repoManager = createRepositoryManager();
|
|
2766
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2767
|
+
spinner.start();
|
|
2768
|
+
const repoPath = await repoManager.ensureRepo();
|
|
2769
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2770
|
+
logger.info(`${t("skill_all_install_progress").replace("{count}", chalk10.bold(String(skillIds.length)))}`);
|
|
2771
|
+
const results = await installSkills(skillIds, tool2, level2, repoPath);
|
|
2772
|
+
let successCount = 0;
|
|
2773
|
+
let failCount = 0;
|
|
2774
|
+
const total = results.length;
|
|
2775
|
+
for (let i = 0; i < results.length; i++) {
|
|
2776
|
+
const result = results[i];
|
|
2777
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2778
|
+
if (result.success) {
|
|
2779
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2780
|
+
successCount++;
|
|
2781
|
+
} else {
|
|
2782
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2783
|
+
failCount++;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
const configRoot = getConfigRoot(tool2, level2);
|
|
2787
|
+
logger.blank();
|
|
2788
|
+
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2789
|
+
logger.blank();
|
|
2790
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
2791
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2792
|
+
logger.blank();
|
|
2793
|
+
return;
|
|
2794
|
+
}
|
|
2474
2795
|
if (names.length === 0) {
|
|
2475
|
-
const scanSpinner2 = createSpinner("
|
|
2796
|
+
const scanSpinner2 = createSpinner(t("loading_skills_list"));
|
|
2476
2797
|
scanSpinner2.start();
|
|
2477
2798
|
try {
|
|
2478
2799
|
const scanRepoManager = createRepositoryManager();
|
|
2479
2800
|
await scanRepoManager.ensureRepoAndScan();
|
|
2480
|
-
scanSpinner2.succeed("
|
|
2801
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2481
2802
|
} catch {
|
|
2482
|
-
scanSpinner2.warn("
|
|
2803
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2483
2804
|
}
|
|
2484
2805
|
let tool2;
|
|
2485
2806
|
if (options.tool) {
|
|
2486
|
-
tool2 = options.tool;
|
|
2807
|
+
tool2 = validateTool(options.tool);
|
|
2487
2808
|
} else {
|
|
2488
2809
|
const detected = await selectToolWithDetection();
|
|
2489
|
-
if (detected === "back") return;
|
|
2810
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2490
2811
|
tool2 = detected;
|
|
2491
2812
|
}
|
|
2813
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2492
2814
|
let step = 1;
|
|
2493
2815
|
while (true) {
|
|
2494
2816
|
switch (step) {
|
|
2495
2817
|
case 1: {
|
|
2496
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2818
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2497
2819
|
if (selectedSkills === "back") {
|
|
2498
2820
|
const redetected = await selectToolWithDetection();
|
|
2499
|
-
if (redetected === "back") return;
|
|
2821
|
+
if (redetected === "back" || redetected === "cancel") return;
|
|
2500
2822
|
tool2 = redetected;
|
|
2501
2823
|
break;
|
|
2502
2824
|
}
|
|
2825
|
+
if (selectedSkills === "cancel") return;
|
|
2503
2826
|
if (selectedSkills.length === 0) {
|
|
2504
|
-
logger.info("
|
|
2827
|
+
logger.info(t("install_no_skill_selected"));
|
|
2505
2828
|
return;
|
|
2506
2829
|
}
|
|
2507
2830
|
names = selectedSkills;
|
|
2508
2831
|
const repoManager = createRepositoryManager();
|
|
2509
|
-
const spinner = createSpinner(t("
|
|
2832
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2510
2833
|
spinner.start();
|
|
2511
2834
|
const repoPath = await repoManager.ensureRepo();
|
|
2512
|
-
spinner.succeed(t("
|
|
2513
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2514
|
-
const level2 = options.level || "project";
|
|
2835
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2836
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(names.length)))}...`);
|
|
2515
2837
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2516
2838
|
let successCount = 0;
|
|
2517
2839
|
let failCount = 0;
|
|
2518
|
-
|
|
2840
|
+
const total = results.length;
|
|
2841
|
+
for (let i = 0; i < results.length; i++) {
|
|
2842
|
+
const result = results[i];
|
|
2843
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2519
2844
|
if (result.success) {
|
|
2520
|
-
logger.success(`${result.skillId}`);
|
|
2845
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2521
2846
|
successCount++;
|
|
2522
2847
|
} else {
|
|
2523
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
2848
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2524
2849
|
failCount++;
|
|
2525
2850
|
}
|
|
2526
2851
|
}
|
|
2527
2852
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2528
2853
|
logger.blank();
|
|
2529
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
2854
|
+
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2530
2855
|
logger.blank();
|
|
2531
|
-
logger.info(
|
|
2532
|
-
logger.info(
|
|
2856
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(configRoot + "/skills/")}`);
|
|
2857
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
2533
2858
|
logger.blank();
|
|
2534
2859
|
return;
|
|
2535
2860
|
}
|
|
2536
2861
|
}
|
|
2537
2862
|
}
|
|
2538
2863
|
}
|
|
2539
|
-
const scanSpinner = createSpinner("
|
|
2864
|
+
const scanSpinner = createSpinner(t("loading_skills_list"));
|
|
2540
2865
|
scanSpinner.start();
|
|
2541
2866
|
try {
|
|
2542
2867
|
const scanRepoManager = createRepositoryManager();
|
|
2543
2868
|
await scanRepoManager.ensureRepoAndScan();
|
|
2544
|
-
scanSpinner.succeed("
|
|
2869
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2545
2870
|
} catch {
|
|
2546
|
-
scanSpinner.warn("
|
|
2871
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2547
2872
|
}
|
|
2548
2873
|
const plugins = [];
|
|
2549
2874
|
const skills = [];
|
|
@@ -2560,22 +2885,22 @@ async function installCommand(names, options) {
|
|
|
2560
2885
|
}
|
|
2561
2886
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2562
2887
|
const allPlugins = getAllPlugins();
|
|
2563
|
-
logger.info("
|
|
2888
|
+
logger.info(t("available_plugins") + ":");
|
|
2564
2889
|
for (const p of allPlugins) {
|
|
2565
2890
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2566
2891
|
}
|
|
2567
|
-
logger.info(
|
|
2892
|
+
logger.info(`${t("available_skills")}: ${chalk10.cyan("install-helper install --list")}`);
|
|
2568
2893
|
return;
|
|
2569
2894
|
}
|
|
2570
2895
|
let tool;
|
|
2571
2896
|
if (options.tool) {
|
|
2572
|
-
tool = options.tool;
|
|
2897
|
+
tool = validateTool(options.tool);
|
|
2573
2898
|
} else {
|
|
2574
2899
|
const detected = await selectToolWithDetection();
|
|
2575
|
-
if (detected === "back") return;
|
|
2900
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2576
2901
|
tool = detected;
|
|
2577
2902
|
}
|
|
2578
|
-
const level = options.level
|
|
2903
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2579
2904
|
if (plugins.length > 0) {
|
|
2580
2905
|
const configRoot = getConfigRoot(tool, level);
|
|
2581
2906
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2603,16 +2928,18 @@ async function installCommand(names, options) {
|
|
|
2603
2928
|
}
|
|
2604
2929
|
if (pluginsToInstall.length > 0) {
|
|
2605
2930
|
const repoManager = createRepositoryManager();
|
|
2606
|
-
const spinner = createSpinner(t("
|
|
2931
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2607
2932
|
spinner.start();
|
|
2608
2933
|
const repoPath = await repoManager.ensureRepo();
|
|
2609
|
-
spinner.succeed(t("
|
|
2934
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2610
2935
|
const allPlugins = getAllPlugins();
|
|
2611
2936
|
const results = await installPlugins(
|
|
2612
2937
|
pluginsToInstall,
|
|
2613
2938
|
tool,
|
|
2614
2939
|
level,
|
|
2615
|
-
repoPath
|
|
2940
|
+
repoPath,
|
|
2941
|
+
void 0,
|
|
2942
|
+
options.yes
|
|
2616
2943
|
);
|
|
2617
2944
|
const summary = results.map((result) => {
|
|
2618
2945
|
const plugin = allPlugins.find((p) => p.id === result.pluginId);
|
|
@@ -2630,32 +2957,35 @@ async function installCommand(names, options) {
|
|
|
2630
2957
|
}
|
|
2631
2958
|
if (skills.length > 0) {
|
|
2632
2959
|
const repoManager = createRepositoryManager();
|
|
2633
|
-
const spinner = createSpinner(t("
|
|
2960
|
+
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2634
2961
|
spinner.start();
|
|
2635
2962
|
const repoPath = await repoManager.ensureRepo();
|
|
2636
|
-
spinner.succeed(t("
|
|
2637
|
-
logger.info(`${t("skill_install_progress")} ${
|
|
2963
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2964
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk10.bold(String(skills.length)))}...`);
|
|
2638
2965
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2639
2966
|
let successCount = 0;
|
|
2640
2967
|
let failCount = 0;
|
|
2641
|
-
|
|
2968
|
+
const total = results.length;
|
|
2969
|
+
for (let i = 0; i < results.length; i++) {
|
|
2970
|
+
const result = results[i];
|
|
2971
|
+
const progress = `[${i + 1}/${total}]`;
|
|
2642
2972
|
if (result.success) {
|
|
2643
|
-
logger.success(`${result.skillId}`);
|
|
2973
|
+
logger.success(`${progress} ${result.skillId}`);
|
|
2644
2974
|
successCount++;
|
|
2645
2975
|
} else {
|
|
2646
|
-
logger.error(`${result.skillId}: ${result.error}`);
|
|
2976
|
+
logger.error(`${progress} ${result.skillId}: ${result.error}`);
|
|
2647
2977
|
failCount++;
|
|
2648
2978
|
}
|
|
2649
2979
|
}
|
|
2650
2980
|
logger.blank();
|
|
2651
|
-
logger.success(`${t("skill_install_done")}: ${
|
|
2981
|
+
logger.success(`${t("skill_install_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2652
2982
|
logger.blank();
|
|
2653
|
-
logger.info(
|
|
2654
|
-
logger.info(
|
|
2983
|
+
logger.info(`${t("install_to")}: ${chalk10.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
2984
|
+
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
2655
2985
|
logger.blank();
|
|
2656
2986
|
}
|
|
2657
2987
|
}
|
|
2658
|
-
async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
2988
|
+
async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes) {
|
|
2659
2989
|
const results = [];
|
|
2660
2990
|
const total = pluginIds.length;
|
|
2661
2991
|
for (let i = 0; i < pluginIds.length; i++) {
|
|
@@ -2667,7 +2997,9 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2667
2997
|
pluginId,
|
|
2668
2998
|
tool,
|
|
2669
2999
|
level,
|
|
2670
|
-
repoPath
|
|
3000
|
+
repoPath,
|
|
3001
|
+
installPath,
|
|
3002
|
+
yes
|
|
2671
3003
|
});
|
|
2672
3004
|
if (result.success) {
|
|
2673
3005
|
spinner.succeed(
|
|
@@ -2683,11 +3015,11 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2683
3015
|
}
|
|
2684
3016
|
|
|
2685
3017
|
// src/commands/uninstall.ts
|
|
2686
|
-
import { existsSync as
|
|
2687
|
-
import
|
|
3018
|
+
import { existsSync as existsSync14, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
|
|
3019
|
+
import chalk11 from "chalk";
|
|
2688
3020
|
async function uninstallCommand(names, options) {
|
|
2689
|
-
const tool = options.tool
|
|
2690
|
-
const level = options.level
|
|
3021
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
3022
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2691
3023
|
const plugins = [];
|
|
2692
3024
|
const skills = [];
|
|
2693
3025
|
for (const name of names) {
|
|
@@ -2701,40 +3033,44 @@ async function uninstallCommand(names, options) {
|
|
|
2701
3033
|
skills.push(skill.id);
|
|
2702
3034
|
continue;
|
|
2703
3035
|
}
|
|
2704
|
-
logger.error(
|
|
3036
|
+
logger.error(`${t("uninstall_not_found")}: ${name}`);
|
|
2705
3037
|
}
|
|
2706
3038
|
for (const pluginId of plugins) {
|
|
2707
3039
|
const plugin = findPlugin(pluginId);
|
|
2708
3040
|
if (!plugin) continue;
|
|
2709
3041
|
const record = readRecord(plugin.id);
|
|
2710
3042
|
if (!record) {
|
|
2711
|
-
logger.warn(`${plugin.displayName}
|
|
2712
|
-
logger.info(
|
|
3043
|
+
logger.warn(`${plugin.displayName} ${t("uninstall_no_record")}`);
|
|
3044
|
+
logger.info(`${t("uninstall_no_record_hint")}`);
|
|
2713
3045
|
continue;
|
|
2714
3046
|
}
|
|
2715
|
-
logger.info(
|
|
3047
|
+
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
2716
3048
|
let removedFiles = 0;
|
|
2717
3049
|
let removedDirs = 0;
|
|
2718
3050
|
for (const filePath of record.files) {
|
|
2719
3051
|
try {
|
|
2720
|
-
if (
|
|
2721
|
-
|
|
3052
|
+
if (existsSync14(filePath) || isSymlink3(filePath)) {
|
|
3053
|
+
unlinkSync5(filePath);
|
|
2722
3054
|
const name = filePath.split("/").pop() || filePath;
|
|
2723
|
-
logger.step(`
|
|
3055
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
2724
3056
|
removedFiles++;
|
|
2725
3057
|
}
|
|
2726
3058
|
} catch {
|
|
2727
3059
|
}
|
|
2728
3060
|
}
|
|
3061
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
2729
3062
|
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
2730
3063
|
for (const dirPath of sortedDirs) {
|
|
2731
3064
|
try {
|
|
2732
|
-
if (
|
|
2733
|
-
|
|
3065
|
+
if (dirPath === configRoot) {
|
|
3066
|
+
continue;
|
|
3067
|
+
}
|
|
3068
|
+
if (existsSync14(dirPath)) {
|
|
3069
|
+
const entries = readdirSync6(dirPath);
|
|
2734
3070
|
if (entries.length === 0) {
|
|
2735
3071
|
rmdirSync2(dirPath);
|
|
2736
3072
|
const name = dirPath.split("/").pop() || dirPath;
|
|
2737
|
-
logger.step(`
|
|
3073
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
2738
3074
|
removedDirs++;
|
|
2739
3075
|
}
|
|
2740
3076
|
}
|
|
@@ -2743,15 +3079,32 @@ async function uninstallCommand(names, options) {
|
|
|
2743
3079
|
}
|
|
2744
3080
|
deleteRecord(plugin.id);
|
|
2745
3081
|
removeInstalledPlugin(plugin.id);
|
|
3082
|
+
const backups = findBackups(configRoot);
|
|
3083
|
+
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
3084
|
+
if (otherBackups.length > 0) {
|
|
3085
|
+
const choice = await showRestorePrompt(otherBackups);
|
|
3086
|
+
if (choice !== "none") {
|
|
3087
|
+
const restored = restoreBackup(choice, configRoot, record.tool);
|
|
3088
|
+
if (restored) {
|
|
3089
|
+
const backupInfo = otherBackups.find((b) => b.filePath === choice);
|
|
3090
|
+
logger.success(`${t("backup_restore_success")}: ${backupInfo?.pluginName || ""}`);
|
|
3091
|
+
deleteBackup(choice);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
const currentBackups = backups.filter((b) => b.pluginId === pluginId);
|
|
3096
|
+
for (const backup of currentBackups) {
|
|
3097
|
+
deleteBackup(backup.filePath);
|
|
3098
|
+
}
|
|
2746
3099
|
const totalRemoved = removedFiles + removedDirs;
|
|
2747
3100
|
if (totalRemoved > 0) {
|
|
2748
|
-
logger.success(
|
|
3101
|
+
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
3102
|
} else {
|
|
2750
|
-
logger.warn(`${plugin.displayName}
|
|
3103
|
+
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
2751
3104
|
}
|
|
2752
3105
|
}
|
|
2753
3106
|
if (skills.length > 0) {
|
|
2754
|
-
logger.info(
|
|
3107
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
2755
3108
|
const results = await uninstallSkills(skills, tool, level);
|
|
2756
3109
|
let successCount = 0;
|
|
2757
3110
|
let failCount = 0;
|
|
@@ -2763,7 +3116,7 @@ async function uninstallCommand(names, options) {
|
|
|
2763
3116
|
}
|
|
2764
3117
|
}
|
|
2765
3118
|
logger.blank();
|
|
2766
|
-
logger.success(`${t("skill_uninstall_done")}: ${
|
|
3119
|
+
logger.success(`${t("skill_uninstall_done")}: ${chalk11.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk11.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk11.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2767
3120
|
logger.blank();
|
|
2768
3121
|
}
|
|
2769
3122
|
}
|
|
@@ -2776,17 +3129,17 @@ function isSymlink3(path) {
|
|
|
2776
3129
|
}
|
|
2777
3130
|
|
|
2778
3131
|
// src/commands/update.ts
|
|
2779
|
-
import
|
|
3132
|
+
import chalk12 from "chalk";
|
|
2780
3133
|
async function updateCommand(pluginNames, options) {
|
|
2781
3134
|
let tool;
|
|
2782
3135
|
if (options.tool) {
|
|
2783
|
-
tool = options.tool;
|
|
3136
|
+
tool = validateTool(options.tool);
|
|
2784
3137
|
} else {
|
|
2785
3138
|
const detected = await selectToolWithDetection();
|
|
2786
|
-
if (detected === "back") return;
|
|
3139
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2787
3140
|
tool = detected;
|
|
2788
3141
|
}
|
|
2789
|
-
const level = options.level
|
|
3142
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2790
3143
|
let pluginsToUpdate = [];
|
|
2791
3144
|
if (pluginNames.length === 0) {
|
|
2792
3145
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -2794,7 +3147,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
2794
3147
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
2795
3148
|
if (pluginsToUpdate.length === 0) {
|
|
2796
3149
|
logger.info(t("update_no_plugins"));
|
|
2797
|
-
logger.info(
|
|
3150
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk12.cyan("install-helper install <plugin>")));
|
|
2798
3151
|
return;
|
|
2799
3152
|
}
|
|
2800
3153
|
} else {
|
|
@@ -2856,42 +3209,42 @@ async function updateCommand(pluginNames, options) {
|
|
|
2856
3209
|
}
|
|
2857
3210
|
|
|
2858
3211
|
// src/commands/info.ts
|
|
2859
|
-
import
|
|
2860
|
-
import { existsSync as
|
|
2861
|
-
import { join as
|
|
3212
|
+
import chalk13 from "chalk";
|
|
3213
|
+
import { existsSync as existsSync15, readdirSync as readdirSync7 } from "fs";
|
|
3214
|
+
import { join as join15 } from "path";
|
|
2862
3215
|
async function infoCommand(pluginName) {
|
|
2863
3216
|
const plugin = findPlugin(pluginName);
|
|
2864
3217
|
if (!plugin) {
|
|
2865
|
-
console.log(
|
|
3218
|
+
console.log(chalk13.red(` ${t("info_not_found")}: ${pluginName}`));
|
|
2866
3219
|
return;
|
|
2867
3220
|
}
|
|
2868
3221
|
const installed = scanInstalled();
|
|
2869
3222
|
const isInstalled = installed.some((p) => p.id === plugin.id);
|
|
2870
|
-
const statusText = isInstalled ?
|
|
3223
|
+
const statusText = isInstalled ? chalk13.green(`\u2713 ${t("status_installed")}`) : chalk13.dim(`\u2014 ${t("status_not_installed")}`);
|
|
2871
3224
|
console.log();
|
|
2872
|
-
console.log(
|
|
2873
|
-
console.log(
|
|
3225
|
+
console.log(chalk13.bold(` ${plugin.displayName}`));
|
|
3226
|
+
console.log(chalk13.dim(" " + "\u2500".repeat(40)));
|
|
2874
3227
|
console.log();
|
|
2875
|
-
console.log(` ${
|
|
2876
|
-
console.log(` ${
|
|
2877
|
-
console.log(` ${
|
|
2878
|
-
console.log(` ${
|
|
2879
|
-
console.log(` ${
|
|
2880
|
-
console.log(` ${
|
|
3228
|
+
console.log(` ${chalk13.dim(t("info_description") + ":")} ${plugin.description}`);
|
|
3229
|
+
console.log(` ${chalk13.dim("ID:")} ${plugin.id}`);
|
|
3230
|
+
console.log(` ${chalk13.dim(t("info_status") + ":")} ${statusText}`);
|
|
3231
|
+
console.log(` ${chalk13.dim(t("info_skills") + ":")} ${plugin.skills}`);
|
|
3232
|
+
console.log(` ${chalk13.dim(t("info_agents") + ":")} ${plugin.agents}`);
|
|
3233
|
+
console.log(` ${chalk13.dim(t("info_aliases") + ":")} ${plugin.aliases.join(", ")}`);
|
|
2881
3234
|
console.log();
|
|
2882
3235
|
try {
|
|
2883
3236
|
const repoManager = createRepositoryManager();
|
|
2884
3237
|
const repoPath = await repoManager.ensureRepo();
|
|
2885
|
-
const quickstartPath =
|
|
2886
|
-
if (
|
|
2887
|
-
console.log(` ${
|
|
3238
|
+
const quickstartPath = join15(repoPath, plugin.dir, "quickstart.md");
|
|
3239
|
+
if (existsSync15(quickstartPath)) {
|
|
3240
|
+
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
2888
3241
|
}
|
|
2889
|
-
const agentsDir =
|
|
2890
|
-
if (
|
|
2891
|
-
const agentFiles =
|
|
3242
|
+
const agentsDir = join15(repoPath, plugin.dir, "agents");
|
|
3243
|
+
if (existsSync15(agentsDir)) {
|
|
3244
|
+
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
2892
3245
|
if (agentFiles.length > 0) {
|
|
2893
3246
|
console.log();
|
|
2894
|
-
console.log(` ${
|
|
3247
|
+
console.log(` ${chalk13.bold("Agents:")}`);
|
|
2895
3248
|
for (const agent of agentFiles) {
|
|
2896
3249
|
console.log(` \u2022 ${agent.replace(".md", "")}`);
|
|
2897
3250
|
}
|
|
@@ -2903,39 +3256,39 @@ async function infoCommand(pluginName) {
|
|
|
2903
3256
|
}
|
|
2904
3257
|
|
|
2905
3258
|
// src/commands/lang.ts
|
|
2906
|
-
import
|
|
3259
|
+
import chalk14 from "chalk";
|
|
2907
3260
|
async function langCommand(action, value) {
|
|
2908
3261
|
const config = readConfig();
|
|
2909
3262
|
if (!action || action === "show") {
|
|
2910
3263
|
console.log();
|
|
2911
|
-
console.log(
|
|
2912
|
-
console.log(
|
|
3264
|
+
console.log(chalk14.bold(` ${t("lang_title")}`));
|
|
3265
|
+
console.log(chalk14.dim(" " + "\u2500".repeat(40)));
|
|
2913
3266
|
console.log();
|
|
2914
|
-
console.log(` ${
|
|
3267
|
+
console.log(` ${chalk14.dim(t("lang_current") + ":")} ${config.language}`);
|
|
2915
3268
|
console.log();
|
|
2916
|
-
console.log(
|
|
2917
|
-
console.log(
|
|
3269
|
+
console.log(chalk14.dim(` ${t("lang_supported")}`));
|
|
3270
|
+
console.log(chalk14.dim(` ${t("lang_usage")}`));
|
|
2918
3271
|
console.log();
|
|
2919
3272
|
return;
|
|
2920
3273
|
}
|
|
2921
3274
|
if (action === "set") {
|
|
2922
3275
|
if (!value) {
|
|
2923
|
-
console.log(
|
|
3276
|
+
console.log(chalk14.red(` ${t("lang_specify")}`));
|
|
2924
3277
|
return;
|
|
2925
3278
|
}
|
|
2926
3279
|
if (value !== "zh_CN" && value !== "en_US") {
|
|
2927
|
-
console.log(
|
|
3280
|
+
console.log(chalk14.red(` ${t("lang_invalid")}`));
|
|
2928
3281
|
return;
|
|
2929
3282
|
}
|
|
2930
3283
|
updateConfig({ language: value });
|
|
2931
3284
|
setLanguage(value);
|
|
2932
3285
|
console.log();
|
|
2933
|
-
console.log(
|
|
3286
|
+
console.log(chalk14.green(` \u2713 ${t("lang_set")} ${value}`));
|
|
2934
3287
|
console.log();
|
|
2935
3288
|
return;
|
|
2936
3289
|
}
|
|
2937
|
-
console.log(
|
|
2938
|
-
console.log(
|
|
3290
|
+
console.log(chalk14.red(` ${t("lang_unknown_action")}: ${action}`));
|
|
3291
|
+
console.log(chalk14.dim(` ${t("lang_supported_actions")}`));
|
|
2939
3292
|
}
|
|
2940
3293
|
|
|
2941
3294
|
// src/cli.ts
|
|
@@ -2945,7 +3298,7 @@ function createCLI() {
|
|
|
2945
3298
|
setLanguage(config.language);
|
|
2946
3299
|
}
|
|
2947
3300
|
const program2 = new Command();
|
|
2948
|
-
program2.name("install-helper").description("Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.
|
|
3301
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.31");
|
|
2949
3302
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
2950
3303
|
await initCommand();
|
|
2951
3304
|
});
|
|
@@ -2958,7 +3311,7 @@ function createCLI() {
|
|
|
2958
3311
|
program2.command("status").description("Show installed plugins").action(async () => {
|
|
2959
3312
|
await statusCommand();
|
|
2960
3313
|
});
|
|
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) => {
|
|
3314
|
+
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
3315
|
await installCommand(names, options);
|
|
2963
3316
|
});
|
|
2964
3317
|
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 +3335,7 @@ function createCLI() {
|
|
|
2982
3335
|
// src/index.ts
|
|
2983
3336
|
process.on("uncaughtException", (error) => {
|
|
2984
3337
|
if (error.name === "ExitPromptError") {
|
|
2985
|
-
console.log("\n
|
|
3338
|
+
console.log("\n" + t("init_cancelled"));
|
|
2986
3339
|
process.exit(0);
|
|
2987
3340
|
}
|
|
2988
3341
|
throw error;
|