@cannbot-ai/install-helper 1.1.6-beta.0 → 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",
@@ -864,7 +865,12 @@ var init_zh_CN = __esm({
864
865
  uninstall_plugin_batch_skip_restore: "\u6279\u91CF\u5378\u8F7D\u6A21\u5F0F\uFF1A\u8DF3\u8FC7\u5907\u4EFD\u6062\u590D",
865
866
  uninstall_confirm_prompt: "\u5373\u5C06\u5378\u8F7D {count} \u9879\uFF0C\u786E\u8BA4\uFF1F",
866
867
  uninstall_select_items: "\u7BA1\u7406\u5DF2\u5B89\u88C5\u7684 Skills\uFF08\u7A7A\u683C\u5207\u6362\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
867
- uninstall_select_plugins: "\u7BA1\u7406\u5DF2\u5B89\u88C5\u7684 Plugins\uFF08\u7A7A\u683C\u5207\u6362\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09"
868
+ uninstall_select_plugins: "\u7BA1\u7406\u5DF2\u5B89\u88C5\u7684 Plugins\uFF08\u7A7A\u683C\u5207\u6362\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
869
+ uninstall_select_category: "\u9009\u62E9\u8981\u7BA1\u7406\u7684 Skill \u7C7B\u522B",
870
+ uninstall_uncheck_all_category: "\u5378\u8F7D\u672C\u7C7B\u522B\u5168\u90E8",
871
+ uninstall_continue_or_confirm: "\u5DF2\u6807\u8BB0 {count} \u4E2A\u5F85\u5378\u8F7D\uFF0C\u8BF7\u9009\u62E9\u64CD\u4F5C",
872
+ uninstall_marked_count: "\u5DF2\u6807\u8BB0 {count} \u4E2A\u5F85\u5378\u8F7D",
873
+ uninstall_uncategorized: "\u672A\u5206\u7C7B"
868
874
  };
869
875
  }
870
876
  });
@@ -1061,12 +1067,13 @@ var init_en_US = __esm({
1061
1067
  doctor_result_without_fix: "{warnings} warnings",
1062
1068
  count_suffix: "",
1063
1069
  try_prompt: "help me develop an Abs operator",
1064
- uninstall_interactive_title: "Manage installed items \u2014 uncheck to uninstall",
1070
+ uninstall_interactive_title: "Manage installed items \u2014 check to uninstall",
1065
1071
  uninstall_select_type: "Select type to uninstall",
1066
1072
  uninstall_type_skill: "Uninstall Skills",
1067
1073
  uninstall_type_plugin: "Uninstall Plugins",
1068
1074
  uninstall_checkbox_hint: "Checked = keep, unchecked = uninstall",
1069
- 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",
1070
1077
  uninstall_confirm_count: "Confirm uninstall ({count} will be removed)",
1071
1078
  uninstall_no_installed: "No installed Skills or Plugins",
1072
1079
  uninstall_no_installed_skills: "No installed Skills",
@@ -1081,7 +1088,12 @@ var init_en_US = __esm({
1081
1088
  uninstall_plugin_batch_skip_restore: "Batch mode: skipping backup restore",
1082
1089
  uninstall_confirm_prompt: "About to uninstall {count} items, confirm?",
1083
1090
  uninstall_select_items: "Manage installed Skills (space to toggle, enter to confirm)",
1084
- uninstall_select_plugins: "Manage installed Plugins (space to toggle, enter to confirm)"
1091
+ uninstall_select_plugins: "Manage installed Plugins (space to toggle, enter to confirm)",
1092
+ uninstall_select_category: "Select Skill category to manage",
1093
+ uninstall_uncheck_all_category: "Uninstall all in this category",
1094
+ uninstall_continue_or_confirm: "{count} marked for uninstall, choose an action",
1095
+ uninstall_marked_count: "{count} marked for uninstall",
1096
+ uninstall_uncategorized: "Uncategorized"
1085
1097
  };
1086
1098
  }
1087
1099
  });
@@ -1451,30 +1463,35 @@ function scanInstalled() {
1451
1463
  function verifyPluginFilesExist(configRoot, manifest, tool, level) {
1452
1464
  const skillsDir = join5(configRoot, "skills");
1453
1465
  const agentsDir = join5(configRoot, "agents");
1466
+ let hasAnyComponent = false;
1454
1467
  if (manifest.installed_skills && manifest.installed_skills.length > 0) {
1455
1468
  const hasAnySkill = manifest.installed_skills.some((skillId) => {
1456
1469
  const skillPath = join5(skillsDir, skillId);
1457
- return existsSync5(skillPath);
1470
+ return existsSync5(skillPath) || isSymlink(skillPath);
1458
1471
  });
1459
- if (!hasAnySkill) return false;
1472
+ if (hasAnySkill) hasAnyComponent = true;
1460
1473
  }
1461
1474
  if (manifest.installed_agents && manifest.installed_agents.length > 0) {
1462
1475
  const hasAnyAgent = manifest.installed_agents.some((agentId) => {
1463
1476
  const agentPath = join5(agentsDir, agentId);
1464
- return existsSync5(agentPath);
1477
+ const agentPathMd = join5(agentsDir, agentId + ".md");
1478
+ return existsSync5(agentPath) || isSymlink(agentPath) || existsSync5(agentPathMd) || isSymlink(agentPathMd);
1465
1479
  });
1466
- if (!hasAnyAgent) return false;
1480
+ if (hasAnyAgent) hasAnyComponent = true;
1467
1481
  }
1468
1482
  const agentsFileName = getAgentsFileName(tool);
1469
1483
  const configFilePath = level === "project" ? join5(configRoot, "..", agentsFileName) : join5(configRoot, agentsFileName);
1470
- if (!existsSync5(configFilePath)) return false;
1471
- 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;
1472
1488
  }
1473
1489
  var init_manifest = __esm({
1474
1490
  "src/core/manifest.ts"() {
1475
1491
  "use strict";
1476
1492
  init_paths();
1477
1493
  init_registry();
1494
+ init_fs_helpers();
1478
1495
  }
1479
1496
  });
1480
1497
 
@@ -2689,7 +2706,7 @@ init_fs();
2689
2706
  init_fs_helpers();
2690
2707
  init_logger();
2691
2708
  init_i18n();
2692
- 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";
2693
2710
  import { join as join10 } from "path";
2694
2711
  function getInstallsDir() {
2695
2712
  return join10(getCannbotConfigDir(), "installs");
@@ -2724,7 +2741,7 @@ function deleteRecord(pluginId) {
2724
2741
  unlinkSync3(recordPath);
2725
2742
  }
2726
2743
  }
2727
- function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames) {
2744
+ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames, configRootConfigLink) {
2728
2745
  const files = [];
2729
2746
  const directories = [];
2730
2747
  if (manifest) {
@@ -2766,9 +2783,15 @@ function scanInstalledFiles(pluginId, displayName, tool, level, installPath, con
2766
2783
  }
2767
2784
  const configFileName = tool === "claude" ? "CLAUDE.md" : "AGENTS.md";
2768
2785
  const configFilePath = level === "project" ? join10(installPath, configFileName) : join10(configRoot, configFileName);
2769
- if (existsSync11(configFilePath)) {
2786
+ if (existsSync11(configFilePath) || isSymlink(configFilePath)) {
2770
2787
  files.push(configFilePath);
2771
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
+ }
2772
2795
  const repoLinks = externalRepoNames && externalRepoNames.length > 0 ? externalRepoNames : ["asc-devkit", "pypto", "tilelang-ascend", "cann-recipes-infer", "cann-samples"];
2773
2796
  for (const repoName of repoLinks) {
2774
2797
  const repoLinkPath = join10(installPath, repoName);
@@ -2833,12 +2856,14 @@ function addSkillsToRecord(skillIds, tool, level, installPath) {
2833
2856
  }
2834
2857
  }
2835
2858
  entry.installTime = now;
2836
- if (!entry.batches) entry.batches = [];
2837
- entry.batches.push({
2838
- batchId: `batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2839
- installedAt: now,
2840
- skills: newSkills.length > 0 ? newSkills : [...skillIds]
2841
- });
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
+ }
2842
2867
  writeSkillRecord(record);
2843
2868
  }
2844
2869
  function removeSkillsFromRecord(skillIds, tool, level, installPath) {
@@ -2871,10 +2896,25 @@ function getInstalledSkills(tool, level, installPath) {
2871
2896
  const recordedSkills = record[tool]?.[level]?.[installPath]?.skills || [];
2872
2897
  const configRoot = getConfigRoot(tool, level);
2873
2898
  const skillsDir = join10(configRoot, "skills");
2874
- return recordedSkills.filter((skillId) => {
2899
+ const fromRecord = recordedSkills.filter((skillId) => {
2875
2900
  const skillPath = join10(skillsDir, skillId);
2876
2901
  return existsSync11(skillPath) || isSymlink(skillPath);
2877
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];
2878
2918
  }
2879
2919
  function getLastBatchSkills(tool, level, installPath) {
2880
2920
  const record = readSkillRecord();
@@ -2893,7 +2933,7 @@ function getLastBatchSkills(tool, level, installPath) {
2893
2933
 
2894
2934
  // src/core/backup.ts
2895
2935
  init_manifest();
2896
- 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";
2897
2937
  import { join as join11 } from "path";
2898
2938
  init_registry();
2899
2939
  init_paths();
@@ -2958,7 +2998,7 @@ function findBackups(configRoot) {
2958
2998
  if (!existsSync12(configRoot)) {
2959
2999
  return [];
2960
3000
  }
2961
- const files = readdirSync3(configRoot);
3001
+ const files = readdirSync4(configRoot);
2962
3002
  const backups = [];
2963
3003
  for (const file of files) {
2964
3004
  if (file.includes(".cannbot-backup.")) {
@@ -3424,7 +3464,8 @@ async function installPlugin(opts) {
3424
3464
  cwd,
3425
3465
  configRoot,
3426
3466
  result.manifest,
3427
- plugin.externalRepos?.map((r) => basename2(r.dir))
3467
+ plugin.externalRepos?.map((r) => basename2(r.dir)),
3468
+ plugin.configRootConfigLink
3428
3469
  );
3429
3470
  if (backupInfo) {
3430
3471
  record.backup = {
@@ -3510,7 +3551,8 @@ async function installPlugin(opts) {
3510
3551
  cwd,
3511
3552
  configRoot,
3512
3553
  manifest,
3513
- plugin.externalRepos?.map((r) => basename2(r.dir))
3554
+ plugin.externalRepos?.map((r) => basename2(r.dir)),
3555
+ plugin.configRootConfigLink
3514
3556
  );
3515
3557
  if (backupInfo) {
3516
3558
  record.backup = {
@@ -3554,7 +3596,7 @@ init_fs_helpers();
3554
3596
  init_constants();
3555
3597
  init_logger();
3556
3598
  init_i18n();
3557
- 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";
3558
3600
  import { join as join14, dirname as dirname5, isAbsolute } from "path";
3559
3601
  import { select as select3, checkbox, Separator as Separator2 } from "@inquirer/prompts";
3560
3602
  import chalk5 from "chalk";
@@ -3654,7 +3696,7 @@ async function uninstallSkills(skillIds, tool, level) {
3654
3696
  }
3655
3697
  if (existsSync15(skillsDir)) {
3656
3698
  try {
3657
- const entries = readdirSync4(skillsDir);
3699
+ const entries = readdirSync5(skillsDir);
3658
3700
  if (entries.length === 0) {
3659
3701
  rmSync4(skillsDir, { recursive: true });
3660
3702
  logger.step(` ${t("uninstall_clean_empty_dir")}: skills/`);
@@ -3816,16 +3858,15 @@ async function interactiveSkillUnselect(tool, level) {
3816
3858
  logger.warn(t("uninstall_no_installed_skills"));
3817
3859
  return "back";
3818
3860
  }
3861
+ if (installedSkills.length <= 15) {
3862
+ return await flatSkillUnselect(installedSkills);
3863
+ }
3864
+ return await categorySkillUnselect(installedSkills);
3865
+ }
3866
+ async function flatSkillUnselect(installedSkills) {
3819
3867
  const categories = getAllCategories();
3820
3868
  const installedSet = new Set(installedSkills);
3821
3869
  const skillChoices = [
3822
- {
3823
- name: `>> ${t("uninstall_uncheck_all")}`,
3824
- value: UNSELECT_ALL,
3825
- checked: false,
3826
- description: t("uninstall_uncheck_all")
3827
- },
3828
- new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
3829
3870
  ...installedSkills.map((skillId) => {
3830
3871
  const skill = findSkill(skillId);
3831
3872
  const cat = categories.find((c) => c.skills.some((s) => s.id === skillId));
@@ -3834,13 +3875,13 @@ async function interactiveSkillUnselect(tool, level) {
3834
3875
  return {
3835
3876
  name: `${chalk5.cyan(skillId)}${catName ? chalk5.dim(` [${catName}]`) : ""}`,
3836
3877
  value: skillId,
3837
- checked: true,
3878
+ checked: false,
3838
3879
  description: desc
3839
3880
  };
3840
3881
  })
3841
3882
  ];
3842
3883
  printBoxTitle(t("uninstall_interactive_title"));
3843
- logger.info(chalk5.dim(` ${t("uninstall_checkbox_hint")}`));
3884
+ logger.info(chalk5.dim(` ${t("uninstall_checkbox_hint_select")}`));
3844
3885
  logger.blank();
3845
3886
  showOperationHints(true);
3846
3887
  const selected = await checkbox({
@@ -3851,25 +3892,189 @@ async function interactiveSkillUnselect(tool, level) {
3851
3892
  theme: checkboxTheme,
3852
3893
  pageSize: 20
3853
3894
  });
3854
- let toKeep;
3855
- if (selected.includes(UNSELECT_ALL)) {
3856
- toKeep = [];
3857
- } else {
3858
- toKeep = selected.filter((id) => id !== UNSELECT_ALL);
3859
- }
3860
- const toUninstall = installedSkills.filter((id) => !toKeep.includes(id));
3861
- if (toUninstall.length === 0) {
3895
+ if (selected.length === 0) {
3862
3896
  logger.info(t("uninstall_nothing_to_uninstall"));
3863
3897
  return "back";
3864
3898
  }
3865
3899
  const confirmed = await confirm({
3866
- message: t("uninstall_confirm_count").replace("{count}", chalk5.bold(String(toUninstall.length))),
3900
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(selected.length))),
3867
3901
  default: false
3868
3902
  });
3869
- if (!confirmed) {
3870
- return "cancel";
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) {
3910
+ const allCategories = getAllCategories();
3911
+ const installedSet = new Set(installedSkills);
3912
+ const categoriesWithInstalled = allCategories.map((cat) => ({
3913
+ ...cat,
3914
+ skills: cat.skills.filter((s) => installedSet.has(s.id))
3915
+ })).filter((cat) => cat.skills.length > 0);
3916
+ const uncategorized = installedSkills.filter(
3917
+ (id) => !allCategories.some((cat) => cat.skills.some((s) => s.id === id))
3918
+ );
3919
+ if (uncategorized.length > 0) {
3920
+ categoriesWithInstalled.push({
3921
+ id: "__uncategorized__",
3922
+ name: t("uninstall_uncategorized"),
3923
+ skills: uncategorized.map((id) => ({ id, description: "", source: "", filePath: "" }))
3924
+ });
3925
+ }
3926
+ const uninstallSet = /* @__PURE__ */ new Set();
3927
+ let step = 0;
3928
+ let selectedCategoryId = "";
3929
+ while (true) {
3930
+ switch (step) {
3931
+ case 0: {
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");
3933
+ printBoxTitle(titleSuffix);
3934
+ const categoryChoices = [
3935
+ {
3936
+ name: `>> ${t("uninstall_uncheck_all")}`,
3937
+ value: UNSELECT_ALL
3938
+ },
3939
+ new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
3940
+ ...categoriesWithInstalled.map((cat) => {
3941
+ const catSelectedCount = cat.skills.filter((s) => uninstallSet.has(s.id)).length;
3942
+ const suffix = catSelectedCount > 0 ? chalk5.yellow(` (${catSelectedCount} selected)`) : "";
3943
+ return {
3944
+ name: `> ${cat.name} (${cat.skills.length} skills)${suffix}`,
3945
+ value: cat.id
3946
+ };
3947
+ }),
3948
+ new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
3949
+ ...uninstallSet.size > 0 ? [{ name: `>> ${t("uninstall_confirm_count").replace("{count}", String(uninstallSet.size))}`, value: "uninstall" }] : [],
3950
+ { name: "<- " + t("wizard_back"), value: BACK },
3951
+ { name: "x " + t("wizard_cancel"), value: CANCEL }
3952
+ ];
3953
+ showOperationHints();
3954
+ selectedCategoryId = await select3({
3955
+ message: t("uninstall_select_category"),
3956
+ choices: categoryChoices,
3957
+ loop: false,
3958
+ theme: selectTheme,
3959
+ pageSize: 15
3960
+ });
3961
+ if (selectedCategoryId === BACK) return "back";
3962
+ if (selectedCategoryId === CANCEL) return "cancel";
3963
+ if (selectedCategoryId === UNSELECT_ALL) {
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: [] };
3973
+ }
3974
+ if (selectedCategoryId === "uninstall") {
3975
+ if (uninstallSet.size === 0) {
3976
+ logger.info(t("uninstall_nothing_to_uninstall"));
3977
+ return "back";
3978
+ }
3979
+ const confirmed = await confirm({
3980
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(uninstallSet.size))),
3981
+ default: false
3982
+ });
3983
+ if (!confirmed) return "cancel";
3984
+ return { toUninstall: [...uninstallSet], toKeep: installedSkills.filter((id) => !uninstallSet.has(id)) };
3985
+ }
3986
+ step = 1;
3987
+ break;
3988
+ }
3989
+ case 1: {
3990
+ const category = categoriesWithInstalled.find((c) => c.id === selectedCategoryId);
3991
+ if (!category) return "back";
3992
+ printBoxTitle(t("wizard_step_4_skill_title").replace("{category}", category.name));
3993
+ const categorySkillIds = new Set(category.skills.map((s) => s.id));
3994
+ const selectedInCategory = new Set([...uninstallSet].filter((id) => categorySkillIds.has(id)));
3995
+ const skillChoices = [
3996
+ {
3997
+ name: `>> ${t("uninstall_uncheck_all_category")}`,
3998
+ value: SELECT_ALL_CATEGORY,
3999
+ checked: selectedInCategory.size === category.skills.length,
4000
+ description: t("uninstall_uncheck_all_category")
4001
+ },
4002
+ new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
4003
+ ...category.skills.map((skill) => {
4004
+ const isSelected = selectedInCategory.has(skill.id);
4005
+ return {
4006
+ name: `${chalk5.cyan(skill.id)}`,
4007
+ value: skill.id,
4008
+ checked: isSelected,
4009
+ description: skill.description || ""
4010
+ };
4011
+ })
4012
+ ];
4013
+ logger.info(chalk5.dim(` ${t("uninstall_checkbox_hint_select")}`));
4014
+ logger.blank();
4015
+ showOperationHints(true);
4016
+ const selectedSkills = await checkbox({
4017
+ message: t("uninstall_select_items"),
4018
+ choices: skillChoices,
4019
+ loop: false,
4020
+ instructions: false,
4021
+ theme: checkboxTheme,
4022
+ pageSize: 15
4023
+ });
4024
+ let selectedInCat;
4025
+ if (selectedSkills.includes(SELECT_ALL_CATEGORY)) {
4026
+ selectedInCat = category.skills.map((s) => s.id);
4027
+ } else {
4028
+ selectedInCat = selectedSkills.filter((id) => id !== SELECT_ALL_CATEGORY);
4029
+ }
4030
+ for (const id of categorySkillIds) {
4031
+ if (selectedInCat.includes(id)) {
4032
+ uninstallSet.add(id);
4033
+ } else {
4034
+ uninstallSet.delete(id);
4035
+ }
4036
+ }
4037
+ const toUninstallCount = uninstallSet.size;
4038
+ const action = await select3({
4039
+ message: toUninstallCount > 0 ? t("uninstall_continue_or_confirm").replace("{count}", String(toUninstallCount)) : t("wizard_no_selection"),
4040
+ choices: toUninstallCount > 0 ? [
4041
+ { name: ">> " + t("uninstall_confirm_count").replace("{count}", String(toUninstallCount)), value: "uninstall" },
4042
+ { name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
4043
+ new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
4044
+ { name: "x " + t("wizard_cancel"), value: "cancel" }
4045
+ ] : [
4046
+ new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
4047
+ { name: "> " + t("skill_continue_select"), value: CONTINUE_SELECT },
4048
+ { name: "<- " + t("wizard_back_to_reselect"), value: "back" },
4049
+ { name: "x " + t("wizard_cancel"), value: "cancel" }
4050
+ ],
4051
+ loop: false,
4052
+ theme: selectTheme
4053
+ });
4054
+ if (action === "uninstall") {
4055
+ if (uninstallSet.size === 0) {
4056
+ logger.info(t("uninstall_nothing_to_uninstall"));
4057
+ return "back";
4058
+ }
4059
+ const confirmed = await confirm({
4060
+ message: t("uninstall_confirm_prompt").replace("{count}", chalk5.bold(String(uninstallSet.size))),
4061
+ default: false
4062
+ });
4063
+ if (!confirmed) return "cancel";
4064
+ return { toUninstall: [...uninstallSet], toKeep: installedSkills.filter((id) => !uninstallSet.has(id)) };
4065
+ }
4066
+ if (action === CONTINUE_SELECT) {
4067
+ step = 0;
4068
+ break;
4069
+ }
4070
+ if (action === "back") {
4071
+ step = 0;
4072
+ break;
4073
+ }
4074
+ return "cancel";
4075
+ }
4076
+ }
3871
4077
  }
3872
- return { toUninstall, toKeep };
3873
4078
  }
3874
4079
  function listAllSkills() {
3875
4080
  const categories = getAllCategories();
@@ -4254,7 +4459,7 @@ init_manifest();
4254
4459
  init_paths();
4255
4460
  init_i18n();
4256
4461
  import chalk8 from "chalk";
4257
- 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";
4258
4463
  import { join as join16 } from "path";
4259
4464
  async function doctorCommand(options = {}) {
4260
4465
  console.log();
@@ -4313,7 +4518,7 @@ async function doctorCommand(options = {}) {
4313
4518
  if (existsSync17(skillsDir)) {
4314
4519
  const brokenLinks = checkBrokenLinks(skillsDir);
4315
4520
  if (brokenLinks === 0) {
4316
- const count = readdirSync5(skillsDir).length;
4521
+ const count = readdirSync6(skillsDir).length;
4317
4522
  console.log(chalk8.green(" \u2713") + ` ${skillsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
4318
4523
  } else {
4319
4524
  console.log(chalk8.yellow(" \u26A0") + ` ${skillsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
@@ -4335,7 +4540,7 @@ async function doctorCommand(options = {}) {
4335
4540
  if (existsSync17(agentsDir)) {
4336
4541
  const brokenLinks = checkBrokenLinks(agentsDir);
4337
4542
  if (brokenLinks === 0) {
4338
- const count = readdirSync5(agentsDir).length;
4543
+ const count = readdirSync6(agentsDir).length;
4339
4544
  console.log(chalk8.green(" \u2713") + ` ${agentsDir} \u2014 ${t("doctor_links_valid").replace("{count}", String(count))}`);
4340
4545
  } else {
4341
4546
  console.log(chalk8.yellow(" \u26A0") + ` ${agentsDir} \u2014 ${t("doctor_broken_links").replace("{count}", String(brokenLinks))}`);
@@ -4382,7 +4587,7 @@ async function doctorCommand(options = {}) {
4382
4587
  function checkBrokenLinks(dir) {
4383
4588
  let broken = 0;
4384
4589
  try {
4385
- const entries = readdirSync5(dir);
4590
+ const entries = readdirSync6(dir);
4386
4591
  for (const entry of entries) {
4387
4592
  const fullPath = join16(dir, entry);
4388
4593
  try {
@@ -4403,7 +4608,7 @@ function checkBrokenLinks(dir) {
4403
4608
  function fixBrokenLinks(dir) {
4404
4609
  let fixed = 0;
4405
4610
  try {
4406
- const entries = readdirSync5(dir);
4611
+ const entries = readdirSync6(dir);
4407
4612
  for (const entry of entries) {
4408
4613
  const fullPath = join16(dir, entry);
4409
4614
  try {
@@ -4790,8 +4995,8 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
4790
4995
 
4791
4996
  // src/commands/uninstall.ts
4792
4997
  init_registry();
4793
- import { existsSync as existsSync18, unlinkSync as unlinkSync6, rmdirSync, readdirSync as readdirSync6 } from "fs";
4794
- 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";
4795
5000
  import chalk11 from "chalk";
4796
5001
  import { select as select5, checkbox as checkbox2, Separator as Separator4, confirm as confirm3 } from "@inquirer/prompts";
4797
5002
  init_config();
@@ -4909,25 +5114,66 @@ async function uninstallRecent(tool, level, yes) {
4909
5114
  async function uninstallInteractive(defaultTool, defaultLevel) {
4910
5115
  let tool = defaultTool;
4911
5116
  let level = defaultLevel;
4912
- printBoxTitle(t("uninstall_interactive_title"));
4913
- const typeChoices = [
4914
- { name: `> ${t("uninstall_type_skill")}`, value: "skill" },
4915
- { name: `> ${t("uninstall_type_plugin")}`, value: "plugin" },
4916
- new Separator4("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
4917
- { name: "x " + t("wizard_cancel"), value: "cancel" }
4918
- ];
4919
- showOperationHints();
4920
- const uninstallType = await select5({
4921
- message: t("uninstall_select_type"),
4922
- choices: typeChoices,
4923
- loop: false,
4924
- theme: selectTheme
4925
- });
4926
- if (uninstallType === "cancel") {
4927
- 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"));
4928
5133
  return;
4929
5134
  }
4930
- 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 {
4931
5177
  const detected = await selectToolWithDetection();
4932
5178
  if (detected === "back" || detected === "cancel") {
4933
5179
  logger.info(t("init_cancelled"));
@@ -4935,7 +5181,18 @@ async function uninstallInteractive(defaultTool, defaultLevel) {
4935
5181
  }
4936
5182
  tool = detected;
4937
5183
  }
4938
- 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 {
4939
5196
  const { stepLevel: stepLevel2 } = await Promise.resolve().then(() => (init_wizard(), wizard_exports));
4940
5197
  const levelResult = await stepLevel2();
4941
5198
  if (levelResult === "__back__" || levelResult === "__cancel__") {
@@ -4973,12 +5230,12 @@ async function interactivePluginUninstall(tool, level) {
4973
5230
  ...installed.map((inst) => ({
4974
5231
  name: `${chalk11.cyan(inst.displayName)} ${chalk11.dim(`(${inst.id})`)}`,
4975
5232
  value: inst.id,
4976
- checked: true,
5233
+ checked: false,
4977
5234
  description: `${inst.skillsCount} skills, ${inst.agentsCount} agents`
4978
5235
  }))
4979
5236
  ];
4980
5237
  printBoxTitle(t("uninstall_interactive_title"));
4981
- logger.info(chalk11.dim(` ${t("uninstall_checkbox_hint")}`));
5238
+ logger.info(chalk11.dim(` ${t("uninstall_checkbox_hint_select")}`));
4982
5239
  logger.blank();
4983
5240
  showOperationHints(true);
4984
5241
  const selected = await checkbox2({
@@ -4989,13 +5246,13 @@ async function interactivePluginUninstall(tool, level) {
4989
5246
  theme: checkboxTheme,
4990
5247
  pageSize: 15
4991
5248
  });
4992
- const toUninstall = installed.filter((inst) => !selected.includes(inst.id));
5249
+ const toUninstall = installed.filter((inst) => selected.includes(inst.id));
4993
5250
  if (toUninstall.length === 0) {
4994
5251
  logger.info(t("uninstall_nothing_to_uninstall"));
4995
5252
  return;
4996
5253
  }
4997
5254
  const confirmed = await confirm3({
4998
- 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))),
4999
5256
  default: false
5000
5257
  });
5001
5258
  if (!confirmed) {
@@ -5033,7 +5290,15 @@ async function uninstallPluginById(pluginId, batchMode) {
5033
5290
  };
5034
5291
  let removedFiles = 0;
5035
5292
  let removedDirs = 0;
5036
- 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) {
5037
5302
  if (!isSafePath(filePath)) continue;
5038
5303
  try {
5039
5304
  if (existsSync18(filePath) || isSymlink(filePath)) {
@@ -5052,7 +5317,7 @@ async function uninstallPluginById(pluginId, batchMode) {
5052
5317
  continue;
5053
5318
  }
5054
5319
  if (existsSync18(dirPath)) {
5055
- const entries = readdirSync6(dirPath);
5320
+ const entries = readdirSync7(dirPath);
5056
5321
  if (entries.length === 0) {
5057
5322
  rmdirSync(dirPath);
5058
5323
  const name = basename3(dirPath);
@@ -5213,8 +5478,8 @@ async function updateCommand(pluginNames, options) {
5213
5478
  // src/commands/info.ts
5214
5479
  init_registry();
5215
5480
  import chalk13 from "chalk";
5216
- import { existsSync as existsSync19, readdirSync as readdirSync7 } from "fs";
5217
- import { join as join18 } from "path";
5481
+ import { existsSync as existsSync19, readdirSync as readdirSync8 } from "fs";
5482
+ import { join as join19 } from "path";
5218
5483
  init_manifest();
5219
5484
  init_i18n();
5220
5485
  async function infoCommand(pluginName) {
@@ -5241,13 +5506,13 @@ async function infoCommand(pluginName) {
5241
5506
  try {
5242
5507
  const repoManager = createRepositoryManager();
5243
5508
  const repoPath = await repoManager.ensureRepo();
5244
- const quickstartPath = join18(repoPath, plugin.dir, "quickstart.md");
5509
+ const quickstartPath = join19(repoPath, plugin.dir, "quickstart.md");
5245
5510
  if (existsSync19(quickstartPath)) {
5246
5511
  console.log(` ${chalk13.dim(t("info_quickstart") + ":")} ${quickstartPath}`);
5247
5512
  }
5248
- const agentsDir = join18(repoPath, plugin.dir, "agents");
5513
+ const agentsDir = join19(repoPath, plugin.dir, "agents");
5249
5514
  if (existsSync19(agentsDir)) {
5250
- const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md"));
5515
+ const agentFiles = readdirSync8(agentsDir).filter((f) => f.endsWith(".md"));
5251
5516
  if (agentFiles.length > 0) {
5252
5517
  console.log();
5253
5518
  console.log(` ${chalk13.bold("Agents:")}`);
@@ -5304,7 +5569,7 @@ init_config();
5304
5569
  init_i18n();
5305
5570
  import { readFileSync as readFileSync9, existsSync as existsSync20 } from "fs";
5306
5571
  import { fileURLToPath as fileURLToPath4 } from "url";
5307
- import { dirname as dirname6, join as join19 } from "path";
5572
+ import { dirname as dirname6, join as join20 } from "path";
5308
5573
  function readVersion() {
5309
5574
  try {
5310
5575
  if (typeof INSTALL_HELPER_VERSION !== "undefined" && INSTALL_HELPER_VERSION) {
@@ -5316,9 +5581,9 @@ function readVersion() {
5316
5581
  const __filename4 = fileURLToPath4(import.meta.url);
5317
5582
  const __dirname4 = dirname6(__filename4);
5318
5583
  const candidates = [
5319
- join19(__dirname4, "..", "package.json"),
5320
- join19(__dirname4, "package.json"),
5321
- join19(process.cwd(), "package.json")
5584
+ join20(__dirname4, "..", "package.json"),
5585
+ join20(__dirname4, "package.json"),
5586
+ join20(process.cwd(), "package.json")
5322
5587
  ];
5323
5588
  for (const pkgPath of candidates) {
5324
5589
  if (existsSync20(pkgPath)) {