@cannbot-ai/install-helper 0.0.2-beta.10 → 0.0.2-beta.12
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 +291 -258
- 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",
|
|
@@ -589,6 +346,8 @@ var en_US = {
|
|
|
589
346
|
error_tool_not_found: "No AI coding tool detected",
|
|
590
347
|
error_plugin_not_found: "Plugin not found",
|
|
591
348
|
error_repo_not_found: "Repository not found",
|
|
349
|
+
error_invalid_tool: "Unsupported AI tool: {tool}\nSupported tools: {tools}",
|
|
350
|
+
error_invalid_level: "Unsupported install level: {level}\nSupported levels: {levels}",
|
|
592
351
|
quick_start: "Launch command",
|
|
593
352
|
quick_start_hint: "Try: help me develop an Abs operator",
|
|
594
353
|
skill_select_category: "Select Skill category",
|
|
@@ -670,6 +429,279 @@ function t(key) {
|
|
|
670
429
|
return currentLang === "zh_CN" ? zh_CN[key] : en_US[key];
|
|
671
430
|
}
|
|
672
431
|
|
|
432
|
+
// src/utils/paths.ts
|
|
433
|
+
var VALID_TOOLS = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
434
|
+
var VALID_LEVELS = ["project", "global"];
|
|
435
|
+
function validateTool(tool) {
|
|
436
|
+
if (!VALID_TOOLS.includes(tool)) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return tool;
|
|
442
|
+
}
|
|
443
|
+
function validateLevel(level) {
|
|
444
|
+
if (!VALID_LEVELS.includes(level)) {
|
|
445
|
+
throw new Error(
|
|
446
|
+
t("error_invalid_level").replace("{level}", level).replace("{levels}", VALID_LEVELS.join(", "))
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
return level;
|
|
450
|
+
}
|
|
451
|
+
function detectTraeVariant() {
|
|
452
|
+
const home = homedir();
|
|
453
|
+
if (existsSync(join(home, ".trae-cn"))) return "ide";
|
|
454
|
+
if (existsSync(join(home, ".marscode"))) return "plugin";
|
|
455
|
+
if (existsSync(join(home, ".traecli"))) return "cli";
|
|
456
|
+
return "unknown";
|
|
457
|
+
}
|
|
458
|
+
function getConfigRoot(tool, level, base) {
|
|
459
|
+
const home = homedir();
|
|
460
|
+
if (level === "global") {
|
|
461
|
+
switch (tool) {
|
|
462
|
+
case "opencode":
|
|
463
|
+
return join(home, ".config", "opencode");
|
|
464
|
+
case "claude":
|
|
465
|
+
return join(home, ".claude");
|
|
466
|
+
case "trae": {
|
|
467
|
+
const variant = detectTraeVariant();
|
|
468
|
+
switch (variant) {
|
|
469
|
+
case "plugin":
|
|
470
|
+
return join(home, ".marscode");
|
|
471
|
+
case "cli":
|
|
472
|
+
return join(home, ".traecli");
|
|
473
|
+
default:
|
|
474
|
+
return join(home, ".trae-cn");
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
case "cursor":
|
|
478
|
+
return join(home, ".cursor");
|
|
479
|
+
case "copilot":
|
|
480
|
+
return join(home, ".copilot");
|
|
481
|
+
default:
|
|
482
|
+
throw new Error(
|
|
483
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const baseDir = base || process.cwd();
|
|
488
|
+
switch (tool) {
|
|
489
|
+
case "opencode":
|
|
490
|
+
return join(baseDir, ".opencode");
|
|
491
|
+
case "claude":
|
|
492
|
+
return join(baseDir, ".claude");
|
|
493
|
+
case "trae": {
|
|
494
|
+
const variant = detectTraeVariant();
|
|
495
|
+
switch (variant) {
|
|
496
|
+
case "plugin":
|
|
497
|
+
return join(baseDir, ".marscode");
|
|
498
|
+
case "cli":
|
|
499
|
+
return join(baseDir, ".traecli");
|
|
500
|
+
default:
|
|
501
|
+
return join(baseDir, ".trae");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
case "cursor":
|
|
505
|
+
return join(baseDir, ".cursor");
|
|
506
|
+
case "copilot":
|
|
507
|
+
return join(baseDir, ".github");
|
|
508
|
+
default:
|
|
509
|
+
throw new Error(
|
|
510
|
+
t("error_invalid_tool").replace("{tool}", tool).replace("{tools}", VALID_TOOLS.join(", "))
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function getConfigFileName(tool) {
|
|
515
|
+
return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
516
|
+
}
|
|
517
|
+
function getSkillsDir(configRoot) {
|
|
518
|
+
return join(configRoot, "skills");
|
|
519
|
+
}
|
|
520
|
+
function getAgentsDir(configRoot) {
|
|
521
|
+
return join(configRoot, "agents");
|
|
522
|
+
}
|
|
523
|
+
function getManifestPath(configRoot) {
|
|
524
|
+
return join(configRoot, "cannbot-manifest.json");
|
|
525
|
+
}
|
|
526
|
+
function getCannbotConfigDir() {
|
|
527
|
+
return join(homedir(), ".cannbot");
|
|
528
|
+
}
|
|
529
|
+
function getCannbotRepoPath() {
|
|
530
|
+
return join(getCannbotConfigDir(), "repo");
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/core/detector.ts
|
|
534
|
+
async function getCommandVersion(cmd, args = ["--version"]) {
|
|
535
|
+
try {
|
|
536
|
+
const result = await execa(cmd, args, { timeout: 5e3 });
|
|
537
|
+
const output = result.stdout.trim();
|
|
538
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
539
|
+
return match ? match[1] : output.split("\n")[0];
|
|
540
|
+
} catch {
|
|
541
|
+
return void 0;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async function getCommandPath(cmd) {
|
|
545
|
+
try {
|
|
546
|
+
const result = await execa("which", [cmd], { timeout: 5e3 });
|
|
547
|
+
return result.stdout.trim();
|
|
548
|
+
} catch {
|
|
549
|
+
return void 0;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
async function detectOpenCode() {
|
|
553
|
+
const path = await getCommandPath("opencode");
|
|
554
|
+
if (!path) return void 0;
|
|
555
|
+
const version = await getCommandVersion("opencode");
|
|
556
|
+
return { name: "opencode", version, path };
|
|
557
|
+
}
|
|
558
|
+
async function detectClaude() {
|
|
559
|
+
const path = await getCommandPath("claude");
|
|
560
|
+
if (!path) return void 0;
|
|
561
|
+
const version = await getCommandVersion("claude");
|
|
562
|
+
return { name: "claude", version, path };
|
|
563
|
+
}
|
|
564
|
+
async function detectTrae() {
|
|
565
|
+
const home = homedir2();
|
|
566
|
+
const variant = detectTraeVariant();
|
|
567
|
+
let path;
|
|
568
|
+
if (variant === "ide" && existsSync2(join2(home, ".trae-cn"))) {
|
|
569
|
+
path = join2(home, ".trae-cn");
|
|
570
|
+
} else if (variant === "plugin" && existsSync2(join2(home, ".marscode"))) {
|
|
571
|
+
path = join2(home, ".marscode");
|
|
572
|
+
} else if (variant === "cli" && existsSync2(join2(home, ".traecli"))) {
|
|
573
|
+
path = join2(home, ".traecli");
|
|
574
|
+
} else if (variant === "unknown") {
|
|
575
|
+
const cmdPath = await getCommandPath("trae");
|
|
576
|
+
if (cmdPath) path = cmdPath;
|
|
577
|
+
}
|
|
578
|
+
if (!path) return void 0;
|
|
579
|
+
return { name: "trae", version: variant, path };
|
|
580
|
+
}
|
|
581
|
+
async function detectCursor() {
|
|
582
|
+
const path = await getCommandPath("cursor");
|
|
583
|
+
if (path) {
|
|
584
|
+
const version = await getCommandVersion("cursor");
|
|
585
|
+
return { name: "cursor", version, path };
|
|
586
|
+
}
|
|
587
|
+
if (process.platform === "darwin") {
|
|
588
|
+
const appPath = "/Applications/Cursor.app";
|
|
589
|
+
if (existsSync2(appPath)) {
|
|
590
|
+
return { name: "cursor", path: appPath };
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return void 0;
|
|
594
|
+
}
|
|
595
|
+
async function detectCopilot() {
|
|
596
|
+
const path = await getCommandPath("gh");
|
|
597
|
+
if (!path) return void 0;
|
|
598
|
+
try {
|
|
599
|
+
await execa("gh", ["extension", "list"], { timeout: 5e3 });
|
|
600
|
+
return { name: "copilot", path };
|
|
601
|
+
} catch {
|
|
602
|
+
return void 0;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async function detectTools() {
|
|
606
|
+
const detectors = [
|
|
607
|
+
detectOpenCode,
|
|
608
|
+
detectClaude,
|
|
609
|
+
detectTrae,
|
|
610
|
+
detectCursor,
|
|
611
|
+
detectCopilot
|
|
612
|
+
];
|
|
613
|
+
const results = await Promise.allSettled(detectors.map((d) => d()));
|
|
614
|
+
const tools = [];
|
|
615
|
+
for (const result of results) {
|
|
616
|
+
if (result.status === "fulfilled" && result.value) {
|
|
617
|
+
tools.push(result.value);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return tools;
|
|
621
|
+
}
|
|
622
|
+
function getToolDisplayName(tool) {
|
|
623
|
+
const names = {
|
|
624
|
+
opencode: "OpenCode",
|
|
625
|
+
claude: "Claude Code",
|
|
626
|
+
trae: "Trae",
|
|
627
|
+
cursor: "Cursor",
|
|
628
|
+
copilot: "GitHub Copilot"
|
|
629
|
+
};
|
|
630
|
+
return names[tool];
|
|
631
|
+
}
|
|
632
|
+
function getAllTools() {
|
|
633
|
+
return ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/core/manifest.ts
|
|
637
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
638
|
+
import { join as join3 } from "path";
|
|
639
|
+
function readManifest(configRoot) {
|
|
640
|
+
const manifestPath = getManifestPath(configRoot);
|
|
641
|
+
if (existsSync3(manifestPath)) {
|
|
642
|
+
try {
|
|
643
|
+
const content = readFileSync(manifestPath, "utf-8");
|
|
644
|
+
return JSON.parse(content);
|
|
645
|
+
} catch {
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
function readAllManifests(configRoot) {
|
|
651
|
+
const manifests = [];
|
|
652
|
+
const plugins = getAllPlugins();
|
|
653
|
+
const standardPath = getManifestPath(configRoot);
|
|
654
|
+
if (existsSync3(standardPath)) {
|
|
655
|
+
try {
|
|
656
|
+
const content = readFileSync(standardPath, "utf-8");
|
|
657
|
+
manifests.push(JSON.parse(content));
|
|
658
|
+
} catch {
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
for (const plugin of plugins) {
|
|
662
|
+
const pluginManifestPath = join3(configRoot, `${plugin.id}-manifest.json`);
|
|
663
|
+
if (existsSync3(pluginManifestPath)) {
|
|
664
|
+
try {
|
|
665
|
+
const content = readFileSync(pluginManifestPath, "utf-8");
|
|
666
|
+
manifests.push(JSON.parse(content));
|
|
667
|
+
} catch {
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return manifests;
|
|
672
|
+
}
|
|
673
|
+
function scanInstalled() {
|
|
674
|
+
const plugins = getAllPlugins();
|
|
675
|
+
const installed = [];
|
|
676
|
+
const tools = ["opencode", "claude", "trae", "cursor", "copilot"];
|
|
677
|
+
const levels = ["project", "global"];
|
|
678
|
+
for (const tool of tools) {
|
|
679
|
+
for (const level of levels) {
|
|
680
|
+
const configRoot = getConfigRoot(tool, level);
|
|
681
|
+
const manifests = readAllManifests(configRoot);
|
|
682
|
+
for (const manifest of manifests) {
|
|
683
|
+
const plugin = plugins.find((p) => p.id === manifest.team);
|
|
684
|
+
if (!plugin) continue;
|
|
685
|
+
const alreadyAdded = installed.some(
|
|
686
|
+
(p) => p.id === plugin.id && p.tool === tool && p.level === level
|
|
687
|
+
);
|
|
688
|
+
if (alreadyAdded) continue;
|
|
689
|
+
installed.push({
|
|
690
|
+
id: plugin.id,
|
|
691
|
+
displayName: plugin.displayName,
|
|
692
|
+
tool,
|
|
693
|
+
level,
|
|
694
|
+
skillsCount: manifest.installed_skills?.length || 0,
|
|
695
|
+
agentsCount: manifest.installed_agents?.length || 0,
|
|
696
|
+
installTime: manifest.install_time,
|
|
697
|
+
configRoot
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return installed;
|
|
703
|
+
}
|
|
704
|
+
|
|
673
705
|
// src/utils/logger.ts
|
|
674
706
|
import chalk from "chalk";
|
|
675
707
|
import ora from "ora";
|
|
@@ -2772,10 +2804,10 @@ async function installCommand(names, options) {
|
|
|
2772
2804
|
}
|
|
2773
2805
|
let tool2;
|
|
2774
2806
|
if (options.tool) {
|
|
2775
|
-
tool2 = options.tool;
|
|
2807
|
+
tool2 = validateTool(options.tool);
|
|
2776
2808
|
} else {
|
|
2777
2809
|
const detected = await selectToolWithDetection();
|
|
2778
|
-
if (detected === "back") return;
|
|
2810
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2779
2811
|
tool2 = detected;
|
|
2780
2812
|
}
|
|
2781
2813
|
let step = 1;
|
|
@@ -2785,10 +2817,11 @@ async function installCommand(names, options) {
|
|
|
2785
2817
|
const selectedSkills = await interactiveSkillSelect();
|
|
2786
2818
|
if (selectedSkills === "back") {
|
|
2787
2819
|
const redetected = await selectToolWithDetection();
|
|
2788
|
-
if (redetected === "back") return;
|
|
2820
|
+
if (redetected === "back" || redetected === "cancel") return;
|
|
2789
2821
|
tool2 = redetected;
|
|
2790
2822
|
break;
|
|
2791
2823
|
}
|
|
2824
|
+
if (selectedSkills === "cancel") return;
|
|
2792
2825
|
if (selectedSkills.length === 0) {
|
|
2793
2826
|
logger.info(t("install_no_skill_selected"));
|
|
2794
2827
|
return;
|
|
@@ -2800,7 +2833,7 @@ async function installCommand(names, options) {
|
|
|
2800
2833
|
const repoPath = await repoManager.ensureRepo();
|
|
2801
2834
|
spinner.succeed("\u63D2\u4EF6\u6570\u636E\u52A0\u8F7D\u5B8C\u6210");
|
|
2802
2835
|
logger.info(`${t("skill_install_progress")} ${chalk9.bold(names.length)} \u4E2A Skills...`);
|
|
2803
|
-
const level2 = options.level
|
|
2836
|
+
const level2 = options.level ? validateLevel(options.level) : "project";
|
|
2804
2837
|
const results = await installSkills(names, tool2, level2, repoPath);
|
|
2805
2838
|
let successCount = 0;
|
|
2806
2839
|
let failCount = 0;
|
|
@@ -2861,13 +2894,13 @@ async function installCommand(names, options) {
|
|
|
2861
2894
|
}
|
|
2862
2895
|
let tool;
|
|
2863
2896
|
if (options.tool) {
|
|
2864
|
-
tool = options.tool;
|
|
2897
|
+
tool = validateTool(options.tool);
|
|
2865
2898
|
} else {
|
|
2866
2899
|
const detected = await selectToolWithDetection();
|
|
2867
|
-
if (detected === "back") return;
|
|
2900
|
+
if (detected === "back" || detected === "cancel") return;
|
|
2868
2901
|
tool = detected;
|
|
2869
2902
|
}
|
|
2870
|
-
const level = options.level
|
|
2903
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2871
2904
|
if (plugins.length > 0) {
|
|
2872
2905
|
const configRoot = getConfigRoot(tool, level);
|
|
2873
2906
|
const manifests = readAllManifests(configRoot);
|
|
@@ -2983,8 +3016,8 @@ async function installPlugins(pluginIds, tool, level, repoPath) {
|
|
|
2983
3016
|
import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync5, lstatSync as lstatSync4 } from "fs";
|
|
2984
3017
|
import chalk10 from "chalk";
|
|
2985
3018
|
async function uninstallCommand(names, options) {
|
|
2986
|
-
const tool = options.tool
|
|
2987
|
-
const level = options.level
|
|
3019
|
+
const tool = options.tool ? validateTool(options.tool) : "opencode";
|
|
3020
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
2988
3021
|
const plugins = [];
|
|
2989
3022
|
const skills = [];
|
|
2990
3023
|
for (const name of names) {
|
|
@@ -3098,13 +3131,13 @@ import chalk11 from "chalk";
|
|
|
3098
3131
|
async function updateCommand(pluginNames, options) {
|
|
3099
3132
|
let tool;
|
|
3100
3133
|
if (options.tool) {
|
|
3101
|
-
tool = options.tool;
|
|
3134
|
+
tool = validateTool(options.tool);
|
|
3102
3135
|
} else {
|
|
3103
3136
|
const detected = await selectToolWithDetection();
|
|
3104
|
-
if (detected === "back") return;
|
|
3137
|
+
if (detected === "back" || detected === "cancel") return;
|
|
3105
3138
|
tool = detected;
|
|
3106
3139
|
}
|
|
3107
|
-
const level = options.level
|
|
3140
|
+
const level = options.level ? validateLevel(options.level) : "project";
|
|
3108
3141
|
let pluginsToUpdate = [];
|
|
3109
3142
|
if (pluginNames.length === 0) {
|
|
3110
3143
|
const configRoot = getConfigRoot(tool, level);
|
|
@@ -3263,7 +3296,7 @@ function createCLI() {
|
|
|
3263
3296
|
setLanguage(config.language);
|
|
3264
3297
|
}
|
|
3265
3298
|
const program2 = new Command();
|
|
3266
|
-
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.
|
|
3299
|
+
program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.12");
|
|
3267
3300
|
program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
|
|
3268
3301
|
await initCommand();
|
|
3269
3302
|
});
|