@cannbot-ai/install-helper 1.1.6-beta.1 → 1.1.6-beta.10

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
@@ -844,12 +844,13 @@ var init_zh_CN = __esm({
844
844
  doctor_result_without_fix: "{warnings} \u4E2A\u8B66\u544A",
845
845
  count_suffix: "\u4E2A",
846
846
  try_prompt: "\u5E2E\u6211\u5F00\u53D1\u4E00\u4E2A Abs \u7B97\u5B50",
847
- uninstall_interactive_title: "\u7BA1\u7406\u5DF2\u5B89\u88C5\u7684\u5185\u5BB9 \u2014 \u53D6\u6D88\u52FE\u9009\u8981\u5378\u8F7D\u7684\u9879",
847
+ uninstall_interactive_title: "\u7BA1\u7406\u5DF2\u5B89\u88C5\u7684\u5185\u5BB9 \u2014 \u52FE\u9009\u8981\u5378\u8F7D\u7684\u9879",
848
848
  uninstall_select_type: "\u9009\u62E9\u8981\u5378\u8F7D\u7684\u7C7B\u578B",
849
849
  uninstall_type_skill: "\u5378\u8F7D Skills",
850
850
  uninstall_type_plugin: "\u5378\u8F7D Plugins",
851
851
  uninstall_checkbox_hint: "\u52FE\u9009=\u4FDD\u7559\uFF0C\u53D6\u6D88\u52FE\u9009=\u5378\u8F7D",
852
- uninstall_uncheck_all: "\u5168\u4E0D\u9009\uFF08\u6807\u8BB0\u5168\u90E8\u5378\u8F7D\uFF09",
852
+ uninstall_checkbox_hint_select: "\u52FE\u9009\u8981\u5378\u8F7D\u7684\u9879\uFF0C\u4E0D\u9009\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE",
853
+ uninstall_uncheck_all: "\u5378\u8F7D\u5168\u90E8",
853
854
  uninstall_confirm_count: "\u786E\u8BA4\u5378\u8F7D\uFF08{count} \u4E2A\u5C06\u5378\u8F7D\uFF09",
854
855
  uninstall_no_installed: "\u6CA1\u6709\u5DF2\u5B89\u88C5\u7684 Skills \u6216 Plugins",
855
856
  uninstall_no_installed_skills: "\u6CA1\u6709\u5DF2\u5B89\u88C5\u7684 Skills",
@@ -866,7 +867,7 @@ var init_zh_CN = __esm({
866
867
  uninstall_select_items: "\u7BA1\u7406\u5DF2\u5B89\u88C5\u7684 Skills\uFF08\u7A7A\u683C\u5207\u6362\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
867
868
  uninstall_select_plugins: "\u7BA1\u7406\u5DF2\u5B89\u88C5\u7684 Plugins\uFF08\u7A7A\u683C\u5207\u6362\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
868
869
  uninstall_select_category: "\u9009\u62E9\u8981\u7BA1\u7406\u7684 Skill \u7C7B\u522B",
869
- uninstall_uncheck_all_category: "\u5168\u4E0D\u9009\u5F53\u524D\u7C7B\u522B\uFF08\u6807\u8BB0\u5168\u90E8\u5378\u8F7D\uFF09",
870
+ uninstall_uncheck_all_category: "\u5378\u8F7D\u672C\u7C7B\u522B\u5168\u90E8",
870
871
  uninstall_continue_or_confirm: "\u5DF2\u6807\u8BB0 {count} \u4E2A\u5F85\u5378\u8F7D\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
871
872
  uninstall_marked_count: "\u5DF2\u6807\u8BB0 {count} \u4E2A\u5F85\u5378\u8F7D",
872
873
  uninstall_uncategorized: "\u672A\u5206\u7C7B"
@@ -1066,12 +1067,13 @@ var init_en_US = __esm({
1066
1067
  doctor_result_without_fix: "{warnings} warnings",
1067
1068
  count_suffix: "",
1068
1069
  try_prompt: "help me develop an Abs operator",
1069
- uninstall_interactive_title: "Manage installed items \u2014 uncheck to uninstall",
1070
+ uninstall_interactive_title: "Manage installed items \u2014 check to uninstall",
1070
1071
  uninstall_select_type: "Select type to uninstall",
1071
1072
  uninstall_type_skill: "Uninstall Skills",
1072
1073
  uninstall_type_plugin: "Uninstall Plugins",
1073
1074
  uninstall_checkbox_hint: "Checked = keep, unchecked = uninstall",
1074
- uninstall_uncheck_all: "Uncheck all (mark all for uninstall)",
1075
+ uninstall_checkbox_hint_select: "Check items to uninstall, press enter without selection to go back",
1076
+ uninstall_uncheck_all: "Uninstall all",
1075
1077
  uninstall_confirm_count: "Confirm uninstall ({count} will be removed)",
1076
1078
  uninstall_no_installed: "No installed Skills or Plugins",
1077
1079
  uninstall_no_installed_skills: "No installed Skills",
@@ -1088,7 +1090,7 @@ var init_en_US = __esm({
1088
1090
  uninstall_select_items: "Manage installed Skills (space to toggle, enter to confirm)",
1089
1091
  uninstall_select_plugins: "Manage installed Plugins (space to toggle, enter to confirm)",
1090
1092
  uninstall_select_category: "Select Skill category to manage",
1091
- uninstall_uncheck_all_category: "Uncheck all in this category (mark for uninstall)",
1093
+ uninstall_uncheck_all_category: "Uninstall all in this category",
1092
1094
  uninstall_continue_or_confirm: "{count} marked for uninstall, choose an action",
1093
1095
  uninstall_marked_count: "{count} marked for uninstall",
1094
1096
  uninstall_uncategorized: "Uncategorized"
@@ -1461,30 +1463,35 @@ function scanInstalled() {
1461
1463
  function verifyPluginFilesExist(configRoot, manifest, tool, level) {
1462
1464
  const skillsDir = join5(configRoot, "skills");
1463
1465
  const agentsDir = join5(configRoot, "agents");
1466
+ let hasAnyComponent = false;
1464
1467
  if (manifest.installed_skills && manifest.installed_skills.length > 0) {
1465
1468
  const hasAnySkill = manifest.installed_skills.some((skillId) => {
1466
1469
  const skillPath = join5(skillsDir, skillId);
1467
- return existsSync5(skillPath);
1470
+ return existsSync5(skillPath) || isSymlink(skillPath);
1468
1471
  });
1469
- if (!hasAnySkill) return false;
1472
+ if (hasAnySkill) hasAnyComponent = true;
1470
1473
  }
1471
1474
  if (manifest.installed_agents && manifest.installed_agents.length > 0) {
1472
1475
  const hasAnyAgent = manifest.installed_agents.some((agentId) => {
1473
1476
  const agentPath = join5(agentsDir, agentId);
1474
- return existsSync5(agentPath);
1477
+ const agentPathMd = join5(agentsDir, agentId + ".md");
1478
+ return existsSync5(agentPath) || isSymlink(agentPath) || existsSync5(agentPathMd) || isSymlink(agentPathMd);
1475
1479
  });
1476
- if (!hasAnyAgent) return false;
1480
+ if (hasAnyAgent) hasAnyComponent = true;
1477
1481
  }
1478
1482
  const agentsFileName = getAgentsFileName(tool);
1479
1483
  const configFilePath = level === "project" ? join5(configRoot, "..", agentsFileName) : join5(configRoot, agentsFileName);
1480
- if (!existsSync5(configFilePath)) return false;
1481
- return true;
1484
+ if (existsSync5(configFilePath) || isSymlink(configFilePath)) hasAnyComponent = true;
1485
+ const pluginManifestPath = join5(configRoot, `${manifest.team}-manifest.json`);
1486
+ if (existsSync5(pluginManifestPath)) hasAnyComponent = true;
1487
+ return hasAnyComponent;
1482
1488
  }
1483
1489
  var init_manifest = __esm({
1484
1490
  "src/core/manifest.ts"() {
1485
1491
  "use strict";
1486
1492
  init_paths();
1487
1493
  init_registry();
1494
+ init_fs_helpers();
1488
1495
  }
1489
1496
  });
1490
1497
 
@@ -2699,7 +2706,7 @@ init_fs();
2699
2706
  init_fs_helpers();
2700
2707
  init_logger();
2701
2708
  init_i18n();
2702
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync3 } from "fs";
2709
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
2703
2710
  import { join as join10 } from "path";
2704
2711
  function getInstallsDir() {
2705
2712
  return join10(getCannbotConfigDir(), "installs");
@@ -2734,7 +2741,7 @@ function deleteRecord(pluginId) {
2734
2741
  unlinkSync3(recordPath);
2735
2742
  }
2736
2743
  }
2737
- function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames) {
2744
+ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames, configRootConfigLink) {
2738
2745
  const files = [];
2739
2746
  const directories = [];
2740
2747
  if (manifest) {
@@ -2776,9 +2783,15 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
2776
2783
  }
2777
2784
  const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
2778
2785
  const configFilePath = level === "project" ? join10(installPath, configFileName) : join10(configRoot, configFileName);
2779
- if (existsSync11(configFilePath)) {
2786
+ if (existsSync11(configFilePath) || isSymlink(configFilePath)) {
2780
2787
  files.push(configFilePath);
2781
2788
  }
2789
+ if (level === "project" && configRootConfigLink !== false) {
2790
+ const configRootConfigPath = join10(configRoot, configFileName);
2791
+ if (configRootConfigPath !== configFilePath && (existsSync11(configRootConfigPath) || isSymlink(configRootConfigPath))) {
2792
+ files.push(configRootConfigPath);
2793
+ }
2794
+ }
2782
2795
  const repoLinks = externalRepoNames && externalRepoNames.length > 0 ? externalRepoNames : ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
2783
2796
  for (const repoName of repoLinks) {
2784
2797
  const repoLinkPath = join10(installPath, repoName);
@@ -2843,12 +2856,14 @@ function addSkillsToRecord(skillIds, tool, level, installPath) {
2843
2856
  }
2844
2857
  }
2845
2858
  entry.installTime = now;
2846
- if (!entry.batches) entry.batches = [];
2847
- entry.batches.push({
2848
- batchId: `batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2849
- installedAt: now,
2850
- skills: newSkills.length > 0 ? newSkills : [...skillIds]
2851
- });
2859
+ if (newSkills.length > 0) {
2860
+ if (!entry.batches) entry.batches = [];
2861
+ entry.batches.push({
2862
+ batchId: `batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2863
+ installedAt: now,
2864
+ skills: newSkills
2865
+ });
2866
+ }
2852
2867
  writeSkillRecord(record);
2853
2868
  }
2854
2869
  function removeSkillsFromRecord(skillIds, tool, level, installPath) {
@@ -2881,10 +2896,25 @@ function getInstalledSkills(tool, level, installPath) {
2881
2896
  const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
2882
2897
  const configRoot = getConfigRoot(tool, level);
2883
2898
  const skillsDir = join10(configRoot, "skills");
2884
- return recordedSkills.filter((skillId) => {
2899
+ const fromRecord = recordedSkills.filter((skillId) => {
2885
2900
  const skillPath = join10(skillsDir, skillId);
2886
2901
  return existsSync11(skillPath) || isSymlink(skillPath);
2887
2902
  });
2903
+ const fromFs = [];
2904
+ if (existsSync11(skillsDir)) {
2905
+ try {
2906
+ const entries = readdirSync3(skillsDir);
2907
+ for (const entry of entries) {
2908
+ if (entry === "." || entry === "..") continue;
2909
+ const entryPath = join10(skillsDir, entry);
2910
+ if (isSymlink(entryPath) && !fromRecord.includes(entry)) {
2911
+ fromFs.push(entry);
2912
+ }
2913
+ }
2914
+ } catch {
2915
+ }
2916
+ }
2917
+ return [...fromRecord, ...fromFs];
2888
2918
  }
2889
2919
  function getLastBatchSkills(tool, level, installPath) {
2890
2920
  const record = readSkillRecord();
@@ -2903,7 +2933,7 @@ function getLastBatchSkills(tool, level, installPath) {
2903
2933
 
2904
2934
  // src/core/backup.ts
2905
2935
  init_manifest();
2906
- import { existsSync as existsSync12, copyFileSync, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
2936
+ import { existsSync as existsSync12, copyFileSync, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "fs";
2907
2937
  import { join as join11 } from "path";
2908
2938
  init_registry();
2909
2939
  init_paths();
@@ -2968,7 +2998,7 @@ function findBackups(configRoot) {
2968
2998
  if (!existsSync12(configRoot)) {
2969
2999
  return [];
2970
3000
  }
2971
- const files = readdirSync3(configRoot);
3001
+ const files = readdirSync4(configRoot);
2972
3002
  const backups = [];
2973
3003
  for (const file of files) {
2974
3004
  if (file.includes(".cannbot-backup.")) {
@@ -3434,7 +3464,8 @@ async function installPlugin(opts) {
3434
3464
  cwd,
3435
3465
  configRoot,
3436
3466
  result.manifest,
3437
- plugin.externalRepos?.map((r) => basename2(r.dir))
3467
+ plugin.externalRepos?.map((r) => basename2(r.dir)),
3468
+ plugin.configRootConfigLink
3438
3469
  );
3439
3470
  if (backupInfo) {
3440
3471
  record.backup = {
@@ -3520,7 +3551,8 @@ async function installPlugin(opts) {
3520
3551
  cwd,
3521
3552
  configRoot,
3522
3553
  manifest,
3523
- plugin.externalRepos?.map((r) => basename2(r.dir))
3554
+ plugin.externalRepos?.map((r) => basename2(r.dir)),
3555
+ plugin.configRootConfigLink
3524
3556
  );
3525
3557
  if (backupInfo) {
3526
3558
  record.backup = {
@@ -3564,7 +3596,7 @@ init_fs_helpers();
3564
3596
  init_constants();
3565
3597
  init_logger();
3566
3598
  init_i18n();
3567
- import { existsSync as existsSync15, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, readdirSync as readdirSync4, rmSync as rmSync4, realpathSync as realpathSync2, readlinkSync as readlinkSync2, cpSync as cpSync2 } from "fs";
3599
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, symlinkSync as symlinkSync2, readdirSync as readdirSync5, rmSync as rmSync4, realpathSync as realpathSync2, readlinkSync as readlinkSync2, cpSync as cpSync2 } from "fs";
3568
3600
  import { join as join14, dirname as dirname5, isAbsolute } from "path";
3569
3601
  import { select as select3, checkbox, Separator as Separator2 } from "@inquirer/prompts";
3570
3602
  import chalk5 from "chalk";
@@ -3664,7 +3696,7 @@ async function uninstallSkills(skillIds, tool, level) {
3664
3696
  }
3665
3697
  if (existsSync15(skillsDir)) {
3666
3698
  try {
3667
- const entries = readdirSync4(skillsDir);
3699
+ const entries = readdirSync5(skillsDir);
3668
3700
  if (entries.length === 0) {
3669
3701
  rmSync4(skillsDir, { recursive: true });
3670
3702
  logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
@@ -3826,6 +3858,55 @@ async function interactiveSkillUnselect(tool, level) {
3826
3858
  logger.warn(t("uninstall_no_installed_skills"));
3827
3859
  return "back";
3828
3860
  }
3861
+ if (installedSkills.length <= 15) {
3862
+ return await flatSkillUnselect(installedSkills);
3863
+ }
3864
+ return await categorySkillUnselect(installedSkills);
3865
+ }
3866
+ async function flatSkillUnselect(installedSkills) {
3867
+ const categories = getAllCategories();
3868
+ const installedSet = new Set(installedSkills);
3869
+ const skillChoices = [
3870
+ ...installedSkills.map((skillId) => {
3871
+ const skill = findSkill(skillId);
3872
+ const cat = categories.find((c) => c.skills.some((s) => s.id === skillId));
3873
+ const catName = cat ? cat.name : "";
3874
+ const desc = skill?.description || "";
3875
+ return {
3876
+ name: `${chalk5.cyan(skillId)}${catName ? chalk5.dim(` [${catName}]`) : ""}`,
3877
+ value: skillId,
3878
+ checked: false,
3879
+ description: desc
3880
+ };
3881
+ })
3882
+ ];
3883
+ printBoxTitle(t("uninstall_interactive_title"));
3884
+ logger.info(chalk5.dim(` ${t("uninstall_checkbox_hint_select")}`));
3885
+ logger.blank();
3886
+ showOperationHints(true);
3887
+ const selected = await checkbox({
3888
+ message: t("uninstall_select_items"),
3889
+ choices: skillChoices,
3890
+ loop: false,
3891
+ instructions: false,
3892
+ theme: checkboxTheme,
3893
+ pageSize: 20
3894
+ });
3895
+ if (selected.length === 0) {
3896
+ logger.info(t("uninstall_nothing_to_uninstall"));
3897
+ return "back";
3898
+ }
3899
+ const confirmed = await confirm({
3900
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(selected.length))),
3901
+ default: false
3902
+ });
3903
+ if (!confirmed) return "cancel";
3904
+ return {
3905
+ toUninstall: selected,
3906
+ toKeep: installedSkills.filter((id) => !selected.includes(id))
3907
+ };
3908
+ }
3909
+ async function categorySkillUnselect(installedSkills) {
3829
3910
  const allCategories = getAllCategories();
3830
3911
  const installedSet = new Set(installedSkills);
3831
3912
  const categoriesWithInstalled = allCategories.map((cat) => ({
@@ -3842,14 +3923,13 @@ async function interactiveSkillUnselect(tool, level) {
3842
3923
  skills: uncategorized.map((id) => ({ id, description: "", source: "", filePath: "" }))
3843
3924
  });
3844
3925
  }
3845
- let keptSet = new Set(installedSkills);
3926
+ const uninstallSet = /* @__PURE__ */ new Set();
3846
3927
  let step = 0;
3847
3928
  let selectedCategoryId = "";
3848
3929
  while (true) {
3849
3930
  switch (step) {
3850
3931
  case 0: {
3851
- const toUninstallCount = installedSkills.length - keptSet.size;
3852
- const titleSuffix = toUninstallCount > 0 ? `${t("wizard_step_3_skill_title")}\uFF08${t("uninstall_marked_count").replace("{count}", String(toUninstallCount))}\uFF09` : t("wizard_step_3_skill_title");
3932
+ const titleSuffix = uninstallSet.size > 0 ? `${t("wizard_step_3_skill_title")}\uFF08${t("uninstall_marked_count").replace("{count}", String(uninstallSet.size))}\uFF09` : t("wizard_step_3_skill_title");
3853
3933
  printBoxTitle(titleSuffix);
3854
3934
  const categoryChoices = [
3855
3935
  {
@@ -3858,16 +3938,15 @@ async function interactiveSkillUnselect(tool, level) {
3858
3938
  },
3859
3939
  new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
3860
3940
  ...categoriesWithInstalled.map((cat) => {
3861
- const catKeptCount = cat.skills.filter((s) => keptSet.has(s.id)).length;
3862
- const catUninstallCount = cat.skills.length - catKeptCount;
3863
- const suffix = catUninstallCount > 0 ? chalk5.yellow(` (${catUninstallCount} to uninstall)`) : "";
3941
+ const catSelectedCount = cat.skills.filter((s) => uninstallSet.has(s.id)).length;
3942
+ const suffix = catSelectedCount > 0 ? chalk5.yellow(` (${catSelectedCount} selected)`) : "";
3864
3943
  return {
3865
3944
  name: `> ${cat.name} (${cat.skills.length} skills)${suffix}`,
3866
3945
  value: cat.id
3867
3946
  };
3868
3947
  }),
3869
3948
  new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
3870
- ...toUninstallCount > 0 ? [{ name: `>> ${t("uninstall_confirm_count").replace("{count}", String(toUninstallCount))}`, value: "uninstall" }] : [],
3949
+ ...uninstallSet.size > 0 ? [{ name: `>> ${t("uninstall_confirm_count").replace("{count}", String(uninstallSet.size))}`, value: "uninstall" }] : [],
3871
3950
  { name: "<- " + t("wizard_back"), value: BACK },
3872
3951
  { name: "x " + t("wizard_cancel"), value: CANCEL }
3873
3952
  ];
@@ -3882,22 +3961,27 @@ async function interactiveSkillUnselect(tool, level) {
3882
3961
  if (selectedCategoryId === BACK) return "back";
3883
3962
  if (selectedCategoryId === CANCEL) return "cancel";
3884
3963
  if (selectedCategoryId === UNSELECT_ALL) {
3885
- keptSet = /* @__PURE__ */ new Set();
3886
- step = 0;
3887
- break;
3964
+ for (const id of installedSkills) {
3965
+ uninstallSet.add(id);
3966
+ }
3967
+ const confirmed = await confirm({
3968
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(installedSkills.length))),
3969
+ default: false
3970
+ });
3971
+ if (!confirmed) return "cancel";
3972
+ return { toUninstall: [...uninstallSet], toKeep: [] };
3888
3973
  }
3889
3974
  if (selectedCategoryId === "uninstall") {
3890
- const toUninstall = installedSkills.filter((id) => !keptSet.has(id));
3891
- if (toUninstall.length === 0) {
3975
+ if (uninstallSet.size === 0) {
3892
3976
  logger.info(t("uninstall_nothing_to_uninstall"));
3893
3977
  return "back";
3894
3978
  }
3895
3979
  const confirmed = await confirm({
3896
- message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(toUninstall.length))),
3980
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(uninstallSet.size))),
3897
3981
  default: false
3898
3982
  });
3899
3983
  if (!confirmed) return "cancel";
3900
- return { toUninstall, toKeep: [...keptSet] };
3984
+ return { toUninstall: [...uninstallSet], toKeep: installedSkills.filter((id) => !uninstallSet.has(id)) };
3901
3985
  }
3902
3986
  step = 1;
3903
3987
  break;
@@ -3907,26 +3991,26 @@ async function interactiveSkillUnselect(tool, level) {
3907
3991
  if (!category) return "back";
3908
3992
  printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
3909
3993
  const categorySkillIds = new Set(category.skills.map((s) => s.id));
3910
- const keptInCategory = new Set([...keptSet].filter((id) => categorySkillIds.has(id)));
3994
+ const selectedInCategory = new Set([...uninstallSet].filter((id) => categorySkillIds.has(id)));
3911
3995
  const skillChoices = [
3912
3996
  {
3913
3997
  name: `>> ${t("uninstall_uncheck_all_category")}`,
3914
3998
  value: SELECT_ALL_CATEGORY,
3915
- checked: keptInCategory.size === 0,
3999
+ checked: selectedInCategory.size === category.skills.length,
3916
4000
  description: t("uninstall_uncheck_all_category")
3917
4001
  },
3918
4002
  new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
3919
4003
  ...category.skills.map((skill) => {
3920
- const isKept = keptInCategory.has(skill.id);
4004
+ const isSelected = selectedInCategory.has(skill.id);
3921
4005
  return {
3922
4006
  name: `${chalk5.cyan(skill.id)}`,
3923
4007
  value: skill.id,
3924
- checked: isKept,
4008
+ checked: isSelected,
3925
4009
  description: skill.description || ""
3926
4010
  };
3927
4011
  })
3928
4012
  ];
3929
- logger.info(chalk5.dim(` ${t("uninstall_checkbox_hint")}`));
4013
+ logger.info(chalk5.dim(` ${t("uninstall_checkbox_hint_select")}`));
3930
4014
  logger.blank();
3931
4015
  showOperationHints(true);
3932
4016
  const selectedSkills = await checkbox({
@@ -3937,20 +4021,20 @@ async function interactiveSkillUnselect(tool, level) {
3937
4021
  theme: checkboxTheme,
3938
4022
  pageSize: 15
3939
4023
  });
3940
- let keptInCat;
4024
+ let selectedInCat;
3941
4025
  if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
3942
- keptInCat = [];
4026
+ selectedInCat = category.skills.map((s) => s.id);
3943
4027
  } else {
3944
- keptInCat = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
4028
+ selectedInCat = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
3945
4029
  }
3946
4030
  for (const id of categorySkillIds) {
3947
- if (keptInCat.includes(id)) {
3948
- keptSet.add(id);
4031
+ if (selectedInCat.includes(id)) {
4032
+ uninstallSet.add(id);
3949
4033
  } else {
3950
- keptSet.delete(id);
4034
+ uninstallSet.delete(id);
3951
4035
  }
3952
4036
  }
3953
- const toUninstallCount = installedSkills.length - keptSet.size;
4037
+ const toUninstallCount = uninstallSet.size;
3954
4038
  const action = await select3({
3955
4039
  message: toUninstallCount > 0 ? t("uninstall_continue_or_confirm").replace("{count}", String(toUninstallCount)) : t("wizard_no_selection"),
3956
4040
  choices: toUninstallCount > 0 ? [
@@ -3968,17 +4052,16 @@ async function interactiveSkillUnselect(tool, level) {
3968
4052
  theme: selectTheme
3969
4053
  });
3970
4054
  if (action === "uninstall") {
3971
- const toUninstall = installedSkills.filter((id) => !keptSet.has(id));
3972
- if (toUninstall.length === 0) {
4055
+ if (uninstallSet.size === 0) {
3973
4056
  logger.info(t("uninstall_nothing_to_uninstall"));
3974
4057
  return "back";
3975
4058
  }
3976
4059
  const confirmed = await confirm({
3977
- message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(toUninstall.length))),
4060
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(uninstallSet.size))),
3978
4061
  default: false
3979
4062
  });
3980
4063
  if (!confirmed) return "cancel";
3981
- return { toUninstall, toKeep: [...keptSet] };
4064
+ return { toUninstall: [...uninstallSet], toKeep: installedSkills.filter((id) => !uninstallSet.has(id)) };
3982
4065
  }
3983
4066
  if (action === CONTINUE_SELECT) {
3984
4067
  step = 0;
@@ -4376,7 +4459,7 @@ init_manifest();
4376
4459
  init_paths();
4377
4460
  init_i18n();
4378
4461
  import chalk8 from "chalk";
4379
- import { existsSync as existsSync17, readdirSync as readdirSync5, lstatSync as lstatSync2, unlinkSync as unlinkSync5, mkdirSync as mkdirSync7 } from "fs";
4462
+ import { existsSync as existsSync17, readdirSync as readdirSync6, lstatSync as lstatSync2, unlinkSync as unlinkSync5, mkdirSync as mkdirSync7 } from "fs";
4380
4463
  import { join as join16 } from "path";
4381
4464
  async function doctorCommand(options = {}) {
4382
4465
  console.log();
@@ -4435,7 +4518,7 @@ async function doctorCommand(options = {}) {
4435
4518
  if (existsSync17(skillsDir)) {
4436
4519
  const brokenLinks = checkBrokenLinks(skillsDir);
4437
4520
  if (brokenLinks === 0) {
4438
- const count = readdirSync5(skillsDir).length;
4521
+ const count = readdirSync6(skillsDir).length;
4439
4522
  console.log(chalk8.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
4440
4523
  } else {
4441
4524
  console.log(chalk8.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
@@ -4457,7 +4540,7 @@ async function doctorCommand(options = {}) {
4457
4540
  if (existsSync17(agentsDir)) {
4458
4541
  const brokenLinks = checkBrokenLinks(agentsDir);
4459
4542
  if (brokenLinks === 0) {
4460
- const count = readdirSync5(agentsDir).length;
4543
+ const count = readdirSync6(agentsDir).length;
4461
4544
  console.log(chalk8.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
4462
4545
  } else {
4463
4546
  console.log(chalk8.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
@@ -4504,7 +4587,7 @@ async function doctorCommand(options = {}) {
4504
4587
  function checkBrokenLinks(dir) {
4505
4588
  let broken = 0;
4506
4589
  try {
4507
- const entries = readdirSync5(dir);
4590
+ const entries = readdirSync6(dir);
4508
4591
  for (const entry of entries) {
4509
4592
  const fullPath = join16(dir, entry);
4510
4593
  try {
@@ -4525,7 +4608,7 @@ function checkBrokenLinks(dir) {
4525
4608
  function fixBrokenLinks(dir) {
4526
4609
  let fixed = 0;
4527
4610
  try {
4528
- const entries = readdirSync5(dir);
4611
+ const entries = readdirSync6(dir);
4529
4612
  for (const entry of entries) {
4530
4613
  const fullPath = join16(dir, entry);
4531
4614
  try {
@@ -4912,8 +4995,8 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
4912
4995
 
4913
4996
  // src/commands/uninstall.ts
4914
4997
  init_registry();
4915
- import { existsSync as existsSync18, unlinkSync as unlinkSync6, rmdirSync, readdirSync as readdirSync6 } from "fs";
4916
- import { resolve, basename as basename3, sep } from "path";
4998
+ import { existsSync as existsSync18, unlinkSync as unlinkSync6, rmdirSync, readdirSync as readdirSync7 } from "fs";
4999
+ import { resolve, basename as basename3, sep, join as join18 } from "path";
4917
5000
  import chalk11 from "chalk";
4918
5001
  import { select as select5, checkbox as checkbox2, Separator as Separator4, confirm as confirm3 } from "@inquirer/prompts";
4919
5002
  init_config();
@@ -5031,24 +5114,66 @@ async function uninstallRecent(tool, level, yes) {
5031
5114
  async function uninstallInteractive(defaultTool, defaultLevel) {
5032
5115
  let tool = defaultTool;
5033
5116
  let level = defaultLevel;
5034
- const typeChoices = [
5035
- { name: `> ${t("uninstall_type_skill")}`, value: "skill" },
5036
- { name: `> ${t("uninstall_type_plugin")}`, value: "plugin" },
5037
- new Separator4("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
5038
- { name: "x " + t("wizard_cancel"), value: "cancel" }
5039
- ];
5040
- showOperationHints();
5041
- const uninstallType = await select5({
5042
- message: t("uninstall_select_type"),
5043
- choices: typeChoices,
5044
- loop: false,
5045
- theme: selectTheme
5046
- });
5047
- if (uninstallType === "cancel") {
5048
- logger.info(t("init_cancelled"));
5117
+ const allInstalledPlugins = scanInstalled();
5118
+ const toolsWithPlugins = new Set(allInstalledPlugins.map((p) => `${p.tool}:${p.level}`));
5119
+ const toolsWithSkills = /* @__PURE__ */ new Set();
5120
+ for (const toolName of ["opencode", "claude", "trae", "cursor", "copilot", "codearts"]) {
5121
+ for (const lvl of ["project", "global"]) {
5122
+ const ip = lvl === "project" ? process.cwd() : getConfigRoot(toolName, lvl);
5123
+ const skills = getInstalledSkills(toolName, lvl, ip);
5124
+ if (skills.length > 0) {
5125
+ toolsWithSkills.add(`${toolName}:${lvl}`);
5126
+ }
5127
+ }
5128
+ }
5129
+ const hasSkills = toolsWithSkills.size > 0;
5130
+ const hasPlugins = toolsWithPlugins.size > 0;
5131
+ if (!hasSkills && !hasPlugins) {
5132
+ logger.warn(t("uninstall_no_installed"));
5049
5133
  return;
5050
5134
  }
5051
- if (!process.env.INSTALL_HELPER_TOOL) {
5135
+ let uninstallType;
5136
+ if (hasSkills && hasPlugins) {
5137
+ printBoxTitle(t("uninstall_interactive_title"));
5138
+ const skillCount = [...toolsWithSkills].reduce((sum, key) => {
5139
+ const [toolName, lvl] = key.split(":");
5140
+ const ip = lvl === "project" ? process.cwd() : getConfigRoot(toolName, lvl);
5141
+ return sum + getInstalledSkills(toolName, lvl, ip).length;
5142
+ }, 0);
5143
+ const pluginCount = allInstalledPlugins.length;
5144
+ const typeChoices = [
5145
+ { name: `> ${t("uninstall_type_skill")} (${skillCount})`, value: "skill" },
5146
+ { name: `> ${t("uninstall_type_plugin")} (${pluginCount})`, value: "plugin" },
5147
+ new Separator4("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
5148
+ { name: "x " + t("wizard_cancel"), value: "cancel" }
5149
+ ];
5150
+ showOperationHints();
5151
+ const choice = await select5({
5152
+ message: t("uninstall_select_type"),
5153
+ choices: typeChoices,
5154
+ loop: false,
5155
+ theme: selectTheme
5156
+ });
5157
+ if (choice === "cancel") {
5158
+ logger.info(t("init_cancelled"));
5159
+ return;
5160
+ }
5161
+ uninstallType = choice;
5162
+ } else if (hasSkills) {
5163
+ uninstallType = "skill";
5164
+ } else {
5165
+ uninstallType = "plugin";
5166
+ }
5167
+ const relevantKeys = uninstallType === "skill" ? toolsWithSkills : toolsWithPlugins;
5168
+ const toolsWithContent = /* @__PURE__ */ new Set();
5169
+ for (const key of relevantKeys) {
5170
+ toolsWithContent.add(key.split(":")[0]);
5171
+ }
5172
+ if (toolsWithContent.size === 1) {
5173
+ tool = [...toolsWithContent][0];
5174
+ } else if (toolsWithContent.has(defaultTool)) {
5175
+ tool = defaultTool;
5176
+ } else {
5052
5177
  const detected = await selectToolWithDetection();
5053
5178
  if (detected === "back" || detected === "cancel") {
5054
5179
  logger.info(t("init_cancelled"));
@@ -5056,7 +5181,18 @@ async function uninstallInteractive(defaultTool, defaultLevel) {
5056
5181
  }
5057
5182
  tool = detected;
5058
5183
  }
5059
- if (!process.env.INSTALL_HELPER_LEVEL) {
5184
+ const levelsWithContent = /* @__PURE__ */ new Set();
5185
+ for (const key of relevantKeys) {
5186
+ const [toolName, lvl] = key.split(":");
5187
+ if (toolName === tool) {
5188
+ levelsWithContent.add(lvl);
5189
+ }
5190
+ }
5191
+ if (levelsWithContent.size === 1) {
5192
+ level = [...levelsWithContent][0];
5193
+ } else if (levelsWithContent.has(defaultLevel)) {
5194
+ level = defaultLevel;
5195
+ } else {
5060
5196
  const { stepLevel: stepLevel2 } = await Promise.resolve().then(() => (init_wizard(), wizard_exports));
5061
5197
  const levelResult = await stepLevel2();
5062
5198
  if (levelResult === "__back__" || levelResult === "__cancel__") {
@@ -5094,12 +5230,12 @@ async function interactivePluginUninstall(tool, level) {
5094
5230
  ...installed.map((inst) => ({
5095
5231
  name: `${chalk11.cyan(inst.displayName)} ${chalk11.dim(`(${inst.id})`)}`,
5096
5232
  value: inst.id,
5097
- checked: true,
5233
+ checked: false,
5098
5234
  description: `${inst.skillsCount} skills, ${inst.agentsCount} agents`
5099
5235
  }))
5100
5236
  ];
5101
5237
  printBoxTitle(t("uninstall_interactive_title"));
5102
- logger.info(chalk11.dim(` ${t("uninstall_checkbox_hint")}`));
5238
+ logger.info(chalk11.dim(` ${t("uninstall_checkbox_hint_select")}`));
5103
5239
  logger.blank();
5104
5240
  showOperationHints(true);
5105
5241
  const selected = await checkbox2({
@@ -5110,13 +5246,13 @@ async function interactivePluginUninstall(tool, level) {
5110
5246
  theme: checkboxTheme,
5111
5247
  pageSize: 15
5112
5248
  });
5113
- const toUninstall = installed.filter((inst) => !selected.includes(inst.id));
5249
+ const toUninstall = installed.filter((inst) => selected.includes(inst.id));
5114
5250
  if (toUninstall.length === 0) {
5115
5251
  logger.info(t("uninstall_nothing_to_uninstall"));
5116
5252
  return;
5117
5253
  }
5118
5254
  const confirmed = await confirm3({
5119
- message: t("uninstall_confirm_count").replace("{count}", chalk11.bold(String(toUninstall.length))),
5255
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk11.bold(String(toUninstall.length))),
5120
5256
  default: false
5121
5257
  });
5122
5258
  if (!confirmed) {
@@ -5154,7 +5290,15 @@ async function uninstallPluginById(pluginId, batchMode) {
5154
5290
  };
5155
5291
  let removedFiles = 0;
5156
5292
  let removedDirs = 0;
5157
- for (const filePath of record.files) {
5293
+ const configFileName = record.tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
5294
+ const configRootConfigPath = join18(configRoot, configFileName);
5295
+ const installPathConfigPath = join18(record.installPath, configFileName);
5296
+ const extraFiles = [];
5297
+ if (record.level === "project" && configRootConfigPath !== installPathConfigPath && !record.files.includes(configRootConfigPath) && (existsSync18(configRootConfigPath) || isSymlink(configRootConfigPath))) {
5298
+ extraFiles.push(configRootConfigPath);
5299
+ }
5300
+ const allFiles = [...record.files, ...extraFiles];
5301
+ for (const filePath of allFiles) {
5158
5302
  if (!isSafePath(filePath)) continue;
5159
5303
  try {
5160
5304
  if (existsSync18(filePath) || isSymlink(filePath)) {
@@ -5173,7 +5317,7 @@ async function uninstallPluginById(pluginId, batchMode) {
5173
5317
  continue;
5174
5318
  }
5175
5319
  if (existsSync18(dirPath)) {
5176
- const entries = readdirSync6(dirPath);
5320
+ const entries = readdirSync7(dirPath);
5177
5321
  if (entries.length === 0) {
5178
5322
  rmdirSync(dirPath);
5179
5323
  const name = basename3(dirPath);
@@ -5334,8 +5478,8 @@ async function updateCommand(pluginNames, options) {
5334
5478
  // src/commands/info.ts
5335
5479
  init_registry();
5336
5480
  import chalk13 from "chalk";
5337
- import { existsSync as existsSync19, readdirSync as readdirSync7 } from "fs";
5338
- import { join as join18 } from "path";
5481
+ import { existsSync as existsSync19, readdirSync as readdirSync8 } from "fs";
5482
+ import { join as join19 } from "path";
5339
5483
  init_manifest();
5340
5484
  init_i18n();
5341
5485
  async function infoCommand(pluginName) {
@@ -5362,13 +5506,13 @@ async function infoCommand(pluginName) {
5362
5506
  try {
5363
5507
  const repoManager = createRepositoryManager();
5364
5508
  const repoPath = await repoManager.ensureRepo();
5365
- const quickstartPath = join18(repoPath, plugin.dir, "quickstart.md");
5509
+ const quickstartPath = join19(repoPath, plugin.dir, "quickstart.md");
5366
5510
  if (existsSync19(quickstartPath)) {
5367
5511
  console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
5368
5512
  }
5369
- const agentsDir = join18(repoPath, plugin.dir, "agents");
5513
+ const agentsDir = join19(repoPath, plugin.dir, "agents");
5370
5514
  if (existsSync19(agentsDir)) {
5371
- const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
5515
+ const agentFiles = readdirSync8(agentsDir).filter((f) => f.endsWith(".md"));
5372
5516
  if (agentFiles.length > 0) {
5373
5517
  console.log();
5374
5518
  console.log(` ${chalk13.bold("Agents:")}`);
@@ -5425,7 +5569,7 @@ init_config();
5425
5569
  init_i18n();
5426
5570
  import { readFileSync as readFileSync9, existsSync as existsSync20 } from "fs";
5427
5571
  import { fileURLToPath as fileURLToPath4 } from "url";
5428
- import { dirname as dirname6, join as join19 } from "path";
5572
+ import { dirname as dirname6, join as join20 } from "path";
5429
5573
  function readVersion() {
5430
5574
  try {
5431
5575
  if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
@@ -5437,9 +5581,9 @@ function readVersion() {
5437
5581
  const __filename4 = fileURLToPath4(import.meta.url);
5438
5582
  const __dirname4 = dirname6(__filename4);
5439
5583
  const candidates = [
5440
- join19(__dirname4, "..", "package.json"),
5441
- join19(__dirname4, "package.json"),
5442
- join19(process.cwd(), "package.json")
5584
+ join20(__dirname4, "..", "package.json"),
5585
+ join20(__dirname4, "package.json"),
5586
+ join20(process.cwd(), "package.json")
5443
5587
  ];
5444
5588
  for (const pkgPath of candidates) {
5445
5589
  if (existsSync20(pkgPath)) {