@cannbot-ai/install-helper 0.0.2-beta.9 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -4
- package/dist/index.js +466 -350
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -145,251 +145,6 @@ import { join as join2 } from "path";
|
|
|
145
145
|
import { homedir } from "os";
|
|
146
146
|
import { join } from "path";
|
|
147
147
|
import { existsSync } from "fs";
|
|
148
|
-
function detectTraeVariant() {
|
|
149
|
-
const home = homedir();
|
|
150
|
-
if (existsSync(join(home, ".trae-cn"))) return "ide";
|
|
151
|
-
if (existsSync(join(home, ".marscode"))) return "plugin";
|
|
152
|
-
if (existsSync(join(home, ".traecli"))) return "cli";
|
|
153
|
-
return "unknown";
|
|
154
|
-
}
|
|
155
|
-
function getConfigRoot(tool, level, base) {
|
|
156
|
-
const home = homedir();
|
|
157
|
-
if (level === "global") {
|
|
158
|
-
switch (tool) {
|
|
159
|
-
case "opencode":
|
|
160
|
-
return join(home, ".config", "opencode");
|
|
161
|
-
case "claude":
|
|
162
|
-
return join(home, ".claude");
|
|
163
|
-
case "trae": {
|
|
164
|
-
const variant = detectTraeVariant();
|
|
165
|
-
switch (variant) {
|
|
166
|
-
case "plugin":
|
|
167
|
-
return join(home, ".marscode");
|
|
168
|
-
case "cli":
|
|
169
|
-
return join(home, ".traecli");
|
|
170
|
-
default:
|
|
171
|
-
return join(home, ".trae-cn");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
case "cursor":
|
|
175
|
-
return join(home, ".cursor");
|
|
176
|
-
case "copilot":
|
|
177
|
-
return join(home, ".copilot");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
const baseDir = base || process.cwd();
|
|
181
|
-
switch (tool) {
|
|
182
|
-
case "opencode":
|
|
183
|
-
return join(baseDir, ".opencode");
|
|
184
|
-
case "claude":
|
|
185
|
-
return join(baseDir, ".claude");
|
|
186
|
-
case "trae": {
|
|
187
|
-
const variant = detectTraeVariant();
|
|
188
|
-
switch (variant) {
|
|
189
|
-
case "plugin":
|
|
190
|
-
return join(baseDir, ".marscode");
|
|
191
|
-
case "cli":
|
|
192
|
-
return join(baseDir, ".traecli");
|
|
193
|
-
default:
|
|
194
|
-
return join(baseDir, ".trae");
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
case "cursor":
|
|
198
|
-
return join(baseDir, ".cursor");
|
|
199
|
-
case "copilot":
|
|
200
|
-
return join(baseDir, ".github");
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function getConfigFileName(tool) {
|
|
204
|
-
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
205
|
-
}
|
|
206
|
-
function getSkillsDir(configRoot) {
|
|
207
|
-
return join(configRoot, "skills");
|
|
208
|
-
}
|
|
209
|
-
function getAgentsDir(configRoot) {
|
|
210
|
-
return join(configRoot, "agents");
|
|
211
|
-
}
|
|
212
|
-
function getManifestPath(configRoot) {
|
|
213
|
-
return join(configRoot, "cannbot-manifest.json");
|
|
214
|
-
}
|
|
215
|
-
function getCannbotConfigDir() {
|
|
216
|
-
return join(homedir(), ".cannbot");
|
|
217
|
-
}
|
|
218
|
-
function getCannbotRepoPath() {
|
|
219
|
-
return join(getCannbotConfigDir(), "repo");
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// src/core/detector.ts
|
|
223
|
-
async function getCommandVersion(cmd, args = ["--version"]) {
|
|
224
|
-
try {
|
|
225
|
-
const result = await execa(cmd, args, { timeout: 5e3 });
|
|
226
|
-
const output = result.stdout.trim();
|
|
227
|
-
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
228
|
-
return match ? match[1] : output.split("\n")[0];
|
|
229
|
-
} catch {
|
|
230
|
-
return void 0;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
async function getCommandPath(cmd) {
|
|
234
|
-
try {
|
|
235
|
-
const result = await execa("which", [cmd], { timeout: 5e3 });
|
|
236
|
-
return result.stdout.trim();
|
|
237
|
-
} catch {
|
|
238
|
-
return void 0;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
async function detectOpenCode() {
|
|
242
|
-
const path = await getCommandPath("opencode");
|
|
243
|
-
if (!path) return void 0;
|
|
244
|
-
const version = await getCommandVersion("opencode");
|
|
245
|
-
return { name: "opencode", version, path };
|
|
246
|
-
}
|
|
247
|
-
async function detectClaude() {
|
|
248
|
-
const path = await getCommandPath("claude");
|
|
249
|
-
if (!path) return void 0;
|
|
250
|
-
const version = await getCommandVersion("claude");
|
|
251
|
-
return { name: "claude", version, path };
|
|
252
|
-
}
|
|
253
|
-
async function detectTrae() {
|
|
254
|
-
const home = homedir2();
|
|
255
|
-
const variant = detectTraeVariant();
|
|
256
|
-
let path;
|
|
257
|
-
if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
|
|
258
|
-
path = join2(home, ".trae-cn");
|
|
259
|
-
} else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
|
|
260
|
-
path = join2(home, ".marscode");
|
|
261
|
-
} else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
|
|
262
|
-
path = join2(home, ".traecli");
|
|
263
|
-
} else if (variant === "unknown") {
|
|
264
|
-
const cmdPath = await getCommandPath("trae");
|
|
265
|
-
if (cmdPath) path = cmdPath;
|
|
266
|
-
}
|
|
267
|
-
if (!path) return void 0;
|
|
268
|
-
return { name: "trae", version: variant, path };
|
|
269
|
-
}
|
|
270
|
-
async function detectCursor() {
|
|
271
|
-
const path = await getCommandPath("cursor");
|
|
272
|
-
if (path) {
|
|
273
|
-
const version = await getCommandVersion("cursor");
|
|
274
|
-
return { name: "cursor", version, path };
|
|
275
|
-
}
|
|
276
|
-
if (process.platform === "darwin") {
|
|
277
|
-
const appPath = "/Applications/Cursor.app";
|
|
278
|
-
if (existsSync2(appPath)) {
|
|
279
|
-
return { name: "cursor", path: appPath };
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return void 0;
|
|
283
|
-
}
|
|
284
|
-
async function detectCopilot() {
|
|
285
|
-
const path = await getCommandPath("gh");
|
|
286
|
-
if (!path) return void 0;
|
|
287
|
-
try {
|
|
288
|
-
await execa("gh", ["extension", "list"], { timeout: 5e3 });
|
|
289
|
-
return { name: "copilot", path };
|
|
290
|
-
} catch {
|
|
291
|
-
return void 0;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
async function detectTools() {
|
|
295
|
-
const detectors = [
|
|
296
|
-
detectOpenCode,
|
|
297
|
-
detectClaude,
|
|
298
|
-
detectTrae,
|
|
299
|
-
detectCursor,
|
|
300
|
-
detectCopilot
|
|
301
|
-
];
|
|
302
|
-
const results = await Promise.allSettled(detectors.map((d) => d()));
|
|
303
|
-
const tools = [];
|
|
304
|
-
for (const result of results) {
|
|
305
|
-
if (result.status === "fulfilled" && result.value) {
|
|
306
|
-
tools.push(result.value);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return tools;
|
|
310
|
-
}
|
|
311
|
-
function getToolDisplayName(tool) {
|
|
312
|
-
const names = {
|
|
313
|
-
opencode: "OpenCode",
|
|
314
|
-
claude: "Claude Code",
|
|
315
|
-
trae: "Trae",
|
|
316
|
-
cursor: "Cursor",
|
|
317
|
-
copilot: "GitHub Copilot"
|
|
318
|
-
};
|
|
319
|
-
return names[tool];
|
|
320
|
-
}
|
|
321
|
-
function getAllTools() {
|
|
322
|
-
return ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// src/core/manifest.ts
|
|
326
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
327
|
-
import { join as join3 } from "path";
|
|
328
|
-
function readManifest(configRoot) {
|
|
329
|
-
const manifestPath = getManifestPath(configRoot);
|
|
330
|
-
if (existsSync3(manifestPath)) {
|
|
331
|
-
try {
|
|
332
|
-
const content = readFileSync(manifestPath, "utf-8");
|
|
333
|
-
return JSON.parse(content);
|
|
334
|
-
} catch {
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
function readAllManifests(configRoot) {
|
|
340
|
-
const manifests = [];
|
|
341
|
-
const plugins = getAllPlugins();
|
|
342
|
-
const standardPath = getManifestPath(configRoot);
|
|
343
|
-
if (existsSync3(standardPath)) {
|
|
344
|
-
try {
|
|
345
|
-
const content = readFileSync(standardPath, "utf-8");
|
|
346
|
-
manifests.push(JSON.parse(content));
|
|
347
|
-
} catch {
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
for (const plugin of plugins) {
|
|
351
|
-
const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
|
|
352
|
-
if (existsSync3(pluginManifestPath)) {
|
|
353
|
-
try {
|
|
354
|
-
const content = readFileSync(pluginManifestPath, "utf-8");
|
|
355
|
-
manifests.push(JSON.parse(content));
|
|
356
|
-
} catch {
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return manifests;
|
|
361
|
-
}
|
|
362
|
-
function scanInstalled() {
|
|
363
|
-
const plugins = getAllPlugins();
|
|
364
|
-
const installed = [];
|
|
365
|
-
const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
366
|
-
const levels = ["project", "global"];
|
|
367
|
-
for (const tool of tools) {
|
|
368
|
-
for (const level of levels) {
|
|
369
|
-
const configRoot = getConfigRoot(tool, level);
|
|
370
|
-
const manifests = readAllManifests(configRoot);
|
|
371
|
-
for (const manifest of manifests) {
|
|
372
|
-
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
373
|
-
if (!plugin) continue;
|
|
374
|
-
const alreadyAdded = installed.some(
|
|
375
|
-
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
376
|
-
);
|
|
377
|
-
if (alreadyAdded) continue;
|
|
378
|
-
installed.push({
|
|
379
|
-
id: plugin.id,
|
|
380
|
-
displayName: plugin.displayName,
|
|
381
|
-
tool,
|
|
382
|
-
level,
|
|
383
|
-
skillsCount: manifest.installed_skills?.length || 0,
|
|
384
|
-
agentsCount: manifest.installed_agents?.length || 0,
|
|
385
|
-
installTime: manifest.install_time,
|
|
386
|
-
configRoot
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return installed;
|
|
392
|
-
}
|
|
393
148
|
|
|
394
149
|
// src/utils/i18n.ts
|
|
395
150
|
var zh_CN = {
|
|
@@ -454,6 +209,8 @@ var zh_CN = {
|
|
|
454
209
|
error_tool_not_found: "\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u7F16\u7A0B\u5DE5\u5177",
|
|
455
210
|
error_plugin_not_found: "\u672A\u627E\u5230\u63D2\u4EF6",
|
|
456
211
|
error_repo_not_found: "\u4ED3\u5E93\u672A\u627E\u5230",
|
|
212
|
+
error_invalid_tool: "\u6682\u4E0D\u652F\u6301\u7684 AI \u5DE5\u5177: {tool}\n\u652F\u6301\u7684\u5DE5\u5177: {tools}",
|
|
213
|
+
error_invalid_level: "\u6682\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u7EA7\u522B: {level}\n\u652F\u6301\u7684\u7EA7\u522B: {levels}",
|
|
457
214
|
quick_start: "\u542F\u52A8\u547D\u4EE4",
|
|
458
215
|
quick_start_hint: "\u8BD5\u8BD5\u8F93\u5165: \u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
|
|
459
216
|
skill_select_category: "\u9009\u62E9 Skill \u7C7B\u522B",
|
|
@@ -525,7 +282,38 @@ var zh_CN = {
|
|
|
525
282
|
wizard_step_4_skill_title: "[4/4] \u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills \u2014 {category}",
|
|
526
283
|
install_level_project: "\u5B89\u88C5\u5230\u5F53\u524D\u76EE\u5F55\uFF08\u63A8\u8350\uFF0C\u4EC5\u5F53\u524D\u9879\u76EE\u53EF\u7528\uFF09",
|
|
527
284
|
install_level_global: "\u5B89\u88C5\u5230\u5168\u5C40\uFF08\u6240\u6709\u9879\u76EE\u5171\u4EAB\uFF09",
|
|
528
|
-
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B"
|
|
285
|
+
wizard_select_mode_title: "\u9009\u62E9\u5B89\u88C5\u7C7B\u578B",
|
|
286
|
+
skill_count_format: "{count} \u4E2A Skills",
|
|
287
|
+
result_success_format: "{count} \u6210\u529F",
|
|
288
|
+
result_failed_format: "{count} \u5931\u8D25",
|
|
289
|
+
uninstall_remove_label: "\u79FB\u9664",
|
|
290
|
+
uninstall_remove_file_format: "\u79FB\u9664 {count} \u4E2A\u6587\u4EF6",
|
|
291
|
+
uninstall_remove_dir_format: "{count} \u4E2A\u76EE\u5F55",
|
|
292
|
+
uninstall_clean_empty_dir: "\u6E05\u7406\u7A7A\u76EE\u5F55",
|
|
293
|
+
uninstall_skill_not_installed: "\u672A\u5B89\u88C5",
|
|
294
|
+
available_plugins: "\u53EF\u7528\u63D2\u4EF6",
|
|
295
|
+
available_skills: "\u53EF\u7528 Skills",
|
|
296
|
+
skill_total_count: "\u5171 {count} \u4E2A Skills",
|
|
297
|
+
wizard_confirm_install_format: "\u5373\u5C06\u5B89\u88C5 {count} \u4E2A\u63D2\u4EF6\u5230 {tool}\uFF08{level} \u7EA7\u522B\uFF09\uFF1A",
|
|
298
|
+
wizard_install_path: "\u5B89\u88C5\u8DEF\u5F84",
|
|
299
|
+
update_install_hint: "\u4F7F\u7528 {cmd} \u5B89\u88C5\u63D2\u4EF6",
|
|
300
|
+
hint_select_move: "\u79FB\u52A8",
|
|
301
|
+
hint_select_space: "\u7A7A\u683C",
|
|
302
|
+
hint_select_check: "\u52FE\u9009",
|
|
303
|
+
hint_select_confirm: "\u786E\u8BA4",
|
|
304
|
+
hint_checkbox_suffix: "\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
|
|
305
|
+
doctor_not_installed: "\u672A\u5B89\u88C5",
|
|
306
|
+
doctor_links_valid: "{count} \u4E2A\u8F6F\u94FE\u63A5\u5747\u6709\u6548",
|
|
307
|
+
doctor_broken_links: "{count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
308
|
+
doctor_fixed: "\u5DF2\u6E05\u7406 {count} \u4E2A\u5931\u6548\u94FE\u63A5",
|
|
309
|
+
doctor_not_exist: "\u4E0D\u5B58\u5728",
|
|
310
|
+
doctor_created: "\u5DF2\u521B\u5EFA {path}",
|
|
311
|
+
doctor_config_exists: "\u5B58\u5728",
|
|
312
|
+
doctor_config_not_exist: "\u4E0D\u5B58\u5728",
|
|
313
|
+
doctor_result_with_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF, {fixes} \u9879\u5DF2\u4FEE\u590D",
|
|
314
|
+
doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A, {errors} \u4E2A\u9519\u8BEF",
|
|
315
|
+
count_suffix: "\u4E2A",
|
|
316
|
+
try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50"
|
|
529
317
|
};
|
|
530
318
|
var en_US = {
|
|
531
319
|
wizard_title: "CANNBot Install Helper",
|
|
@@ -589,6 +377,8 @@ var en_US = {
|
|
|
589
377
|
error_tool_not_found: "No AI coding tool detected",
|
|
590
378
|
error_plugin_not_found: "Plugin not found",
|
|
591
379
|
error_repo_not_found: "Repository not found",
|
|
380
|
+
error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
|
|
381
|
+
error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
|
|
592
382
|
quick_start: "Launch command",
|
|
593
383
|
quick_start_hint: "Try: help me develop an Abs operator",
|
|
594
384
|
skill_select_category: "Select Skill category",
|
|
@@ -660,14 +450,347 @@ var en_US = {
|
|
|
660
450
|
wizard_step_4_skill_title: "[4/4] Select Skills to install \u2014 {category}",
|
|
661
451
|
install_level_project: "Install to current directory (recommended, current project only)",
|
|
662
452
|
install_level_global: "Install globally (all projects)",
|
|
663
|
-
wizard_select_mode_title: "Select installation type"
|
|
453
|
+
wizard_select_mode_title: "Select installation type",
|
|
454
|
+
skill_count_format: "{count} Skills",
|
|
455
|
+
result_success_format: "{count} succeeded",
|
|
456
|
+
result_failed_format: "{count} failed",
|
|
457
|
+
uninstall_remove_label: "Removed",
|
|
458
|
+
uninstall_remove_file_format: "Removed {count} files",
|
|
459
|
+
uninstall_remove_dir_format: "{count} directories",
|
|
460
|
+
uninstall_clean_empty_dir: "Cleaned empty directory",
|
|
461
|
+
uninstall_skill_not_installed: "Not installed",
|
|
462
|
+
available_plugins: "Available plugins",
|
|
463
|
+
available_skills: "Available Skills",
|
|
464
|
+
skill_total_count: "Total {count} Skills",
|
|
465
|
+
wizard_confirm_install_format: "About to install {count} plugins to {tool} ({level} level):",
|
|
466
|
+
wizard_install_path: "Install path",
|
|
467
|
+
update_install_hint: "Use {cmd} to install plugins",
|
|
468
|
+
hint_select_move: "Navigate",
|
|
469
|
+
hint_select_space: "Space",
|
|
470
|
+
hint_select_check: "Toggle",
|
|
471
|
+
hint_select_confirm: "Confirm",
|
|
472
|
+
hint_checkbox_suffix: "Press enter without selection to go back",
|
|
473
|
+
doctor_not_installed: "Not installed",
|
|
474
|
+
doctor_links_valid: "{count} symlinks all valid",
|
|
475
|
+
doctor_broken_links: "{count} broken links",
|
|
476
|
+
doctor_fixed: "Cleaned {count} broken links",
|
|
477
|
+
doctor_not_exist: "Does not exist",
|
|
478
|
+
doctor_created: "Created {path}",
|
|
479
|
+
doctor_config_exists: "Exists",
|
|
480
|
+
doctor_config_not_exist: "Does not exist",
|
|
481
|
+
doctor_result_with_fix: "{warnings} warnings, {errors} errors, {fixes} fixed",
|
|
482
|
+
doctor_result_without_fix: "{warnings} warnings, {errors} errors",
|
|
483
|
+
count_suffix: "",
|
|
484
|
+
try_prompt: "help me develop an Abs operator"
|
|
664
485
|
};
|
|
665
486
|
var currentLang = "zh_CN";
|
|
666
487
|
function setLanguage(lang) {
|
|
667
488
|
currentLang = lang;
|
|
668
489
|
}
|
|
669
|
-
function t(key) {
|
|
670
|
-
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
490
|
+
function t(key) {
|
|
491
|
+
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/utils/paths.ts
|
|
495
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
496
|
+
var VALID_LEVELS = ["project", "global"];
|
|
497
|
+
function validateTool(tool) {
|
|
498
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
499
|
+
throw new Error(
|
|
500
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
return tool;
|
|
504
|
+
}
|
|
505
|
+
function validateLevel(level) {
|
|
506
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
return level;
|
|
512
|
+
}
|
|
513
|
+
function detectTraeVariant() {
|
|
514
|
+
const home = homedir();
|
|
515
|
+
if (existsSync(join(home, ".trae-cn"))) return "ide";
|
|
516
|
+
if (existsSync(join(home, ".marscode"))) return "plugin";
|
|
517
|
+
if (existsSync(join(home, ".traecli"))) return "cli";
|
|
518
|
+
return "unknown";
|
|
519
|
+
}
|
|
520
|
+
function getConfigRoot(tool, level, base) {
|
|
521
|
+
const home = homedir();
|
|
522
|
+
if (level === "global") {
|
|
523
|
+
switch (tool) {
|
|
524
|
+
case "opencode":
|
|
525
|
+
return join(home, ".config", "opencode");
|
|
526
|
+
case "claude":
|
|
527
|
+
return join(home, ".claude");
|
|
528
|
+
case "trae": {
|
|
529
|
+
const variant = detectTraeVariant();
|
|
530
|
+
switch (variant) {
|
|
531
|
+
case "plugin":
|
|
532
|
+
return join(home, ".marscode");
|
|
533
|
+
case "cli":
|
|
534
|
+
return join(home, ".traecli");
|
|
535
|
+
default:
|
|
536
|
+
return join(home, ".trae-cn");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
case "cursor":
|
|
540
|
+
return join(home, ".cursor");
|
|
541
|
+
case "copilot":
|
|
542
|
+
return join(home, ".copilot");
|
|
543
|
+
default:
|
|
544
|
+
throw new Error(
|
|
545
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
const baseDir = base || process.cwd();
|
|
550
|
+
switch (tool) {
|
|
551
|
+
case "opencode":
|
|
552
|
+
return join(baseDir, ".opencode");
|
|
553
|
+
case "claude":
|
|
554
|
+
return join(baseDir, ".claude");
|
|
555
|
+
case "trae": {
|
|
556
|
+
const variant = detectTraeVariant();
|
|
557
|
+
switch (variant) {
|
|
558
|
+
case "plugin":
|
|
559
|
+
return join(baseDir, ".marscode");
|
|
560
|
+
case "cli":
|
|
561
|
+
return join(baseDir, ".traecli");
|
|
562
|
+
default:
|
|
563
|
+
return join(baseDir, ".trae");
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
case "cursor":
|
|
567
|
+
return join(baseDir, ".cursor");
|
|
568
|
+
case "copilot":
|
|
569
|
+
return join(baseDir, ".github");
|
|
570
|
+
default:
|
|
571
|
+
throw new Error(
|
|
572
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function getConfigFileName(tool) {
|
|
577
|
+
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
578
|
+
}
|
|
579
|
+
function getSkillsDir(configRoot) {
|
|
580
|
+
return join(configRoot, "skills");
|
|
581
|
+
}
|
|
582
|
+
function getAgentsDir(configRoot) {
|
|
583
|
+
return join(configRoot, "agents");
|
|
584
|
+
}
|
|
585
|
+
function getManifestPath(configRoot) {
|
|
586
|
+
return join(configRoot, "cannbot-manifest.json");
|
|
587
|
+
}
|
|
588
|
+
function getCannbotConfigDir() {
|
|
589
|
+
return join(homedir(), ".cannbot");
|
|
590
|
+
}
|
|
591
|
+
function getCannbotRepoPath() {
|
|
592
|
+
return join(getCannbotConfigDir(), "repo");
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/core/detector.ts
|
|
596
|
+
async function getCommandVersion(cmd, args = ["--version"]) {
|
|
597
|
+
try {
|
|
598
|
+
const result = await execa(cmd, args, { timeout: 5e3 });
|
|
599
|
+
const output = result.stdout.trim();
|
|
600
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
601
|
+
return match ? match[1] : output.split("\n")[0];
|
|
602
|
+
} catch {
|
|
603
|
+
return void 0;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function getCommandPath(cmd) {
|
|
607
|
+
try {
|
|
608
|
+
const result = await execa("which", [cmd], { timeout: 5e3 });
|
|
609
|
+
return result.stdout.trim();
|
|
610
|
+
} catch {
|
|
611
|
+
return void 0;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
async function detectOpenCode() {
|
|
615
|
+
const path = await getCommandPath("opencode");
|
|
616
|
+
if (!path) return void 0;
|
|
617
|
+
const version = await getCommandVersion("opencode");
|
|
618
|
+
return { name: "opencode", version, path };
|
|
619
|
+
}
|
|
620
|
+
async function detectClaude() {
|
|
621
|
+
const path = await getCommandPath("claude");
|
|
622
|
+
if (!path) return void 0;
|
|
623
|
+
const version = await getCommandVersion("claude");
|
|
624
|
+
return { name: "claude", version, path };
|
|
625
|
+
}
|
|
626
|
+
async function detectTrae() {
|
|
627
|
+
const home = homedir2();
|
|
628
|
+
const variant = detectTraeVariant();
|
|
629
|
+
let path;
|
|
630
|
+
if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
|
|
631
|
+
path = join2(home, ".trae-cn");
|
|
632
|
+
} else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
|
|
633
|
+
path = join2(home, ".marscode");
|
|
634
|
+
} else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
|
|
635
|
+
path = join2(home, ".traecli");
|
|
636
|
+
} else if (variant === "unknown") {
|
|
637
|
+
const cmdPath = await getCommandPath("trae");
|
|
638
|
+
if (cmdPath) path = cmdPath;
|
|
639
|
+
}
|
|
640
|
+
if (!path) return void 0;
|
|
641
|
+
return { name: "trae", version: variant, path };
|
|
642
|
+
}
|
|
643
|
+
async function detectCursor() {
|
|
644
|
+
const path = await getCommandPath("cursor");
|
|
645
|
+
if (path) {
|
|
646
|
+
const version = await getCommandVersion("cursor");
|
|
647
|
+
return { name: "cursor", version, path };
|
|
648
|
+
}
|
|
649
|
+
if (process.platform === "darwin") {
|
|
650
|
+
const appPath = "/Applications/Cursor.app";
|
|
651
|
+
if (existsSync2(appPath)) {
|
|
652
|
+
return { name: "cursor", path: appPath };
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return void 0;
|
|
656
|
+
}
|
|
657
|
+
async function detectCopilot() {
|
|
658
|
+
const path = await getCommandPath("gh");
|
|
659
|
+
if (!path) return void 0;
|
|
660
|
+
try {
|
|
661
|
+
await execa("gh", ["extension", "list"], { timeout: 5e3 });
|
|
662
|
+
return { name: "copilot", path };
|
|
663
|
+
} catch {
|
|
664
|
+
return void 0;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
async function detectTools() {
|
|
668
|
+
const detectors = [
|
|
669
|
+
detectOpenCode,
|
|
670
|
+
detectClaude,
|
|
671
|
+
detectTrae,
|
|
672
|
+
detectCursor,
|
|
673
|
+
detectCopilot
|
|
674
|
+
];
|
|
675
|
+
const results = await Promise.allSettled(detectors.map((d) => d()));
|
|
676
|
+
const tools = [];
|
|
677
|
+
for (const result of results) {
|
|
678
|
+
if (result.status === "fulfilled" && result.value) {
|
|
679
|
+
tools.push(result.value);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return tools;
|
|
683
|
+
}
|
|
684
|
+
function getToolDisplayName(tool) {
|
|
685
|
+
const names = {
|
|
686
|
+
opencode: "OpenCode",
|
|
687
|
+
claude: "Claude Code",
|
|
688
|
+
trae: "Trae",
|
|
689
|
+
cursor: "Cursor",
|
|
690
|
+
copilot: "GitHub Copilot"
|
|
691
|
+
};
|
|
692
|
+
return names[tool];
|
|
693
|
+
}
|
|
694
|
+
function getAllTools() {
|
|
695
|
+
return ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// src/core/manifest.ts
|
|
699
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
700
|
+
import { join as join3 } from "path";
|
|
701
|
+
function readManifest(configRoot) {
|
|
702
|
+
const manifestPath = getManifestPath(configRoot);
|
|
703
|
+
if (existsSync3(manifestPath)) {
|
|
704
|
+
try {
|
|
705
|
+
const content = readFileSync(manifestPath, "utf-8");
|
|
706
|
+
return JSON.parse(content);
|
|
707
|
+
} catch {
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
function readAllManifests(configRoot) {
|
|
713
|
+
const manifests = [];
|
|
714
|
+
const plugins = getAllPlugins();
|
|
715
|
+
const standardPath = getManifestPath(configRoot);
|
|
716
|
+
if (existsSync3(standardPath)) {
|
|
717
|
+
try {
|
|
718
|
+
const content = readFileSync(standardPath, "utf-8");
|
|
719
|
+
manifests.push(JSON.parse(content));
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
for (const plugin of plugins) {
|
|
724
|
+
const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
|
|
725
|
+
if (existsSync3(pluginManifestPath)) {
|
|
726
|
+
try {
|
|
727
|
+
const content = readFileSync(pluginManifestPath, "utf-8");
|
|
728
|
+
manifests.push(JSON.parse(content));
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return manifests;
|
|
734
|
+
}
|
|
735
|
+
function scanInstalled() {
|
|
736
|
+
const plugins = getAllPlugins();
|
|
737
|
+
const installed = [];
|
|
738
|
+
const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
739
|
+
const levels = ["project", "global"];
|
|
740
|
+
for (const tool of tools) {
|
|
741
|
+
for (const level of levels) {
|
|
742
|
+
const configRoot = getConfigRoot(tool, level);
|
|
743
|
+
const manifests = readAllManifests(configRoot);
|
|
744
|
+
for (const manifest of manifests) {
|
|
745
|
+
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
746
|
+
if (!plugin) continue;
|
|
747
|
+
if (!verifyPluginFilesExist(configRoot, manifest, tool, level)) {
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
const alreadyAdded = installed.some(
|
|
751
|
+
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
752
|
+
);
|
|
753
|
+
if (alreadyAdded) continue;
|
|
754
|
+
installed.push({
|
|
755
|
+
id: plugin.id,
|
|
756
|
+
displayName: plugin.displayName,
|
|
757
|
+
tool,
|
|
758
|
+
level,
|
|
759
|
+
skillsCount: manifest.installed_skills?.length || 0,
|
|
760
|
+
agentsCount: manifest.installed_agents?.length || 0,
|
|
761
|
+
installTime: manifest.install_time,
|
|
762
|
+
configRoot
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return installed;
|
|
768
|
+
}
|
|
769
|
+
function verifyPluginFilesExist(configRoot, manifest, tool, level) {
|
|
770
|
+
const skillsDir = join3(configRoot, "skills");
|
|
771
|
+
const agentsDir = join3(configRoot, "agents");
|
|
772
|
+
if (manifest.installed_skills && manifest.installed_skills.length > 0) {
|
|
773
|
+
const hasAnySkill = manifest.installed_skills.some((skillId) => {
|
|
774
|
+
const skillPath = join3(skillsDir, skillId);
|
|
775
|
+
return existsSync3(skillPath);
|
|
776
|
+
});
|
|
777
|
+
if (!hasAnySkill) return false;
|
|
778
|
+
}
|
|
779
|
+
if (manifest.installed_agents && manifest.installed_agents.length > 0) {
|
|
780
|
+
const hasAnyAgent = manifest.installed_agents.some((agentId) => {
|
|
781
|
+
const agentPath = join3(agentsDir, agentId);
|
|
782
|
+
return existsSync3(agentPath);
|
|
783
|
+
});
|
|
784
|
+
if (!hasAnyAgent) return false;
|
|
785
|
+
}
|
|
786
|
+
let agentsMdPath;
|
|
787
|
+
if (tool === "opencode" && level === "project") {
|
|
788
|
+
agentsMdPath = join3(configRoot, "..", "AGENTS.md");
|
|
789
|
+
} else {
|
|
790
|
+
agentsMdPath = join3(configRoot, "AGENTS.md");
|
|
791
|
+
}
|
|
792
|
+
if (!existsSync3(agentsMdPath)) return false;
|
|
793
|
+
return true;
|
|
671
794
|
}
|
|
672
795
|
|
|
673
796
|
// src/utils/logger.ts
|
|
@@ -724,10 +847,10 @@ function printBoxTitle(title, width = 65) {
|
|
|
724
847
|
}
|
|
725
848
|
function showOperationHints(isCheckbox = false) {
|
|
726
849
|
if (isCheckbox) {
|
|
727
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
850
|
+
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
|
|
728
851
|
`);
|
|
729
852
|
} else {
|
|
730
|
-
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m
|
|
853
|
+
console.log(` \u{1F4A1} \x1B[36m\u2191\u2193\x1B[0m ${t("hint_select_move")} | \x1B[36m\u23CE\x1B[0m ${t("hint_select_confirm")}
|
|
731
854
|
`);
|
|
732
855
|
}
|
|
733
856
|
}
|
|
@@ -1036,8 +1159,8 @@ async function stepConfirm(tool, level, plugins) {
|
|
|
1036
1159
|
const displayPath = getConfigRoot(tool, level);
|
|
1037
1160
|
const toolName = getToolDisplayName(tool);
|
|
1038
1161
|
const levelText = level === "project" ? "project" : "global";
|
|
1039
|
-
logger.info(
|
|
1040
|
-
logger.info(
|
|
1162
|
+
logger.info(t("wizard_confirm_install_format").replace("{count}", chalk2.bold(String(plugins.length))).replace("{tool}", chalk2.green(toolName)).replace("{level}", chalk2.cyan(levelText)));
|
|
1163
|
+
logger.info(`${t("wizard_install_path")}: ${chalk2.cyan(displayPath)}`);
|
|
1041
1164
|
for (const plugin of selectedPlugins) {
|
|
1042
1165
|
if (plugin) {
|
|
1043
1166
|
logger.step(` \u2022 ${chalk2.bold(plugin.displayName)}`);
|
|
@@ -1064,7 +1187,7 @@ import { execa as execa2 } from "execa";
|
|
|
1064
1187
|
|
|
1065
1188
|
// src/core/scanner.ts
|
|
1066
1189
|
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1067
|
-
import { join as join5 } from "path";
|
|
1190
|
+
import { join as join5, dirname } from "path";
|
|
1068
1191
|
import { parse as parseYaml } from "yaml";
|
|
1069
1192
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
1070
1193
|
"node_modules",
|
|
@@ -1202,8 +1325,7 @@ function writeScanCache(cache) {
|
|
|
1202
1325
|
}
|
|
1203
1326
|
}
|
|
1204
1327
|
function getCachePath() {
|
|
1205
|
-
|
|
1206
|
-
return join5(homeDir, ".cannbot", "scan-cache.json");
|
|
1328
|
+
return join5(getCannbotConfigDir(), "scan-cache.json");
|
|
1207
1329
|
}
|
|
1208
1330
|
function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
|
|
1209
1331
|
const selfSkillMd = join5(dirPath, "SKILL.md");
|
|
@@ -1672,7 +1794,6 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
|
|
|
1672
1794
|
files.push(repoLinkInConfig);
|
|
1673
1795
|
}
|
|
1674
1796
|
}
|
|
1675
|
-
directories.push(configRoot);
|
|
1676
1797
|
return {
|
|
1677
1798
|
pluginId,
|
|
1678
1799
|
displayName,
|
|
@@ -1749,11 +1870,17 @@ function removeSkillsFromRecord(skillIds, tool, level, installPath) {
|
|
|
1749
1870
|
}
|
|
1750
1871
|
function getInstalledSkills(tool, level, installPath) {
|
|
1751
1872
|
const record = readSkillRecord();
|
|
1752
|
-
|
|
1873
|
+
const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
|
|
1874
|
+
const configRoot = getConfigRoot(tool, level);
|
|
1875
|
+
const skillsDir = join7(configRoot, "skills");
|
|
1876
|
+
return recordedSkills.filter((skillId) => {
|
|
1877
|
+
const skillPath = join7(skillsDir, skillId);
|
|
1878
|
+
return existsSync7(skillPath) || isSymlink(skillPath);
|
|
1879
|
+
});
|
|
1753
1880
|
}
|
|
1754
1881
|
|
|
1755
1882
|
// src/core/backup.ts
|
|
1756
|
-
import { existsSync as existsSync8, copyFileSync, readdirSync as
|
|
1883
|
+
import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1757
1884
|
import { join as join8 } from "path";
|
|
1758
1885
|
function getAgentsFileName(tool) {
|
|
1759
1886
|
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
@@ -1811,7 +1938,7 @@ function findBackups(configRoot) {
|
|
|
1811
1938
|
if (!existsSync8(configRoot)) {
|
|
1812
1939
|
return [];
|
|
1813
1940
|
}
|
|
1814
|
-
const files =
|
|
1941
|
+
const files = readdirSync2(configRoot);
|
|
1815
1942
|
const backups = [];
|
|
1816
1943
|
for (const file of files) {
|
|
1817
1944
|
if (file.includes(".cannbot-backup.")) {
|
|
@@ -2050,7 +2177,7 @@ async function installPlugin(opts) {
|
|
|
2050
2177
|
}
|
|
2051
2178
|
|
|
2052
2179
|
// src/core/skill-installer.ts
|
|
2053
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as
|
|
2180
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
|
|
2054
2181
|
import { join as join10 } from "path";
|
|
2055
2182
|
import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
|
|
2056
2183
|
import chalk4 from "chalk";
|
|
@@ -2105,10 +2232,10 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2105
2232
|
unlinkSync3(targetPath);
|
|
2106
2233
|
results.push({ skillId, success: true });
|
|
2107
2234
|
removedIds.push(skillId);
|
|
2108
|
-
logger.step(`
|
|
2235
|
+
logger.step(` ${t("uninstall_remove_label")}: ${skillId}`);
|
|
2109
2236
|
} else {
|
|
2110
2237
|
results.push({ skillId, success: false, error: "Not installed" });
|
|
2111
|
-
logger.warn(`${skillId}
|
|
2238
|
+
logger.warn(`${skillId} ${t("uninstall_skill_not_installed")}`);
|
|
2112
2239
|
}
|
|
2113
2240
|
} catch (error) {
|
|
2114
2241
|
results.push({ skillId, success: false, error: error instanceof Error ? error.message : "Unknown error" });
|
|
@@ -2119,27 +2246,17 @@ async function uninstallSkills(skillIds, tool, level) {
|
|
|
2119
2246
|
}
|
|
2120
2247
|
if (existsSync10(skillsDir)) {
|
|
2121
2248
|
try {
|
|
2122
|
-
const entries =
|
|
2249
|
+
const entries = readdirSync3(skillsDir);
|
|
2123
2250
|
if (entries.length === 0) {
|
|
2124
2251
|
rmdirSync(skillsDir);
|
|
2125
|
-
logger.step(`
|
|
2126
|
-
}
|
|
2127
|
-
} catch {
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
if (existsSync10(configRoot)) {
|
|
2131
|
-
try {
|
|
2132
|
-
const entries = readdirSync4(configRoot);
|
|
2133
|
-
if (entries.length === 0) {
|
|
2134
|
-
rmdirSync(configRoot);
|
|
2135
|
-
logger.step(` \u6E05\u7406\u7A7A\u76EE\u5F55: ${configRoot.split("/").pop()}/`);
|
|
2252
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
|
|
2136
2253
|
}
|
|
2137
2254
|
} catch {
|
|
2138
2255
|
}
|
|
2139
2256
|
}
|
|
2140
2257
|
return results;
|
|
2141
2258
|
}
|
|
2142
|
-
async function interactiveSkillSelect() {
|
|
2259
|
+
async function interactiveSkillSelect(tool, level) {
|
|
2143
2260
|
const BACK3 = "__back__";
|
|
2144
2261
|
const CANCEL3 = "__cancel__";
|
|
2145
2262
|
const categories = getAllCategories();
|
|
@@ -2175,15 +2292,8 @@ async function interactiveSkillSelect() {
|
|
|
2175
2292
|
const category = categories.find((c) => c.id === selectedCategoryId);
|
|
2176
2293
|
if (!category) return [];
|
|
2177
2294
|
printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
|
|
2178
|
-
const
|
|
2179
|
-
const
|
|
2180
|
-
const installedSkills = [];
|
|
2181
|
-
for (const tk of tools) {
|
|
2182
|
-
for (const l of levels) {
|
|
2183
|
-
const path = l === "project" ? process.cwd() : getConfigRoot(tk, l);
|
|
2184
|
-
installedSkills.push(...getInstalledSkills(tk, l, path));
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2295
|
+
const installPath = level === "project" ? process.cwd() : getConfigRoot(tool, level);
|
|
2296
|
+
const installedSkills = getInstalledSkills(tool, level, installPath);
|
|
2187
2297
|
const installedSet = new Set(installedSkills);
|
|
2188
2298
|
const skillChoices = category.skills.map((skill) => {
|
|
2189
2299
|
const isInstalled = installedSet.has(skill.id);
|
|
@@ -2248,7 +2358,7 @@ function listAllSkills() {
|
|
|
2248
2358
|
console.log(table.toString());
|
|
2249
2359
|
}
|
|
2250
2360
|
console.log();
|
|
2251
|
-
console.log(chalk4.dim(`
|
|
2361
|
+
console.log(chalk4.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
|
|
2252
2362
|
console.log();
|
|
2253
2363
|
}
|
|
2254
2364
|
function isSymlink2(path) {
|
|
@@ -2285,7 +2395,8 @@ function printPluginList(plugins, installed) {
|
|
|
2285
2395
|
]);
|
|
2286
2396
|
});
|
|
2287
2397
|
console.log();
|
|
2288
|
-
|
|
2398
|
+
const countSuffix = t("count_suffix");
|
|
2399
|
+
console.log(chalk5.bold(` ${t("list_title")} (${plugins.length}${countSuffix ? " " + countSuffix : ""})`));
|
|
2289
2400
|
console.log(table.toString());
|
|
2290
2401
|
console.log();
|
|
2291
2402
|
}
|
|
@@ -2318,15 +2429,16 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
2318
2429
|
return;
|
|
2319
2430
|
}
|
|
2320
2431
|
const toolName = getToolDisplayName(tool);
|
|
2432
|
+
const countSuffix = t("count_suffix");
|
|
2321
2433
|
const lines = [
|
|
2322
2434
|
chalk5.bold(` ${t("install_done")}!`),
|
|
2323
2435
|
"",
|
|
2324
2436
|
` ${chalk5.dim(t("install_installed_to") + ":")} ${configRoot}`,
|
|
2325
|
-
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}
|
|
2437
|
+
` ${chalk5.dim(t("install_skills_count") + ":")} ${totalSkills}${countSuffix ? " " + countSuffix : ""} ${chalk5.dim("|")} ${chalk5.dim(t("install_agents_count") + ":")} ${totalAgents}${countSuffix ? " " + countSuffix : ""}`,
|
|
2326
2438
|
"",
|
|
2327
2439
|
chalk5.bold(` ${t("install_enhanced_next_steps")}:`),
|
|
2328
2440
|
` ${chalk5.cyan("1.")} ${t("install_enhanced_launch")}: ${chalk5.green(toolName.toLowerCase())}`,
|
|
2329
|
-
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green("
|
|
2441
|
+
` ${chalk5.cyan("2.")} ${t("install_enhanced_try")}: ${chalk5.green(t("try_prompt"))}`,
|
|
2330
2442
|
` ${chalk5.cyan("3.")} ${t("install_enhanced_more")}: ${chalk5.green("install-helper list")}`,
|
|
2331
2443
|
` ${chalk5.cyan("4.")} ${t("install_enhanced_check")}: ${chalk5.green("install-helper doctor")}`,
|
|
2332
2444
|
""
|
|
@@ -2516,7 +2628,7 @@ async function skillInstallFlow() {
|
|
|
2516
2628
|
break;
|
|
2517
2629
|
}
|
|
2518
2630
|
case 2: {
|
|
2519
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2631
|
+
const selectedSkills = await interactiveSkillSelect(tool, level);
|
|
2520
2632
|
if (selectedSkills === "back") {
|
|
2521
2633
|
step = 1;
|
|
2522
2634
|
break;
|
|
@@ -2534,7 +2646,7 @@ async function skillInstallFlow() {
|
|
|
2534
2646
|
spinner.start();
|
|
2535
2647
|
const repoPath = await repoManager.ensureRepo();
|
|
2536
2648
|
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2537
|
-
logger.info(`${t("skill_install_progress")} ${chalk6.bold(selectedSkills.length)}
|
|
2649
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk6.bold(String(selectedSkills.length)))}...`);
|
|
2538
2650
|
const results = await installSkills(selectedSkills, tool, level, repoPath);
|
|
2539
2651
|
let successCount = 0;
|
|
2540
2652
|
let failCount = 0;
|
|
@@ -2552,7 +2664,7 @@ async function skillInstallFlow() {
|
|
|
2552
2664
|
}
|
|
2553
2665
|
const configRoot = getConfigRoot(tool, level);
|
|
2554
2666
|
logger.blank();
|
|
2555
|
-
logger.success(`${t("skill_install_done")}: ${chalk6.green(
|
|
2667
|
+
logger.success(`${t("skill_install_done")}: ${chalk6.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk6.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk6.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2556
2668
|
logger.blank();
|
|
2557
2669
|
logger.info(`${t("install_to")}: ${chalk6.cyan(configRoot + "/skills/")}`);
|
|
2558
2670
|
logger.info(`${t("start_to_use").replace("{tool}", chalk6.green(tool))}`);
|
|
@@ -2578,7 +2690,7 @@ async function listCommand() {
|
|
|
2578
2690
|
|
|
2579
2691
|
// src/commands/doctor.ts
|
|
2580
2692
|
import chalk7 from "chalk";
|
|
2581
|
-
import { existsSync as existsSync12, readdirSync as
|
|
2693
|
+
import { existsSync as existsSync12, readdirSync as readdirSync4, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
2582
2694
|
import { join as join12 } from "path";
|
|
2583
2695
|
async function doctorCommand(options = {}) {
|
|
2584
2696
|
console.log();
|
|
@@ -2598,7 +2710,7 @@ async function doctorCommand(options = {}) {
|
|
|
2598
2710
|
chalk7.green(" \u2713") + ` ${getToolDisplayName(tool)}${detected.version ? ` v${detected.version}` : ""}`
|
|
2599
2711
|
);
|
|
2600
2712
|
} else {
|
|
2601
|
-
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014
|
|
2713
|
+
console.log(chalk7.dim(" \u2014") + ` ${getToolDisplayName(tool)} \u2014 ${t("doctor_not_installed")}`);
|
|
2602
2714
|
}
|
|
2603
2715
|
}
|
|
2604
2716
|
console.log();
|
|
@@ -2613,7 +2725,7 @@ async function doctorCommand(options = {}) {
|
|
|
2613
2725
|
chalk7.green(" \u2713") + ` ${plugin.id.padEnd(30)} ${inst.skillsCount} skills, ${inst.agentsCount} agents`
|
|
2614
2726
|
);
|
|
2615
2727
|
} else {
|
|
2616
|
-
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014
|
|
2728
|
+
console.log(chalk7.dim(" \u2014") + ` ${plugin.id.padEnd(30)} \u2014 ${t("doctor_not_installed")}`);
|
|
2617
2729
|
}
|
|
2618
2730
|
}
|
|
2619
2731
|
console.log();
|
|
@@ -2625,45 +2737,45 @@ async function doctorCommand(options = {}) {
|
|
|
2625
2737
|
if (existsSync12(skillsDir)) {
|
|
2626
2738
|
const brokenLinks = checkBrokenLinks(skillsDir);
|
|
2627
2739
|
if (brokenLinks === 0) {
|
|
2628
|
-
const count =
|
|
2629
|
-
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${count}
|
|
2740
|
+
const count = readdirSync4(skillsDir).length;
|
|
2741
|
+
console.log(chalk7.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2630
2742
|
} else {
|
|
2631
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${brokenLinks}
|
|
2743
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2632
2744
|
warnings++;
|
|
2633
2745
|
if (options.fix) {
|
|
2634
2746
|
const fixed = fixBrokenLinks(skillsDir);
|
|
2635
2747
|
fixes += fixed;
|
|
2636
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2748
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2637
2749
|
}
|
|
2638
2750
|
}
|
|
2639
2751
|
} else {
|
|
2640
|
-
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014
|
|
2752
|
+
console.log(chalk7.dim(" \u2014") + ` ${skillsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2641
2753
|
if (options.fix) {
|
|
2642
2754
|
mkdirSync6(skillsDir, { recursive: true });
|
|
2643
2755
|
fixes++;
|
|
2644
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2756
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
|
|
2645
2757
|
}
|
|
2646
2758
|
}
|
|
2647
2759
|
if (existsSync12(agentsDir)) {
|
|
2648
2760
|
const brokenLinks = checkBrokenLinks(agentsDir);
|
|
2649
2761
|
if (brokenLinks === 0) {
|
|
2650
|
-
const count =
|
|
2651
|
-
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${count}
|
|
2762
|
+
const count = readdirSync4(agentsDir).length;
|
|
2763
|
+
console.log(chalk7.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
|
|
2652
2764
|
} else {
|
|
2653
|
-
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${brokenLinks}
|
|
2765
|
+
console.log(chalk7.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
|
|
2654
2766
|
warnings++;
|
|
2655
2767
|
if (options.fix) {
|
|
2656
2768
|
const fixed = fixBrokenLinks(agentsDir);
|
|
2657
2769
|
fixes += fixed;
|
|
2658
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}:
|
|
2770
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_cleaning")}: ${t("doctor_fixed").replace("{count}", String(fixed))}`);
|
|
2659
2771
|
}
|
|
2660
2772
|
}
|
|
2661
2773
|
} else {
|
|
2662
|
-
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014
|
|
2774
|
+
console.log(chalk7.dim(" \u2014") + ` ${agentsDir} \u2014 ${t("doctor_not_exist")}`);
|
|
2663
2775
|
if (options.fix) {
|
|
2664
2776
|
mkdirSync6(agentsDir, { recursive: true });
|
|
2665
2777
|
fixes++;
|
|
2666
|
-
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}:
|
|
2778
|
+
console.log(chalk7.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", agentsDir)}`);
|
|
2667
2779
|
}
|
|
2668
2780
|
}
|
|
2669
2781
|
console.log();
|
|
@@ -2671,19 +2783,19 @@ async function doctorCommand(options = {}) {
|
|
|
2671
2783
|
const configFile = getConfigFileName(primaryTool);
|
|
2672
2784
|
const configPath = join12(process.cwd(), configFile);
|
|
2673
2785
|
if (existsSync12(configPath)) {
|
|
2674
|
-
console.log(chalk7.green(" \u2713") + ` ${configFile}
|
|
2786
|
+
console.log(chalk7.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
|
|
2675
2787
|
} else {
|
|
2676
|
-
console.log(chalk7.dim(" \u2014") + ` ${configFile}
|
|
2788
|
+
console.log(chalk7.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
|
|
2677
2789
|
}
|
|
2678
2790
|
console.log();
|
|
2679
2791
|
console.log(chalk7.dim(" " + "\u2500".repeat(46)));
|
|
2680
2792
|
if (options.fix && fixes > 0) {
|
|
2681
2793
|
console.log(
|
|
2682
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2794
|
+
` ${t("doctor_result")}: ${t("doctor_result_with_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors)).replace("{fixes}", String(fixes))}`
|
|
2683
2795
|
);
|
|
2684
2796
|
} else {
|
|
2685
2797
|
console.log(
|
|
2686
|
-
` ${t("doctor_result")}: ${warnings}
|
|
2798
|
+
` ${t("doctor_result")}: ${t("doctor_result_without_fix").replace("{warnings}", String(warnings)).replace("{errors}", String(errors))}`
|
|
2687
2799
|
);
|
|
2688
2800
|
}
|
|
2689
2801
|
console.log();
|
|
@@ -2691,7 +2803,7 @@ async function doctorCommand(options = {}) {
|
|
|
2691
2803
|
function checkBrokenLinks(dir) {
|
|
2692
2804
|
let broken = 0;
|
|
2693
2805
|
try {
|
|
2694
|
-
const entries =
|
|
2806
|
+
const entries = readdirSync4(dir);
|
|
2695
2807
|
for (const entry of entries) {
|
|
2696
2808
|
const fullPath = join12(dir, entry);
|
|
2697
2809
|
try {
|
|
@@ -2712,7 +2824,7 @@ function checkBrokenLinks(dir) {
|
|
|
2712
2824
|
function fixBrokenLinks(dir) {
|
|
2713
2825
|
let fixed = 0;
|
|
2714
2826
|
try {
|
|
2715
|
-
const entries =
|
|
2827
|
+
const entries = readdirSync4(dir);
|
|
2716
2828
|
for (const entry of entries) {
|
|
2717
2829
|
const fullPath = join12(dir, entry);
|
|
2718
2830
|
try {
|
|
@@ -2778,29 +2890,31 @@ async function installCommand(names, options) {
|
|
|
2778
2890
|
try {
|
|
2779
2891
|
const scanRepoManager = createRepositoryManager();
|
|
2780
2892
|
await scanRepoManager.ensureRepoAndScan();
|
|
2781
|
-
scanSpinner2.succeed("
|
|
2893
|
+
scanSpinner2.succeed(t("loading_skills_list_complete"));
|
|
2782
2894
|
} catch {
|
|
2783
|
-
scanSpinner2.warn("
|
|
2895
|
+
scanSpinner2.warn(t("loading_skills_list_failed"));
|
|
2784
2896
|
}
|
|
2785
2897
|
let tool2;
|
|
2786
2898
|
if (options.tool) {
|
|
2787
|
-
tool2 = options.tool;
|
|
2899
|
+
tool2 = validateTool(options.tool);
|
|
2788
2900
|
} else {
|
|
2789
2901
|
const detected = await selectToolWithDetection();
|
|
2790
|
-
if (detected === "back") return;
|
|
2902
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2791
2903
|
tool2 = detected;
|
|
2792
2904
|
}
|
|
2905
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2793
2906
|
let step = 1;
|
|
2794
2907
|
while (true) {
|
|
2795
2908
|
switch (step) {
|
|
2796
2909
|
case 1: {
|
|
2797
|
-
const selectedSkills = await interactiveSkillSelect();
|
|
2910
|
+
const selectedSkills = await interactiveSkillSelect(tool2, level2);
|
|
2798
2911
|
if (selectedSkills === "back") {
|
|
2799
2912
|
const redetected = await selectToolWithDetection();
|
|
2800
|
-
if (redetected === "back") return;
|
|
2913
|
+
if (redetected === "back" || redetected === "cancel") return;
|
|
2801
2914
|
tool2 = redetected;
|
|
2802
2915
|
break;
|
|
2803
2916
|
}
|
|
2917
|
+
if (selectedSkills === "cancel") return;
|
|
2804
2918
|
if (selectedSkills.length === 0) {
|
|
2805
2919
|
logger.info(t("install_no_skill_selected"));
|
|
2806
2920
|
return;
|
|
@@ -2810,9 +2924,8 @@ async function installCommand(names, options) {
|
|
|
2810
2924
|
const spinner = createSpinner(t("loading_plugin_data"));
|
|
2811
2925
|
spinner.start();
|
|
2812
2926
|
const repoPath = await repoManager.ensureRepo();
|
|
2813
|
-
spinner.succeed("
|
|
2814
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)}
|
|
2815
|
-
const level2 = options.level || "project";
|
|
2927
|
+
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2928
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(names.length)))}...`);
|
|
2816
2929
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2817
2930
|
let successCount = 0;
|
|
2818
2931
|
let failCount = 0;
|
|
@@ -2830,7 +2943,7 @@ async function installCommand(names, options) {
|
|
|
2830
2943
|
}
|
|
2831
2944
|
const configRoot = getConfigRoot(tool2, level2);
|
|
2832
2945
|
logger.blank();
|
|
2833
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
2946
|
+
logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2834
2947
|
logger.blank();
|
|
2835
2948
|
logger.info(`${t("install_to")}: ${chalk9.cyan(configRoot + "/skills/")}`);
|
|
2836
2949
|
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool2))}`);
|
|
@@ -2845,9 +2958,9 @@ async function installCommand(names, options) {
|
|
|
2845
2958
|
try {
|
|
2846
2959
|
const scanRepoManager = createRepositoryManager();
|
|
2847
2960
|
await scanRepoManager.ensureRepoAndScan();
|
|
2848
|
-
scanSpinner.succeed("
|
|
2961
|
+
scanSpinner.succeed(t("loading_skills_list_complete"));
|
|
2849
2962
|
} catch {
|
|
2850
|
-
scanSpinner.warn("
|
|
2963
|
+
scanSpinner.warn(t("loading_skills_list_failed"));
|
|
2851
2964
|
}
|
|
2852
2965
|
const plugins = [];
|
|
2853
2966
|
const skills = [];
|
|
@@ -2864,22 +2977,22 @@ async function installCommand(names, options) {
|
|
|
2864
2977
|
}
|
|
2865
2978
|
logger.error(`${t("error_plugin_not_found")}: ${name}`);
|
|
2866
2979
|
const allPlugins = getAllPlugins();
|
|
2867
|
-
logger.info("
|
|
2980
|
+
logger.info(t("available_plugins") + ":");
|
|
2868
2981
|
for (const p of allPlugins) {
|
|
2869
2982
|
logger.step(` ${p.id} (${p.aliases.join(", ")})`);
|
|
2870
2983
|
}
|
|
2871
|
-
logger.info(
|
|
2984
|
+
logger.info(`${t("available_skills")}: ${chalk9.cyan("install-helper install --list")}`);
|
|
2872
2985
|
return;
|
|
2873
2986
|
}
|
|
2874
2987
|
let tool;
|
|
2875
2988
|
if (options.tool) {
|
|
2876
|
-
tool = options.tool;
|
|
2989
|
+
tool = validateTool(options.tool);
|
|
2877
2990
|
} else {
|
|
2878
2991
|
const detected = await selectToolWithDetection();
|
|
2879
|
-
if (detected === "back") return;
|
|
2992
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2880
2993
|
tool = detected;
|
|
2881
2994
|
}
|
|
2882
|
-
const level = options.level
|
|
2995
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2883
2996
|
if (plugins.length > 0) {
|
|
2884
2997
|
const configRoot = getConfigRoot(tool, level);
|
|
2885
2998
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2940,7 +3053,7 @@ async function installCommand(names, options) {
|
|
|
2940
3053
|
spinner.start();
|
|
2941
3054
|
const repoPath = await repoManager.ensureRepo();
|
|
2942
3055
|
spinner.succeed(t("loading_plugin_data_complete"));
|
|
2943
|
-
logger.info(`${t("skill_install_progress")} ${chalk9.bold(skills.length)}
|
|
3056
|
+
logger.info(`${t("skill_install_progress")} ${t("skill_count_format").replace("{count}", chalk9.bold(String(skills.length)))}...`);
|
|
2944
3057
|
const results = await installSkills(skills, tool, level, repoPath);
|
|
2945
3058
|
let successCount = 0;
|
|
2946
3059
|
let failCount = 0;
|
|
@@ -2957,7 +3070,7 @@ async function installCommand(names, options) {
|
|
|
2957
3070
|
}
|
|
2958
3071
|
}
|
|
2959
3072
|
logger.blank();
|
|
2960
|
-
logger.success(`${t("skill_install_done")}: ${chalk9.green(
|
|
3073
|
+
logger.success(`${t("skill_install_done")}: ${chalk9.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk9.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk9.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
2961
3074
|
logger.blank();
|
|
2962
3075
|
logger.info(`${t("install_to")}: ${chalk9.cyan(getConfigRoot(tool, level) + "/skills/")}`);
|
|
2963
3076
|
logger.info(`${t("start_to_use").replace("{tool}", chalk9.green(tool))}`);
|
|
@@ -2992,11 +3105,11 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2992
3105
|
}
|
|
2993
3106
|
|
|
2994
3107
|
// src/commands/uninstall.ts
|
|
2995
|
-
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as
|
|
3108
|
+
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
|
|
2996
3109
|
import chalk10 from "chalk";
|
|
2997
3110
|
async function uninstallCommand(names, options) {
|
|
2998
|
-
const tool = options.tool
|
|
2999
|
-
const level = options.level
|
|
3111
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
3112
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
3000
3113
|
const plugins = [];
|
|
3001
3114
|
const skills = [];
|
|
3002
3115
|
for (const name of names) {
|
|
@@ -3029,21 +3142,25 @@ async function uninstallCommand(names, options) {
|
|
|
3029
3142
|
if (existsSync13(filePath) || isSymlink3(filePath)) {
|
|
3030
3143
|
unlinkSync5(filePath);
|
|
3031
3144
|
const name = filePath.split("/").pop() || filePath;
|
|
3032
|
-
logger.step(`
|
|
3145
|
+
logger.step(` ${t("uninstall_remove_label")}: ${name}`);
|
|
3033
3146
|
removedFiles++;
|
|
3034
3147
|
}
|
|
3035
3148
|
} catch {
|
|
3036
3149
|
}
|
|
3037
3150
|
}
|
|
3151
|
+
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3038
3152
|
const sortedDirs = [...record.directories].sort((a, b) => b.length - a.length);
|
|
3039
3153
|
for (const dirPath of sortedDirs) {
|
|
3040
3154
|
try {
|
|
3155
|
+
if (dirPath === configRoot) {
|
|
3156
|
+
continue;
|
|
3157
|
+
}
|
|
3041
3158
|
if (existsSync13(dirPath)) {
|
|
3042
|
-
const entries =
|
|
3159
|
+
const entries = readdirSync5(dirPath);
|
|
3043
3160
|
if (entries.length === 0) {
|
|
3044
3161
|
rmdirSync2(dirPath);
|
|
3045
3162
|
const name = dirPath.split("/").pop() || dirPath;
|
|
3046
|
-
logger.step(`
|
|
3163
|
+
logger.step(` ${t("uninstall_clean_empty_dir")}: ${name}/`);
|
|
3047
3164
|
removedDirs++;
|
|
3048
3165
|
}
|
|
3049
3166
|
}
|
|
@@ -3052,7 +3169,6 @@ async function uninstallCommand(names, options) {
|
|
|
3052
3169
|
}
|
|
3053
3170
|
deleteRecord(plugin.id);
|
|
3054
3171
|
removeInstalledPlugin(plugin.id);
|
|
3055
|
-
const configRoot = getConfigRoot(record.tool, record.level);
|
|
3056
3172
|
const backups = findBackups(configRoot);
|
|
3057
3173
|
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|
|
3058
3174
|
if (otherBackups.length > 0) {
|
|
@@ -3072,13 +3188,13 @@ async function uninstallCommand(names, options) {
|
|
|
3072
3188
|
}
|
|
3073
3189
|
const totalRemoved = removedFiles + removedDirs;
|
|
3074
3190
|
if (totalRemoved > 0) {
|
|
3075
|
-
logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08
|
|
3191
|
+
logger.success(`${t("uninstall_removed")} ${plugin.displayName}\uFF08${t("uninstall_remove_file_format").replace("{count}", String(removedFiles))}\uFF0C${t("uninstall_remove_dir_format").replace("{count}", String(removedDirs))}\uFF09`);
|
|
3076
3192
|
} else {
|
|
3077
3193
|
logger.warn(`${plugin.displayName}${t("uninstall_files_gone")}`);
|
|
3078
3194
|
}
|
|
3079
3195
|
}
|
|
3080
3196
|
if (skills.length > 0) {
|
|
3081
|
-
logger.info(`${t("uninstall_skills_in_progress")} ${skills.length}
|
|
3197
|
+
logger.info(`${t("uninstall_skills_in_progress")} ${t("skill_count_format").replace("{count}", String(skills.length))}...`);
|
|
3082
3198
|
const results = await uninstallSkills(skills, tool, level);
|
|
3083
3199
|
let successCount = 0;
|
|
3084
3200
|
let failCount = 0;
|
|
@@ -3090,7 +3206,7 @@ async function uninstallCommand(names, options) {
|
|
|
3090
3206
|
}
|
|
3091
3207
|
}
|
|
3092
3208
|
logger.blank();
|
|
3093
|
-
logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(
|
|
3209
|
+
logger.success(`${t("skill_uninstall_done")}: ${chalk10.green(t("result_success_format").replace("{count}", String(successCount)))}, ${failCount > 0 ? chalk10.red(t("result_failed_format").replace("{count}", String(failCount))) : chalk10.dim(t("result_failed_format").replace("{count}", String(failCount)))}`);
|
|
3094
3210
|
logger.blank();
|
|
3095
3211
|
}
|
|
3096
3212
|
}
|
|
@@ -3107,13 +3223,13 @@ import chalk11 from "chalk";
|
|
|
3107
3223
|
async function updateCommand(pluginNames, options) {
|
|
3108
3224
|
let tool;
|
|
3109
3225
|
if (options.tool) {
|
|
3110
|
-
tool = options.tool;
|
|
3226
|
+
tool = validateTool(options.tool);
|
|
3111
3227
|
} else {
|
|
3112
3228
|
const detected = await selectToolWithDetection();
|
|
3113
|
-
if (detected === "back") return;
|
|
3229
|
+
if (detected === "back" || detected === "cancel") return;
|
|
3114
3230
|
tool = detected;
|
|
3115
3231
|
}
|
|
3116
|
-
const level = options.level
|
|
3232
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
3117
3233
|
let pluginsToUpdate = [];
|
|
3118
3234
|
if (pluginNames.length === 0) {
|
|
3119
3235
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -3121,7 +3237,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3121
3237
|
pluginsToUpdate = manifests.map((m) => m.team);
|
|
3122
3238
|
if (pluginsToUpdate.length === 0) {
|
|
3123
3239
|
logger.info(t("update_no_plugins"));
|
|
3124
|
-
logger.info(
|
|
3240
|
+
logger.info(t("update_install_hint").replace("{cmd}", chalk11.cyan("install-helper install <plugin>")));
|
|
3125
3241
|
return;
|
|
3126
3242
|
}
|
|
3127
3243
|
} else {
|
|
@@ -3184,7 +3300,7 @@ async function updateCommand(pluginNames, options) {
|
|
|
3184
3300
|
|
|
3185
3301
|
// src/commands/info.ts
|
|
3186
3302
|
import chalk12 from "chalk";
|
|
3187
|
-
import { existsSync as existsSync14, readdirSync as
|
|
3303
|
+
import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
|
|
3188
3304
|
import { join as join13 } from "path";
|
|
3189
3305
|
async function infoCommand(pluginName) {
|
|
3190
3306
|
const plugin = findPlugin(pluginName);
|
|
@@ -3215,7 +3331,7 @@ async function infoCommand(pluginName) {
|
|
|
3215
3331
|
}
|
|
3216
3332
|
const agentsDir = join13(repoPath, plugin.dir, "agents");
|
|
3217
3333
|
if (existsSync14(agentsDir)) {
|
|
3218
|
-
const agentFiles =
|
|
3334
|
+
const agentFiles = readdirSync6(agentsDir).filter((f) => f.endsWith(".md"));
|
|
3219
3335
|
if (agentFiles.length > 0) {
|
|
3220
3336
|
console.log();
|
|
3221
3337
|
console.log(` ${chalk12.bold("Agents:")}`);
|
|
@@ -3272,7 +3388,7 @@ function createCLI() {
|
|
|
3272
3388
|
setLanguage(config.language);
|
|
3273
3389
|
}
|
|
3274
3390
|
const program2 = new Command();
|
|
3275
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("
|
|
3391
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("1.0.2");
|
|
3276
3392
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3277
3393
|
await initCommand();
|
|
3278
3394
|
});
|
|
@@ -3309,7 +3425,7 @@ function createCLI() {
|
|
|
3309
3425
|
// src/index.ts
|
|
3310
3426
|
process.on("uncaughtException", (error) => {
|
|
3311
3427
|
if (error.name === "ExitPromptError") {
|
|
3312
|
-
console.log("\n
|
|
3428
|
+
console.log("\n" + t("init_cancelled"));
|
|
3313
3429
|
process.exit(0);
|
|
3314
3430
|
}
|
|
3315
3431
|
throw error;
|