@cannbot-ai/install-helper 0.0.2-beta.32 → 0.0.2-beta.34
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 +266 -198
- package/dist/index.js.map +1 -1
- package/dist/locales/en_US.json +7 -2
- package/dist/locales/zh_CN.json +7 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,18 +12,115 @@ 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, statSync } from "fs";
|
|
15
|
+
import { readdirSync, readFileSync as readFileSync2, existsSync, statSync } 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 chalk 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/utils/logger.ts
|
|
56
|
+
var logger = {
|
|
57
|
+
success: (msg) => console.log(chalk.green("\u2713") + " " + msg),
|
|
58
|
+
error: (msg) => console.log(chalk.red("\u2717") + " " + msg),
|
|
59
|
+
warn: (msg) => console.log(chalk.yellow("\u26A0") + " " + msg),
|
|
60
|
+
info: (msg) => console.log(chalk.cyan("\u2192") + " " + msg),
|
|
61
|
+
step: (msg) => console.log(chalk.dim(msg)),
|
|
62
|
+
blank: () => console.log()
|
|
63
|
+
};
|
|
64
|
+
function createSpinner(text) {
|
|
65
|
+
return ora({ text, color: "cyan" });
|
|
66
|
+
}
|
|
67
|
+
function printBanner(subtitle) {
|
|
68
|
+
console.log(chalk.cyan(`
|
|
69
|
+
____ _ _ _ _ _ ____ _
|
|
70
|
+
/ ___| / \\ | \\ | | \\ | | __ ) ___ | |_
|
|
71
|
+
| | / _ \\ | \\| | \\| | _ \\ / _ \\| __|
|
|
72
|
+
| |___ / ___ \\| |\\ | |\\ | |_) | (_) | |_
|
|
73
|
+
\\____/_/ \\_\\_| \\_|_| \\_|____/ \\___/ \\__|
|
|
74
|
+
`));
|
|
75
|
+
if (subtitle) {
|
|
76
|
+
console.log(chalk.bold(` ${subtitle}`));
|
|
77
|
+
console.log();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function getDisplayWidth(str) {
|
|
81
|
+
let width = 0;
|
|
82
|
+
for (const ch of str) {
|
|
83
|
+
const code = ch.codePointAt(0);
|
|
84
|
+
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) {
|
|
85
|
+
width += 2;
|
|
86
|
+
} else {
|
|
87
|
+
width += 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return width;
|
|
91
|
+
}
|
|
92
|
+
function printBoxTitle(title, width = 65) {
|
|
93
|
+
const border = "\u2550".repeat(width);
|
|
94
|
+
const displayWidth = getDisplayWidth(title);
|
|
95
|
+
const totalPadding = width - displayWidth;
|
|
96
|
+
const leftPadding = Math.floor(totalPadding / 2);
|
|
97
|
+
const rightPadding = totalPadding - leftPadding;
|
|
98
|
+
const paddedTitle = " ".repeat(Math.max(0, leftPadding)) + title + " ".repeat(Math.max(0, rightPadding));
|
|
99
|
+
console.log();
|
|
100
|
+
console.log(chalk.cyan.bold(` \u2554${border}\u2557`));
|
|
101
|
+
console.log(chalk.cyan.bold(` \u2551`) + chalk.cyan.bold(paddedTitle) + chalk.cyan.bold(`\u2551`));
|
|
102
|
+
console.log(chalk.cyan.bold(` \u255A${border}\u255D`));
|
|
103
|
+
console.log();
|
|
104
|
+
}
|
|
105
|
+
function showOperationHints(isCheckbox = false) {
|
|
106
|
+
if (isCheckbox) {
|
|
107
|
+
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
|
|
108
|
+
`);
|
|
109
|
+
} else {
|
|
110
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
111
|
+
`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/core/registry.ts
|
|
116
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
117
|
+
var __dirname2 = dirname2(__filename2);
|
|
21
118
|
function loadPluginsFromYaml() {
|
|
22
|
-
const pluginsDir =
|
|
23
|
-
const defaultsPath =
|
|
119
|
+
const pluginsDir = join2(__dirname2, "plugins.d");
|
|
120
|
+
const defaultsPath = join2(pluginsDir, "_defaults.yml");
|
|
24
121
|
let defaults = {};
|
|
25
122
|
try {
|
|
26
|
-
defaults = parseYaml(
|
|
123
|
+
defaults = parseYaml(readFileSync2(defaultsPath, "utf-8")) || {};
|
|
27
124
|
} catch {
|
|
28
125
|
}
|
|
29
126
|
const plugins = [];
|
|
@@ -31,7 +128,7 @@ function loadPluginsFromYaml() {
|
|
|
31
128
|
for (const file of readdirSync(pluginsDir)) {
|
|
32
129
|
if (file.startsWith("_") || !file.endsWith(".yml")) continue;
|
|
33
130
|
try {
|
|
34
|
-
const content = parseYaml(
|
|
131
|
+
const content = parseYaml(readFileSync2(join2(pluginsDir, file), "utf-8"));
|
|
35
132
|
if (!content || !content.id) continue;
|
|
36
133
|
plugins.push({
|
|
37
134
|
id: content.id,
|
|
@@ -44,6 +141,7 @@ function loadPluginsFromYaml() {
|
|
|
44
141
|
description: content.description || ""
|
|
45
142
|
});
|
|
46
143
|
} catch {
|
|
144
|
+
logger.warn(t("plugin_yaml_error").replace("{file}", file));
|
|
47
145
|
}
|
|
48
146
|
}
|
|
49
147
|
} catch {
|
|
@@ -60,7 +158,7 @@ function mergeDynamicPlugins(repoPath) {
|
|
|
60
158
|
const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
|
|
61
159
|
const dynamic = [];
|
|
62
160
|
for (const pluginDir of PLUGIN_DIRS) {
|
|
63
|
-
const parent =
|
|
161
|
+
const parent = join2(repoPath, pluginDir);
|
|
64
162
|
if (!existsSync(parent)) continue;
|
|
65
163
|
let entries = [];
|
|
66
164
|
try {
|
|
@@ -69,10 +167,10 @@ function mergeDynamicPlugins(repoPath) {
|
|
|
69
167
|
continue;
|
|
70
168
|
}
|
|
71
169
|
for (const entry of entries) {
|
|
72
|
-
const pluginPath =
|
|
170
|
+
const pluginPath = join2(parent, entry);
|
|
73
171
|
if (!isDirectory(pluginPath)) continue;
|
|
74
172
|
if (entry === "install-helper") continue;
|
|
75
|
-
const initSh =
|
|
173
|
+
const initSh = join2(pluginPath, "init.sh");
|
|
76
174
|
if (!existsSync(initSh)) continue;
|
|
77
175
|
const pluginId = entry;
|
|
78
176
|
if (existingIds.has(pluginId)) continue;
|
|
@@ -133,39 +231,6 @@ import { join as join4 } from "path";
|
|
|
133
231
|
import { homedir } from "os";
|
|
134
232
|
import { join as join3 } from "path";
|
|
135
233
|
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
234
|
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
170
235
|
var VALID_LEVELS = ["project", "global"];
|
|
171
236
|
function validateTool(tool) {
|
|
@@ -467,73 +532,32 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
|
467
532
|
return true;
|
|
468
533
|
}
|
|
469
534
|
|
|
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;
|
|
535
|
+
// src/utils/config.ts
|
|
536
|
+
import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync4 } from "fs";
|
|
537
|
+
import { join as join6 } from "path";
|
|
538
|
+
import { homedir as homedir3 } from "os";
|
|
539
|
+
import { parse, stringify } from "yaml";
|
|
540
|
+
|
|
541
|
+
// src/utils/fs.ts
|
|
542
|
+
import { writeFileSync, renameSync, existsSync as existsSync5, unlinkSync } from "fs";
|
|
543
|
+
import { dirname as dirname3 } from "path";
|
|
544
|
+
function atomicWriteFileSync(filePath, data, encoding = "utf-8") {
|
|
545
|
+
const dir = dirname3(filePath);
|
|
546
|
+
if (!existsSync5(dir)) {
|
|
547
|
+
throw new Error(`Directory does not exist: ${dir}`);
|
|
548
|
+
}
|
|
549
|
+
const tmpPath = `${filePath}.tmp`;
|
|
550
|
+
writeFileSync(tmpPath, data, encoding);
|
|
551
|
+
if (existsSync5(filePath)) {
|
|
552
|
+
try {
|
|
553
|
+
unlinkSync(filePath);
|
|
554
|
+
} catch {
|
|
505
555
|
}
|
|
506
556
|
}
|
|
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
|
-
}
|
|
557
|
+
renameSync(tmpPath, filePath);
|
|
530
558
|
}
|
|
531
559
|
|
|
532
560
|
// 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
561
|
function getConfigDir() {
|
|
538
562
|
return join6(homedir3(), ".cannbot");
|
|
539
563
|
}
|
|
@@ -542,7 +566,7 @@ function getConfigPath() {
|
|
|
542
566
|
}
|
|
543
567
|
function readConfig() {
|
|
544
568
|
const configPath = getConfigPath();
|
|
545
|
-
if (!
|
|
569
|
+
if (!existsSync6(configPath)) {
|
|
546
570
|
return {
|
|
547
571
|
language: "zh_CN",
|
|
548
572
|
installedPlugins: []
|
|
@@ -559,6 +583,7 @@ function readConfig() {
|
|
|
559
583
|
installedPlugins: parsed.installedPlugins || []
|
|
560
584
|
};
|
|
561
585
|
} catch {
|
|
586
|
+
logger.warn(t("config_corrupted"));
|
|
562
587
|
return {
|
|
563
588
|
language: "zh_CN",
|
|
564
589
|
installedPlugins: []
|
|
@@ -567,12 +592,12 @@ function readConfig() {
|
|
|
567
592
|
}
|
|
568
593
|
function writeConfig(config) {
|
|
569
594
|
const configDir = getConfigDir();
|
|
570
|
-
if (!
|
|
595
|
+
if (!existsSync6(configDir)) {
|
|
571
596
|
mkdirSync(configDir, { recursive: true });
|
|
572
597
|
}
|
|
573
598
|
const configPath = getConfigPath();
|
|
574
599
|
const content = stringify(config);
|
|
575
|
-
|
|
600
|
+
atomicWriteFileSync(configPath, content);
|
|
576
601
|
}
|
|
577
602
|
function updateConfig(updates) {
|
|
578
603
|
const config = readConfig();
|
|
@@ -918,19 +943,19 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
918
943
|
}
|
|
919
944
|
|
|
920
945
|
// src/core/repository.ts
|
|
921
|
-
import { existsSync as
|
|
922
|
-
import { join as join8, dirname as
|
|
946
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
947
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
923
948
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
924
949
|
import { execa as execa2 } from "execa";
|
|
925
950
|
import { parse as parseYaml3 } from "yaml";
|
|
926
951
|
|
|
927
952
|
// src/core/scanner.ts
|
|
928
|
-
import { existsSync as
|
|
929
|
-
import { join as join7, dirname as
|
|
953
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
954
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
930
955
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
931
956
|
import { parse as parseYaml2 } from "yaml";
|
|
932
957
|
var __filename3 = fileURLToPath3(import.meta.url);
|
|
933
|
-
var __dirname3 =
|
|
958
|
+
var __dirname3 = dirname4(__filename3);
|
|
934
959
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
935
960
|
"node_modules",
|
|
936
961
|
".git",
|
|
@@ -974,26 +999,26 @@ function scanSkills(repoPath) {
|
|
|
974
999
|
const seen = /* @__PURE__ */ new Set();
|
|
975
1000
|
for (const dir of SKILL_SCAN_DIRS) {
|
|
976
1001
|
const fullPath = join7(repoPath, dir);
|
|
977
|
-
if (!
|
|
1002
|
+
if (!existsSync7(fullPath)) continue;
|
|
978
1003
|
scanDirectory(fullPath, repoPath, dir, skills, seen);
|
|
979
1004
|
}
|
|
980
1005
|
for (const pluginDir of PLUGIN_SCAN_DIRS) {
|
|
981
1006
|
const pluginParent = join7(repoPath, pluginDir);
|
|
982
|
-
if (!
|
|
1007
|
+
if (!existsSync7(pluginParent)) continue;
|
|
983
1008
|
for (const plugin of safeReaddir(pluginParent)) {
|
|
984
1009
|
const pluginPath = join7(pluginParent, plugin);
|
|
985
1010
|
if (!isDirectory2(pluginPath)) continue;
|
|
986
1011
|
const skillsDir = join7(pluginPath, "skills");
|
|
987
|
-
if (
|
|
1012
|
+
if (existsSync7(skillsDir)) {
|
|
988
1013
|
scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
|
|
989
1014
|
}
|
|
990
1015
|
const skillDir = join7(pluginPath, "skill");
|
|
991
|
-
if (
|
|
1016
|
+
if (existsSync7(skillDir)) {
|
|
992
1017
|
scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
|
|
993
1018
|
}
|
|
994
1019
|
const rootSkillMd = join7(pluginPath, "SKILL.md");
|
|
995
1020
|
const initSh = join7(pluginPath, "init.sh");
|
|
996
|
-
if (
|
|
1021
|
+
if (existsSync7(rootSkillMd) && !existsSync7(initSh)) {
|
|
997
1022
|
const frontmatter = parseFrontmatter(rootSkillMd);
|
|
998
1023
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
999
1024
|
seen.add(frontmatter.name);
|
|
@@ -1027,8 +1052,12 @@ function parseFrontmatter(filePath) {
|
|
|
1027
1052
|
const parsed = parseYaml2(yamlBlock);
|
|
1028
1053
|
if (!parsed || typeof parsed !== "object") return null;
|
|
1029
1054
|
if (!parsed.name || typeof parsed.name !== "string") return null;
|
|
1055
|
+
const trimmedName = parsed.name.trim();
|
|
1056
|
+
if (!trimmedName || /[\/\\]|\.\./.test(trimmedName) || !/^[a-zA-Z0-9._-]+$/.test(trimmedName)) {
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
1030
1059
|
return {
|
|
1031
|
-
name:
|
|
1060
|
+
name: trimmedName,
|
|
1032
1061
|
description: (parsed.description || "").trim()
|
|
1033
1062
|
};
|
|
1034
1063
|
} catch {
|
|
@@ -1038,15 +1067,15 @@ function parseFrontmatter(filePath) {
|
|
|
1038
1067
|
function getCurrentCommit(repoPath) {
|
|
1039
1068
|
try {
|
|
1040
1069
|
const headFile = join7(repoPath, ".git", "HEAD");
|
|
1041
|
-
if (!
|
|
1070
|
+
if (!existsSync7(headFile)) return "unknown";
|
|
1042
1071
|
const headContent = readFileSync5(headFile, "utf-8").trim();
|
|
1043
1072
|
if (headContent.startsWith("ref: ")) {
|
|
1044
1073
|
const refPath = join7(repoPath, ".git", headContent.slice(5));
|
|
1045
|
-
if (
|
|
1074
|
+
if (existsSync7(refPath)) {
|
|
1046
1075
|
return readFileSync5(refPath, "utf-8").trim();
|
|
1047
1076
|
}
|
|
1048
1077
|
const packedRefsPath = join7(repoPath, ".git", "packed-refs");
|
|
1049
|
-
if (
|
|
1078
|
+
if (existsSync7(packedRefsPath)) {
|
|
1050
1079
|
const packedRefs = readFileSync5(packedRefsPath, "utf-8");
|
|
1051
1080
|
const refName = headContent.slice(5);
|
|
1052
1081
|
const lines = packedRefs.split("\n");
|
|
@@ -1065,7 +1094,7 @@ function getCurrentCommit(repoPath) {
|
|
|
1065
1094
|
function readScanCache() {
|
|
1066
1095
|
try {
|
|
1067
1096
|
const cachePath = getCachePath();
|
|
1068
|
-
if (!
|
|
1097
|
+
if (!existsSync7(cachePath)) return null;
|
|
1069
1098
|
const content = readFileSync5(cachePath, "utf-8");
|
|
1070
1099
|
const cache = JSON.parse(content);
|
|
1071
1100
|
if (!cache.skills || !cache.timestamp) return null;
|
|
@@ -1079,11 +1108,11 @@ function readScanCache() {
|
|
|
1079
1108
|
function writeScanCache(cache) {
|
|
1080
1109
|
try {
|
|
1081
1110
|
const cachePath = getCachePath();
|
|
1082
|
-
const cacheDir =
|
|
1083
|
-
if (!
|
|
1111
|
+
const cacheDir = dirname4(cachePath);
|
|
1112
|
+
if (!existsSync7(cacheDir)) {
|
|
1084
1113
|
mkdirSync2(cacheDir, { recursive: true });
|
|
1085
1114
|
}
|
|
1086
|
-
|
|
1115
|
+
atomicWriteFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
1087
1116
|
} catch {
|
|
1088
1117
|
}
|
|
1089
1118
|
}
|
|
@@ -1096,7 +1125,7 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
|
1096
1125
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
1097
1126
|
if (!isDirectory2(fullPath)) continue;
|
|
1098
1127
|
const skillMd = join7(fullPath, "SKILL.md");
|
|
1099
|
-
if (
|
|
1128
|
+
if (existsSync7(skillMd)) {
|
|
1100
1129
|
const frontmatter = parseFrontmatter(skillMd);
|
|
1101
1130
|
if (frontmatter && !seen.has(frontmatter.name)) {
|
|
1102
1131
|
seen.add(frontmatter.name);
|
|
@@ -1364,7 +1393,7 @@ function getAllCategories() {
|
|
|
1364
1393
|
|
|
1365
1394
|
// src/core/repository.ts
|
|
1366
1395
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
1367
|
-
var __dirname4 =
|
|
1396
|
+
var __dirname4 = dirname5(__filename4);
|
|
1368
1397
|
function loadRepoUrl() {
|
|
1369
1398
|
if (process.env.CANNBOT_REPO_URL) {
|
|
1370
1399
|
return process.env.CANNBOT_REPO_URL;
|
|
@@ -1428,6 +1457,7 @@ var RepositoryManager = class {
|
|
|
1428
1457
|
try {
|
|
1429
1458
|
await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
|
|
1430
1459
|
} catch {
|
|
1460
|
+
logger.warn(t("repo_pull_failed"));
|
|
1431
1461
|
}
|
|
1432
1462
|
mergeDynamicPlugins(repoPath);
|
|
1433
1463
|
}
|
|
@@ -1438,18 +1468,18 @@ var RepositoryManager = class {
|
|
|
1438
1468
|
return this.repoPath;
|
|
1439
1469
|
}
|
|
1440
1470
|
isValidRepo(path) {
|
|
1441
|
-
if (!
|
|
1471
|
+
if (!existsSync8(path)) return false;
|
|
1442
1472
|
const gitDir = join8(path, ".git");
|
|
1443
1473
|
const pluginsDir = join8(path, "plugins-official");
|
|
1444
|
-
return
|
|
1474
|
+
return existsSync8(gitDir) && existsSync8(pluginsDir);
|
|
1445
1475
|
}
|
|
1446
1476
|
async cloneRepo() {
|
|
1447
1477
|
const configDir = getCannbotConfigDir();
|
|
1448
|
-
if (!
|
|
1478
|
+
if (!existsSync8(configDir)) {
|
|
1449
1479
|
mkdirSync3(configDir, { recursive: true });
|
|
1450
1480
|
}
|
|
1451
1481
|
const targetPath = getCannbotRepoPath();
|
|
1452
|
-
if (
|
|
1482
|
+
if (existsSync8(targetPath)) {
|
|
1453
1483
|
if (this.isValidRepo(targetPath)) {
|
|
1454
1484
|
this.repoPath = targetPath;
|
|
1455
1485
|
return targetPath;
|
|
@@ -1475,12 +1505,12 @@ function createRepositoryManager(customPath) {
|
|
|
1475
1505
|
}
|
|
1476
1506
|
|
|
1477
1507
|
// src/core/installer.ts
|
|
1478
|
-
import { existsSync as
|
|
1508
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1479
1509
|
import { join as join11 } from "path";
|
|
1480
1510
|
import { execa as execa3 } from "execa";
|
|
1481
1511
|
|
|
1482
1512
|
// src/core/record.ts
|
|
1483
|
-
import { existsSync as
|
|
1513
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync7, lstatSync, unlinkSync as unlinkSync2 } from "fs";
|
|
1484
1514
|
import { join as join9 } from "path";
|
|
1485
1515
|
function getInstallsDir() {
|
|
1486
1516
|
return join9(getCannbotConfigDir(), "installs");
|
|
@@ -1490,28 +1520,29 @@ function getRecordPath(pluginId) {
|
|
|
1490
1520
|
}
|
|
1491
1521
|
function readRecord(pluginId) {
|
|
1492
1522
|
const recordPath = getRecordPath(pluginId);
|
|
1493
|
-
if (!
|
|
1523
|
+
if (!existsSync9(recordPath)) {
|
|
1494
1524
|
return null;
|
|
1495
1525
|
}
|
|
1496
1526
|
try {
|
|
1497
1527
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1498
1528
|
return JSON.parse(content);
|
|
1499
1529
|
} catch {
|
|
1530
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
1500
1531
|
return null;
|
|
1501
1532
|
}
|
|
1502
1533
|
}
|
|
1503
1534
|
function writeRecord(record) {
|
|
1504
1535
|
const installsDir = getInstallsDir();
|
|
1505
|
-
if (!
|
|
1536
|
+
if (!existsSync9(installsDir)) {
|
|
1506
1537
|
mkdirSync4(installsDir, { recursive: true });
|
|
1507
1538
|
}
|
|
1508
1539
|
const recordPath = getRecordPath(record.pluginId);
|
|
1509
|
-
|
|
1540
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1510
1541
|
}
|
|
1511
1542
|
function deleteRecord(pluginId) {
|
|
1512
1543
|
const recordPath = getRecordPath(pluginId);
|
|
1513
|
-
if (
|
|
1514
|
-
|
|
1544
|
+
if (existsSync9(recordPath)) {
|
|
1545
|
+
unlinkSync2(recordPath);
|
|
1515
1546
|
}
|
|
1516
1547
|
}
|
|
1517
1548
|
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest) {
|
|
@@ -1519,24 +1550,24 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1519
1550
|
const directories = [];
|
|
1520
1551
|
if (manifest) {
|
|
1521
1552
|
const skillsDir = join9(configRoot, "skills");
|
|
1522
|
-
if (
|
|
1553
|
+
if (existsSync9(skillsDir)) {
|
|
1523
1554
|
directories.push(skillsDir);
|
|
1524
1555
|
for (const skillName of manifest.installed_skills || []) {
|
|
1525
1556
|
const skillPath = join9(skillsDir, skillName);
|
|
1526
|
-
if (
|
|
1557
|
+
if (existsSync9(skillPath) || isSymlink(skillPath)) {
|
|
1527
1558
|
files.push(skillPath);
|
|
1528
1559
|
}
|
|
1529
1560
|
}
|
|
1530
1561
|
}
|
|
1531
1562
|
const agentsDir = join9(configRoot, "agents");
|
|
1532
|
-
if (
|
|
1563
|
+
if (existsSync9(agentsDir)) {
|
|
1533
1564
|
directories.push(agentsDir);
|
|
1534
1565
|
for (const agentName of manifest.installed_agents || []) {
|
|
1535
1566
|
const agentPath = join9(agentsDir, agentName);
|
|
1536
1567
|
const agentPathMd = join9(agentsDir, agentName + ".md");
|
|
1537
|
-
if (
|
|
1568
|
+
if (existsSync9(agentPath) || isSymlink(agentPath)) {
|
|
1538
1569
|
files.push(agentPath);
|
|
1539
|
-
} else if (
|
|
1570
|
+
} else if (existsSync9(agentPathMd) || isSymlink(agentPathMd)) {
|
|
1540
1571
|
files.push(agentPathMd);
|
|
1541
1572
|
}
|
|
1542
1573
|
}
|
|
@@ -1547,16 +1578,16 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1547
1578
|
files.push(workflowsLink);
|
|
1548
1579
|
}
|
|
1549
1580
|
const manifestPath = join9(configRoot, "cannbot-manifest.json");
|
|
1550
|
-
if (
|
|
1581
|
+
if (existsSync9(manifestPath)) {
|
|
1551
1582
|
files.push(manifestPath);
|
|
1552
1583
|
}
|
|
1553
1584
|
const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
|
|
1554
|
-
if (
|
|
1585
|
+
if (existsSync9(pluginManifestPath)) {
|
|
1555
1586
|
files.push(pluginManifestPath);
|
|
1556
1587
|
}
|
|
1557
1588
|
const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
1558
1589
|
const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
|
|
1559
|
-
if (
|
|
1590
|
+
if (existsSync9(configFilePath)) {
|
|
1560
1591
|
files.push(configFilePath);
|
|
1561
1592
|
}
|
|
1562
1593
|
const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
|
|
@@ -1594,23 +1625,24 @@ function getSkillRecordPath() {
|
|
|
1594
1625
|
}
|
|
1595
1626
|
function readSkillRecord() {
|
|
1596
1627
|
const recordPath = getSkillRecordPath();
|
|
1597
|
-
if (!
|
|
1628
|
+
if (!existsSync9(recordPath)) {
|
|
1598
1629
|
return {};
|
|
1599
1630
|
}
|
|
1600
1631
|
try {
|
|
1601
1632
|
const content = readFileSync7(recordPath, "utf-8");
|
|
1602
1633
|
return JSON.parse(content);
|
|
1603
1634
|
} catch {
|
|
1635
|
+
logger.warn(t("skill_record_corrupted"));
|
|
1604
1636
|
return {};
|
|
1605
1637
|
}
|
|
1606
1638
|
}
|
|
1607
1639
|
function writeSkillRecord(record) {
|
|
1608
1640
|
const installsDir = getInstallsDir();
|
|
1609
|
-
if (!
|
|
1641
|
+
if (!existsSync9(installsDir)) {
|
|
1610
1642
|
mkdirSync4(installsDir, { recursive: true });
|
|
1611
1643
|
}
|
|
1612
1644
|
const recordPath = getSkillRecordPath();
|
|
1613
|
-
|
|
1645
|
+
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
1614
1646
|
}
|
|
1615
1647
|
function addSkillsToRecord(skillIds, tool, level, installPath) {
|
|
1616
1648
|
const record = readSkillRecord();
|
|
@@ -1651,12 +1683,12 @@ function getInstalledSkills(tool, level, installPath) {
|
|
|
1651
1683
|
const skillsDir = join9(configRoot, "skills");
|
|
1652
1684
|
return recordedSkills.filter((skillId) => {
|
|
1653
1685
|
const skillPath = join9(skillsDir, skillId);
|
|
1654
|
-
return
|
|
1686
|
+
return existsSync9(skillPath) || isSymlink(skillPath);
|
|
1655
1687
|
});
|
|
1656
1688
|
}
|
|
1657
1689
|
|
|
1658
1690
|
// src/core/backup.ts
|
|
1659
|
-
import { existsSync as
|
|
1691
|
+
import { existsSync as existsSync10, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
1660
1692
|
import { join as join10 } from "path";
|
|
1661
1693
|
function getAgentsFileName(tool) {
|
|
1662
1694
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
@@ -1673,7 +1705,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1673
1705
|
}
|
|
1674
1706
|
}
|
|
1675
1707
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1676
|
-
if (!
|
|
1708
|
+
if (!existsSync10(agentsFile)) {
|
|
1677
1709
|
return null;
|
|
1678
1710
|
}
|
|
1679
1711
|
const allRecords = readAllRecords();
|
|
@@ -1692,7 +1724,7 @@ function detectCurrentPlugin(configRoot, tool) {
|
|
|
1692
1724
|
}
|
|
1693
1725
|
function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
1694
1726
|
const agentsFile = join10(configRoot, getAgentsFileName(tool));
|
|
1695
|
-
if (!
|
|
1727
|
+
if (!existsSync10(agentsFile)) {
|
|
1696
1728
|
return null;
|
|
1697
1729
|
}
|
|
1698
1730
|
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
@@ -1711,7 +1743,7 @@ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
|
|
|
1711
1743
|
}
|
|
1712
1744
|
}
|
|
1713
1745
|
function findBackups(configRoot) {
|
|
1714
|
-
if (!
|
|
1746
|
+
if (!existsSync10(configRoot)) {
|
|
1715
1747
|
return [];
|
|
1716
1748
|
}
|
|
1717
1749
|
const files = readdirSync3(configRoot);
|
|
@@ -1749,8 +1781,8 @@ function restoreBackup(backupPath, configRoot, tool) {
|
|
|
1749
1781
|
}
|
|
1750
1782
|
function deleteBackup(backupPath) {
|
|
1751
1783
|
try {
|
|
1752
|
-
if (
|
|
1753
|
-
|
|
1784
|
+
if (existsSync10(backupPath)) {
|
|
1785
|
+
unlinkSync3(backupPath);
|
|
1754
1786
|
return true;
|
|
1755
1787
|
}
|
|
1756
1788
|
return false;
|
|
@@ -1842,7 +1874,7 @@ async function installPlugin(opts) {
|
|
|
1842
1874
|
};
|
|
1843
1875
|
}
|
|
1844
1876
|
const scriptPath = join11(opts.repoPath, plugin.dir, plugin.script);
|
|
1845
|
-
if (!
|
|
1877
|
+
if (!existsSync11(scriptPath)) {
|
|
1846
1878
|
return {
|
|
1847
1879
|
success: false,
|
|
1848
1880
|
pluginId: opts.pluginId,
|
|
@@ -1860,7 +1892,7 @@ async function installPlugin(opts) {
|
|
|
1860
1892
|
const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
|
|
1861
1893
|
const agentsFile = join11(configRoot, getAgentsFileName(opts.tool));
|
|
1862
1894
|
let backupInfo = null;
|
|
1863
|
-
if (
|
|
1895
|
+
if (existsSync11(agentsFile)) {
|
|
1864
1896
|
const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
|
|
1865
1897
|
if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
|
|
1866
1898
|
let choice = "overwrite";
|
|
@@ -1887,9 +1919,18 @@ async function installPlugin(opts) {
|
|
|
1887
1919
|
currentPlugin.pluginId,
|
|
1888
1920
|
currentPlugin.pluginName
|
|
1889
1921
|
);
|
|
1890
|
-
if (backupInfo) {
|
|
1891
|
-
logger.
|
|
1922
|
+
if (!backupInfo) {
|
|
1923
|
+
logger.error(t("backup_failed"));
|
|
1924
|
+
return {
|
|
1925
|
+
success: false,
|
|
1926
|
+
pluginId: opts.pluginId,
|
|
1927
|
+
skillsCount: 0,
|
|
1928
|
+
agentsCount: 0,
|
|
1929
|
+
errors: [t("backup_failed")],
|
|
1930
|
+
warnings: []
|
|
1931
|
+
};
|
|
1892
1932
|
}
|
|
1933
|
+
logger.success(`${t("backup_created")}: ${backupInfo.filePath}`);
|
|
1893
1934
|
}
|
|
1894
1935
|
}
|
|
1895
1936
|
try {
|
|
@@ -1930,6 +1971,7 @@ async function installPlugin(opts) {
|
|
|
1930
1971
|
}
|
|
1931
1972
|
writeRecord(record);
|
|
1932
1973
|
} catch {
|
|
1974
|
+
logger.warn(t("record_write_failed"));
|
|
1933
1975
|
}
|
|
1934
1976
|
return {
|
|
1935
1977
|
success: true,
|
|
@@ -1953,8 +1995,8 @@ async function installPlugin(opts) {
|
|
|
1953
1995
|
}
|
|
1954
1996
|
|
|
1955
1997
|
// src/core/skill-installer.ts
|
|
1956
|
-
import { existsSync as
|
|
1957
|
-
import { join as join12, dirname as
|
|
1998
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync4, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
|
|
1999
|
+
import { join as join12, dirname as dirname6 } from "path";
|
|
1958
2000
|
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
1959
2001
|
import chalk5 from "chalk";
|
|
1960
2002
|
import Table from "cli-table3";
|
|
@@ -1962,7 +2004,7 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1962
2004
|
const configRoot = getConfigRoot(tool, level);
|
|
1963
2005
|
const skillsDir = join12(configRoot, "skills");
|
|
1964
2006
|
const installPath = level === "project" ? process.cwd() : configRoot;
|
|
1965
|
-
if (!
|
|
2007
|
+
if (!existsSync12(skillsDir)) {
|
|
1966
2008
|
mkdirSync5(skillsDir, { recursive: true });
|
|
1967
2009
|
}
|
|
1968
2010
|
const results = [];
|
|
@@ -1975,15 +2017,16 @@ async function installSkills(skillIds, tool, level, repoPath) {
|
|
|
1975
2017
|
}
|
|
1976
2018
|
const sourcePath = resolveSkillSourcePath(skill, repoPath);
|
|
1977
2019
|
const targetPath = join12(skillsDir, skillId);
|
|
1978
|
-
if (!
|
|
2020
|
+
if (!existsSync12(sourcePath)) {
|
|
1979
2021
|
results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
|
|
1980
2022
|
continue;
|
|
1981
2023
|
}
|
|
1982
2024
|
try {
|
|
1983
|
-
|
|
1984
|
-
|
|
2025
|
+
const resolvedSource = realpathSync(sourcePath);
|
|
2026
|
+
if (existsSync12(targetPath) || isSymlink2(targetPath)) {
|
|
2027
|
+
unlinkSync4(targetPath);
|
|
1985
2028
|
}
|
|
1986
|
-
symlinkSync(
|
|
2029
|
+
symlinkSync(resolvedSource, targetPath);
|
|
1987
2030
|
results.push({ skillId, success: true });
|
|
1988
2031
|
installedIds.push(skillId);
|
|
1989
2032
|
} catch (error) {
|
|
@@ -2004,8 +2047,8 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2004
2047
|
for (const skillId of skillIds) {
|
|
2005
2048
|
const targetPath = join12(skillsDir, skillId);
|
|
2006
2049
|
try {
|
|
2007
|
-
if (
|
|
2008
|
-
|
|
2050
|
+
if (existsSync12(targetPath) || isSymlink2(targetPath)) {
|
|
2051
|
+
unlinkSync4(targetPath);
|
|
2009
2052
|
results.push({ skillId, success: true });
|
|
2010
2053
|
removedIds.push(skillId);
|
|
2011
2054
|
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
@@ -2020,7 +2063,7 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2020
2063
|
if (removedIds.length > 0) {
|
|
2021
2064
|
removeSkillsFromRecord(removedIds, tool, level, installPath);
|
|
2022
2065
|
}
|
|
2023
|
-
if (
|
|
2066
|
+
if (existsSync12(skillsDir)) {
|
|
2024
2067
|
try {
|
|
2025
2068
|
const entries = readdirSync4(skillsDir);
|
|
2026
2069
|
if (entries.length === 0) {
|
|
@@ -2206,8 +2249,8 @@ function listAllSkills() {
|
|
|
2206
2249
|
function resolveSkillSourcePath(skill, repoPath) {
|
|
2207
2250
|
if (skill.filePath) {
|
|
2208
2251
|
const absPath = skill.filePath.startsWith("/") ? skill.filePath : join12(repoPath, skill.filePath);
|
|
2209
|
-
if (
|
|
2210
|
-
return
|
|
2252
|
+
if (existsSync12(absPath)) {
|
|
2253
|
+
return dirname6(absPath);
|
|
2211
2254
|
}
|
|
2212
2255
|
}
|
|
2213
2256
|
return join12(repoPath, skill.source, skill.id);
|
|
@@ -2309,13 +2352,13 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2309
2352
|
}
|
|
2310
2353
|
|
|
2311
2354
|
// src/commands/init.ts
|
|
2312
|
-
import { existsSync as
|
|
2355
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
2313
2356
|
import { join as join13 } from "path";
|
|
2314
2357
|
var BACK2 = "__back__";
|
|
2315
2358
|
var CANCEL2 = "__cancel__";
|
|
2316
2359
|
async function initCommand() {
|
|
2317
2360
|
const cwd = process.cwd();
|
|
2318
|
-
if (
|
|
2361
|
+
if (existsSync13(join13(cwd, "package.json")) && (() => {
|
|
2319
2362
|
try {
|
|
2320
2363
|
const pkg = JSON.parse(readFileSync8(join13(cwd, "package.json"), "utf-8"));
|
|
2321
2364
|
return pkg.name === "@cannbot-ai/install-helper";
|
|
@@ -2543,7 +2586,7 @@ async function listCommand() {
|
|
|
2543
2586
|
|
|
2544
2587
|
// src/commands/doctor.ts
|
|
2545
2588
|
import chalk8 from "chalk";
|
|
2546
|
-
import { existsSync as
|
|
2589
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync5, mkdirSync as mkdirSync6 } from "fs";
|
|
2547
2590
|
import { join as join14 } from "path";
|
|
2548
2591
|
async function doctorCommand(options = {}) {
|
|
2549
2592
|
console.log();
|
|
@@ -2587,7 +2630,7 @@ async function doctorCommand(options = {}) {
|
|
|
2587
2630
|
const configRoot = getConfigRoot(primaryTool, "project");
|
|
2588
2631
|
const skillsDir = getSkillsDir(configRoot);
|
|
2589
2632
|
const agentsDir = getAgentsDir(configRoot);
|
|
2590
|
-
if (
|
|
2633
|
+
if (existsSync14(skillsDir)) {
|
|
2591
2634
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2592
2635
|
if (brokenLinks === 0) {
|
|
2593
2636
|
const count = readdirSync5(skillsDir).length;
|
|
@@ -2609,7 +2652,7 @@ async function doctorCommand(options = {}) {
|
|
|
2609
2652
|
console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2610
2653
|
}
|
|
2611
2654
|
}
|
|
2612
|
-
if (
|
|
2655
|
+
if (existsSync14(agentsDir)) {
|
|
2613
2656
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2614
2657
|
if (brokenLinks === 0) {
|
|
2615
2658
|
const count = readdirSync5(agentsDir).length;
|
|
@@ -2635,7 +2678,7 @@ async function doctorCommand(options = {}) {
|
|
|
2635
2678
|
console.log(chalk8.bold(` ${t("doctor_config")}`));
|
|
2636
2679
|
const configFile = getConfigFileName(primaryTool);
|
|
2637
2680
|
const configPath = join14(process.cwd(), configFile);
|
|
2638
|
-
if (
|
|
2681
|
+
if (existsSync14(configPath)) {
|
|
2639
2682
|
console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2640
2683
|
} else {
|
|
2641
2684
|
console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
@@ -2662,7 +2705,7 @@ function checkBrokenLinks(dir) {
|
|
|
2662
2705
|
try {
|
|
2663
2706
|
const stats = lstatSync3(fullPath);
|
|
2664
2707
|
if (stats.isSymbolicLink()) {
|
|
2665
|
-
if (!
|
|
2708
|
+
if (!existsSync14(fullPath)) {
|
|
2666
2709
|
broken++;
|
|
2667
2710
|
}
|
|
2668
2711
|
}
|
|
@@ -2683,8 +2726,8 @@ function fixBrokenLinks(dir) {
|
|
|
2683
2726
|
try {
|
|
2684
2727
|
const stats = lstatSync3(fullPath);
|
|
2685
2728
|
if (stats.isSymbolicLink()) {
|
|
2686
|
-
if (!
|
|
2687
|
-
|
|
2729
|
+
if (!existsSync14(fullPath)) {
|
|
2730
|
+
unlinkSync5(fullPath);
|
|
2688
2731
|
fixed++;
|
|
2689
2732
|
}
|
|
2690
2733
|
}
|
|
@@ -2758,7 +2801,10 @@ async function installCommand(names, options) {
|
|
|
2758
2801
|
tool2 = validateTool(options.tool);
|
|
2759
2802
|
} else {
|
|
2760
2803
|
const detected = await selectToolWithDetection();
|
|
2761
|
-
if (detected === "back" || detected === "cancel")
|
|
2804
|
+
if (detected === "back" || detected === "cancel") {
|
|
2805
|
+
logger.info(t("init_cancelled"));
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2762
2808
|
tool2 = detected;
|
|
2763
2809
|
}
|
|
2764
2810
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2807,7 +2853,10 @@ async function installCommand(names, options) {
|
|
|
2807
2853
|
tool2 = validateTool(options.tool);
|
|
2808
2854
|
} else {
|
|
2809
2855
|
const detected = await selectToolWithDetection();
|
|
2810
|
-
if (detected === "back" || detected === "cancel")
|
|
2856
|
+
if (detected === "back" || detected === "cancel") {
|
|
2857
|
+
logger.info(t("init_cancelled"));
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2811
2860
|
tool2 = detected;
|
|
2812
2861
|
}
|
|
2813
2862
|
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
@@ -2818,11 +2867,17 @@ async function installCommand(names, options) {
|
|
|
2818
2867
|
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2819
2868
|
if (selectedSkills === "back") {
|
|
2820
2869
|
const redetected = await selectToolWithDetection();
|
|
2821
|
-
if (redetected === "back" || redetected === "cancel")
|
|
2870
|
+
if (redetected === "back" || redetected === "cancel") {
|
|
2871
|
+
logger.info(t("init_cancelled"));
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2822
2874
|
tool2 = redetected;
|
|
2823
2875
|
break;
|
|
2824
2876
|
}
|
|
2825
|
-
if (selectedSkills === "cancel")
|
|
2877
|
+
if (selectedSkills === "cancel") {
|
|
2878
|
+
logger.info(t("init_cancelled"));
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2826
2881
|
if (selectedSkills.length === 0) {
|
|
2827
2882
|
logger.info(t("install_no_skill_selected"));
|
|
2828
2883
|
return;
|
|
@@ -2897,7 +2952,10 @@ async function installCommand(names, options) {
|
|
|
2897
2952
|
tool = validateTool(options.tool);
|
|
2898
2953
|
} else {
|
|
2899
2954
|
const detected = await selectToolWithDetection();
|
|
2900
|
-
if (detected === "back" || detected === "cancel")
|
|
2955
|
+
if (detected === "back" || detected === "cancel") {
|
|
2956
|
+
logger.info(t("init_cancelled"));
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2901
2959
|
tool = detected;
|
|
2902
2960
|
}
|
|
2903
2961
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -3015,7 +3073,8 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
|
|
|
3015
3073
|
}
|
|
3016
3074
|
|
|
3017
3075
|
// src/commands/uninstall.ts
|
|
3018
|
-
import { existsSync as
|
|
3076
|
+
import { existsSync as existsSync15, unlinkSync as unlinkSync6, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
|
|
3077
|
+
import { resolve } from "path";
|
|
3019
3078
|
import chalk11 from "chalk";
|
|
3020
3079
|
async function uninstallCommand(names, options) {
|
|
3021
3080
|
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
@@ -3045,12 +3104,19 @@ async function uninstallCommand(names, options) {
|
|
|
3045
3104
|
continue;
|
|
3046
3105
|
}
|
|
3047
3106
|
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
3107
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3108
|
+
const allowedBases = [resolve(configRoot), resolve(record.installPath)];
|
|
3109
|
+
const isSafePath = (p) => {
|
|
3110
|
+
const resolved = resolve(p);
|
|
3111
|
+
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + "/"));
|
|
3112
|
+
};
|
|
3048
3113
|
let removedFiles = 0;
|
|
3049
3114
|
let removedDirs = 0;
|
|
3050
3115
|
for (const filePath of record.files) {
|
|
3116
|
+
if (!isSafePath(filePath)) continue;
|
|
3051
3117
|
try {
|
|
3052
|
-
if (
|
|
3053
|
-
|
|
3118
|
+
if (existsSync15(filePath) || isSymlink3(filePath)) {
|
|
3119
|
+
unlinkSync6(filePath);
|
|
3054
3120
|
const name = filePath.split("/").pop() || filePath;
|
|
3055
3121
|
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
3056
3122
|
removedFiles++;
|
|
@@ -3058,14 +3124,13 @@ async function uninstallCommand(names, options) {
|
|
|
3058
3124
|
} catch {
|
|
3059
3125
|
}
|
|
3060
3126
|
}
|
|
3061
|
-
const
|
|
3062
|
-
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
3127
|
+
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
3063
3128
|
for (const dirPath of sortedDirs) {
|
|
3064
3129
|
try {
|
|
3065
|
-
if (dirPath === configRoot) {
|
|
3130
|
+
if (resolve(dirPath) === resolve(configRoot)) {
|
|
3066
3131
|
continue;
|
|
3067
3132
|
}
|
|
3068
|
-
if (
|
|
3133
|
+
if (existsSync15(dirPath)) {
|
|
3069
3134
|
const entries = readdirSync6(dirPath);
|
|
3070
3135
|
if (entries.length === 0) {
|
|
3071
3136
|
rmdirSync2(dirPath);
|
|
@@ -3136,7 +3201,10 @@ async function updateCommand(pluginNames, options) {
|
|
|
3136
3201
|
tool = validateTool(options.tool);
|
|
3137
3202
|
} else {
|
|
3138
3203
|
const detected = await selectToolWithDetection();
|
|
3139
|
-
if (detected === "back" || detected === "cancel")
|
|
3204
|
+
if (detected === "back" || detected === "cancel") {
|
|
3205
|
+
logger.info(t("init_cancelled"));
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3140
3208
|
tool = detected;
|
|
3141
3209
|
}
|
|
3142
3210
|
const level = options.level ? validateLevel(options.level) : "project";
|
|
@@ -3210,7 +3278,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3210
3278
|
|
|
3211
3279
|
// src/commands/info.ts
|
|
3212
3280
|
import chalk13 from "chalk";
|
|
3213
|
-
import { existsSync as
|
|
3281
|
+
import { existsSync as existsSync16, readdirSync as readdirSync7 } from "fs";
|
|
3214
3282
|
import { join as join15 } from "path";
|
|
3215
3283
|
async function infoCommand(pluginName) {
|
|
3216
3284
|
const plugin = findPlugin(pluginName);
|
|
@@ -3236,11 +3304,11 @@ async function infoCommand(pluginName) {
|
|
|
3236
3304
|
const repoManager = createRepositoryManager();
|
|
3237
3305
|
const repoPath = await repoManager.ensureRepo();
|
|
3238
3306
|
const quickstartPath = join15(repoPath, plugin.dir, "quickstart.md");
|
|
3239
|
-
if (
|
|
3307
|
+
if (existsSync16(quickstartPath)) {
|
|
3240
3308
|
console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
|
|
3241
3309
|
}
|
|
3242
3310
|
const agentsDir = join15(repoPath, plugin.dir, "agents");
|
|
3243
|
-
if (
|
|
3311
|
+
if (existsSync16(agentsDir)) {
|
|
3244
3312
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3245
3313
|
if (agentFiles.length > 0) {
|
|
3246
3314
|
console.log();
|
|
@@ -3298,7 +3366,7 @@ function createCLI() {
|
|
|
3298
3366
|
setLanguage(config.language);
|
|
3299
3367
|
}
|
|
3300
3368
|
const program2 = new Command();
|
|
3301
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.
|
|
3369
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.34");
|
|
3302
3370
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3303
3371
|
await initCommand();
|
|
3304
3372
|
});
|