bfi-cli 0.1.57 → 0.1.58

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.
@@ -33,8 +33,12 @@ function saveCache(agents, maxUpdatedAt) {
33
33
  }));
34
34
  } catch {}
35
35
  }
36
+ function expandTilde(p) {
37
+ if (p && p.startsWith("~/")) return path.join(os.homedir(), p.slice(2));
38
+ return p;
39
+ }
36
40
  function mapToAgentConfig(a) {
37
- const gsd = a.globalSkillsDir || void 0;
41
+ const gsd = expandTilde(a.globalSkillsDir || void 0);
38
42
  return {
39
43
  name: a.name,
40
44
  displayName: a.displayName || a.name,
@@ -45,7 +49,7 @@ function mapToAgentConfig(a) {
45
49
  };
46
50
  }
47
51
  function mapCustomEntry(name, entry) {
48
- const gsd = entry.globalSkillsDir || void 0;
52
+ const gsd = expandTilde(entry.globalSkillsDir || void 0);
49
53
  return {
50
54
  name,
51
55
  displayName: entry.displayName || name,
package/dist/cli.mjs CHANGED
@@ -2796,12 +2796,14 @@ async function tryBlobInstall(ownerRepo, options = {}) {
2796
2796
  };
2797
2797
  }
2798
2798
  var add_exports = /* @__PURE__ */ __exportAll({
2799
+ ensureUniversalAgents: () => ensureUniversalAgents,
2799
2800
  formatEveInstallPromptMessage: () => formatEveInstallPromptMessage,
2800
2801
  getLockSource: () => getLockSource,
2801
2802
  getProjectLockSourceUrl: () => getProjectLockSourceUrl,
2802
2803
  parseAddOptions: () => parseAddOptions,
2803
2804
  promptForAgents: () => promptForAgents,
2804
- runAdd: () => runAdd
2805
+ runAdd: () => runAdd,
2806
+ selectAgentsInteractive: () => selectAgentsInteractive
2805
2807
  });
2806
2808
  const isCancelled = (value) => typeof value === "symbol";
2807
2809
  const EVE_AGENT_LABEL = "eve agent";
@@ -2876,20 +2878,20 @@ function computeSingleFileSkillHash(contents) {
2876
2878
  hash.update(contents);
2877
2879
  return hash.digest("hex");
2878
2880
  }
2879
- function formatList$1(items, maxShow = 5) {
2881
+ function formatList$2(items, maxShow = 5) {
2880
2882
  if (items.length <= maxShow) return items.join(", ");
2881
2883
  const shown = items.slice(0, maxShow);
2882
2884
  const remaining = items.length - maxShow;
2883
2885
  return `${shown.join(", ")} +${remaining} more`;
2884
2886
  }
2885
2887
  function formatSkillPromptSubject(skills) {
2886
- const namedSubject = formatList$1(skills.map((skill) => import_picocolors.default.cyan(getSkillDisplayName(skill))), 3);
2888
+ const namedSubject = formatList$2(skills.map((skill) => import_picocolors.default.cyan(getSkillDisplayName(skill))), 3);
2887
2889
  return stripTerminalEscapes(namedSubject).length <= 80 ? namedSubject : `${skills.length} selected skills`;
2888
2890
  }
2889
2891
  function formatEveInstallPromptMessage(skills) {
2890
2892
  return `Detected an eve project. Install ${formatSkillPromptSubject(skills)} for your ${EVE_AGENT_LABEL} to use?`;
2891
2893
  }
2892
- function splitAgentsByType(agentTypes) {
2894
+ function splitAgentsByType$1(agentTypes) {
2893
2895
  const universal = [];
2894
2896
  const symlinked = [];
2895
2897
  for (const a of agentTypes) if (isUniversalAgent(a)) universal.push(agents[a].displayName);
@@ -2901,29 +2903,29 @@ function splitAgentsByType(agentTypes) {
2901
2903
  }
2902
2904
  function buildAgentSummaryLines(targetAgents, installMode, selectedAgents) {
2903
2905
  const lines = [];
2904
- const { universal, symlinked } = splitAgentsByType(targetAgents);
2906
+ const { universal, symlinked } = splitAgentsByType$1(targetAgents);
2905
2907
  const selectedSet = new Set(selectedAgents || targetAgents);
2906
2908
  if (installMode === "symlink") {
2907
2909
  const selectedUniversal = universal.filter((a) => selectedSet.has(a));
2908
2910
  const selectedSymlink = symlinked.filter((a) => selectedSet.has(a));
2909
- if (selectedUniversal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$1(selectedUniversal)}`);
2910
- if (selectedSymlink.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$1(selectedSymlink)}`);
2911
+ if (selectedUniversal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$2(selectedUniversal)}`);
2912
+ if (selectedSymlink.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$2(selectedSymlink)}`);
2911
2913
  const otherUniversal = universal.filter((a) => !selectedSet.has(a));
2912
2914
  if (otherUniversal.length > 0) lines.push(` ${import_picocolors.default.dim(`(另外 ${otherUniversal.length} 个使用共享目录的 Agent 也会自动获得此技能)`)}`);
2913
2915
  } else {
2914
2916
  const allNames = targetAgents.filter((a) => selectedSet.has(a)).map((a) => agents[a].displayName);
2915
- lines.push(` ${import_picocolors.default.dim("复制 →")} ${formatList$1(allNames)}`);
2917
+ lines.push(` ${import_picocolors.default.dim("复制 →")} ${formatList$2(allNames)}`);
2916
2918
  }
2917
2919
  return lines;
2918
2920
  }
2919
- function targetDisplayName(target) {
2921
+ function targetDisplayName$1(target) {
2920
2922
  const base = agents[target.agent].displayName;
2921
2923
  return target.subagent ? `${base} (${target.subagent})` : base;
2922
2924
  }
2923
- function targetKey(target) {
2925
+ function targetKey$1(target) {
2924
2926
  return target.subagent ? `${target.agent}:${target.subagent}` : target.agent;
2925
2927
  }
2926
- function buildInstallTargets(targetAgents, eveSubagentTargets) {
2928
+ function buildInstallTargets$1(targetAgents, eveSubagentTargets) {
2927
2929
  const targets = [];
2928
2930
  for (const agent of targetAgents) if (agent === "eve") for (const subagent of eveSubagentTargets) targets.push({
2929
2931
  agent,
@@ -2932,18 +2934,18 @@ function buildInstallTargets(targetAgents, eveSubagentTargets) {
2932
2934
  else targets.push({ agent });
2933
2935
  return targets;
2934
2936
  }
2935
- function buildTargetSummaryLines(targets, installMode) {
2937
+ function buildTargetSummaryLines$1(targets, installMode) {
2936
2938
  const lines = [];
2937
2939
  const rootAgents = targets.filter((t) => !t.subagent).map((t) => t.agent);
2938
- const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName);
2939
- const { universal, symlinked } = splitAgentsByType(rootAgents);
2940
+ const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName$1);
2941
+ const { universal, symlinked } = splitAgentsByType$1(rootAgents);
2940
2942
  if (installMode === "symlink") {
2941
- if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$1(universal, 3)}`);
2942
- if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$1(symlinked)}`);
2943
- if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(subagentNames)}`);
2943
+ if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$2(universal, 3)}`);
2944
+ if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$2(symlinked)}`);
2945
+ if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$2(subagentNames)}`);
2944
2946
  } else {
2945
- const allNames = targets.map(targetDisplayName);
2946
- lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(allNames)}`);
2947
+ const allNames = targets.map(targetDisplayName$1);
2948
+ lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$2(allNames)}`);
2947
2949
  }
2948
2950
  return lines;
2949
2951
  }
@@ -2955,12 +2957,12 @@ function ensureUniversalAgents(targetAgents) {
2955
2957
  }
2956
2958
  function buildResultLines(results, targetAgents) {
2957
2959
  const lines = [];
2958
- const { universal, symlinked: symlinkAgents } = splitAgentsByType(targetAgents);
2960
+ const { universal, symlinked: symlinkAgents } = splitAgentsByType$1(targetAgents);
2959
2961
  const successfulSymlinks = results.filter((r) => !r.symlinkFailed && !r.skipped && !universal.includes(r.agent)).map((r) => r.agent);
2960
2962
  const failedSymlinks = results.filter((r) => r.symlinkFailed && !r.skipped).map((r) => r.agent);
2961
- if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$1(universal)}`);
2962
- if (successfulSymlinks.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接:")} ${formatList$1(successfulSymlinks)}`);
2963
- if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$1(failedSymlinks)}`);
2963
+ if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$2(universal)}`);
2964
+ if (successfulSymlinks.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接:")} ${formatList$2(successfulSymlinks)}`);
2965
+ if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$2(failedSymlinks)}`);
2964
2966
  return lines;
2965
2967
  }
2966
2968
  function multiselect(opts) {
@@ -3215,7 +3217,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3215
3217
  if (skill.files.size > 1) summaryLines.push(` ${import_picocolors.default.dim("files:")} ${skill.files.size}`);
3216
3218
  const skillOverwrites = overwriteStatus.get(skill.installName);
3217
3219
  const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
3218
- if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
3220
+ if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$2(overwriteAgents)}`);
3219
3221
  }
3220
3222
  console.log();
3221
3223
  note(summaryLines.join("\n"), "安装摘要");
@@ -3304,7 +3306,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3304
3306
  const title = import_picocolors.default.green(`已安装 ${skillCount} 个技能`);
3305
3307
  note(resultLines.join("\n"), title);
3306
3308
  if (symlinkFailures.length > 0) {
3307
- log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$1(copiedAgents)}`));
3309
+ log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$2(copiedAgents)}`));
3308
3310
  log.message(import_picocolors.default.dim(" 已改为复制文件。在 Windows 上,请启用开发者模式以支持符号链接。"));
3309
3311
  }
3310
3312
  }
@@ -3500,10 +3502,10 @@ async function runAdd(args, options = {}) {
3500
3502
  else if (!process.stdin.isTTY) showInstallTip();
3501
3503
  let tempDir = null;
3502
3504
  try {
3503
- const spinner$2 = spinner();
3504
- spinner$2.start("正在解析源...");
3505
+ const spinner$3 = spinner();
3506
+ spinner$3.start("正在解析源...");
3505
3507
  const parsed = parseSource(source);
3506
- spinner$2.stop(`源:${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
3508
+ spinner$3.stop(`源:${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
3507
3509
  const ownerRepoRaw = getOwnerRepo(parsed);
3508
3510
  const repoPrivacyPromise = (() => {
3509
3511
  if (!ownerRepoRaw) return Promise.resolve(null);
@@ -3512,7 +3514,7 @@ async function runAdd(args, options = {}) {
3512
3514
  return isRepoPrivate(ownerRepo.owner, ownerRepo.repo).catch(() => null);
3513
3515
  })();
3514
3516
  if (parsed.type === "well-known") {
3515
- await handleWellKnownSkills(source, parsed.url, options, spinner$2);
3517
+ await handleWellKnownSkills(source, parsed.url, options, spinner$3);
3516
3518
  return;
3517
3519
  }
3518
3520
  if (parsed.skillFilter) {
@@ -3523,18 +3525,18 @@ async function runAdd(args, options = {}) {
3523
3525
  let skills;
3524
3526
  let blobResult = null;
3525
3527
  if (parsed.type === "skillhub") {
3526
- const result = await installFromSkillHub(parsed, options, spinner$2);
3528
+ const result = await installFromSkillHub(parsed, options, spinner$3);
3527
3529
  skills = result.skills;
3528
3530
  tempDir = result.tempDir;
3529
3531
  } else if (parsed.type === "local") {
3530
- spinner$2.start("正在验证本地路径...");
3532
+ spinner$3.start("正在验证本地路径...");
3531
3533
  if (!existsSync(parsed.localPath)) {
3532
- spinner$2.stop(import_picocolors.default.red("路径未找到"));
3534
+ spinner$3.stop(import_picocolors.default.red("路径未找到"));
3533
3535
  outro(import_picocolors.default.red(`本地路径不存在:${parsed.localPath}`));
3534
3536
  process.exit(1);
3535
3537
  }
3536
- spinner$2.stop("本地路径验证通过");
3537
- spinner$2.start("正在发现技能...");
3538
+ spinner$3.stop("本地路径验证通过");
3539
+ spinner$3.start("正在发现技能...");
3538
3540
  skills = await discoverSkills(parsed.localPath, parsed.subpath, {
3539
3541
  includeInternal,
3540
3542
  fullDepth: options.fullDepth
@@ -3549,7 +3551,7 @@ async function runAdd(args, options = {}) {
3549
3551
  const owner = ownerRepo?.split("/")[0]?.toLowerCase();
3550
3552
  const isSelfHostedRepo = !!ownerRepo && Object.hasOwn(BLOB_ALLOWED_REPOS, ownerRepo.toLowerCase());
3551
3553
  if (ownerRepo && owner && (isSelfHostedRepo || BLOB_ALLOWED_OWNERS.includes(owner))) {
3552
- spinner$2.start("正在获取技能...");
3554
+ spinner$3.start("正在获取技能...");
3553
3555
  blobResult = await tryBlobInstall(ownerRepo, {
3554
3556
  subpath: parsed.subpath,
3555
3557
  skillFilter: parsed.skillFilter,
@@ -3557,36 +3559,36 @@ async function runAdd(args, options = {}) {
3557
3559
  getToken: getGitHubToken,
3558
3560
  includeInternal
3559
3561
  });
3560
- if (!blobResult) spinner$2.stop(import_picocolors.default.dim("Falling back to clone..."));
3562
+ if (!blobResult) spinner$3.stop(import_picocolors.default.dim("Falling back to clone..."));
3561
3563
  }
3562
3564
  if (blobResult) {
3563
3565
  skills = blobResult.skills;
3564
- spinner$2.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
3566
+ spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
3565
3567
  } else {
3566
- tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$2);
3567
- spinner$2.stop("仓库克隆完成");
3568
- spinner$2.start("正在发现技能...");
3568
+ tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$3);
3569
+ spinner$3.stop("仓库克隆完成");
3570
+ spinner$3.start("正在发现技能...");
3569
3571
  skills = await discoverSkills(tempDir, parsed.subpath, {
3570
3572
  includeInternal,
3571
3573
  fullDepth: options.fullDepth
3572
3574
  });
3573
3575
  }
3574
3576
  } else {
3575
- tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$2);
3576
- spinner$2.stop("仓库克隆完成");
3577
- spinner$2.start("正在发现技能...");
3577
+ tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$3);
3578
+ spinner$3.stop("仓库克隆完成");
3579
+ spinner$3.start("正在发现技能...");
3578
3580
  skills = await discoverSkills(tempDir, parsed.subpath, {
3579
3581
  includeInternal,
3580
3582
  fullDepth: options.fullDepth
3581
3583
  });
3582
3584
  }
3583
3585
  if (skills.length === 0) {
3584
- spinner$2.stop(import_picocolors.default.red("未找到技能"));
3586
+ spinner$3.stop(import_picocolors.default.red("未找到技能"));
3585
3587
  outro(import_picocolors.default.red("未找到有效技能。技能需要包含 name 和 description 的 SKILL.md 文件。"));
3586
3588
  await cleanup(tempDir);
3587
3589
  process.exit(1);
3588
3590
  }
3589
- if (!blobResult) spinner$2.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
3591
+ if (!blobResult) spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
3590
3592
  if (options.list) {
3591
3593
  console.log();
3592
3594
  log.step(import_picocolors.default.bold("可用技能"));
@@ -3700,10 +3702,10 @@ async function runAdd(args, options = {}) {
3700
3702
  }
3701
3703
  targetAgents = options.agent;
3702
3704
  } else {
3703
- spinner$2.start("正在加载 Agent...");
3705
+ spinner$3.start("正在加载 Agent...");
3704
3706
  const installedAgents = await detectInstalledAgents();
3705
3707
  const totalAgents = Object.keys(agents).length;
3706
- spinner$2.stop(`${totalAgents} agents`);
3708
+ spinner$3.stop(`${totalAgents} agents`);
3707
3709
  if (installedAgents.includes("eve") && (options.yes || !agentResult.isAgent)) {
3708
3710
  const useEve = options.yes ? true : await confirm({
3709
3711
  message: formatEveInstallPromptMessage(selectedSkills),
@@ -3786,7 +3788,7 @@ async function runAdd(args, options = {}) {
3786
3788
  eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
3787
3789
  }
3788
3790
  }
3789
- const installTargets = buildInstallTargets(targetAgents, eveSubagentTargets);
3791
+ const installTargets = buildInstallTargets$1(targetAgents, eveSubagentTargets);
3790
3792
  let installGlobally = options.global ?? false;
3791
3793
  const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
3792
3794
  if (options.global === void 0 && !options.yes && supportsGlobal) {
@@ -3845,7 +3847,7 @@ async function runAdd(args, options = {}) {
3845
3847
  const overwriteStatus = /* @__PURE__ */ new Map();
3846
3848
  for (const { skillName, target, installed } of overwriteChecks) {
3847
3849
  if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
3848
- overwriteStatus.get(skillName).set(targetKey(target), installed);
3850
+ overwriteStatus.get(skillName).set(targetKey$1(target), installed);
3849
3851
  }
3850
3852
  const groupedSummary = {};
3851
3853
  const ungroupedSummary = [];
@@ -3863,10 +3865,10 @@ async function runAdd(args, options = {}) {
3863
3865
  eveSubagent: installTargets[0].subagent
3864
3866
  }) : getCanonicalPath(skill.name, { global: installGlobally }), cwd);
3865
3867
  summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
3866
- summaryLines.push(...buildTargetSummaryLines(installTargets, installMode));
3868
+ summaryLines.push(...buildTargetSummaryLines$1(installTargets, installMode));
3867
3869
  const skillOverwrites = overwriteStatus.get(skill.name);
3868
- const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey(t))).map(targetDisplayName);
3869
- if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
3870
+ const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey$1(t))).map(targetDisplayName$1);
3871
+ if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$2(overwriteAgents)}`);
3870
3872
  }
3871
3873
  };
3872
3874
  const sortedGroups = Object.keys(groupedSummary).sort();
@@ -3903,7 +3905,7 @@ async function runAdd(args, options = {}) {
3903
3905
  process.exit(0);
3904
3906
  }
3905
3907
  }
3906
- spinner$2.start("正在安装技能...");
3908
+ spinner$3.start("正在安装技能...");
3907
3909
  const results = [];
3908
3910
  for (const skill of selectedSkills) for (const target of installTargets) {
3909
3911
  const { agent, subagent } = target;
@@ -3935,12 +3937,12 @@ async function runAdd(args, options = {}) {
3935
3937
  });
3936
3938
  results.push({
3937
3939
  skill: getSkillDisplayName(skill),
3938
- agent: targetDisplayName(target),
3940
+ agent: targetDisplayName$1(target),
3939
3941
  pluginName: skill.pluginName,
3940
3942
  ...result
3941
3943
  });
3942
3944
  }
3943
- spinner$2.stop("安装完成");
3945
+ spinner$3.stop("安装完成");
3944
3946
  console.log();
3945
3947
  const successful = results.filter((r) => r.success);
3946
3948
  const failed = results.filter((r) => !r.success);
@@ -4061,7 +4063,7 @@ async function runAdd(args, options = {}) {
4061
4063
  const title = import_picocolors.default.green(`已安装 ${skillCount} 个技能`);
4062
4064
  note(resultLines.join("\n"), title);
4063
4065
  if (symlinkFailures.length > 0) {
4064
- log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$1(copiedAgents)}`));
4066
+ log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$2(copiedAgents)}`));
4065
4067
  log.message(import_picocolors.default.dim(" 已改为复制文件。在 Windows 上,请启用开发者模式以支持符号链接。"));
4066
4068
  }
4067
4069
  }
@@ -4467,7 +4469,7 @@ function shortenPath(fullPath, cwd) {
4467
4469
  if (fullPath.startsWith(cwd)) return "." + fullPath.slice(cwd.length);
4468
4470
  return fullPath;
4469
4471
  }
4470
- function formatList(items, maxShow = 5) {
4472
+ function formatList$1(items, maxShow = 5) {
4471
4473
  if (items.length <= maxShow) return items.join(", ");
4472
4474
  const shown = items.slice(0, maxShow);
4473
4475
  const remaining = items.length - maxShow;
@@ -4531,7 +4533,7 @@ async function runList(args) {
4531
4533
  const prefix = indent ? " " : "";
4532
4534
  const shortPath = shortenPath(skill.canonicalPath, cwd);
4533
4535
  const agentNames = skill.agents.map((a) => agents[a].displayName);
4534
- const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$4}not linked${RESET$10}`;
4536
+ const agentInfo = skill.agents.length > 0 ? formatList$1(agentNames) : `${YELLOW$4}not linked${RESET$10}`;
4535
4537
  const paddedName = sanitizeMetadata(skill.name).padEnd(maxNameLength);
4536
4538
  const paddedPath = shortPath.padEnd(maxPathLength);
4537
4539
  console.log(`${prefix}${CYAN$1}${paddedName}${RESET$10} ${DIM$10}${paddedPath}${RESET$10} ${DIM$10}Agents:${RESET$10} ${agentInfo}`);
@@ -4605,8 +4607,8 @@ async function removeCommand(skillNames, options) {
4605
4607
  }
4606
4608
  const isGlobal = options.global ?? false;
4607
4609
  const cwd = process.cwd();
4608
- const spinner$1 = spinner();
4609
- spinner$1.start("正在扫描已安装的技能...");
4610
+ const spinner$2 = spinner();
4611
+ spinner$2.start("正在扫描已安装的技能...");
4610
4612
  const skillNamesSet = /* @__PURE__ */ new Set();
4611
4613
  const scanDir = async (dir) => {
4612
4614
  try {
@@ -4625,7 +4627,7 @@ async function removeCommand(skillNames, options) {
4625
4627
  for (const subagent of getEveSubagents(cwd)) await scanDir(getEveSubagentSkillsDir(subagent, cwd));
4626
4628
  }
4627
4629
  const installedSkills = Array.from(skillNamesSet).sort();
4628
- spinner$1.stop(`发现 ${installedSkills.length} 个已安装技能`);
4630
+ spinner$2.stop(`发现 ${installedSkills.length} 个已安装技能`);
4629
4631
  if (installedSkills.length === 0) {
4630
4632
  outro(import_picocolors.default.yellow("没有可卸载的技能。"));
4631
4633
  return;
@@ -4667,7 +4669,7 @@ async function removeCommand(skillNames, options) {
4667
4669
  if (options.agent && options.agent.length > 0) targetAgents = options.agent;
4668
4670
  else {
4669
4671
  targetAgents = Object.keys(agents);
4670
- spinner$1.stop(`Targeting ${targetAgents.length} potential agent(s)`);
4672
+ spinner$2.stop(`Targeting ${targetAgents.length} potential agent(s)`);
4671
4673
  }
4672
4674
  if (!options.yes) {
4673
4675
  console.log();
@@ -4680,7 +4682,7 @@ async function removeCommand(skillNames, options) {
4680
4682
  process.exit(0);
4681
4683
  }
4682
4684
  }
4683
- spinner$1.start("正在卸载技能...");
4685
+ spinner$2.start("正在卸载技能...");
4684
4686
  const results = [];
4685
4687
  for (const skillName of selectedSkills) try {
4686
4688
  const canonicalPath = getCanonicalPath(skillName, {
@@ -4742,7 +4744,7 @@ async function removeCommand(skillNames, options) {
4742
4744
  error: err instanceof Error ? err.message : String(err)
4743
4745
  });
4744
4746
  }
4745
- spinner$1.stop("卸载完成");
4747
+ spinner$2.stop("卸载完成");
4746
4748
  const successful = results.filter((r) => r.success);
4747
4749
  const failed = results.filter((r) => !r.success);
4748
4750
  if (successful.length > 0) {
@@ -6749,6 +6751,51 @@ async function handleLocalList(args, globalOpts) {
6749
6751
  console.log();
6750
6752
  }
6751
6753
  }
6754
+ function targetDisplayName(target) {
6755
+ const base = agents[target.agent].displayName;
6756
+ return target.subagent ? `${base} (${target.subagent})` : base;
6757
+ }
6758
+ function targetKey(target) {
6759
+ return target.subagent ? `${target.agent}:${target.subagent}` : target.agent;
6760
+ }
6761
+ function buildInstallTargets(targetAgents, eveSubagentTargets) {
6762
+ const targets = [];
6763
+ for (const agent of targetAgents) if (agent === "eve") for (const subagent of eveSubagentTargets) targets.push({
6764
+ agent,
6765
+ subagent
6766
+ });
6767
+ else targets.push({ agent });
6768
+ return targets;
6769
+ }
6770
+ function splitAgentsByType(agentTypes) {
6771
+ const universal = [];
6772
+ const symlinked = [];
6773
+ for (const a of agentTypes) if (isUniversalAgent(a)) universal.push(agents[a].displayName);
6774
+ else symlinked.push(agents[a].displayName);
6775
+ return {
6776
+ universal,
6777
+ symlinked
6778
+ };
6779
+ }
6780
+ function formatList(items, maxShow = 5) {
6781
+ if (items.length <= maxShow) return items.join(", ");
6782
+ return `${items.slice(0, maxShow).join(", ")} +${items.length - maxShow} more`;
6783
+ }
6784
+ function buildTargetSummaryLines(targets, installMode) {
6785
+ const lines = [];
6786
+ const rootAgents = targets.filter((t) => !t.subagent).map((t) => t.agent);
6787
+ const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName);
6788
+ const { universal, symlinked } = splitAgentsByType(rootAgents);
6789
+ if (installMode === "symlink") {
6790
+ if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList(universal, 3)}`);
6791
+ if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList(symlinked)}`);
6792
+ if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList(subagentNames)}`);
6793
+ } else {
6794
+ const allNames = targets.map(targetDisplayName);
6795
+ lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList(allNames)}`);
6796
+ }
6797
+ return lines;
6798
+ }
6752
6799
  async function handleInstall(args, api, globalOpts) {
6753
6800
  const pkgRef = args[0];
6754
6801
  if (!pkgRef) {
@@ -6757,6 +6804,8 @@ async function handleInstall(args, api, globalOpts) {
6757
6804
  process.exit(1);
6758
6805
  }
6759
6806
  const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
6807
+ const isYes = args.includes("-y") || args.includes("--yes") || globalOpts.yes;
6808
+ const isGlobal = args.includes("-g") || args.includes("--global");
6760
6809
  console.log(`${DIM$3}正在获取包信息...${RESET$3}`);
6761
6810
  let pkgInfo;
6762
6811
  try {
@@ -6773,16 +6822,185 @@ async function handleInstall(args, api, globalOpts) {
6773
6822
  }
6774
6823
  console.log(`${BOLD$1}${pkgInfo.name || slug}${RESET$3}(${skills.length} 个技能):`);
6775
6824
  console.log();
6825
+ for (const skill of skills) {
6826
+ const ref = `@${skill.namespace || namespace}/${skill.slug}`;
6827
+ console.log(` ${DIM$3}• ${skill.displayName || skill.slug}${RESET$3} ${DIM$3}${ref}${RESET$3}`);
6828
+ }
6829
+ console.log();
6830
+ let targetAgents;
6831
+ let installGlobally;
6832
+ let installMode;
6833
+ let eveSubagentTargets = [void 0];
6834
+ if (isYes) {
6835
+ const installed = await detectInstalledAgents();
6836
+ const validAgents = Object.keys(agents);
6837
+ targetAgents = ensureUniversalAgents(installed.length > 0 ? installed : validAgents);
6838
+ installGlobally = isGlobal;
6839
+ installMode = "symlink";
6840
+ console.log(`${DIM$3}安装到:${targetAgents.map((a) => agents[a].displayName).join(", ")}${RESET$3}`);
6841
+ console.log(`${DIM$3}范围:${installGlobally ? "全局" : "项目"} | 方式:${installMode === "symlink" ? "符号链接" : "复制"}${RESET$3}`);
6842
+ console.log();
6843
+ } else {
6844
+ const spinner$1 = spinner();
6845
+ spinner$1.start("正在加载 Agent...");
6846
+ const installedAgents = await detectInstalledAgents();
6847
+ const totalAgents = Object.keys(agents).length;
6848
+ spinner$1.stop(`${totalAgents} agents`);
6849
+ const skillNames = skills.map((s) => s.displayName || s.slug);
6850
+ const skillSubject = skillNames.length <= 3 ? skillNames.map((n) => import_picocolors.default.cyan(n)).join(", ") : `${skills.length} 个技能`;
6851
+ if (installedAgents.includes("eve")) {
6852
+ const useEve = await confirm({
6853
+ message: `检测到 Eve 项目。为您的 eve agent 安装 ${skillSubject}?`,
6854
+ initialValue: true
6855
+ });
6856
+ if (isCancel(useEve)) {
6857
+ cancel("安装已取消");
6858
+ process.exit(0);
6859
+ }
6860
+ if (useEve) {
6861
+ targetAgents = ["eve"];
6862
+ console.log(`${DIM$3}安装到:eve agent${RESET$3}`);
6863
+ } else {
6864
+ const selected = await selectAgentsInteractive({ global: false });
6865
+ if (isCancel(selected)) {
6866
+ cancel("安装已取消");
6867
+ process.exit(0);
6868
+ }
6869
+ targetAgents = selected;
6870
+ }
6871
+ } else if (installedAgents.length === 0) {
6872
+ const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).filter(([key]) => key !== "eve").map(([key, config]) => ({
6873
+ value: key,
6874
+ label: config.displayName
6875
+ })));
6876
+ if (isCancel(selected)) {
6877
+ cancel("安装已取消");
6878
+ process.exit(0);
6879
+ }
6880
+ targetAgents = selected;
6881
+ } else if (installedAgents.length === 1) {
6882
+ targetAgents = ensureUniversalAgents(installedAgents);
6883
+ const firstAgent = installedAgents[0];
6884
+ console.log(`${DIM$3}安装到:${agents[firstAgent].displayName}${RESET$3}`);
6885
+ } else {
6886
+ const selected = await selectAgentsInteractive({ global: false });
6887
+ if (isCancel(selected)) {
6888
+ cancel("安装已取消");
6889
+ process.exit(0);
6890
+ }
6891
+ targetAgents = selected;
6892
+ }
6893
+ if (targetAgents.includes("eve")) {
6894
+ const availableSubagents = getEveSubagents(process.cwd());
6895
+ if (availableSubagents.length > 0) {
6896
+ const selectedSubagents = await multiselect$1({
6897
+ message: "Eve 技能安装位置?",
6898
+ options: [{
6899
+ value: "",
6900
+ label: "根 Agent",
6901
+ hint: "agent/skills"
6902
+ }, ...availableSubagents.map((name) => ({
6903
+ value: name,
6904
+ label: name,
6905
+ hint: `agent/subagents/${name}/skills`
6906
+ }))],
6907
+ initialValues: [""],
6908
+ required: true
6909
+ });
6910
+ if (isCancel(selectedSubagents)) {
6911
+ cancel("安装已取消");
6912
+ process.exit(0);
6913
+ }
6914
+ eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
6915
+ }
6916
+ }
6917
+ const installTargets = buildInstallTargets(targetAgents, eveSubagentTargets);
6918
+ installGlobally = isGlobal;
6919
+ const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
6920
+ if (!isGlobal && supportsGlobal) {
6921
+ const scope = await select({
6922
+ message: "安装范围",
6923
+ options: [{
6924
+ value: false,
6925
+ label: "项目",
6926
+ hint: "安装到当前目录(随项目一起提交)"
6927
+ }, {
6928
+ value: true,
6929
+ label: "全局",
6930
+ hint: "安装到主目录(跨所有项目可用)"
6931
+ }]
6932
+ });
6933
+ if (isCancel(scope)) {
6934
+ cancel("安装已取消");
6935
+ process.exit(0);
6936
+ }
6937
+ installGlobally = scope;
6938
+ }
6939
+ const allEve = installTargets.every((t) => t.agent === "eve");
6940
+ if (new Set(installTargets.map((t) => t.subagent ? `eve:subagent:${t.subagent}` : agents[t.agent].skillsDir)).size > 1 && !allEve) {
6941
+ const modeChoice = await select({
6942
+ message: "安装方式",
6943
+ options: [{
6944
+ value: "symlink",
6945
+ label: "符号链接(推荐)",
6946
+ hint: "单一来源,易于更新"
6947
+ }, {
6948
+ value: "copy",
6949
+ label: "复制到所有 Agent",
6950
+ hint: "每个 Agent 独立副本"
6951
+ }]
6952
+ });
6953
+ if (isCancel(modeChoice)) {
6954
+ cancel("安装已取消");
6955
+ process.exit(0);
6956
+ }
6957
+ installMode = modeChoice;
6958
+ } else installMode = "copy";
6959
+ const overwriteChecks = await Promise.all(skills.flatMap((skill) => installTargets.map(async (target) => ({
6960
+ skillName: skill.slug,
6961
+ target,
6962
+ installed: await isSkillInstalled(skill.slug, target.agent, {
6963
+ global: installGlobally,
6964
+ eveSubagent: target.subagent
6965
+ })
6966
+ }))));
6967
+ const overwriteStatus = /* @__PURE__ */ new Map();
6968
+ for (const { skillName, target, installed } of overwriteChecks) {
6969
+ if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
6970
+ overwriteStatus.get(skillName).set(targetKey(target), installed);
6971
+ }
6972
+ const summaryLines = [];
6973
+ for (const skill of skills) {
6974
+ if (summaryLines.length > 0) summaryLines.push("");
6975
+ const skillOverwrites = overwriteStatus.get(skill.slug);
6976
+ const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey(t))).map(targetDisplayName);
6977
+ summaryLines.push(`${import_picocolors.default.cyan(skill.displayName || skill.slug)}`);
6978
+ summaryLines.push(...buildTargetSummaryLines(installTargets, installMode));
6979
+ if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("覆盖:")} ${overwriteAgents.join(", ")}`);
6980
+ }
6981
+ console.log();
6982
+ note(summaryLines.join("\n"), "安装摘要");
6983
+ const confirmed = await confirm({ message: "确认安装?" });
6984
+ if (isCancel(confirmed) || !confirmed) {
6985
+ cancel("安装已取消");
6986
+ process.exit(0);
6987
+ }
6988
+ }
6776
6989
  const { runAdd } = await Promise.resolve().then(() => add_exports);
6777
6990
  const installedSkills = [];
6991
+ const nonEveAgents = targetAgents.filter((a) => a !== "eve");
6992
+ const runAddBaseOpts = {
6993
+ global: installGlobally,
6994
+ copy: installMode === "copy",
6995
+ yes: true
6996
+ };
6997
+ if (nonEveAgents.length > 0) runAddBaseOpts.agent = nonEveAgents;
6998
+ if (targetAgents.includes("eve")) runAddBaseOpts.subagent = eveSubagentTargets.map((s) => s ?? "root");
6778
6999
  for (const skill of skills) {
6779
- const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
7000
+ const skillRef = `@${skill.namespace || namespace}/${skill.slug}`;
6780
7001
  console.log(` ${DIM$3}正在安装 ${skillRef}...${RESET$3}`);
6781
7002
  try {
6782
- await runAdd([skillRef], {
6783
- ...globalOpts,
6784
- yes: true
6785
- });
7003
+ await runAdd([skillRef], runAddBaseOpts);
6786
7004
  console.log(` ${GREEN$2}✓ ${skill.slug}${RESET$3}`);
6787
7005
  installedSkills.push(skill.slug);
6788
7006
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bfi-cli",
3
- "version": "0.1.57",
3
+ "version": "0.1.58",
4
4
  "description": "用友大业财BFI统一命令行工具,支持技能,技能包,元数据,UI元数据等",
5
5
  "type": "module",
6
6
  "bin": {