@cannbot-ai/install-helper 0.0.2-beta.33 → 0.0.2-beta.35
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/dist/index.js +380 -368
- package/dist/index.js.map +1 -1
- package/dist/locales/en_US.json +14 -9
- package/dist/locales/zh_CN.json +14 -9
- package/dist/plugins.d/ops-direct-invoke.yml +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,18 +12,195 @@ import { select, checkbox, Separator } from "@inquirer/prompts";
|
|
|
12
12
|
import chalk3 from "chalk";
|
|
13
13
|
|
|
14
14
|
// src/core/registry.ts
|
|
15
|
-
import { readdirSync, readFileSync, existsSync
|
|
15
|
+
import { readdirSync, readFileSync as readFileSync2, existsSync } from "fs";
|
|
16
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
17
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18
|
+
import { parse as parseYaml } from "yaml";
|
|
19
|
+
|
|
20
|
+
// src/utils/logger.ts
|
|
21
|
+
import chalk2 from "chalk";
|
|
22
|
+
import ora from "ora";
|
|
23
|
+
|
|
24
|
+
// src/utils/i18n.ts
|
|
25
|
+
import { readFileSync } from "fs";
|
|
16
26
|
import { join, dirname } from "path";
|
|
17
27
|
import { fileURLToPath } from "url";
|
|
18
|
-
import { parse as parseYaml } from "yaml";
|
|
19
28
|
var __filename = fileURLToPath(import.meta.url);
|
|
20
29
|
var __dirname = dirname(__filename);
|
|
30
|
+
function loadLocale(locale) {
|
|
31
|
+
const localePath = join(__dirname, "locales", `${locale}.json`);
|
|
32
|
+
try {
|
|
33
|
+
const content = readFileSync(localePath, "utf-8");
|
|
34
|
+
return JSON.parse(content);
|
|
35
|
+
} catch {
|
|
36
|
+
const fallbackPath = join(__dirname, "locales", "zh_CN.json");
|
|
37
|
+
try {
|
|
38
|
+
const content = readFileSync(fallbackPath, "utf-8");
|
|
39
|
+
return JSON.parse(content);
|
|
40
|
+
} catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var currentLang = "zh_CN";
|
|
46
|
+
var messages = loadLocale("zh_CN");
|
|
47
|
+
function setLanguage(lang) {
|
|
48
|
+
currentLang = lang;
|
|
49
|
+
messages = loadLocale(lang);
|
|
50
|
+
}
|
|
51
|
+
function t(key) {
|
|
52
|
+
return messages[key] !== void 0 ? messages[key] : key;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/ui/theme.ts
|
|
56
|
+
import chalk from "chalk";
|
|
57
|
+
var selectTheme = {
|
|
58
|
+
helpMode: "never"
|
|
59
|
+
};
|
|
60
|
+
function isCJK(code) {
|
|
61
|
+
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;
|
|
62
|
+
}
|
|
63
|
+
function isEmoji(code) {
|
|
64
|
+
return code >= 127744 && code <= 129791 || code >= 9728 && code <= 10175;
|
|
65
|
+
}
|
|
66
|
+
function getDisplayWidth(str) {
|
|
67
|
+
let width = 0;
|
|
68
|
+
for (const ch of str) {
|
|
69
|
+
const code = ch.codePointAt(0);
|
|
70
|
+
if (isCJK(code) || isEmoji(code)) {
|
|
71
|
+
width += 2;
|
|
72
|
+
} else {
|
|
73
|
+
width += 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return width;
|
|
77
|
+
}
|
|
78
|
+
function wrapDescription(text) {
|
|
79
|
+
const prefix = " \u{1F4A1} ";
|
|
80
|
+
const indent = " ";
|
|
81
|
+
const cols = process.stdout.columns || 120;
|
|
82
|
+
const maxLen = Math.max(20, cols - getDisplayWidth(prefix) - 2);
|
|
83
|
+
const tokens = [];
|
|
84
|
+
let i = 0;
|
|
85
|
+
while (i < text.length) {
|
|
86
|
+
const code = text[i].codePointAt(0);
|
|
87
|
+
if (isCJK(code)) {
|
|
88
|
+
tokens.push({ text: text[i], width: 2 });
|
|
89
|
+
i++;
|
|
90
|
+
} else {
|
|
91
|
+
let j = i;
|
|
92
|
+
while (j < text.length) {
|
|
93
|
+
const c = text[j].codePointAt(0);
|
|
94
|
+
if (isCJK(c)) break;
|
|
95
|
+
j++;
|
|
96
|
+
}
|
|
97
|
+
const word = text.slice(i, j);
|
|
98
|
+
tokens.push({ text: word, width: getDisplayWidth(word) });
|
|
99
|
+
i = j;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const lines = [];
|
|
103
|
+
let line = "";
|
|
104
|
+
let lineWidth = 0;
|
|
105
|
+
for (const token of tokens) {
|
|
106
|
+
if (lineWidth + token.width > maxLen && line) {
|
|
107
|
+
lines.push(line.trimEnd());
|
|
108
|
+
line = token.text;
|
|
109
|
+
lineWidth = token.width;
|
|
110
|
+
} else {
|
|
111
|
+
line += token.text;
|
|
112
|
+
lineWidth += token.width;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (line) lines.push(line.trimEnd());
|
|
116
|
+
return lines.map((l, i2) => chalk.cyan(i2 === 0 ? prefix + l : indent + l)).join("\n");
|
|
117
|
+
}
|
|
118
|
+
var checkboxTheme = {
|
|
119
|
+
icon: {
|
|
120
|
+
checked: "\x1B[32m\u2611\x1B[22m",
|
|
121
|
+
unchecked: "\u2610",
|
|
122
|
+
cursor: "\u276F"
|
|
123
|
+
},
|
|
124
|
+
helpMode: "never",
|
|
125
|
+
style: {
|
|
126
|
+
description: (text) => wrapDescription(text)
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// src/utils/logger.ts
|
|
131
|
+
var logger = {
|
|
132
|
+
success: (msg) => console.log(chalk2.green("\u2713") + " " + msg),
|
|
133
|
+
error: (msg) => console.log(chalk2.red("\u2717") + " " + msg),
|
|
134
|
+
warn: (msg) => console.log(chalk2.yellow("\u26A0") + " " + msg),
|
|
135
|
+
info: (msg) => console.log(chalk2.cyan("\u2192") + " " + msg),
|
|
136
|
+
step: (msg) => console.log(chalk2.dim(msg)),
|
|
137
|
+
blank: () => console.log()
|
|
138
|
+
};
|
|
139
|
+
function createSpinner(text) {
|
|
140
|
+
return ora({ text, color: "cyan" });
|
|
141
|
+
}
|
|
142
|
+
function printBanner(subtitle) {
|
|
143
|
+
console.log(chalk2.cyan(`
|
|
144
|
+
____ _ _ _ _ _ ____ _
|
|
145
|
+
/ ___| / \\ | \\ | | \\ | | __ ) ___ | |_
|
|
146
|
+
| | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
|
|
147
|
+
| |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
|
|
148
|
+
\\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
|
|
149
|
+
`));
|
|
150
|
+
if (subtitle) {
|
|
151
|
+
console.log(chalk2.bold(` ${subtitle}`));
|
|
152
|
+
console.log();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function printBoxTitle(title, width = 65) {
|
|
156
|
+
const border = "\u2550".repeat(width);
|
|
157
|
+
const displayWidth = getDisplayWidth(title);
|
|
158
|
+
const totalPadding = width - displayWidth;
|
|
159
|
+
const leftPadding = Math.floor(totalPadding / 2);
|
|
160
|
+
const rightPadding = totalPadding - leftPadding;
|
|
161
|
+
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
162
|
+
console.log();
|
|
163
|
+
console.log(chalk2.cyan.bold(` \u2554${border}\u2557`));
|
|
164
|
+
console.log(chalk2.cyan.bold(` \u2551`) + chalk2.cyan.bold(paddedTitle) + chalk2.cyan.bold(`\u2551`));
|
|
165
|
+
console.log(chalk2.cyan.bold(` \u255A${border}\u255D`));
|
|
166
|
+
console.log();
|
|
167
|
+
}
|
|
168
|
+
function showOperationHints(isCheckbox = false) {
|
|
169
|
+
if (isCheckbox) {
|
|
170
|
+
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
|
|
171
|
+
`);
|
|
172
|
+
} else {
|
|
173
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/utils/fs-helpers.ts
|
|
179
|
+
import { lstatSync, statSync } from "fs";
|
|
180
|
+
function isSymlink(path) {
|
|
181
|
+
try {
|
|
182
|
+
return lstatSync(path).isSymbolicLink();
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function isDirectory(path) {
|
|
188
|
+
try {
|
|
189
|
+
return statSync(path).isDirectory();
|
|
190
|
+
} catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/core/registry.ts
|
|
196
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
197
|
+
var __dirname2 = dirname2(__filename2);
|
|
21
198
|
function loadPluginsFromYaml() {
|
|
22
|
-
const pluginsDir =
|
|
23
|
-
const defaultsPath =
|
|
199
|
+
const pluginsDir = join2(__dirname2, "plugins.d");
|
|
200
|
+
const defaultsPath = join2(pluginsDir, "_defaults.yml");
|
|
24
201
|
let defaults = {};
|
|
25
202
|
try {
|
|
26
|
-
defaults = parseYaml(
|
|
203
|
+
defaults = parseYaml(readFileSync2(defaultsPath, "utf-8")) || {};
|
|
27
204
|
} catch {
|
|
28
205
|
}
|
|
29
206
|
const plugins = [];
|
|
@@ -31,7 +208,7 @@ function loadPluginsFromYaml() {
|
|
|
31
208
|
for (const file of readdirSync(pluginsDir)) {
|
|
32
209
|
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
33
210
|
try {
|
|
34
|
-
const content = parseYaml(
|
|
211
|
+
const content = parseYaml(readFileSync2(join2(pluginsDir, file), "utf-8"));
|
|
35
212
|
if (!content || !content.id) continue;
|
|
36
213
|
plugins.push({
|
|
37
214
|
id: content.id,
|
|
@@ -44,6 +221,7 @@ function loadPluginsFromYaml() {
|
|
|
44
221
|
description: content.description || ""
|
|
45
222
|
});
|
|
46
223
|
} catch {
|
|
224
|
+
logger.warn(t("plugin_yaml_error").replace("{file}", file));
|
|
47
225
|
}
|
|
48
226
|
}
|
|
49
227
|
} catch {
|
|
@@ -60,7 +238,7 @@ function mergeDynamicPlugins(repoPath) {
|
|
|
60
238
|
const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
|
|
61
239
|
const dynamic = [];
|
|
62
240
|
for (const pluginDir of PLUGIN_DIRS) {
|
|
63
|
-
const parent =
|
|
241
|
+
const parent = join2(repoPath, pluginDir);
|
|
64
242
|
if (!existsSync(parent)) continue;
|
|
65
243
|
let entries = [];
|
|
66
244
|
try {
|
|
@@ -69,10 +247,10 @@ function mergeDynamicPlugins(repoPath) {
|
|
|
69
247
|
continue;
|
|
70
248
|
}
|
|
71
249
|
for (const entry of entries) {
|
|
72
|
-
const pluginPath =
|
|
250
|
+
const pluginPath = join2(parent, entry);
|
|
73
251
|
if (!isDirectory(pluginPath)) continue;
|
|
74
252
|
if (entry === "install-helper") continue;
|
|
75
|
-
const initSh =
|
|
253
|
+
const initSh = join2(pluginPath, "init.sh");
|
|
76
254
|
if (!existsSync(initSh)) continue;
|
|
77
255
|
const pluginId = entry;
|
|
78
256
|
if (existingIds.has(pluginId)) continue;
|
|
@@ -95,13 +273,6 @@ function mergeDynamicPlugins(repoPath) {
|
|
|
95
273
|
);
|
|
96
274
|
}
|
|
97
275
|
}
|
|
98
|
-
function isDirectory(path) {
|
|
99
|
-
try {
|
|
100
|
-
return statSync(path).isDirectory();
|
|
101
|
-
} catch {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
276
|
function findPlugin(query) {
|
|
106
277
|
const normalized = query.toLowerCase().trim();
|
|
107
278
|
const exactMatch = PLUGIN_REGISTRY.find(
|
|
@@ -133,39 +304,6 @@ import { join as join4 } from "path";
|
|
|
133
304
|
import { homedir } from "os";
|
|
134
305
|
import { join as join3 } from "path";
|
|
135
306
|
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
307
|
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
170
308
|
var VALID_LEVELS = ["project", "global"];
|
|
171
309
|
function validateTool(tool) {
|
|
@@ -366,7 +504,7 @@ function getToolDisplayName(tool) {
|
|
|
366
504
|
return names[tool];
|
|
367
505
|
}
|
|
368
506
|
function getAllTools() {
|
|
369
|
-
return
|
|
507
|
+
return VALID_TOOLS;
|
|
370
508
|
}
|
|
371
509
|
|
|
372
510
|
// src/core/manifest.ts
|
|
@@ -409,7 +547,7 @@ function readAllManifests(configRoot) {
|
|
|
409
547
|
function scanInstalled() {
|
|
410
548
|
const plugins = getAllPlugins();
|
|
411
549
|
const installed = [];
|
|
412
|
-
const tools =
|
|
550
|
+
const tools = VALID_TOOLS;
|
|
413
551
|
const levels = ["project", "global"];
|
|
414
552
|
for (const tool of tools) {
|
|
415
553
|
for (const level of levels) {
|
|
@@ -467,82 +605,40 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
|
467
605
|
return true;
|
|
468
606
|
}
|
|
469
607
|
|
|
470
|
-
// src/utils/
|
|
471
|
-
import
|
|
472
|
-
import
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
| |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
|
|
490
|
-
\\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
|
|
491
|
-
`));
|
|
492
|
-
if (subtitle) {
|
|
493
|
-
console.log(chalk.bold(` ${subtitle}`));
|
|
494
|
-
console.log();
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
function getDisplayWidth(str) {
|
|
498
|
-
let width = 0;
|
|
499
|
-
for (const ch of str) {
|
|
500
|
-
const code = ch.codePointAt(0);
|
|
501
|
-
if (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) {
|
|
502
|
-
width += 2;
|
|
503
|
-
} else {
|
|
504
|
-
width += 1;
|
|
608
|
+
// src/utils/config.ts
|
|
609
|
+
import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
610
|
+
import { join as join6 } from "path";
|
|
611
|
+
import { parse, stringify } from "yaml";
|
|
612
|
+
|
|
613
|
+
// src/utils/fs.ts
|
|
614
|
+
import { writeFileSync, renameSync, existsSync as existsSync5, unlinkSync } from "fs";
|
|
615
|
+
import { dirname as dirname3 } from "path";
|
|
616
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
617
|
+
const dir = dirname3(filePath);
|
|
618
|
+
if (!existsSync5(dir)) {
|
|
619
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
620
|
+
}
|
|
621
|
+
const tmpPath = `${filePath}.tmp`;
|
|
622
|
+
writeFileSync(tmpPath, data, encoding);
|
|
623
|
+
if (existsSync5(filePath)) {
|
|
624
|
+
try {
|
|
625
|
+
unlinkSync(filePath);
|
|
626
|
+
} catch {
|
|
505
627
|
}
|
|
506
628
|
}
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
function printBoxTitle(title, width = 65) {
|
|
510
|
-
const border = "\u2550".repeat(width);
|
|
511
|
-
const displayWidth = getDisplayWidth(title);
|
|
512
|
-
const totalPadding = width - displayWidth;
|
|
513
|
-
const leftPadding = Math.floor(totalPadding / 2);
|
|
514
|
-
const rightPadding = totalPadding - leftPadding;
|
|
515
|
-
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
516
|
-
console.log();
|
|
517
|
-
console.log(chalk.cyan.bold(` \u2554${border}\u2557`));
|
|
518
|
-
console.log(chalk.cyan.bold(` \u2551`) + chalk.cyan.bold(paddedTitle) + chalk.cyan.bold(`\u2551`));
|
|
519
|
-
console.log(chalk.cyan.bold(` \u255A${border}\u255D`));
|
|
520
|
-
console.log();
|
|
521
|
-
}
|
|
522
|
-
function showOperationHints(isCheckbox = false) {
|
|
523
|
-
if (isCheckbox) {
|
|
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
|
|
525
|
-
`);
|
|
526
|
-
} else {
|
|
527
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
528
|
-
`);
|
|
529
|
-
}
|
|
629
|
+
renameSync(tmpPath, filePath);
|
|
530
630
|
}
|
|
531
631
|
|
|
532
632
|
// src/utils/config.ts
|
|
533
|
-
import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
534
|
-
import { join as join6 } from "path";
|
|
535
|
-
import { homedir as homedir3 } from "os";
|
|
536
|
-
import { parse, stringify } from "yaml";
|
|
537
633
|
function getConfigDir() {
|
|
538
|
-
return
|
|
634
|
+
return getCannbotConfigDir();
|
|
539
635
|
}
|
|
540
636
|
function getConfigPath() {
|
|
541
637
|
return join6(getConfigDir(), "config.yaml");
|
|
542
638
|
}
|
|
543
639
|
function readConfig() {
|
|
544
640
|
const configPath = getConfigPath();
|
|
545
|
-
if (!
|
|
641
|
+
if (!existsSync6(configPath)) {
|
|
546
642
|
return {
|
|
547
643
|
language: "zh_CN",
|
|
548
644
|
installedPlugins: []
|
|
@@ -559,6 +655,7 @@ function readConfig() {
|
|
|
559
655
|
installedPlugins: parsed.installedPlugins || []
|
|
560
656
|
};
|
|
561
657
|
} catch {
|
|
658
|
+
logger.warn(t("config_corrupted"));
|
|
562
659
|
return {
|
|
563
660
|
language: "zh_CN",
|
|
564
661
|
installedPlugins: []
|
|
@@ -567,12 +664,12 @@ function readConfig() {
|
|
|
567
664
|
}
|
|
568
665
|
function writeConfig(config) {
|
|
569
666
|
const configDir = getConfigDir();
|
|
570
|
-
if (!
|
|
667
|
+
if (!existsSync6(configDir)) {
|
|
571
668
|
mkdirSync(configDir, { recursive: true });
|
|
572
669
|
}
|
|
573
670
|
const configPath = getConfigPath();
|
|
574
671
|
const content = stringify(config);
|
|
575
|
-
|
|
672
|
+
atomicWriteFileSync(configPath, content);
|
|
576
673
|
}
|
|
577
674
|
function updateConfig(updates) {
|
|
578
675
|
const config = readConfig();
|
|
@@ -595,84 +692,11 @@ function removeInstalledPlugin(pluginId) {
|
|
|
595
692
|
writeConfig(config);
|
|
596
693
|
}
|
|
597
694
|
|
|
598
|
-
// src/
|
|
599
|
-
import chalk2 from "chalk";
|
|
600
|
-
var selectTheme = {
|
|
601
|
-
helpMode: "never"
|
|
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
|
-
}
|
|
661
|
-
var checkboxTheme = {
|
|
662
|
-
icon: {
|
|
663
|
-
checked: "\x1B[32m\u2611\x1B[22m",
|
|
664
|
-
unchecked: "\u2610",
|
|
665
|
-
cursor: "\u276F"
|
|
666
|
-
},
|
|
667
|
-
helpMode: "never",
|
|
668
|
-
style: {
|
|
669
|
-
description: (text) => wrapDescription(text)
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
// src/ui/wizard.ts
|
|
695
|
+
// src/utils/constants.ts
|
|
674
696
|
var BACK = "__back__";
|
|
675
697
|
var CANCEL = "__cancel__";
|
|
698
|
+
|
|
699
|
+
// src/ui/wizard.ts
|
|
676
700
|
async function selectToolWithDetection() {
|
|
677
701
|
const config = readConfig();
|
|
678
702
|
logger.blank();
|
|
@@ -756,7 +780,7 @@ async function stepTool(detectedTools, lastTool) {
|
|
|
756
780
|
if (detectedTools.length === 0) {
|
|
757
781
|
logger.error(t("error_tool_not_found"));
|
|
758
782
|
const choices2 = [
|
|
759
|
-
...
|
|
783
|
+
...VALID_TOOLS.map((tool) => ({
|
|
760
784
|
name: `> ${getToolDisplayName(tool)}`,
|
|
761
785
|
value: tool
|
|
762
786
|
})),
|
|
@@ -918,19 +942,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
918
942
|
}
|
|
919
943
|
|
|
920
944
|
// src/core/repository.ts
|
|
921
|
-
import { existsSync as
|
|
922
|
-
import { join as join8, dirname as
|
|
945
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
946
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
923
947
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
924
948
|
import { execa as execa2 } from "execa";
|
|
925
949
|
import { parse as parseYaml3 } from "yaml";
|
|
926
950
|
|
|
927
951
|
// src/core/scanner.ts
|
|
928
|
-
import { existsSync as
|
|
929
|
-
import { join as join7, dirname as
|
|
952
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
953
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
930
954
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
931
955
|
import { parse as parseYaml2 } from "yaml";
|
|
932
956
|
var __filename3 = fileURLToPath3(import.meta.url);
|
|
933
|
-
var __dirname3 =
|
|
957
|
+
var __dirname3 = dirname4(__filename3);
|
|
934
958
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
935
959
|
"node_modules",
|
|
936
960
|
".git",
|
|
@@ -974,26 +998,26 @@ function scanSkills(repoPath) {
|
|
|
974
998
|
const seen = /* @__PURE__ */ new Set();
|
|
975
999
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
976
1000
|
const fullPath = join7(repoPath, dir);
|
|
977
|
-
if (!
|
|
1001
|
+
if (!existsSync7(fullPath)) continue;
|
|
978
1002
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
979
1003
|
}
|
|
980
1004
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
981
1005
|
const pluginParent = join7(repoPath, pluginDir);
|
|
982
|
-
if (!
|
|
1006
|
+
if (!existsSync7(pluginParent)) continue;
|
|
983
1007
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
984
1008
|
const pluginPath = join7(pluginParent, plugin);
|
|
985
|
-
if (!
|
|
1009
|
+
if (!isDirectory(pluginPath)) continue;
|
|
986
1010
|
const skillsDir = join7(pluginPath, "skills");
|
|
987
|
-
if (
|
|
1011
|
+
if (existsSync7(skillsDir)) {
|
|
988
1012
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
989
1013
|
}
|
|
990
1014
|
const skillDir = join7(pluginPath, "skill");
|
|
991
|
-
if (
|
|
1015
|
+
if (existsSync7(skillDir)) {
|
|
992
1016
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
993
1017
|
}
|
|
994
1018
|
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
995
1019
|
const initSh = join7(pluginPath, "init.sh");
|
|
996
|
-
if (
|
|
1020
|
+
if (existsSync7(rootSkillMd) && !existsSync7(initSh)) {
|
|
997
1021
|
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
998
1022
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
999
1023
|
seen.add(frontmatter.name);
|
|
@@ -1042,15 +1066,15 @@ function parseFrontmatter(filePath) {
|
|
|
1042
1066
|
function getCurrentCommit(repoPath) {
|
|
1043
1067
|
try {
|
|
1044
1068
|
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1045
|
-
if (!
|
|
1069
|
+
if (!existsSync7(headFile)) return "unknown";
|
|
1046
1070
|
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1047
1071
|
if (headContent.startsWith("ref: ")) {
|
|
1048
1072
|
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1049
|
-
if (
|
|
1073
|
+
if (existsSync7(refPath)) {
|
|
1050
1074
|
return readFileSync5(refPath, "utf-8").trim();
|
|
1051
1075
|
}
|
|
1052
1076
|
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1053
|
-
if (
|
|
1077
|
+
if (existsSync7(packedRefsPath)) {
|
|
1054
1078
|
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1055
1079
|
const refName = headContent.slice(5);
|
|
1056
1080
|
const lines = packedRefs.split("\n");
|
|
@@ -1069,7 +1093,7 @@ function getCurrentCommit(repoPath) {
|
|
|
1069
1093
|
function readScanCache() {
|
|
1070
1094
|
try {
|
|
1071
1095
|
const cachePath = getCachePath();
|
|
1072
|
-
if (!
|
|
1096
|
+
if (!existsSync7(cachePath)) return null;
|
|
1073
1097
|
const content = readFileSync5(cachePath, "utf-8");
|
|
1074
1098
|
const cache = JSON.parse(content);
|
|
1075
1099
|
if (!cache.skills || !cache.timestamp) return null;
|
|
@@ -1083,11 +1107,11 @@ function readScanCache() {
|
|
|
1083
1107
|
function writeScanCache(cache) {
|
|
1084
1108
|
try {
|
|
1085
1109
|
const cachePath = getCachePath();
|
|
1086
|
-
const cacheDir =
|
|
1087
|
-
if (!
|
|
1110
|
+
const cacheDir = dirname4(cachePath);
|
|
1111
|
+
if (!existsSync7(cacheDir)) {
|
|
1088
1112
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1089
1113
|
}
|
|
1090
|
-
|
|
1114
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1091
1115
|
} catch {
|
|
1092
1116
|
}
|
|
1093
1117
|
}
|
|
@@ -1098,9 +1122,9 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1098
1122
|
for (const entry of safeReaddir(dirPath)) {
|
|
1099
1123
|
const fullPath = join7(dirPath, entry);
|
|
1100
1124
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1101
|
-
if (!
|
|
1125
|
+
if (!isDirectory(fullPath)) continue;
|
|
1102
1126
|
const skillMd = join7(fullPath, "SKILL.md");
|
|
1103
|
-
if (
|
|
1127
|
+
if (existsSync7(skillMd)) {
|
|
1104
1128
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1105
1129
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1106
1130
|
seen.add(frontmatter.name);
|
|
@@ -1123,29 +1147,22 @@ function safeReaddir(dir) {
|
|
|
1123
1147
|
return [];
|
|
1124
1148
|
}
|
|
1125
1149
|
}
|
|
1126
|
-
function isDirectory2(path) {
|
|
1127
|
-
try {
|
|
1128
|
-
return statSync2(path).isDirectory();
|
|
1129
|
-
} catch {
|
|
1130
|
-
return false;
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
1150
|
|
|
1134
1151
|
// src/core/skill-registry.ts
|
|
1135
1152
|
var CATEGORY_DEFS = [
|
|
1136
|
-
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003" },
|
|
1137
|
-
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177" },
|
|
1138
|
-
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD" },
|
|
1139
|
-
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF" },
|
|
1140
|
-
{ id: "ascendc", name: "AscendC \u5F00\u53D1" },
|
|
1141
|
-
{ id: "pypto", name: "PyPTO \u5F00\u53D1" },
|
|
1142
|
-
{ id: "tilelang", name: "TileLang \u5F00\u53D1" },
|
|
1143
|
-
{ id: "triton", name: "Triton \u5F00\u53D1" },
|
|
1144
|
-
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
|
|
1145
|
-
{ id: "runtime", name: "Runtime \u8FC1\u79FB" },
|
|
1146
|
-
{ id: "graph", name: "\u56FE\u6A21\u5F0F" },
|
|
1147
|
-
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177" },
|
|
1148
|
-
{ id: "other", name: "\u5176\u4ED6 Skills" }
|
|
1153
|
+
{ id: "knowledge", name: "\u77E5\u8BC6\u4E0E\u53C2\u8003", nameEn: "Knowledge & Reference" },
|
|
1154
|
+
{ id: "env-tools", name: "\u73AF\u5883\u4E0E\u5DE5\u5177", nameEn: "Environment & Tools" },
|
|
1155
|
+
{ id: "debug", name: "\u8C03\u8BD5\u4E0E\u8BCA\u65AD", nameEn: "Debug & Diagnostics" },
|
|
1156
|
+
{ id: "testing", name: "\u6D4B\u8BD5\u4E0E\u8D28\u91CF", nameEn: "Testing & Quality" },
|
|
1157
|
+
{ id: "ascendc", name: "AscendC \u5F00\u53D1", nameEn: "AscendC Development" },
|
|
1158
|
+
{ id: "pypto", name: "PyPTO \u5F00\u53D1", nameEn: "PyPTO Development" },
|
|
1159
|
+
{ id: "tilelang", name: "TileLang \u5F00\u53D1", nameEn: "TileLang Development" },
|
|
1160
|
+
{ id: "triton", name: "Triton \u5F00\u53D1", nameEn: "Triton Development" },
|
|
1161
|
+
{ id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316", nameEn: "Model Inference Optimization" },
|
|
1162
|
+
{ id: "runtime", name: "Runtime \u8FC1\u79FB", nameEn: "Runtime Migration" },
|
|
1163
|
+
{ id: "graph", name: "\u56FE\u6A21\u5F0F", nameEn: "Graph Mode" },
|
|
1164
|
+
{ id: "platform", name: "\u5E73\u53F0\u5DE5\u5177", nameEn: "Platform Tools" },
|
|
1165
|
+
{ id: "other", name: "\u5176\u4ED6 Skills", nameEn: "Other Skills" }
|
|
1149
1166
|
];
|
|
1150
1167
|
var dynamicSkills = [];
|
|
1151
1168
|
var initialized = false;
|
|
@@ -1355,9 +1372,10 @@ function getAllSkills() {
|
|
|
1355
1372
|
}
|
|
1356
1373
|
function getAllCategories() {
|
|
1357
1374
|
const skills = getActiveSkills();
|
|
1375
|
+
const isEn = t("list_name") === "Name";
|
|
1358
1376
|
return CATEGORY_DEFS.map((def) => ({
|
|
1359
1377
|
id: def.id,
|
|
1360
|
-
name: def.name,
|
|
1378
|
+
name: isEn ? def.nameEn : def.name,
|
|
1361
1379
|
skills: skills.filter((s) => {
|
|
1362
1380
|
const catId = CATEGORY_MAP[s.id];
|
|
1363
1381
|
if (catId) return catId === def.id;
|
|
@@ -1368,7 +1386,7 @@ function getAllCategories() {
|
|
|
1368
1386
|
|
|
1369
1387
|
// src/core/repository.ts
|
|
1370
1388
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1371
|
-
var __dirname4 =
|
|
1389
|
+
var __dirname4 = dirname5(__filename4);
|
|
1372
1390
|
function loadRepoUrl() {
|
|
1373
1391
|
if (process.env.CANNBOT_REPO_URL) {
|
|
1374
1392
|
return process.env.CANNBOT_REPO_URL;
|
|
@@ -1432,6 +1450,7 @@ var RepositoryManager = class {
|
|
|
1432
1450
|
try {
|
|
1433
1451
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1434
1452
|
} catch {
|
|
1453
|
+
logger.warn(t("repo_pull_failed"));
|
|
1435
1454
|
}
|
|
1436
1455
|
mergeDynamicPlugins(repoPath);
|
|
1437
1456
|
}
|
|
@@ -1442,18 +1461,18 @@ var RepositoryManager = class {
|
|
|
1442
1461
|
return this.repoPath;
|
|
1443
1462
|
}
|
|
1444
1463
|
isValidRepo(path) {
|
|
1445
|
-
if (!
|
|
1464
|
+
if (!existsSync8(path)) return false;
|
|
1446
1465
|
const gitDir = join8(path, ".git");
|
|
1447
1466
|
const pluginsDir = join8(path, "plugins-official");
|
|
1448
|
-
return
|
|
1467
|
+
return existsSync8(gitDir) && existsSync8(pluginsDir);
|
|
1449
1468
|
}
|
|
1450
1469
|
async cloneRepo() {
|
|
1451
1470
|
const configDir = getCannbotConfigDir();
|
|
1452
|
-
if (!
|
|
1471
|
+
if (!existsSync8(configDir)) {
|
|
1453
1472
|
mkdirSync3(configDir, { recursive: true });
|
|
1454
1473
|
}
|
|
1455
1474
|
const targetPath = getCannbotRepoPath();
|
|
1456
|
-
if (
|
|
1475
|
+
if (existsSync8(targetPath)) {
|
|
1457
1476
|
if (this.isValidRepo(targetPath)) {
|
|
1458
1477
|
this.repoPath = targetPath;
|
|
1459
1478
|
return targetPath;
|
|
@@ -1479,12 +1498,12 @@ function createRepositoryManager(customPath) {
|
|
|
1479
1498
|
}
|
|
1480
1499
|
|
|
1481
1500
|
// src/core/installer.ts
|
|
1482
|
-
import { existsSync as
|
|
1501
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1483
1502
|
import { join as join11 } from "path";
|
|
1484
1503
|
import { execa as execa3 } from "execa";
|
|
1485
1504
|
|
|
1486
1505
|
// src/core/record.ts
|
|
1487
|
-
import { existsSync as
|
|
1506
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
1488
1507
|
import { join as join9 } from "path";
|
|
1489
1508
|
function getInstallsDir() {
|
|
1490
1509
|
return join9(getCannbotConfigDir(), "installs");
|
|
@@ -1494,28 +1513,29 @@ function getRecordPath(pluginId) {
|
|
|
1494
1513
|
}
|
|
1495
1514
|
function readRecord(pluginId) {
|
|
1496
1515
|
const recordPath = getRecordPath(pluginId);
|
|
1497
|
-
if (!
|
|
1516
|
+
if (!existsSync9(recordPath)) {
|
|
1498
1517
|
return null;
|
|
1499
1518
|
}
|
|
1500
1519
|
try {
|
|
1501
1520
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1502
1521
|
return JSON.parse(content);
|
|
1503
1522
|
} catch {
|
|
1523
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1504
1524
|
return null;
|
|
1505
1525
|
}
|
|
1506
1526
|
}
|
|
1507
1527
|
function writeRecord(record) {
|
|
1508
1528
|
const installsDir = getInstallsDir();
|
|
1509
|
-
if (!
|
|
1529
|
+
if (!existsSync9(installsDir)) {
|
|
1510
1530
|
mkdirSync4(installsDir, { recursive: true });
|
|
1511
1531
|
}
|
|
1512
1532
|
const recordPath = getRecordPath(record.pluginId);
|
|
1513
|
-
|
|
1533
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1514
1534
|
}
|
|
1515
1535
|
function deleteRecord(pluginId) {
|
|
1516
1536
|
const recordPath = getRecordPath(pluginId);
|
|
1517
|
-
if (
|
|
1518
|
-
|
|
1537
|
+
if (existsSync9(recordPath)) {
|
|
1538
|
+
unlinkSync2(recordPath);
|
|
1519
1539
|
}
|
|
1520
1540
|
}
|
|
1521
1541
|
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
@@ -1523,24 +1543,24 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1523
1543
|
const directories = [];
|
|
1524
1544
|
if (manifest) {
|
|
1525
1545
|
const skillsDir = join9(configRoot, "skills");
|
|
1526
|
-
if (
|
|
1546
|
+
if (existsSync9(skillsDir)) {
|
|
1527
1547
|
directories.push(skillsDir);
|
|
1528
1548
|
for (const skillName of manifest.installed_skills || []) {
|
|
1529
1549
|
const skillPath = join9(skillsDir, skillName);
|
|
1530
|
-
if (
|
|
1550
|
+
if (existsSync9(skillPath) || isSymlink(skillPath)) {
|
|
1531
1551
|
files.push(skillPath);
|
|
1532
1552
|
}
|
|
1533
1553
|
}
|
|
1534
1554
|
}
|
|
1535
1555
|
const agentsDir = join9(configRoot, "agents");
|
|
1536
|
-
if (
|
|
1556
|
+
if (existsSync9(agentsDir)) {
|
|
1537
1557
|
directories.push(agentsDir);
|
|
1538
1558
|
for (const agentName of manifest.installed_agents || []) {
|
|
1539
1559
|
const agentPath = join9(agentsDir, agentName);
|
|
1540
1560
|
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
1541
|
-
if (
|
|
1561
|
+
if (existsSync9(agentPath) || isSymlink(agentPath)) {
|
|
1542
1562
|
files.push(agentPath);
|
|
1543
|
-
} else if (
|
|
1563
|
+
} else if (existsSync9(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1544
1564
|
files.push(agentPathMd);
|
|
1545
1565
|
}
|
|
1546
1566
|
}
|
|
@@ -1551,16 +1571,16 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1551
1571
|
files.push(workflowsLink);
|
|
1552
1572
|
}
|
|
1553
1573
|
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
1554
|
-
if (
|
|
1574
|
+
if (existsSync9(manifestPath)) {
|
|
1555
1575
|
files.push(manifestPath);
|
|
1556
1576
|
}
|
|
1557
1577
|
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
1558
|
-
if (
|
|
1578
|
+
if (existsSync9(pluginManifestPath)) {
|
|
1559
1579
|
files.push(pluginManifestPath);
|
|
1560
1580
|
}
|
|
1561
1581
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1562
1582
|
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
1563
|
-
if (
|
|
1583
|
+
if (existsSync9(configFilePath)) {
|
|
1564
1584
|
files.push(configFilePath);
|
|
1565
1585
|
}
|
|
1566
1586
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
@@ -1586,35 +1606,29 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1586
1606
|
directories
|
|
1587
1607
|
};
|
|
1588
1608
|
}
|
|
1589
|
-
function isSymlink(path) {
|
|
1590
|
-
try {
|
|
1591
|
-
return lstatSync(path).isSymbolicLink();
|
|
1592
|
-
} catch {
|
|
1593
|
-
return false;
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
1609
|
function getSkillRecordPath() {
|
|
1597
1610
|
return join9(getInstallsDir(), "skills.json");
|
|
1598
1611
|
}
|
|
1599
1612
|
function readSkillRecord() {
|
|
1600
1613
|
const recordPath = getSkillRecordPath();
|
|
1601
|
-
if (!
|
|
1614
|
+
if (!existsSync9(recordPath)) {
|
|
1602
1615
|
return {};
|
|
1603
1616
|
}
|
|
1604
1617
|
try {
|
|
1605
1618
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1606
1619
|
return JSON.parse(content);
|
|
1607
1620
|
} catch {
|
|
1621
|
+
logger.warn(t("skill_record_corrupted"));
|
|
1608
1622
|
return {};
|
|
1609
1623
|
}
|
|
1610
1624
|
}
|
|
1611
1625
|
function writeSkillRecord(record) {
|
|
1612
1626
|
const installsDir = getInstallsDir();
|
|
1613
|
-
if (!
|
|
1627
|
+
if (!existsSync9(installsDir)) {
|
|
1614
1628
|
mkdirSync4(installsDir, { recursive: true });
|
|
1615
1629
|
}
|
|
1616
1630
|
const recordPath = getSkillRecordPath();
|
|
1617
|
-
|
|
1631
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1618
1632
|
}
|
|
1619
1633
|
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
1620
1634
|
const record = readSkillRecord();
|
|
@@ -1655,16 +1669,13 @@ function getInstalledSkills(tool, level, installPath) {
|
|
|
1655
1669
|
const skillsDir = join9(configRoot, "skills");
|
|
1656
1670
|
return recordedSkills.filter((skillId) => {
|
|
1657
1671
|
const skillPath = join9(skillsDir, skillId);
|
|
1658
|
-
return
|
|
1672
|
+
return existsSync9(skillPath) || isSymlink(skillPath);
|
|
1659
1673
|
});
|
|
1660
1674
|
}
|
|
1661
1675
|
|
|
1662
1676
|
// src/core/backup.ts
|
|
1663
|
-
import { existsSync as
|
|
1677
|
+
import { existsSync as existsSync10, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
1664
1678
|
import { join as join10 } from "path";
|
|
1665
|
-
function getAgentsFileName(tool) {
|
|
1666
|
-
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1667
|
-
}
|
|
1668
1679
|
function detectCurrentPlugin(configRoot, tool) {
|
|
1669
1680
|
const manifest = readManifest(configRoot);
|
|
1670
1681
|
if (manifest?.team) {
|
|
@@ -1677,7 +1688,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1677
1688
|
}
|
|
1678
1689
|
}
|
|
1679
1690
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1680
|
-
if (!
|
|
1691
|
+
if (!existsSync10(agentsFile)) {
|
|
1681
1692
|
return null;
|
|
1682
1693
|
}
|
|
1683
1694
|
const allRecords = readAllRecords();
|
|
@@ -1696,7 +1707,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1696
1707
|
}
|
|
1697
1708
|
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1698
1709
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1699
|
-
if (!
|
|
1710
|
+
if (!existsSync10(agentsFile)) {
|
|
1700
1711
|
return null;
|
|
1701
1712
|
}
|
|
1702
1713
|
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
@@ -1715,7 +1726,7 @@ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
|
1715
1726
|
}
|
|
1716
1727
|
}
|
|
1717
1728
|
function findBackups(configRoot) {
|
|
1718
|
-
if (!
|
|
1729
|
+
if (!existsSync10(configRoot)) {
|
|
1719
1730
|
return [];
|
|
1720
1731
|
}
|
|
1721
1732
|
const files = readdirSync3(configRoot);
|
|
@@ -1753,8 +1764,8 @@ function restoreBackup(backupPath, configRoot, tool) {
|
|
|
1753
1764
|
}
|
|
1754
1765
|
function deleteBackup(backupPath) {
|
|
1755
1766
|
try {
|
|
1756
|
-
if (
|
|
1757
|
-
|
|
1767
|
+
if (existsSync10(backupPath)) {
|
|
1768
|
+
unlinkSync3(backupPath);
|
|
1758
1769
|
return true;
|
|
1759
1770
|
}
|
|
1760
1771
|
return false;
|
|
@@ -1773,20 +1784,9 @@ function formatTimestamp(date) {
|
|
|
1773
1784
|
}
|
|
1774
1785
|
function readAllRecords() {
|
|
1775
1786
|
const records = [];
|
|
1776
|
-
const plugins =
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
"ops-registry-invoke",
|
|
1780
|
-
"pypto-op-orchestrator",
|
|
1781
|
-
"triton-op-generator",
|
|
1782
|
-
"tilelang-op-orchestrator",
|
|
1783
|
-
"model-infer-optimize",
|
|
1784
|
-
"catlass-op-generator",
|
|
1785
|
-
"ops-code-reviewer",
|
|
1786
|
-
"torch-compile"
|
|
1787
|
-
];
|
|
1788
|
-
for (const pluginId of plugins) {
|
|
1789
|
-
const record = readRecord(pluginId);
|
|
1787
|
+
const plugins = getAllPlugins();
|
|
1788
|
+
for (const plugin of plugins) {
|
|
1789
|
+
const record = readRecord(plugin.id);
|
|
1790
1790
|
if (record) {
|
|
1791
1791
|
records.push(record);
|
|
1792
1792
|
}
|
|
@@ -1841,18 +1841,18 @@ async function installPlugin(opts) {
|
|
|
1841
1841
|
pluginId: opts.pluginId,
|
|
1842
1842
|
skillsCount: 0,
|
|
1843
1843
|
agentsCount: 0,
|
|
1844
|
-
errors: [
|
|
1844
|
+
errors: [t("error_plugin_not_found") + ": " + opts.pluginId],
|
|
1845
1845
|
warnings: []
|
|
1846
1846
|
};
|
|
1847
1847
|
}
|
|
1848
1848
|
const scriptPath = join11(opts.repoPath, plugin.dir, plugin.script);
|
|
1849
|
-
if (!
|
|
1849
|
+
if (!existsSync11(scriptPath)) {
|
|
1850
1850
|
return {
|
|
1851
1851
|
success: false,
|
|
1852
1852
|
pluginId: opts.pluginId,
|
|
1853
1853
|
skillsCount: 0,
|
|
1854
1854
|
agentsCount: 0,
|
|
1855
|
-
errors: [
|
|
1855
|
+
errors: [t("error_script_not_found").replace("{path}", scriptPath)],
|
|
1856
1856
|
warnings: []
|
|
1857
1857
|
};
|
|
1858
1858
|
}
|
|
@@ -1862,9 +1862,9 @@ async function installPlugin(opts) {
|
|
|
1862
1862
|
}
|
|
1863
1863
|
const cwd = opts.installPath || process.cwd();
|
|
1864
1864
|
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1865
|
-
const agentsFile = join11(configRoot,
|
|
1865
|
+
const agentsFile = join11(configRoot, getAgentsFileName2(opts.tool));
|
|
1866
1866
|
let backupInfo = null;
|
|
1867
|
-
if (
|
|
1867
|
+
if (existsSync11(agentsFile)) {
|
|
1868
1868
|
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
1869
1869
|
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
1870
1870
|
let choice = "overwrite";
|
|
@@ -1943,6 +1943,7 @@ async function installPlugin(opts) {
|
|
|
1943
1943
|
}
|
|
1944
1944
|
writeRecord(record);
|
|
1945
1945
|
} catch {
|
|
1946
|
+
logger.warn(t("record_write_failed"));
|
|
1946
1947
|
}
|
|
1947
1948
|
return {
|
|
1948
1949
|
success: true,
|
|
@@ -1953,7 +1954,7 @@ async function installPlugin(opts) {
|
|
|
1953
1954
|
warnings: []
|
|
1954
1955
|
};
|
|
1955
1956
|
} catch (error) {
|
|
1956
|
-
const errorMessage = error instanceof Error ? error.message : "
|
|
1957
|
+
const errorMessage = error instanceof Error ? error.message : t("error_unknown");
|
|
1957
1958
|
return {
|
|
1958
1959
|
success: false,
|
|
1959
1960
|
pluginId: opts.pluginId,
|
|
@@ -1966,8 +1967,8 @@ async function installPlugin(opts) {
|
|
|
1966
1967
|
}
|
|
1967
1968
|
|
|
1968
1969
|
// src/core/skill-installer.ts
|
|
1969
|
-
import { existsSync as
|
|
1970
|
-
import { join as join12, dirname as
|
|
1970
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, readdirSync as readdirSync4, rmdirSync, realpathSync } from "fs";
|
|
1971
|
+
import { join as join12, dirname as dirname6 } from "path";
|
|
1971
1972
|
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
1972
1973
|
import chalk5 from "chalk";
|
|
1973
1974
|
import Table from "cli-table3";
|
|
@@ -1975,7 +1976,7 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1975
1976
|
const configRoot = getConfigRoot(tool, level);
|
|
1976
1977
|
const skillsDir = join12(configRoot, "skills");
|
|
1977
1978
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1978
|
-
if (!
|
|
1979
|
+
if (!existsSync12(skillsDir)) {
|
|
1979
1980
|
mkdirSync5(skillsDir, { recursive: true });
|
|
1980
1981
|
}
|
|
1981
1982
|
const results = [];
|
|
@@ -1983,25 +1984,25 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1983
1984
|
for (const skillId of skillIds) {
|
|
1984
1985
|
const skill = findSkill(skillId);
|
|
1985
1986
|
if (!skill) {
|
|
1986
|
-
results.push({ skillId, success: false, error: "
|
|
1987
|
+
results.push({ skillId, success: false, error: t("error_skill_not_found") });
|
|
1987
1988
|
continue;
|
|
1988
1989
|
}
|
|
1989
1990
|
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
1990
1991
|
const targetPath = join12(skillsDir, skillId);
|
|
1991
|
-
if (!
|
|
1992
|
-
results.push({ skillId, success: false, error:
|
|
1992
|
+
if (!existsSync12(sourcePath)) {
|
|
1993
|
+
results.push({ skillId, success: false, error: t("error_source_not_found").replace("{path}", sourcePath) });
|
|
1993
1994
|
continue;
|
|
1994
1995
|
}
|
|
1995
1996
|
try {
|
|
1996
1997
|
const resolvedSource = realpathSync(sourcePath);
|
|
1997
|
-
if (
|
|
1998
|
-
|
|
1998
|
+
if (existsSync12(targetPath) || isSymlink(targetPath)) {
|
|
1999
|
+
unlinkSync4(targetPath);
|
|
1999
2000
|
}
|
|
2000
2001
|
symlinkSync(resolvedSource, targetPath);
|
|
2001
2002
|
results.push({ skillId, success: true });
|
|
2002
2003
|
installedIds.push(skillId);
|
|
2003
2004
|
} catch (error) {
|
|
2004
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
2005
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
2005
2006
|
}
|
|
2006
2007
|
}
|
|
2007
2008
|
if (installedIds.length > 0) {
|
|
@@ -2018,23 +2019,23 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2018
2019
|
for (const skillId of skillIds) {
|
|
2019
2020
|
const targetPath = join12(skillsDir, skillId);
|
|
2020
2021
|
try {
|
|
2021
|
-
if (
|
|
2022
|
-
|
|
2022
|
+
if (existsSync12(targetPath) || isSymlink(targetPath)) {
|
|
2023
|
+
unlinkSync4(targetPath);
|
|
2023
2024
|
results.push({ skillId, success: true });
|
|
2024
2025
|
removedIds.push(skillId);
|
|
2025
2026
|
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
2026
2027
|
} else {
|
|
2027
|
-
results.push({ skillId, success: false, error: "
|
|
2028
|
+
results.push({ skillId, success: false, error: t("error_not_installed") });
|
|
2028
2029
|
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
2029
2030
|
}
|
|
2030
2031
|
} catch (error) {
|
|
2031
|
-
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "
|
|
2032
|
+
results.push({ skillId, success: false, error: error instanceof Error ? error.message : t("error_unknown") });
|
|
2032
2033
|
}
|
|
2033
2034
|
}
|
|
2034
2035
|
if (removedIds.length > 0) {
|
|
2035
2036
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
2036
2037
|
}
|
|
2037
|
-
if (
|
|
2038
|
+
if (existsSync12(skillsDir)) {
|
|
2038
2039
|
try {
|
|
2039
2040
|
const entries = readdirSync4(skillsDir);
|
|
2040
2041
|
if (entries.length === 0) {
|
|
@@ -2050,8 +2051,6 @@ var SELECT_ALL = "__select_all__";
|
|
|
2050
2051
|
var SELECT_ALL_CATEGORY = "__select_all_category__";
|
|
2051
2052
|
var CONTINUE_SELECT = "__continue_select__";
|
|
2052
2053
|
async function interactiveSkillSelect(tool, level) {
|
|
2053
|
-
const BACK3 = "__back__";
|
|
2054
|
-
const CANCEL3 = "__cancel__";
|
|
2055
2054
|
const categories = getAllCategories();
|
|
2056
2055
|
const allSkillIds = getAllSkills().map((s) => s.id);
|
|
2057
2056
|
let accumulated = [];
|
|
@@ -2074,8 +2073,8 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
2074
2073
|
})),
|
|
2075
2074
|
new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2076
2075
|
...accumulated.length > 0 ? [{ name: `>> ${t("skill_confirm_install_count").replace("{count}", String(accumulated.length))}`, value: "install" }] : [],
|
|
2077
|
-
{ name: "<- " + t("wizard_back"), value:
|
|
2078
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
2076
|
+
{ name: "<- " + t("wizard_back"), value: BACK },
|
|
2077
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2079
2078
|
];
|
|
2080
2079
|
showOperationHints();
|
|
2081
2080
|
selectedCategoryId = await select3({
|
|
@@ -2085,8 +2084,8 @@ async function interactiveSkillSelect(tool, level) {
|
|
|
2085
2084
|
theme: selectTheme,
|
|
2086
2085
|
pageSize: 15
|
|
2087
2086
|
});
|
|
2088
|
-
if (selectedCategoryId ===
|
|
2089
|
-
if (selectedCategoryId ===
|
|
2087
|
+
if (selectedCategoryId === BACK) return "back";
|
|
2088
|
+
if (selectedCategoryId === CANCEL) return "cancel";
|
|
2090
2089
|
if (selectedCategoryId === SELECT_ALL) {
|
|
2091
2090
|
return allSkillIds;
|
|
2092
2091
|
}
|
|
@@ -2220,19 +2219,12 @@ function listAllSkills() {
|
|
|
2220
2219
|
function resolveSkillSourcePath(skill, repoPath) {
|
|
2221
2220
|
if (skill.filePath) {
|
|
2222
2221
|
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join12(repoPath, skill.filePath);
|
|
2223
|
-
if (
|
|
2224
|
-
return
|
|
2222
|
+
if (existsSync12(absPath)) {
|
|
2223
|
+
return dirname6(absPath);
|
|
2225
2224
|
}
|
|
2226
2225
|
}
|
|
2227
2226
|
return join12(repoPath, skill.source, skill.id);
|
|
2228
2227
|
}
|
|
2229
|
-
function isSymlink2(path) {
|
|
2230
|
-
try {
|
|
2231
|
-
return lstatSync2(path).isSymbolicLink();
|
|
2232
|
-
} catch {
|
|
2233
|
-
return false;
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
2228
|
|
|
2237
2229
|
// src/ui/display.ts
|
|
2238
2230
|
import chalk6 from "chalk";
|
|
@@ -2323,13 +2315,11 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2323
2315
|
}
|
|
2324
2316
|
|
|
2325
2317
|
// src/commands/init.ts
|
|
2326
|
-
import { existsSync as
|
|
2318
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
2327
2319
|
import { join as join13 } from "path";
|
|
2328
|
-
var BACK2 = "__back__";
|
|
2329
|
-
var CANCEL2 = "__cancel__";
|
|
2330
2320
|
async function initCommand() {
|
|
2331
2321
|
const cwd = process.cwd();
|
|
2332
|
-
if (
|
|
2322
|
+
if (existsSync13(join13(cwd, "package.json")) && (() => {
|
|
2333
2323
|
try {
|
|
2334
2324
|
const pkg = JSON.parse(readFileSync8(join13(cwd, "package.json"), "utf-8"));
|
|
2335
2325
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
@@ -2385,7 +2375,6 @@ async function initCommand() {
|
|
|
2385
2375
|
}
|
|
2386
2376
|
async function selectMode() {
|
|
2387
2377
|
printBoxTitle(t("wizard_select_mode_title"));
|
|
2388
|
-
const CANCEL3 = "__cancel__";
|
|
2389
2378
|
const choices = [
|
|
2390
2379
|
{
|
|
2391
2380
|
name: `> ${t("wizard_mode_plugin")} \u2014 ${t("wizard_mode_plugin_desc")}`,
|
|
@@ -2396,7 +2385,7 @@ async function selectMode() {
|
|
|
2396
2385
|
value: "skill"
|
|
2397
2386
|
},
|
|
2398
2387
|
new Separator3("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
2399
|
-
{ name: "x " + t("wizard_cancel"), value:
|
|
2388
|
+
{ name: "x " + t("wizard_cancel"), value: CANCEL }
|
|
2400
2389
|
];
|
|
2401
2390
|
showOperationHints();
|
|
2402
2391
|
const result = await select4({
|
|
@@ -2405,7 +2394,7 @@ async function selectMode() {
|
|
|
2405
2394
|
loop: false,
|
|
2406
2395
|
theme: selectTheme
|
|
2407
2396
|
});
|
|
2408
|
-
if (result ===
|
|
2397
|
+
if (result === CANCEL) return null;
|
|
2409
2398
|
return result;
|
|
2410
2399
|
}
|
|
2411
2400
|
async function pluginInstallFlow() {
|
|
@@ -2482,11 +2471,11 @@ async function skillInstallFlow() {
|
|
|
2482
2471
|
}
|
|
2483
2472
|
case 1: {
|
|
2484
2473
|
const result = await stepLevel();
|
|
2485
|
-
if (result ===
|
|
2474
|
+
if (result === BACK) {
|
|
2486
2475
|
step = 0;
|
|
2487
2476
|
break;
|
|
2488
2477
|
}
|
|
2489
|
-
if (result ===
|
|
2478
|
+
if (result === CANCEL) {
|
|
2490
2479
|
logger.info(t("init_cancelled"));
|
|
2491
2480
|
return "done";
|
|
2492
2481
|
}
|
|
@@ -2557,13 +2546,12 @@ async function listCommand() {
|
|
|
2557
2546
|
|
|
2558
2547
|
// src/commands/doctor.ts
|
|
2559
2548
|
import chalk8 from "chalk";
|
|
2560
|
-
import { existsSync as
|
|
2549
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync5, mkdirSync as mkdirSync6 } from "fs";
|
|
2561
2550
|
import { join as join14 } from "path";
|
|
2562
2551
|
async function doctorCommand(options = {}) {
|
|
2563
2552
|
console.log();
|
|
2564
2553
|
console.log(chalk8.bold(` ${t("doctor_title")}`));
|
|
2565
2554
|
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2566
|
-
let errors = 0;
|
|
2567
2555
|
let warnings = 0;
|
|
2568
2556
|
let fixes = 0;
|
|
2569
2557
|
console.log();
|
|
@@ -2601,7 +2589,7 @@ async function doctorCommand(options = {}) {
|
|
|
2601
2589
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2602
2590
|
const skillsDir = getSkillsDir(configRoot);
|
|
2603
2591
|
const agentsDir = getAgentsDir(configRoot);
|
|
2604
|
-
if (
|
|
2592
|
+
if (existsSync14(skillsDir)) {
|
|
2605
2593
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2606
2594
|
if (brokenLinks === 0) {
|
|
2607
2595
|
const count = readdirSync5(skillsDir).length;
|
|
@@ -2623,7 +2611,7 @@ async function doctorCommand(options = {}) {
|
|
|
2623
2611
|
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2624
2612
|
}
|
|
2625
2613
|
}
|
|
2626
|
-
if (
|
|
2614
|
+
if (existsSync14(agentsDir)) {
|
|
2627
2615
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2628
2616
|
if (brokenLinks === 0) {
|
|
2629
2617
|
const count = readdirSync5(agentsDir).length;
|
|
@@ -2649,7 +2637,7 @@ async function doctorCommand(options = {}) {
|
|
|
2649
2637
|
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2650
2638
|
const configFile = getConfigFileName(primaryTool);
|
|
2651
2639
|
const configPath = join14(process.cwd(), configFile);
|
|
2652
|
-
if (
|
|
2640
|
+
if (existsSync14(configPath)) {
|
|
2653
2641
|
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2654
2642
|
} else {
|
|
2655
2643
|
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
@@ -2658,11 +2646,11 @@ async function doctorCommand(options = {}) {
|
|
|
2658
2646
|
console.log(chalk8.dim(" " + "\u2500".repeat(46)));
|
|
2659
2647
|
if (options.fix && fixes > 0) {
|
|
2660
2648
|
console.log(
|
|
2661
|
-
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{
|
|
2649
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{fixes}", String(fixes))}`
|
|
2662
2650
|
);
|
|
2663
2651
|
} else {
|
|
2664
2652
|
console.log(
|
|
2665
|
-
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))
|
|
2653
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings))}`
|
|
2666
2654
|
);
|
|
2667
2655
|
}
|
|
2668
2656
|
console.log();
|
|
@@ -2674,9 +2662,9 @@ function checkBrokenLinks(dir) {
|
|
|
2674
2662
|
for (const entry of entries) {
|
|
2675
2663
|
const fullPath = join14(dir, entry);
|
|
2676
2664
|
try {
|
|
2677
|
-
const stats =
|
|
2665
|
+
const stats = lstatSync2(fullPath);
|
|
2678
2666
|
if (stats.isSymbolicLink()) {
|
|
2679
|
-
if (!
|
|
2667
|
+
if (!existsSync14(fullPath)) {
|
|
2680
2668
|
broken++;
|
|
2681
2669
|
}
|
|
2682
2670
|
}
|
|
@@ -2695,10 +2683,10 @@ function fixBrokenLinks(dir) {
|
|
|
2695
2683
|
for (const entry of entries) {
|
|
2696
2684
|
const fullPath = join14(dir, entry);
|
|
2697
2685
|
try {
|
|
2698
|
-
const stats =
|
|
2686
|
+
const stats = lstatSync2(fullPath);
|
|
2699
2687
|
if (stats.isSymbolicLink()) {
|
|
2700
|
-
if (!
|
|
2701
|
-
|
|
2688
|
+
if (!existsSync14(fullPath)) {
|
|
2689
|
+
unlinkSync5(fullPath);
|
|
2702
2690
|
fixed++;
|
|
2703
2691
|
}
|
|
2704
2692
|
}
|
|
@@ -2713,7 +2701,6 @@ function fixBrokenLinks(dir) {
|
|
|
2713
2701
|
// src/commands/status.ts
|
|
2714
2702
|
import chalk9 from "chalk";
|
|
2715
2703
|
async function statusCommand() {
|
|
2716
|
-
const plugins = getAllPlugins();
|
|
2717
2704
|
const installed = scanInstalled();
|
|
2718
2705
|
const installedMap = new Map(installed.map((p) => [p.id, p]));
|
|
2719
2706
|
console.log();
|
|
@@ -2772,7 +2759,10 @@ async function installCommand(names, options) {
|
|
|
2772
2759
|
tool2 = validateTool(options.tool);
|
|
2773
2760
|
} else {
|
|
2774
2761
|
const detected = await selectToolWithDetection();
|
|
2775
|
-
if (detected === "back" || detected === "cancel")
|
|
2762
|
+
if (detected === "back" || detected === "cancel") {
|
|
2763
|
+
logger.info(t("init_cancelled"));
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2776
2766
|
tool2 = detected;
|
|
2777
2767
|
}
|
|
2778
2768
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2821,7 +2811,10 @@ async function installCommand(names, options) {
|
|
|
2821
2811
|
tool2 = validateTool(options.tool);
|
|
2822
2812
|
} else {
|
|
2823
2813
|
const detected = await selectToolWithDetection();
|
|
2824
|
-
if (detected === "back" || detected === "cancel")
|
|
2814
|
+
if (detected === "back" || detected === "cancel") {
|
|
2815
|
+
logger.info(t("init_cancelled"));
|
|
2816
|
+
return;
|
|
2817
|
+
}
|
|
2825
2818
|
tool2 = detected;
|
|
2826
2819
|
}
|
|
2827
2820
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2832,11 +2825,17 @@ async function installCommand(names, options) {
|
|
|
2832
2825
|
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2833
2826
|
if (selectedSkills === "back") {
|
|
2834
2827
|
const redetected = await selectToolWithDetection();
|
|
2835
|
-
if (redetected === "back" || redetected === "cancel")
|
|
2828
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
2829
|
+
logger.info(t("init_cancelled"));
|
|
2830
|
+
return;
|
|
2831
|
+
}
|
|
2836
2832
|
tool2 = redetected;
|
|
2837
2833
|
break;
|
|
2838
2834
|
}
|
|
2839
|
-
if (selectedSkills === "cancel")
|
|
2835
|
+
if (selectedSkills === "cancel") {
|
|
2836
|
+
logger.info(t("init_cancelled"));
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2840
2839
|
if (selectedSkills.length === 0) {
|
|
2841
2840
|
logger.info(t("install_no_skill_selected"));
|
|
2842
2841
|
return;
|
|
@@ -2911,7 +2910,10 @@ async function installCommand(names, options) {
|
|
|
2911
2910
|
tool = validateTool(options.tool);
|
|
2912
2911
|
} else {
|
|
2913
2912
|
const detected = await selectToolWithDetection();
|
|
2914
|
-
if (detected === "back" || detected === "cancel")
|
|
2913
|
+
if (detected === "back" || detected === "cancel") {
|
|
2914
|
+
logger.info(t("init_cancelled"));
|
|
2915
|
+
return;
|
|
2916
|
+
}
|
|
2915
2917
|
tool = detected;
|
|
2916
2918
|
}
|
|
2917
2919
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -3029,7 +3031,7 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
|
|
|
3029
3031
|
}
|
|
3030
3032
|
|
|
3031
3033
|
// src/commands/uninstall.ts
|
|
3032
|
-
import { existsSync as
|
|
3034
|
+
import { existsSync as existsSync15, unlinkSync as unlinkSync6, rmdirSync as rmdirSync2, readdirSync as readdirSync6 } from "fs";
|
|
3033
3035
|
import { resolve } from "path";
|
|
3034
3036
|
import chalk11 from "chalk";
|
|
3035
3037
|
async function uninstallCommand(names, options) {
|
|
@@ -3071,8 +3073,8 @@ async function uninstallCommand(names, options) {
|
|
|
3071
3073
|
for (const filePath of record.files) {
|
|
3072
3074
|
if (!isSafePath(filePath)) continue;
|
|
3073
3075
|
try {
|
|
3074
|
-
if (
|
|
3075
|
-
|
|
3076
|
+
if (existsSync15(filePath) || isSymlink(filePath)) {
|
|
3077
|
+
unlinkSync6(filePath);
|
|
3076
3078
|
const name = filePath.split("/").pop() || filePath;
|
|
3077
3079
|
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
3078
3080
|
removedFiles++;
|
|
@@ -3086,7 +3088,7 @@ async function uninstallCommand(names, options) {
|
|
|
3086
3088
|
if (resolve(dirPath) === resolve(configRoot)) {
|
|
3087
3089
|
continue;
|
|
3088
3090
|
}
|
|
3089
|
-
if (
|
|
3091
|
+
if (existsSync15(dirPath)) {
|
|
3090
3092
|
const entries = readdirSync6(dirPath);
|
|
3091
3093
|
if (entries.length === 0) {
|
|
3092
3094
|
rmdirSync2(dirPath);
|
|
@@ -3119,7 +3121,7 @@ async function uninstallCommand(names, options) {
|
|
|
3119
3121
|
}
|
|
3120
3122
|
const totalRemoved = removedFiles + removedDirs;
|
|
3121
3123
|
if (totalRemoved > 0) {
|
|
3122
|
-
logger.success(
|
|
3124
|
+
logger.success(t("uninstall_summary_format").replace("{name}", plugin.displayName).replace("{files}", String(removedFiles)).replace("{dirs}", String(removedDirs)));
|
|
3123
3125
|
} else {
|
|
3124
3126
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3125
3127
|
}
|
|
@@ -3141,13 +3143,6 @@ async function uninstallCommand(names, options) {
|
|
|
3141
3143
|
logger.blank();
|
|
3142
3144
|
}
|
|
3143
3145
|
}
|
|
3144
|
-
function isSymlink3(path) {
|
|
3145
|
-
try {
|
|
3146
|
-
return lstatSync4(path).isSymbolicLink();
|
|
3147
|
-
} catch {
|
|
3148
|
-
return false;
|
|
3149
|
-
}
|
|
3150
|
-
}
|
|
3151
3146
|
|
|
3152
3147
|
// src/commands/update.ts
|
|
3153
3148
|
import chalk12 from "chalk";
|
|
@@ -3157,7 +3152,10 @@ async function updateCommand(pluginNames, options) {
|
|
|
3157
3152
|
tool = validateTool(options.tool);
|
|
3158
3153
|
} else {
|
|
3159
3154
|
const detected = await selectToolWithDetection();
|
|
3160
|
-
if (detected === "back" || detected === "cancel")
|
|
3155
|
+
if (detected === "back" || detected === "cancel") {
|
|
3156
|
+
logger.info(t("init_cancelled"));
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3161
3159
|
tool = detected;
|
|
3162
3160
|
}
|
|
3163
3161
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -3231,7 +3229,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3231
3229
|
|
|
3232
3230
|
// src/commands/info.ts
|
|
3233
3231
|
import chalk13 from "chalk";
|
|
3234
|
-
import { existsSync as
|
|
3232
|
+
import { existsSync as existsSync16, readdirSync as readdirSync7 } from "fs";
|
|
3235
3233
|
import { join as join15 } from "path";
|
|
3236
3234
|
async function infoCommand(pluginName) {
|
|
3237
3235
|
const plugin = findPlugin(pluginName);
|
|
@@ -3257,11 +3255,11 @@ async function infoCommand(pluginName) {
|
|
|
3257
3255
|
const repoManager = createRepositoryManager();
|
|
3258
3256
|
const repoPath = await repoManager.ensureRepo();
|
|
3259
3257
|
const quickstartPath = join15(repoPath, plugin.dir, "quickstart.md");
|
|
3260
|
-
if (
|
|
3258
|
+
if (existsSync16(quickstartPath)) {
|
|
3261
3259
|
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
3262
3260
|
}
|
|
3263
3261
|
const agentsDir = join15(repoPath, plugin.dir, "agents");
|
|
3264
|
-
if (
|
|
3262
|
+
if (existsSync16(agentsDir)) {
|
|
3265
3263
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3266
3264
|
if (agentFiles.length > 0) {
|
|
3267
3265
|
console.log();
|
|
@@ -3313,13 +3311,27 @@ async function langCommand(action, value) {
|
|
|
3313
3311
|
}
|
|
3314
3312
|
|
|
3315
3313
|
// src/cli.ts
|
|
3314
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
3315
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3316
|
+
import { dirname as dirname7, join as join16 } from "path";
|
|
3317
|
+
function readVersion() {
|
|
3318
|
+
try {
|
|
3319
|
+
const __filename5 = fileURLToPath5(import.meta.url);
|
|
3320
|
+
const __dirname5 = dirname7(__filename5);
|
|
3321
|
+
const pkgPath = join16(__dirname5, "..", "package.json");
|
|
3322
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
3323
|
+
return pkg.version || "0.0.0";
|
|
3324
|
+
} catch {
|
|
3325
|
+
return "0.0.0";
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3316
3328
|
function createCLI() {
|
|
3317
3329
|
const config = readConfig();
|
|
3318
3330
|
if (config.language) {
|
|
3319
3331
|
setLanguage(config.language);
|
|
3320
3332
|
}
|
|
3321
3333
|
const program2 = new Command();
|
|
3322
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(
|
|
3334
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version(readVersion());
|
|
3323
3335
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3324
3336
|
await initCommand();
|
|
3325
3337
|
});
|