@cannbot-ai/install-helper 0.0.2-beta.30 → 0.0.2-beta.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ 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 } from "fs";
15
+ import { readdirSync, readFileSync, existsSync, statSync } from "fs";
16
16
  import { join, dirname } from "path";
17
17
  import { fileURLToPath } from "url";
18
18
  import { parse as parseYaml } from "yaml";
@@ -54,6 +54,54 @@ function loadPluginsFromYaml() {
54
54
  return plugins;
55
55
  }
56
56
  var PLUGIN_REGISTRY = loadPluginsFromYaml();
57
+ var PLUGIN_DIRS = ["plugins-official", "plugins-community"];
58
+ function mergeDynamicPlugins(repoPath) {
59
+ if (!existsSync(repoPath)) return;
60
+ const existingIds = new Set(PLUGIN_REGISTRY.map((p) => p.id));
61
+ const dynamic = [];
62
+ for (const pluginDir of PLUGIN_DIRS) {
63
+ const parent = join(repoPath, pluginDir);
64
+ if (!existsSync(parent)) continue;
65
+ let entries = [];
66
+ try {
67
+ entries = readdirSync(parent);
68
+ } catch {
69
+ continue;
70
+ }
71
+ for (const entry of entries) {
72
+ const pluginPath = join(parent, entry);
73
+ if (!isDirectory(pluginPath)) continue;
74
+ if (entry === "install-helper") continue;
75
+ const initSh = join(pluginPath, "init.sh");
76
+ if (!existsSync(initSh)) continue;
77
+ const pluginId = entry;
78
+ if (existingIds.has(pluginId)) continue;
79
+ dynamic.push({
80
+ id: pluginId,
81
+ dir: `${pluginDir}/${entry}`,
82
+ displayName: entry,
83
+ script: "init.sh",
84
+ aliases: [],
85
+ skills: 0,
86
+ agents: 0,
87
+ description: ""
88
+ });
89
+ existingIds.add(pluginId);
90
+ }
91
+ }
92
+ if (dynamic.length > 0) {
93
+ PLUGIN_REGISTRY = [...PLUGIN_REGISTRY, ...dynamic].sort(
94
+ (a, b) => a.displayName < b.displayName ? -1 : a.displayName > b.displayName ? 1 : 0
95
+ );
96
+ }
97
+ }
98
+ function isDirectory(path) {
99
+ try {
100
+ return statSync(path).isDirectory();
101
+ } catch {
102
+ return false;
103
+ }
104
+ }
57
105
  function findPlugin(query) {
58
106
  const normalized = query.toLowerCase().trim();
59
107
  const exactMatch = PLUGIN_REGISTRY.find(
@@ -77,14 +125,14 @@ function getPluginById(id) {
77
125
 
78
126
  // src/core/detector.ts
79
127
  import { execa } from "execa";
80
- import { existsSync as existsSync2 } from "fs";
128
+ import { existsSync as existsSync3 } from "fs";
81
129
  import { homedir as homedir2 } from "os";
82
130
  import { join as join4 } from "path";
83
131
 
84
132
  // src/utils/paths.ts
85
133
  import { homedir } from "os";
86
134
  import { join as join3 } from "path";
87
- import { existsSync } from "fs";
135
+ import { existsSync as existsSync2 } from "fs";
88
136
 
89
137
  // src/utils/i18n.ts
90
138
  import { readFileSync as readFileSync2 } from "fs";
@@ -138,9 +186,9 @@ function validateLevel(level) {
138
186
  }
139
187
  function detectTraeVariant() {
140
188
  const home = homedir();
141
- if (existsSync(join3(home, ".trae-cn"))) return "ide";
142
- if (existsSync(join3(home, ".marscode"))) return "plugin";
143
- if (existsSync(join3(home, ".traecli"))) return "cli";
189
+ if (existsSync2(join3(home, ".trae-cn"))) return "ide";
190
+ if (existsSync2(join3(home, ".marscode"))) return "plugin";
191
+ if (existsSync2(join3(home, ".traecli"))) return "cli";
144
192
  return "unknown";
145
193
  }
146
194
  function getConfigRoot(tool, level, base) {
@@ -253,11 +301,11 @@ async function detectTrae() {
253
301
  const home = homedir2();
254
302
  const variant = detectTraeVariant();
255
303
  let path;
256
- if (variant === "ide" && existsSync2(join4(home, ".trae-cn"))) {
304
+ if (variant === "ide" && existsSync3(join4(home, ".trae-cn"))) {
257
305
  path = join4(home, ".trae-cn");
258
- } else if (variant === "plugin" && existsSync2(join4(home, ".marscode"))) {
306
+ } else if (variant === "plugin" && existsSync3(join4(home, ".marscode"))) {
259
307
  path = join4(home, ".marscode");
260
- } else if (variant === "cli" && existsSync2(join4(home, ".traecli"))) {
308
+ } else if (variant === "cli" && existsSync3(join4(home, ".traecli"))) {
261
309
  path = join4(home, ".traecli");
262
310
  } else if (variant === "unknown") {
263
311
  const cmdPath = await getCommandPath("trae");
@@ -274,7 +322,7 @@ async function detectCursor() {
274
322
  }
275
323
  if (process.platform === "darwin") {
276
324
  const appPath = "/Applications/Cursor.app";
277
- if (existsSync2(appPath)) {
325
+ if (existsSync3(appPath)) {
278
326
  return { name: "cursor", path: appPath };
279
327
  }
280
328
  }
@@ -322,11 +370,11 @@ function getAllTools() {
322
370
  }
323
371
 
324
372
  // src/core/manifest.ts
325
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
373
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
326
374
  import { join as join5 } from "path";
327
375
  function readManifest(configRoot) {
328
376
  const manifestPath = getManifestPath(configRoot);
329
- if (existsSync3(manifestPath)) {
377
+ if (existsSync4(manifestPath)) {
330
378
  try {
331
379
  const content = readFileSync3(manifestPath, "utf-8");
332
380
  return JSON.parse(content);
@@ -339,7 +387,7 @@ function readAllManifests(configRoot) {
339
387
  const manifests = [];
340
388
  const plugins = getAllPlugins();
341
389
  const standardPath = getManifestPath(configRoot);
342
- if (existsSync3(standardPath)) {
390
+ if (existsSync4(standardPath)) {
343
391
  try {
344
392
  const content = readFileSync3(standardPath, "utf-8");
345
393
  manifests.push(JSON.parse(content));
@@ -348,7 +396,7 @@ function readAllManifests(configRoot) {
348
396
  }
349
397
  for (const plugin of plugins) {
350
398
  const pluginManifestPath = join5(configRoot, `${plugin.id}-manifest.json`);
351
- if (existsSync3(pluginManifestPath)) {
399
+ if (existsSync4(pluginManifestPath)) {
352
400
  try {
353
401
  const content = readFileSync3(pluginManifestPath, "utf-8");
354
402
  manifests.push(JSON.parse(content));
@@ -398,14 +446,14 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
398
446
  if (manifest.installed_skills && manifest.installed_skills.length > 0) {
399
447
  const hasAnySkill = manifest.installed_skills.some((skillId) => {
400
448
  const skillPath = join5(skillsDir, skillId);
401
- return existsSync3(skillPath);
449
+ return existsSync4(skillPath);
402
450
  });
403
451
  if (!hasAnySkill) return false;
404
452
  }
405
453
  if (manifest.installed_agents && manifest.installed_agents.length > 0) {
406
454
  const hasAnyAgent = manifest.installed_agents.some((agentId) => {
407
455
  const agentPath = join5(agentsDir, agentId);
408
- return existsSync3(agentPath);
456
+ return existsSync4(agentPath);
409
457
  });
410
458
  if (!hasAnyAgent) return false;
411
459
  }
@@ -415,7 +463,7 @@ function verifyPluginFilesExist(configRoot, manifest, tool, level) {
415
463
  } else {
416
464
  agentsMdPath = join5(configRoot, "AGENTS.md");
417
465
  }
418
- if (!existsSync3(agentsMdPath)) return false;
466
+ if (!existsSync4(agentsMdPath)) return false;
419
467
  return true;
420
468
  }
421
469
 
@@ -482,7 +530,7 @@ function showOperationHints(isCheckbox = false) {
482
530
  }
483
531
 
484
532
  // src/utils/config.ts
485
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
533
+ import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "fs";
486
534
  import { join as join6 } from "path";
487
535
  import { homedir as homedir3 } from "os";
488
536
  import { parse, stringify } from "yaml";
@@ -494,7 +542,7 @@ function getConfigPath() {
494
542
  }
495
543
  function readConfig() {
496
544
  const configPath = getConfigPath();
497
- if (!existsSync4(configPath)) {
545
+ if (!existsSync5(configPath)) {
498
546
  return {
499
547
  language: "zh_CN",
500
548
  installedPlugins: []
@@ -519,7 +567,7 @@ function readConfig() {
519
567
  }
520
568
  function writeConfig(config) {
521
569
  const configDir = getConfigDir();
522
- if (!existsSync4(configDir)) {
570
+ if (!existsSync5(configDir)) {
523
571
  mkdirSync(configDir, { recursive: true });
524
572
  }
525
573
  const configPath = getConfigPath();
@@ -870,14 +918,14 @@ async function stepConfirm(tool, level, plugins) {
870
918
  }
871
919
 
872
920
  // src/core/repository.ts
873
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
921
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "fs";
874
922
  import { join as join8, dirname as dirname4 } from "path";
875
923
  import { fileURLToPath as fileURLToPath4 } from "url";
876
924
  import { execa as execa2 } from "execa";
877
925
  import { parse as parseYaml3 } from "yaml";
878
926
 
879
927
  // src/core/scanner.ts
880
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
928
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
881
929
  import { join as join7, dirname as dirname3 } from "path";
882
930
  import { fileURLToPath as fileURLToPath3 } from "url";
883
931
  import { parse as parseYaml2 } from "yaml";
@@ -908,12 +956,12 @@ function loadScanConfig() {
908
956
  try {
909
957
  const config = parseYaml2(readFileSync5(configPath, "utf-8"));
910
958
  return {
911
- skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab"],
959
+ skillDirs: config.scanDirs || ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
912
960
  pluginDirs: config.pluginDirs || ["plugins-official", "plugins-community"]
913
961
  };
914
962
  } catch {
915
963
  return {
916
- skillDirs: ["ops", "model", "graph", "infra", "ops-lab"],
964
+ skillDirs: ["ops", "model", "graph", "infra", "ops-lab", "runtime"],
917
965
  pluginDirs: ["plugins-official", "plugins-community"]
918
966
  };
919
967
  }
@@ -926,23 +974,37 @@ function scanSkills(repoPath) {
926
974
  const seen = /* @__PURE__ */ new Set();
927
975
  for (const dir of SKILL_SCAN_DIRS) {
928
976
  const fullPath = join7(repoPath, dir);
929
- if (!existsSync5(fullPath)) continue;
977
+ if (!existsSync6(fullPath)) continue;
930
978
  scanDirectory(fullPath, repoPath, dir, skills, seen);
931
979
  }
932
980
  for (const pluginDir of PLUGIN_SCAN_DIRS) {
933
981
  const pluginParent = join7(repoPath, pluginDir);
934
- if (!existsSync5(pluginParent)) continue;
982
+ if (!existsSync6(pluginParent)) continue;
935
983
  for (const plugin of safeReaddir(pluginParent)) {
936
984
  const pluginPath = join7(pluginParent, plugin);
937
- if (!isDirectory(pluginPath)) continue;
985
+ if (!isDirectory2(pluginPath)) continue;
938
986
  const skillsDir = join7(pluginPath, "skills");
939
- if (existsSync5(skillsDir)) {
987
+ if (existsSync6(skillsDir)) {
940
988
  scanDirectory(skillsDir, repoPath, `${pluginDir}/${plugin}/skills`, skills, seen);
941
989
  }
942
990
  const skillDir = join7(pluginPath, "skill");
943
- if (existsSync5(skillDir)) {
991
+ if (existsSync6(skillDir)) {
944
992
  scanDirectory(skillDir, repoPath, `${pluginDir}/${plugin}/skill`, skills, seen);
945
993
  }
994
+ const rootSkillMd = join7(pluginPath, "SKILL.md");
995
+ const initSh = join7(pluginPath, "init.sh");
996
+ if (existsSync6(rootSkillMd) && !existsSync6(initSh)) {
997
+ const frontmatter = parseFrontmatter(rootSkillMd);
998
+ if (frontmatter && !seen.has(frontmatter.name)) {
999
+ seen.add(frontmatter.name);
1000
+ skills.push({
1001
+ id: frontmatter.name,
1002
+ description: frontmatter.description,
1003
+ source: `${pluginDir}/${plugin}`,
1004
+ filePath: rootSkillMd
1005
+ });
1006
+ }
1007
+ }
946
1008
  }
947
1009
  }
948
1010
  return skills;
@@ -976,15 +1038,15 @@ function parseFrontmatter(filePath) {
976
1038
  function getCurrentCommit(repoPath) {
977
1039
  try {
978
1040
  const headFile = join7(repoPath, ".git", "HEAD");
979
- if (!existsSync5(headFile)) return "unknown";
1041
+ if (!existsSync6(headFile)) return "unknown";
980
1042
  const headContent = readFileSync5(headFile, "utf-8").trim();
981
1043
  if (headContent.startsWith("ref: ")) {
982
1044
  const refPath = join7(repoPath, ".git", headContent.slice(5));
983
- if (existsSync5(refPath)) {
1045
+ if (existsSync6(refPath)) {
984
1046
  return readFileSync5(refPath, "utf-8").trim();
985
1047
  }
986
1048
  const packedRefsPath = join7(repoPath, ".git", "packed-refs");
987
- if (existsSync5(packedRefsPath)) {
1049
+ if (existsSync6(packedRefsPath)) {
988
1050
  const packedRefs = readFileSync5(packedRefsPath, "utf-8");
989
1051
  const refName = headContent.slice(5);
990
1052
  const lines = packedRefs.split("\n");
@@ -1003,7 +1065,7 @@ function getCurrentCommit(repoPath) {
1003
1065
  function readScanCache() {
1004
1066
  try {
1005
1067
  const cachePath = getCachePath();
1006
- if (!existsSync5(cachePath)) return null;
1068
+ if (!existsSync6(cachePath)) return null;
1007
1069
  const content = readFileSync5(cachePath, "utf-8");
1008
1070
  const cache = JSON.parse(content);
1009
1071
  if (!cache.skills || !cache.timestamp) return null;
@@ -1018,7 +1080,7 @@ function writeScanCache(cache) {
1018
1080
  try {
1019
1081
  const cachePath = getCachePath();
1020
1082
  const cacheDir = dirname3(cachePath);
1021
- if (!existsSync5(cacheDir)) {
1083
+ if (!existsSync6(cacheDir)) {
1022
1084
  mkdirSync2(cacheDir, { recursive: true });
1023
1085
  }
1024
1086
  writeFileSync2(cachePath, JSON.stringify(cache, null, 2));
@@ -1032,9 +1094,9 @@ function scanDirectory(dirPath, repoPath, sourcePrefix, skills, seen) {
1032
1094
  for (const entry of safeReaddir(dirPath)) {
1033
1095
  const fullPath = join7(dirPath, entry);
1034
1096
  if (EXCLUDE_DIRS.has(entry)) continue;
1035
- if (!isDirectory(fullPath)) continue;
1097
+ if (!isDirectory2(fullPath)) continue;
1036
1098
  const skillMd = join7(fullPath, "SKILL.md");
1037
- if (existsSync5(skillMd)) {
1099
+ if (existsSync6(skillMd)) {
1038
1100
  const frontmatter = parseFrontmatter(skillMd);
1039
1101
  if (frontmatter && !seen.has(frontmatter.name)) {
1040
1102
  seen.add(frontmatter.name);
@@ -1057,9 +1119,9 @@ function safeReaddir(dir) {
1057
1119
  return [];
1058
1120
  }
1059
1121
  }
1060
- function isDirectory(path) {
1122
+ function isDirectory2(path) {
1061
1123
  try {
1062
- return statSync(path).isDirectory();
1124
+ return statSync2(path).isDirectory();
1063
1125
  } catch {
1064
1126
  return false;
1065
1127
  }
@@ -1076,6 +1138,7 @@ var CATEGORY_DEFS = [
1076
1138
  { id: "tilelang", name: "TileLang \u5F00\u53D1" },
1077
1139
  { id: "triton", name: "Triton \u5F00\u53D1" },
1078
1140
  { id: "model", name: "\u6A21\u578B\u63A8\u7406\u4F18\u5316" },
1141
+ { id: "runtime", name: "Runtime \u8FC1\u79FB" },
1079
1142
  { id: "graph", name: "\u56FE\u6A21\u5F0F" },
1080
1143
  { id: "platform", name: "\u5E73\u53F0\u5DE5\u5177" },
1081
1144
  { id: "other", name: "\u5176\u4ED6 Skills" }
@@ -1086,7 +1149,8 @@ function initFromScan(scanned) {
1086
1149
  dynamicSkills = scanned.map((s) => ({
1087
1150
  id: s.id,
1088
1151
  description: s.description,
1089
- source: s.source
1152
+ source: s.source,
1153
+ filePath: s.filePath
1090
1154
  }));
1091
1155
  initialized = true;
1092
1156
  }
@@ -1162,9 +1226,7 @@ var STATIC_SKILL_CATEGORIES = [
1162
1226
  { id: "catlass-op-develop", description: "Catlass \u7B97\u5B50\u5F00\u53D1", source: "ops" },
1163
1227
  { id: "catlass-op-perf-tune", description: "Catlass \u6027\u80FD\u8C03\u4F18", source: "ops" },
1164
1228
  { id: "cuda2ascend-simt", description: "CUDA \u8FC1\u79FB\u5230 Ascend C SIMT", source: "ops-lab" },
1165
- { id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" },
1166
- { id: "ops-registry-invoke-workflow", description: "\u6CE8\u518C\u8C03\u7528\u5DE5\u4F5C\u6D41", source: "plugins-official/ops-registry-invoke" },
1167
- { id: "ops-easyasc-dsl", description: "EasyASC DSL \u7B97\u5B50\u5F00\u53D1", source: "plugins-community/ops-easyasc-dsl/skill" }
1229
+ { id: "ops-direct-invoke-flash", description: "\u4ECE\u96F6\u6784\u5EFA Ascend C \u6838\u51FD\u6570", source: "plugins-official/ops-direct-invoke-flash/skills" }
1168
1230
  ]
1169
1231
  },
1170
1232
  {
@@ -1221,7 +1283,9 @@ var STATIC_SKILL_CATEGORIES = [
1221
1283
  { id: "model-infer-prefetch", description: "\u6743\u91CD\u9884\u53D6\u9002\u914D", source: "model" },
1222
1284
  { id: "model-infer-superkernel", description: "SuperKernel \u9002\u914D", source: "model" },
1223
1285
  { id: "model-infer-precision-debug", description: "NPU \u63A8\u7406\u7CBE\u5EA6\u8BCA\u65AD", source: "model" },
1224
- { id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" }
1286
+ { id: "model-infer-runtime-debug", description: "NPU \u63A8\u7406\u8FD0\u884C\u65F6\u9519\u8BEF\u8BCA\u65AD", source: "model" },
1287
+ { id: "model-infer-harmony", description: "\u7AEF\u4FA7\u9E3F\u8499 ASR \u91CF\u5316\u8F6C\u6362\u4E0E\u6253\u5305", source: "model" },
1288
+ { id: "science-model-npu-migration", description: "NPU \u4EE3\u7801\u7EA7\u8FC1\u79FB\uFF08\u73AF\u5883\u95E8\u7981/\u811A\u672C\u9002\u914D/\u7CBE\u5EA6\u6027\u80FD\u5BF9\u6BD4\uFF09", source: "plugins-community/science-model-npu-migration", filePath: "plugins-community/science-model-npu-migration/SKILL.md" }
1225
1289
  ]
1226
1290
  },
1227
1291
  {
@@ -1237,6 +1301,13 @@ var STATIC_SKILL_CATEGORIES = [
1237
1301
  { id: "torch-custom-ops-guide", description: "\u81EA\u5B9A\u4E49\u7B97\u5B50\u5165\u56FE\u6307\u5357", source: "graph" }
1238
1302
  ]
1239
1303
  },
1304
+ {
1305
+ id: "runtime",
1306
+ name: "Runtime \u8FC1\u79FB",
1307
+ skills: [
1308
+ { id: "runtime_migration", description: "CUDA \u5E94\u7528\u8FC1\u79FB\u5230 CANN \u5E73\u53F0\u6307\u5357", source: "runtime" }
1309
+ ]
1310
+ },
1240
1311
  {
1241
1312
  id: "platform",
1242
1313
  name: "\u5E73\u53F0\u5DE5\u5177",
@@ -1331,10 +1402,13 @@ var RepositoryManager = class {
1331
1402
  this.repoPath = cachedPath;
1332
1403
  return cachedPath;
1333
1404
  }
1334
- return await this.cloneRepo();
1405
+ const clonedPath = await this.cloneRepo();
1406
+ return clonedPath;
1335
1407
  }
1336
1408
  async ensureRepoAndScan() {
1337
1409
  const repoPath = await this.ensureRepo();
1410
+ await this.updateRepo();
1411
+ mergeDynamicPlugins(repoPath);
1338
1412
  const cache = readScanCache();
1339
1413
  if (cache && cache.repoCommit === getCurrentCommit(repoPath)) {
1340
1414
  initFromScan(cache.skills);
@@ -1355,6 +1429,7 @@ var RepositoryManager = class {
1355
1429
  await execa2("git", ["pull", "--quiet"], { cwd: repoPath, timeout: 3e4 });
1356
1430
  } catch {
1357
1431
  }
1432
+ mergeDynamicPlugins(repoPath);
1358
1433
  }
1359
1434
  getRepoPath() {
1360
1435
  if (!this.repoPath) {
@@ -1363,18 +1438,18 @@ var RepositoryManager = class {
1363
1438
  return this.repoPath;
1364
1439
  }
1365
1440
  isValidRepo(path) {
1366
- if (!existsSync6(path)) return false;
1441
+ if (!existsSync7(path)) return false;
1367
1442
  const gitDir = join8(path, ".git");
1368
1443
  const pluginsDir = join8(path, "plugins-official");
1369
- return existsSync6(gitDir) && existsSync6(pluginsDir);
1444
+ return existsSync7(gitDir) && existsSync7(pluginsDir);
1370
1445
  }
1371
1446
  async cloneRepo() {
1372
1447
  const configDir = getCannbotConfigDir();
1373
- if (!existsSync6(configDir)) {
1448
+ if (!existsSync7(configDir)) {
1374
1449
  mkdirSync3(configDir, { recursive: true });
1375
1450
  }
1376
1451
  const targetPath = getCannbotRepoPath();
1377
- if (existsSync6(targetPath)) {
1452
+ if (existsSync7(targetPath)) {
1378
1453
  if (this.isValidRepo(targetPath)) {
1379
1454
  this.repoPath = targetPath;
1380
1455
  return targetPath;
@@ -1400,12 +1475,12 @@ function createRepositoryManager(customPath) {
1400
1475
  }
1401
1476
 
1402
1477
  // src/core/installer.ts
1403
- import { existsSync as existsSync9 } from "fs";
1478
+ import { existsSync as existsSync10 } from "fs";
1404
1479
  import { join as join11 } from "path";
1405
1480
  import { execa as execa3 } from "execa";
1406
1481
 
1407
1482
  // src/core/record.ts
1408
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
1483
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, lstatSync, unlinkSync } from "fs";
1409
1484
  import { join as join9 } from "path";
1410
1485
  function getInstallsDir() {
1411
1486
  return join9(getCannbotConfigDir(), "installs");
@@ -1415,7 +1490,7 @@ function getRecordPath(pluginId) {
1415
1490
  }
1416
1491
  function readRecord(pluginId) {
1417
1492
  const recordPath = getRecordPath(pluginId);
1418
- if (!existsSync7(recordPath)) {
1493
+ if (!existsSync8(recordPath)) {
1419
1494
  return null;
1420
1495
  }
1421
1496
  try {
@@ -1427,7 +1502,7 @@ function readRecord(pluginId) {
1427
1502
  }
1428
1503
  function writeRecord(record) {
1429
1504
  const installsDir = getInstallsDir();
1430
- if (!existsSync7(installsDir)) {
1505
+ if (!existsSync8(installsDir)) {
1431
1506
  mkdirSync4(installsDir, { recursive: true });
1432
1507
  }
1433
1508
  const recordPath = getRecordPath(record.pluginId);
@@ -1435,7 +1510,7 @@ function writeRecord(record) {
1435
1510
  }
1436
1511
  function deleteRecord(pluginId) {
1437
1512
  const recordPath = getRecordPath(pluginId);
1438
- if (existsSync7(recordPath)) {
1513
+ if (existsSync8(recordPath)) {
1439
1514
  unlinkSync(recordPath);
1440
1515
  }
1441
1516
  }
@@ -1444,24 +1519,24 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1444
1519
  const directories = [];
1445
1520
  if (manifest) {
1446
1521
  const skillsDir = join9(configRoot, "skills");
1447
- if (existsSync7(skillsDir)) {
1522
+ if (existsSync8(skillsDir)) {
1448
1523
  directories.push(skillsDir);
1449
1524
  for (const skillName of manifest.installed_skills || []) {
1450
1525
  const skillPath = join9(skillsDir, skillName);
1451
- if (existsSync7(skillPath) || isSymlink(skillPath)) {
1526
+ if (existsSync8(skillPath) || isSymlink(skillPath)) {
1452
1527
  files.push(skillPath);
1453
1528
  }
1454
1529
  }
1455
1530
  }
1456
1531
  const agentsDir = join9(configRoot, "agents");
1457
- if (existsSync7(agentsDir)) {
1532
+ if (existsSync8(agentsDir)) {
1458
1533
  directories.push(agentsDir);
1459
1534
  for (const agentName of manifest.installed_agents || []) {
1460
1535
  const agentPath = join9(agentsDir, agentName);
1461
1536
  const agentPathMd = join9(agentsDir, agentName + ".md");
1462
- if (existsSync7(agentPath) || isSymlink(agentPath)) {
1537
+ if (existsSync8(agentPath) || isSymlink(agentPath)) {
1463
1538
  files.push(agentPath);
1464
- } else if (existsSync7(agentPathMd) || isSymlink(agentPathMd)) {
1539
+ } else if (existsSync8(agentPathMd) || isSymlink(agentPathMd)) {
1465
1540
  files.push(agentPathMd);
1466
1541
  }
1467
1542
  }
@@ -1472,16 +1547,16 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
1472
1547
  files.push(workflowsLink);
1473
1548
  }
1474
1549
  const manifestPath = join9(configRoot, "cannbot-manifest.json");
1475
- if (existsSync7(manifestPath)) {
1550
+ if (existsSync8(manifestPath)) {
1476
1551
  files.push(manifestPath);
1477
1552
  }
1478
1553
  const pluginManifestPath = join9(configRoot, `${pluginId}-manifest.json`);
1479
- if (existsSync7(pluginManifestPath)) {
1554
+ if (existsSync8(pluginManifestPath)) {
1480
1555
  files.push(pluginManifestPath);
1481
1556
  }
1482
1557
  const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
1483
1558
  const configFilePath = level === "project" ? join9(installPath, configFileName) : join9(configRoot, configFileName);
1484
- if (existsSync7(configFilePath)) {
1559
+ if (existsSync8(configFilePath)) {
1485
1560
  files.push(configFilePath);
1486
1561
  }
1487
1562
  const repoLinks = ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
@@ -1519,7 +1594,7 @@ function getSkillRecordPath() {
1519
1594
  }
1520
1595
  function readSkillRecord() {
1521
1596
  const recordPath = getSkillRecordPath();
1522
- if (!existsSync7(recordPath)) {
1597
+ if (!existsSync8(recordPath)) {
1523
1598
  return {};
1524
1599
  }
1525
1600
  try {
@@ -1531,7 +1606,7 @@ function readSkillRecord() {
1531
1606
  }
1532
1607
  function writeSkillRecord(record) {
1533
1608
  const installsDir = getInstallsDir();
1534
- if (!existsSync7(installsDir)) {
1609
+ if (!existsSync8(installsDir)) {
1535
1610
  mkdirSync4(installsDir, { recursive: true });
1536
1611
  }
1537
1612
  const recordPath = getSkillRecordPath();
@@ -1576,12 +1651,12 @@ function getInstalledSkills(tool, level, installPath) {
1576
1651
  const skillsDir = join9(configRoot, "skills");
1577
1652
  return recordedSkills.filter((skillId) => {
1578
1653
  const skillPath = join9(skillsDir, skillId);
1579
- return existsSync7(skillPath) || isSymlink(skillPath);
1654
+ return existsSync8(skillPath) || isSymlink(skillPath);
1580
1655
  });
1581
1656
  }
1582
1657
 
1583
1658
  // src/core/backup.ts
1584
- import { existsSync as existsSync8, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
1659
+ import { existsSync as existsSync9, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
1585
1660
  import { join as join10 } from "path";
1586
1661
  function getAgentsFileName(tool) {
1587
1662
  return tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
@@ -1598,7 +1673,7 @@ function detectCurrentPlugin(configRoot, tool) {
1598
1673
  }
1599
1674
  }
1600
1675
  const agentsFile = join10(configRoot, getAgentsFileName(tool));
1601
- if (!existsSync8(agentsFile)) {
1676
+ if (!existsSync9(agentsFile)) {
1602
1677
  return null;
1603
1678
  }
1604
1679
  const allRecords = readAllRecords();
@@ -1617,7 +1692,7 @@ function detectCurrentPlugin(configRoot, tool) {
1617
1692
  }
1618
1693
  function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
1619
1694
  const agentsFile = join10(configRoot, getAgentsFileName(tool));
1620
- if (!existsSync8(agentsFile)) {
1695
+ if (!existsSync9(agentsFile)) {
1621
1696
  return null;
1622
1697
  }
1623
1698
  const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
@@ -1636,7 +1711,7 @@ function createBackup(configRoot, tool, fromPluginId, fromPluginName) {
1636
1711
  }
1637
1712
  }
1638
1713
  function findBackups(configRoot) {
1639
- if (!existsSync8(configRoot)) {
1714
+ if (!existsSync9(configRoot)) {
1640
1715
  return [];
1641
1716
  }
1642
1717
  const files = readdirSync3(configRoot);
@@ -1674,7 +1749,7 @@ function restoreBackup(backupPath, configRoot, tool) {
1674
1749
  }
1675
1750
  function deleteBackup(backupPath) {
1676
1751
  try {
1677
- if (existsSync8(backupPath)) {
1752
+ if (existsSync9(backupPath)) {
1678
1753
  unlinkSync2(backupPath);
1679
1754
  return true;
1680
1755
  }
@@ -1767,7 +1842,7 @@ async function installPlugin(opts) {
1767
1842
  };
1768
1843
  }
1769
1844
  const scriptPath = join11(opts.repoPath, plugin.dir, plugin.script);
1770
- if (!existsSync9(scriptPath)) {
1845
+ if (!existsSync10(scriptPath)) {
1771
1846
  return {
1772
1847
  success: false,
1773
1848
  pluginId: opts.pluginId,
@@ -1785,7 +1860,7 @@ async function installPlugin(opts) {
1785
1860
  const configRoot = getConfigRoot(opts.tool, opts.level, opts.installPath);
1786
1861
  const agentsFile = join11(configRoot, getAgentsFileName(opts.tool));
1787
1862
  let backupInfo = null;
1788
- if (existsSync9(agentsFile)) {
1863
+ if (existsSync10(agentsFile)) {
1789
1864
  const currentPlugin = detectCurrentPlugin(configRoot, opts.tool);
1790
1865
  if (currentPlugin && currentPlugin.pluginId !== opts.pluginId) {
1791
1866
  let choice = "overwrite";
@@ -1878,8 +1953,8 @@ async function installPlugin(opts) {
1878
1953
  }
1879
1954
 
1880
1955
  // src/core/skill-installer.ts
1881
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
1882
- import { join as join12 } from "path";
1956
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, symlinkSync, unlinkSync as unlinkSync3, readdirSync as readdirSync4, rmdirSync, realpathSync, lstatSync as lstatSync2 } from "fs";
1957
+ import { join as join12, dirname as dirname5 } from "path";
1883
1958
  import { select as select3, checkbox as checkbox2, Separator as Separator2 } from "@inquirer/prompts";
1884
1959
  import chalk5 from "chalk";
1885
1960
  import Table from "cli-table3";
@@ -1887,7 +1962,7 @@ async function installSkills(skillIds, tool, level, repoPath) {
1887
1962
  const configRoot = getConfigRoot(tool, level);
1888
1963
  const skillsDir = join12(configRoot, "skills");
1889
1964
  const installPath = level === "project" ? process.cwd() : configRoot;
1890
- if (!existsSync10(skillsDir)) {
1965
+ if (!existsSync11(skillsDir)) {
1891
1966
  mkdirSync5(skillsDir, { recursive: true });
1892
1967
  }
1893
1968
  const results = [];
@@ -1898,14 +1973,14 @@ async function installSkills(skillIds, tool, level, repoPath) {
1898
1973
  results.push({ skillId, success: false, error: "Skill not found in registry" });
1899
1974
  continue;
1900
1975
  }
1901
- const sourcePath = join12(repoPath, skill.source, skill.id);
1976
+ const sourcePath = resolveSkillSourcePath(skill, repoPath);
1902
1977
  const targetPath = join12(skillsDir, skillId);
1903
- if (!existsSync10(sourcePath)) {
1978
+ if (!existsSync11(sourcePath)) {
1904
1979
  results.push({ skillId, success: false, error: `Source not found: ${sourcePath}` });
1905
1980
  continue;
1906
1981
  }
1907
1982
  try {
1908
- if (existsSync10(targetPath) || isSymlink2(targetPath)) {
1983
+ if (existsSync11(targetPath) || isSymlink2(targetPath)) {
1909
1984
  unlinkSync3(targetPath);
1910
1985
  }
1911
1986
  symlinkSync(realpathSync(sourcePath), targetPath);
@@ -1929,7 +2004,7 @@ async function uninstallSkills(skillIds, tool, level) {
1929
2004
  for (const skillId of skillIds) {
1930
2005
  const targetPath = join12(skillsDir, skillId);
1931
2006
  try {
1932
- if (existsSync10(targetPath) || isSymlink2(targetPath)) {
2007
+ if (existsSync11(targetPath) || isSymlink2(targetPath)) {
1933
2008
  unlinkSync3(targetPath);
1934
2009
  results.push({ skillId, success: true });
1935
2010
  removedIds.push(skillId);
@@ -1945,7 +2020,7 @@ async function uninstallSkills(skillIds, tool, level) {
1945
2020
  if (removedIds.length > 0) {
1946
2021
  removeSkillsFromRecord(removedIds, tool, level, installPath);
1947
2022
  }
1948
- if (existsSync10(skillsDir)) {
2023
+ if (existsSync11(skillsDir)) {
1949
2024
  try {
1950
2025
  const entries = readdirSync4(skillsDir);
1951
2026
  if (entries.length === 0) {
@@ -2128,6 +2203,15 @@ function listAllSkills() {
2128
2203
  console.log(chalk5.dim(` ${t("skill_total_count").replace("{count}", String(getAllSkills().length))}`));
2129
2204
  console.log();
2130
2205
  }
2206
+ function resolveSkillSourcePath(skill, repoPath) {
2207
+ if (skill.filePath) {
2208
+ const absPath = skill.filePath.startsWith("/") ? skill.filePath : join12(repoPath, skill.filePath);
2209
+ if (existsSync11(absPath)) {
2210
+ return dirname5(absPath);
2211
+ }
2212
+ }
2213
+ return join12(repoPath, skill.source, skill.id);
2214
+ }
2131
2215
  function isSymlink2(path) {
2132
2216
  try {
2133
2217
  return lstatSync2(path).isSymbolicLink();
@@ -2225,13 +2309,13 @@ function printEnhancedSummary(results, tool, configRoot) {
2225
2309
  }
2226
2310
 
2227
2311
  // src/commands/init.ts
2228
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
2312
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
2229
2313
  import { join as join13 } from "path";
2230
2314
  var BACK2 = "__back__";
2231
2315
  var CANCEL2 = "__cancel__";
2232
2316
  async function initCommand() {
2233
2317
  const cwd = process.cwd();
2234
- if (existsSync11(join13(cwd, "package.json")) && (() => {
2318
+ if (existsSync12(join13(cwd, "package.json")) && (() => {
2235
2319
  try {
2236
2320
  const pkg = JSON.parse(readFileSync8(join13(cwd, "package.json"), "utf-8"));
2237
2321
  return pkg.name === "@cannbot-ai/install-helper";
@@ -2459,7 +2543,7 @@ async function listCommand() {
2459
2543
 
2460
2544
  // src/commands/doctor.ts
2461
2545
  import chalk8 from "chalk";
2462
- import { existsSync as existsSync12, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2546
+ import { existsSync as existsSync13, readdirSync as readdirSync5, lstatSync as lstatSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync6 } from "fs";
2463
2547
  import { join as join14 } from "path";
2464
2548
  async function doctorCommand(options = {}) {
2465
2549
  console.log();
@@ -2503,7 +2587,7 @@ async function doctorCommand(options = {}) {
2503
2587
  const configRoot = getConfigRoot(primaryTool, "project");
2504
2588
  const skillsDir = getSkillsDir(configRoot);
2505
2589
  const agentsDir = getAgentsDir(configRoot);
2506
- if (existsSync12(skillsDir)) {
2590
+ if (existsSync13(skillsDir)) {
2507
2591
  const brokenLinks = checkBrokenLinks(skillsDir);
2508
2592
  if (brokenLinks === 0) {
2509
2593
  const count = readdirSync5(skillsDir).length;
@@ -2525,7 +2609,7 @@ async function doctorCommand(options = {}) {
2525
2609
  console.log(chalk8.green(" \u2713") + ` ${t("doctor_fix_rebuilding")}: ${t("doctor_created").replace("{path}", skillsDir)}`);
2526
2610
  }
2527
2611
  }
2528
- if (existsSync12(agentsDir)) {
2612
+ if (existsSync13(agentsDir)) {
2529
2613
  const brokenLinks = checkBrokenLinks(agentsDir);
2530
2614
  if (brokenLinks === 0) {
2531
2615
  const count = readdirSync5(agentsDir).length;
@@ -2551,7 +2635,7 @@ async function doctorCommand(options = {}) {
2551
2635
  console.log(chalk8.bold(` ${t("doctor_config")}`));
2552
2636
  const configFile = getConfigFileName(primaryTool);
2553
2637
  const configPath = join14(process.cwd(), configFile);
2554
- if (existsSync12(configPath)) {
2638
+ if (existsSync13(configPath)) {
2555
2639
  console.log(chalk8.green(" \u2713") + ` ${configFile} ${t("doctor_config_exists")}`);
2556
2640
  } else {
2557
2641
  console.log(chalk8.dim(" \u2014") + ` ${configFile} ${t("doctor_config_not_exist")}`);
@@ -2578,7 +2662,7 @@ function checkBrokenLinks(dir) {
2578
2662
  try {
2579
2663
  const stats = lstatSync3(fullPath);
2580
2664
  if (stats.isSymbolicLink()) {
2581
- if (!existsSync12(fullPath)) {
2665
+ if (!existsSync13(fullPath)) {
2582
2666
  broken++;
2583
2667
  }
2584
2668
  }
@@ -2599,7 +2683,7 @@ function fixBrokenLinks(dir) {
2599
2683
  try {
2600
2684
  const stats = lstatSync3(fullPath);
2601
2685
  if (stats.isSymbolicLink()) {
2602
- if (!existsSync12(fullPath)) {
2686
+ if (!existsSync13(fullPath)) {
2603
2687
  unlinkSync4(fullPath);
2604
2688
  fixed++;
2605
2689
  }
@@ -2931,7 +3015,7 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
2931
3015
  }
2932
3016
 
2933
3017
  // src/commands/uninstall.ts
2934
- import { existsSync as existsSync13, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
3018
+ import { existsSync as existsSync14, unlinkSync as unlinkSync5, rmdirSync as rmdirSync2, readdirSync as readdirSync6, lstatSync as lstatSync4 } from "fs";
2935
3019
  import chalk11 from "chalk";
2936
3020
  async function uninstallCommand(names, options) {
2937
3021
  const tool = options.tool ? validateTool(options.tool) : "opencode";
@@ -2965,7 +3049,7 @@ async function uninstallCommand(names, options) {
2965
3049
  let removedDirs = 0;
2966
3050
  for (const filePath of record.files) {
2967
3051
  try {
2968
- if (existsSync13(filePath) || isSymlink3(filePath)) {
3052
+ if (existsSync14(filePath) || isSymlink3(filePath)) {
2969
3053
  unlinkSync5(filePath);
2970
3054
  const name = filePath.split("/").pop() || filePath;
2971
3055
  logger.step(` ${t("uninstall_remove_label")}: ${name}`);
@@ -2981,7 +3065,7 @@ async function uninstallCommand(names, options) {
2981
3065
  if (dirPath === configRoot) {
2982
3066
  continue;
2983
3067
  }
2984
- if (existsSync13(dirPath)) {
3068
+ if (existsSync14(dirPath)) {
2985
3069
  const entries = readdirSync6(dirPath);
2986
3070
  if (entries.length === 0) {
2987
3071
  rmdirSync2(dirPath);
@@ -3126,7 +3210,7 @@ async function updateCommand(pluginNames, options) {
3126
3210
 
3127
3211
  // src/commands/info.ts
3128
3212
  import chalk13 from "chalk";
3129
- import { existsSync as existsSync14, readdirSync as readdirSync7 } from "fs";
3213
+ import { existsSync as existsSync15, readdirSync as readdirSync7 } from "fs";
3130
3214
  import { join as join15 } from "path";
3131
3215
  async function infoCommand(pluginName) {
3132
3216
  const plugin = findPlugin(pluginName);
@@ -3152,11 +3236,11 @@ async function infoCommand(pluginName) {
3152
3236
  const repoManager = createRepositoryManager();
3153
3237
  const repoPath = await repoManager.ensureRepo();
3154
3238
  const quickstartPath = join15(repoPath, plugin.dir, "quickstart.md");
3155
- if (existsSync14(quickstartPath)) {
3239
+ if (existsSync15(quickstartPath)) {
3156
3240
  console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
3157
3241
  }
3158
3242
  const agentsDir = join15(repoPath, plugin.dir, "agents");
3159
- if (existsSync14(agentsDir)) {
3243
+ if (existsSync15(agentsDir)) {
3160
3244
  const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
3161
3245
  if (agentFiles.length > 0) {
3162
3246
  console.log();
@@ -3214,7 +3298,7 @@ function createCLI() {
3214
3298
  setLanguage(config.language);
3215
3299
  }
3216
3300
  const program2 = new Command();
3217
- program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.30");
3301
+ program2.name("install-helper").description("CANNBot Install Helper - Interactive installer for CANN operator development skills").version("0.0.2-beta.31");
3218
3302
  program2.command("init", { isDefault: false }).description("Run interactive installation wizard").action(async () => {
3219
3303
  await initCommand();
3220
3304
  });