bfi-cli 0.1.56 → 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) {
@@ -6195,6 +6197,11 @@ const RESET$5 = "\x1B[0m";
6195
6197
  const BOLD$3 = "\x1B[1m";
6196
6198
  const DIM$5 = "\x1B[38;5;102m";
6197
6199
  const TEXT$4 = "\x1B[38;5;145m";
6200
+ function formatFileSize(bytes) {
6201
+ if (bytes < 1024) return `${bytes} B`;
6202
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
6203
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
6204
+ }
6198
6205
  async function runDocs(args, globalOpts) {
6199
6206
  switch (args[0]) {
6200
6207
  case "upload":
@@ -6240,8 +6247,7 @@ async function handleUpload(args, globalOpts) {
6240
6247
  const { registry, token } = resolveConfig(globalOpts);
6241
6248
  const api = new SkillHubAPI(registry, token);
6242
6249
  const fileName = path$1.basename(resolvedPath);
6243
- const fileSize = stats.size;
6244
- const fileSizeStr = fileSize < 1024 ? `${fileSize} B` : fileSize < 1024 * 1024 ? `${(fileSize / 1024).toFixed(1)} KB` : `${(fileSize / (1024 * 1024)).toFixed(1)} MB`;
6250
+ const fileSizeStr = formatFileSize(stats.size);
6245
6251
  console.log(`${DIM$5}正在上传 ${fileName} (${fileSizeStr})...${RESET$5}`);
6246
6252
  try {
6247
6253
  const fileBuffer = fs.readFileSync(resolvedPath);
@@ -6280,11 +6286,14 @@ async function handleList(args, globalOpts) {
6280
6286
  for (const doc of docs) {
6281
6287
  const id = doc.id || "?";
6282
6288
  const fileName = doc.fileName || doc.file_name || "unknown";
6283
- const status = doc.status || "N/A";
6289
+ const fileSize = doc.fileSize || doc.file_size;
6290
+ const sizeStr = fileSize ? formatFileSize(Number(fileSize)) : "N/A";
6291
+ const user = doc.userDisplayName || doc.userId || "N/A";
6292
+ const ip = doc.clientIp || "N/A";
6284
6293
  const createdAt = doc.createdAt || doc.created_at;
6285
6294
  const dateStr = createdAt ? new Date(createdAt).toISOString().split("T")[0] : "N/A";
6286
6295
  console.log(` ${BOLD$3}#${id}${RESET$5} ${TEXT$4}${fileName}${RESET$5}`);
6287
- console.log(` ${DIM$5}状态:${status} | 日期:${dateStr}${RESET$5}`);
6296
+ console.log(` ${DIM$5}${sizeStr} | ${user} | ${ip} | ${dateStr}${RESET$5}`);
6288
6297
  console.log();
6289
6298
  }
6290
6299
  if (total && Number(total) > (page + 1) * size) console.log(`${DIM$5}下一页:bfi docs list ${page + 1}${RESET$5}`);
@@ -6314,13 +6323,20 @@ async function handleInfo(args, globalOpts) {
6314
6323
  console.log(`${DIM$5}未找到文档:${id}${RESET$5}`);
6315
6324
  process.exit(1);
6316
6325
  }
6326
+ const content = doc.content;
6327
+ const hasContent = typeof content === "string" && content.trim().length > 0;
6317
6328
  console.log();
6318
6329
  console.log(`${BOLD$3}文档 #${doc.id}${RESET$5}`);
6319
6330
  console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
6320
6331
  if (doc.fileName || doc.file_name) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$4}${doc.fileName || doc.file_name}${RESET$5}`);
6332
+ const fileSize = doc.fileSize || doc.file_size;
6333
+ if (fileSize) console.log(` ${BOLD$3}大小:${RESET$5} ${TEXT$4}${formatFileSize(Number(fileSize))}${RESET$5}`);
6334
+ const user = doc.userDisplayName || doc.userId;
6335
+ if (user) console.log(` ${BOLD$3}上传者:${RESET$5} ${TEXT$4}${user}${RESET$5}`);
6336
+ if (doc.clientIp) console.log(` ${BOLD$3}IP:${RESET$5} ${TEXT$4}${doc.clientIp}${RESET$5}`);
6321
6337
  if (doc.status) {
6322
6338
  const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$4;
6323
- console.log(` ${BOLD$3}状态:${RESET$5} ${statusColor}${doc.status}${RESET$5}`);
6339
+ console.log(` ${BOLD$3}状态:${RESET$5} ${statusColor}${doc.status}${RESET$5}`);
6324
6340
  }
6325
6341
  if (doc.createdAt || doc.created_at) {
6326
6342
  const date = new Date(doc.createdAt || doc.created_at).toISOString().replace("T", " ").slice(0, 19);
@@ -6330,17 +6346,18 @@ async function handleInfo(args, globalOpts) {
6330
6346
  const date = new Date(doc.updatedAt || doc.updated_at).toISOString().replace("T", " ").slice(0, 19);
6331
6347
  console.log(` ${BOLD$3}更新时间:${RESET$5} ${TEXT$4}${date}${RESET$5}`);
6332
6348
  }
6333
- if (doc.result) {
6334
- console.log();
6335
- console.log(` ${BOLD$3}结果:${RESET$5}`);
6336
- const resultStr = typeof doc.result === "string" ? doc.result : JSON.stringify(doc.result, null, 4);
6337
- for (const line of resultStr.split("\n")) console.log(` ${DIM$5}${line}${RESET$5}`);
6338
- }
6339
6349
  if (doc.error || doc.errorMessage) {
6340
6350
  console.log();
6341
6351
  console.log(` \x1b[31m${BOLD$3}错误:${RESET$5}`);
6342
6352
  console.log(` \x1b[31m${doc.error || doc.errorMessage}${RESET$5}`);
6343
6353
  }
6354
+ if (hasContent) {
6355
+ console.log();
6356
+ console.log(` ${BOLD$3}内容:${RESET$5}`);
6357
+ console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
6358
+ for (const line of content.split("\n")) console.log(` ${TEXT$4}${line}${RESET$5}`);
6359
+ console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
6360
+ }
6344
6361
  console.log();
6345
6362
  } catch (error) {
6346
6363
  if (error.status === 404) console.error(`未找到文档:${id}`);
@@ -6734,6 +6751,51 @@ async function handleLocalList(args, globalOpts) {
6734
6751
  console.log();
6735
6752
  }
6736
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
+ }
6737
6799
  async function handleInstall(args, api, globalOpts) {
6738
6800
  const pkgRef = args[0];
6739
6801
  if (!pkgRef) {
@@ -6742,6 +6804,8 @@ async function handleInstall(args, api, globalOpts) {
6742
6804
  process.exit(1);
6743
6805
  }
6744
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");
6745
6809
  console.log(`${DIM$3}正在获取包信息...${RESET$3}`);
6746
6810
  let pkgInfo;
6747
6811
  try {
@@ -6758,16 +6822,185 @@ async function handleInstall(args, api, globalOpts) {
6758
6822
  }
6759
6823
  console.log(`${BOLD$1}${pkgInfo.name || slug}${RESET$3}(${skills.length} 个技能):`);
6760
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
+ }
6761
6989
  const { runAdd } = await Promise.resolve().then(() => add_exports);
6762
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");
6763
6999
  for (const skill of skills) {
6764
- const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
7000
+ const skillRef = `@${skill.namespace || namespace}/${skill.slug}`;
6765
7001
  console.log(` ${DIM$3}正在安装 ${skillRef}...${RESET$3}`);
6766
7002
  try {
6767
- await runAdd([skillRef], {
6768
- ...globalOpts,
6769
- yes: true
6770
- });
7003
+ await runAdd([skillRef], runAddBaseOpts);
6771
7004
  console.log(` ${GREEN$2}✓ ${skill.slug}${RESET$3}`);
6772
7005
  installedSkills.push(skill.slug);
6773
7006
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bfi-cli",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "description": "用友大业财BFI统一命令行工具,支持技能,技能包,元数据,UI元数据等",
5
5
  "type": "module",
6
6
  "bin": {