bfi-cli 0.1.98 → 0.1.100

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/cli.mjs CHANGED
@@ -4,7 +4,7 @@ import { isCancel } from "./_chunks/libs/@clack/core.mjs";
4
4
  import { cancel, confirm, groupMultiselect, intro, log, multiselect as multiselect$1, note, outro, select, spinner } from "./_chunks/libs/@clack/prompts.mjs";
5
5
  import { require_picocolors } from "./_chunks/libs/picocolors.mjs";
6
6
  import { addSkillToLocalLock, addSkillToLock, computeSkillFolderHash, discoverSkills, dismissPrompt, filterSkills, getAllLockedSkills, getGitHubToken, getLastSelectedAgents, getSkillDisplayName, getSkillFromLock, isPromptDismissed, parseFrontmatter, parseSkillMd, readLocalLock, readSkillLock, removeSkillFromLock, sanitizeMetadata, saveSelectedAgents, stripTerminalEscapes } from "./_chunks/skills.mjs";
7
- import { PluginRegistryClient } from "./_chunks/registry-client.mjs";
7
+ import { PluginRegistryClient, parsePackageIntent } from "./_chunks/intent.mjs";
8
8
  import { loadGlobalConfig, loadLockfile, loadProjectConfig, resolveConfig, saveGlobalConfig, saveLockfile, setConfigValue } from "./_chunks/config.mjs";
9
9
  import { esm_default } from "./_chunks/libs/simple-git.mjs";
10
10
  import { EVE_SUBAGENTS_DIR, agents, detectInstalledAgents, getEveSubagents, getNonUniversalAgents, getUniversalAgents, getVisibleUniversalAgents, isUniversalAgent } from "./_chunks/agents.mjs";
@@ -451,8 +451,10 @@ async function searchMultiselect(options) {
451
451
  }
452
452
  if (key.name === "space") {
453
453
  const item = filtered[cursor];
454
- if (item) if (selected.has(item.value)) selected.delete(item.value);
455
- else selected.add(item.value);
454
+ if (item) {
455
+ if (selected.has(item.value)) selected.delete(item.value);
456
+ else selected.add(item.value);
457
+ }
456
458
  render();
457
459
  return;
458
460
  }
@@ -473,6 +475,46 @@ async function searchMultiselect(options) {
473
475
  render();
474
476
  });
475
477
  }
478
+ function getCapabilities(version) {
479
+ return {
480
+ schemaVersion: 1,
481
+ version,
482
+ capabilities: [
483
+ "skill-install-v1",
484
+ "content-plan-v1",
485
+ "collection-bundle-v1",
486
+ "collection-mcp-v1",
487
+ "scope-selection-v1",
488
+ "plugin-plan-v1",
489
+ "plugin-export-v1",
490
+ "plugin-handoff-v1"
491
+ ]
492
+ };
493
+ }
494
+ function installationArgumentError(args) {
495
+ const command = ["skills", "skill"].includes(args[0] ?? "") ? args.slice(1) : args;
496
+ const isPackageInstall = ["package", "pkg"].includes(command[0] ?? "") && ["install", "i"].includes(command[1] ?? "");
497
+ if (!isPackageInstall && ![
498
+ "add",
499
+ "a",
500
+ "i",
501
+ "install"
502
+ ].includes(command[0] ?? "")) return;
503
+ if (isPackageInstall) try {
504
+ if (parsePackageIntent(command.slice(1)).mode === "plugin") return;
505
+ } catch {
506
+ return "安装参数或模式无效;插件参数必须使用明确的 --as plugin,尚未支持的技能参数不会执行。";
507
+ }
508
+ const reserved = [
509
+ "--content-plan",
510
+ "--scope",
511
+ "--from-archive",
512
+ "--input-env",
513
+ "--skills-only"
514
+ ];
515
+ const found = command.find((argument) => reserved.includes(argument.split("=")[0]));
516
+ if (found) return `当前版本尚未支持 ${found.split("=")[0]};完整内容计划安装将在后续阶段开放。现有安装不会静默忽略此参数。`;
517
+ }
476
518
  var api_exports = /* @__PURE__ */ __exportAll({ SkillHubAPI: () => SkillHubAPI });
477
519
  var SkillHubAPI = class {
478
520
  registry;
@@ -512,12 +554,14 @@ var SkillHubAPI = class {
512
554
  if (url.protocol === "https:") requestOptions.rejectUnauthorized = this.rejectUnauthorized;
513
555
  let bodyStr;
514
556
  let bodyBuf;
515
- if (options.body !== void 0) if (options.body instanceof Buffer) bodyBuf = options.body;
516
- else if (typeof options.body === "string") bodyStr = options.body;
517
- else if (typeof FormData !== "undefined" && options.body instanceof FormData) return this.fetchRequest(url, requestOptions, options.body);
518
- else {
519
- requestOptions.headers["Content-Type"] = "application/json";
520
- bodyStr = JSON.stringify(options.body);
557
+ if (options.body !== void 0) {
558
+ if (options.body instanceof Buffer) bodyBuf = options.body;
559
+ else if (typeof options.body === "string") bodyStr = options.body;
560
+ else if (typeof FormData !== "undefined" && options.body instanceof FormData) return this.fetchRequest(url, requestOptions, options.body);
561
+ else {
562
+ requestOptions.headers["Content-Type"] = "application/json";
563
+ bodyStr = JSON.stringify(options.body);
564
+ }
521
565
  }
522
566
  return new Promise((resolve, reject) => {
523
567
  const req = (url.protocol === "https:" ? https : http).request(url, requestOptions, (res) => {
@@ -674,7 +718,7 @@ var SkillHubAPI = class {
674
718
  return body.data || body;
675
719
  }
676
720
  async getPackage(namespace, slug) {
677
- const body = (await this.request("GET", `/namespaces/${namespace}/packages/${slug}`)).body;
721
+ const body = (await this.request("GET", `/namespaces/${namespace}/packages/${slug}`, { headers: { "X-BFI-Capabilities": getCapabilities("").capabilities.join(",") } })).body;
678
722
  return body.data || body;
679
723
  }
680
724
  async listComponentFiles(namespace, slug, componentId) {
@@ -866,7 +910,12 @@ var SkillHubAPI = class {
866
910
  try {
867
911
  const parsed = JSON.parse(data);
868
912
  if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) resolve(parsed);
869
- else reject(new Error(parsed.msg || parsed.message || `HTTP ${res.statusCode}`));
913
+ else {
914
+ const error = new Error(parsed.msg || parsed.message || `HTTP ${res.statusCode}`);
915
+ error.status = res.statusCode;
916
+ error.body = parsed;
917
+ reject(error);
918
+ }
870
919
  } catch {
871
920
  reject(new Error(data || `HTTP ${res.statusCode}`));
872
921
  }
@@ -1296,10 +1345,12 @@ async function isDirEntryOrSymlinkToDir(entry, entryPath) {
1296
1345
  }
1297
1346
  }
1298
1347
  function getCanonicalSkillsDir(global, cwd) {
1299
- return join(global ? homedir() : cwd || process.cwd(), AGENTS_DIR, SKILLS_SUBDIR);
1348
+ const baseDir = global ? homedir() : cwd || process.cwd();
1349
+ return join(baseDir, AGENTS_DIR, SKILLS_SUBDIR);
1300
1350
  }
1301
1351
  function getEveSubagentSkillsDir(subagent, cwd) {
1302
- return join(cwd || process.cwd(), EVE_SUBAGENTS_DIR, sanitizeName(subagent), "skills");
1352
+ const baseDir = cwd || process.cwd();
1353
+ return join(baseDir, EVE_SUBAGENTS_DIR, sanitizeName(subagent), "skills");
1303
1354
  }
1304
1355
  function getAgentBaseDir(agentType, global, cwd, eveSubagent) {
1305
1356
  if (isUniversalAgent(agentType)) return getCanonicalSkillsDir(global, cwd);
@@ -1377,7 +1428,8 @@ async function resolveParentSymlinks(path) {
1377
1428
  const dir = dirname(resolved);
1378
1429
  const base = basename(resolved);
1379
1430
  try {
1380
- return join(await realpath(dir), base);
1431
+ const realDir = await realpath(dir);
1432
+ return join(realDir, base);
1381
1433
  } catch {
1382
1434
  return resolved;
1383
1435
  }
@@ -1401,7 +1453,8 @@ async function createSymlink(target, linkPath) {
1401
1453
  }
1402
1454
  const linkDir = dirname(linkPath);
1403
1455
  await mkdir(linkDir, { recursive: true });
1404
- const relativePath = relative(await resolveParentSymlinks(linkDir), target);
1456
+ const realLinkDir = await resolveParentSymlinks(linkDir);
1457
+ const relativePath = relative(realLinkDir, target);
1405
1458
  const symlinkType = platform() === "win32" ? "junction" : void 0;
1406
1459
  await symlink(symlinkType === "junction" ? resolvedTarget : relativePath, linkPath, symlinkType);
1407
1460
  return true;
@@ -1426,6 +1479,8 @@ async function installSkillForAgent(skill, agentType, options = {}) {
1426
1479
  const canonicalDir = join(canonicalBase, skillName);
1427
1480
  const agentBase = getAgentBaseDir(agentType, isGlobal, cwd, eveSubagent);
1428
1481
  const agentDir = join(agentBase, skillName);
1482
+ const { assertLegacyUnmanaged } = await import("./_chunks/ownership.mjs");
1483
+ await assertLegacyUnmanaged([canonicalDir, agentDir]);
1429
1484
  if (!isPathSafe$1(canonicalBase, canonicalDir)) return {
1430
1485
  success: false,
1431
1486
  path: agentDir,
@@ -1470,7 +1525,8 @@ async function installSkillForAgent(skill, agentType, options = {}) {
1470
1525
  mode: "symlink"
1471
1526
  };
1472
1527
  if (!isGlobal && !isUniversalAgent(agentType)) {
1473
- if (!existsSync(join(cwd, agents[agentType].skillsDir.split("/")[0])) && agentType !== "claude-code") return {
1528
+ const agentRootDir = join(cwd, agents[agentType].skillsDir.split("/")[0]);
1529
+ if (!existsSync(agentRootDir) && agentType !== "claude-code") return {
1474
1530
  success: true,
1475
1531
  path: canonicalDir,
1476
1532
  canonicalPath: canonicalDir,
@@ -1544,7 +1600,8 @@ async function copyDirectory(src, dest, agentType) {
1544
1600
  dereference: true,
1545
1601
  recursive: true
1546
1602
  });
1547
- await chmod(destPath, (await stat(srcPath)).mode & 511);
1603
+ const sourceStats = await stat(srcPath);
1604
+ await chmod(destPath, sourceStats.mode & 511);
1548
1605
  } catch (err) {
1549
1606
  if (err instanceof Error && "code" in err && err.code === "ENOENT" && entry.isSymbolicLink()) console.warn(`Skipping broken symlink: ${srcPath}`);
1550
1607
  else throw err;
@@ -1761,7 +1818,8 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
1761
1818
  mode: "symlink"
1762
1819
  };
1763
1820
  if (!isGlobal && !isUniversalAgent(agentType)) {
1764
- if (!existsSync(join(cwd, agents[agentType].skillsDir.split("/")[0]))) return {
1821
+ const agentRootDir = join(cwd, agents[agentType].skillsDir.split("/")[0]);
1822
+ if (!existsSync(agentRootDir)) return {
1765
1823
  success: true,
1766
1824
  path: canonicalDir,
1767
1825
  canonicalPath: canonicalDir,
@@ -1989,7 +2047,7 @@ var ProviderRegistryImpl = class {
1989
2047
  };
1990
2048
  new ProviderRegistryImpl();
1991
2049
  const DISCOVERY_SCHEMA_V2 = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
1992
- const MAX_ARCHIVE_UNPACKED_BYTES = 50 * 1024 * 1024;
2050
+ const MAX_ARCHIVE_UNPACKED_BYTES = 52428800;
1993
2051
  const MAX_ARCHIVE_FILES = 1e3;
1994
2052
  var WellKnownProvider = class {
1995
2053
  id = "well-known";
@@ -3142,22 +3200,23 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3142
3200
  const installedAgents = await detectInstalledAgents();
3143
3201
  const totalAgents = Object.keys(agents).length;
3144
3202
  spinner.stop(`${totalAgents} agents`);
3145
- if (installedAgents.length === 0) if (options.yes) {
3146
- targetAgents = validAgents;
3147
- log.info("正在安装到全部 Agent");
3148
- } else {
3149
- log.info("请选择要安装技能的 Agent");
3150
- const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).map(([key, config]) => ({
3151
- value: key,
3152
- label: config.displayName
3153
- })));
3154
- if (isCancel(selected)) {
3155
- cancel("安装已取消");
3156
- process.exit(0);
3203
+ if (installedAgents.length === 0) {
3204
+ if (options.yes) {
3205
+ targetAgents = validAgents;
3206
+ log.info("正在安装到全部 Agent");
3207
+ } else {
3208
+ log.info("请选择要安装技能的 Agent");
3209
+ const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).map(([key, config]) => ({
3210
+ value: key,
3211
+ label: config.displayName
3212
+ })));
3213
+ if (isCancel(selected)) {
3214
+ cancel("安装已取消");
3215
+ process.exit(0);
3216
+ }
3217
+ targetAgents = selected;
3157
3218
  }
3158
- targetAgents = selected;
3159
- }
3160
- else if (installedAgents.length === 1 || options.yes) {
3219
+ } else if (installedAgents.length === 1 || options.yes) {
3161
3220
  targetAgents = ensureUniversalAgents(installedAgents);
3162
3221
  if (installedAgents.length === 1) {
3163
3222
  const firstAgent = installedAgents[0];
@@ -3392,13 +3451,14 @@ async function installFromSkillHub(parsed, options, spinner) {
3392
3451
  slug = hit.slug;
3393
3452
  log.info(`自动选择:${import_picocolors.default.cyan(`@${namespace}/${slug}`)} — ${hit.description || hit.name}`);
3394
3453
  } else {
3454
+ const choices = hits.map((h, idx) => ({
3455
+ value: idx,
3456
+ label: `@${h.namespace}/${h.slug}`,
3457
+ hint: h.description || h.name
3458
+ }));
3395
3459
  const idx = await select({
3396
3460
  message: "匹配到多个技能 — 请选择一个",
3397
- options: hits.map((h, idx) => ({
3398
- value: idx,
3399
- label: `@${h.namespace}/${h.slug}`,
3400
- hint: h.description || h.name
3401
- }))
3461
+ options: choices
3402
3462
  });
3403
3463
  if (isCancel(idx)) {
3404
3464
  cancel("安装已取消");
@@ -3425,7 +3485,8 @@ async function installFromSkillHub(parsed, options, spinner) {
3425
3485
  }
3426
3486
  const meta = metadata;
3427
3487
  spinner.stop(`Skill: ${import_picocolors.default.cyan(`@${namespace}/${slug}`)}${meta.description ? ` — ${import_picocolors.default.dim(meta.description)}` : ""}`);
3428
- tempDir = join(tmpdir(), `.skillhub-skillshub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
3488
+ const tmpRoot = tmpdir();
3489
+ tempDir = join(tmpRoot, `.skillhub-skillshub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
3429
3490
  mkdirSync(tempDir, { recursive: true });
3430
3491
  const zipPath = join(tempDir, "skill.zip");
3431
3492
  spinner.start("正在下载技能...");
@@ -3761,23 +3822,24 @@ async function runAdd(args, options = {}) {
3761
3822
  }
3762
3823
  targetAgents = selected;
3763
3824
  }
3764
- } else if (installedAgents.length === 0) if (options.yes) {
3765
- targetAgents = validAgents;
3766
- log.info("正在安装到全部 Agent");
3767
- } else {
3768
- log.info("请选择要安装技能的 Agent");
3769
- const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).filter(([key]) => key !== "eve").map(([key, config]) => ({
3770
- value: key,
3771
- label: config.displayName
3772
- })));
3773
- if (isCancel(selected)) {
3774
- cancel("安装已取消");
3775
- await cleanup(tempDir);
3776
- process.exit(0);
3825
+ } else if (installedAgents.length === 0) {
3826
+ if (options.yes) {
3827
+ targetAgents = validAgents;
3828
+ log.info("正在安装到全部 Agent");
3829
+ } else {
3830
+ log.info("请选择要安装技能的 Agent");
3831
+ const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).filter(([key]) => key !== "eve").map(([key, config]) => ({
3832
+ value: key,
3833
+ label: config.displayName
3834
+ })));
3835
+ if (isCancel(selected)) {
3836
+ cancel("安装已取消");
3837
+ await cleanup(tempDir);
3838
+ process.exit(0);
3839
+ }
3840
+ targetAgents = selected;
3777
3841
  }
3778
- targetAgents = selected;
3779
- }
3780
- else if (installedAgents.length === 1 || options.yes) {
3842
+ } else if (installedAgents.length === 1 || options.yes) {
3781
3843
  targetAgents = ensureUniversalAgents(installedAgents);
3782
3844
  if (installedAgents.length === 1) {
3783
3845
  const firstAgent = installedAgents[0];
@@ -3799,17 +3861,18 @@ async function runAdd(args, options = {}) {
3799
3861
  const availableSubagents = getEveSubagents(process.cwd());
3800
3862
  if (options.subagent && options.subagent.length > 0) eveSubagentTargets = options.subagent.map((s) => s === "root" || s === "." ? void 0 : s);
3801
3863
  else if (availableSubagents.length > 0 && !options.yes) {
3864
+ const subagentChoices = [{
3865
+ value: "",
3866
+ label: "根 Agent",
3867
+ hint: "agent/skills"
3868
+ }, ...availableSubagents.map((name) => ({
3869
+ value: name,
3870
+ label: name,
3871
+ hint: `agent/subagents/${name}/skills`
3872
+ }))];
3802
3873
  const selectedSubagents = await multiselect$1({
3803
3874
  message: "Eve 技能安装位置?",
3804
- options: [{
3805
- value: "",
3806
- label: "根 Agent",
3807
- hint: "agent/skills"
3808
- }, ...availableSubagents.map((name) => ({
3809
- value: name,
3810
- label: name,
3811
- hint: `agent/subagents/${name}/skills`
3812
- }))],
3875
+ options: subagentChoices,
3813
3876
  initialValues: [""],
3814
3877
  required: true
3815
3878
  });
@@ -3987,9 +4050,11 @@ async function runAdd(args, options = {}) {
3987
4050
  const normalizedSource = getOwnerRepo(parsed);
3988
4051
  const lockSource = getLockSource(parsed.url, normalizedSource);
3989
4052
  const projectLockSourceUrl = getProjectLockSourceUrl(parsed.type, parsed.url);
3990
- if (normalizedSource) if (parseOwnerRepo(normalizedSource)) {
3991
- if (await repoPrivacyPromise === false) selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
3992
- } else selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
4053
+ if (normalizedSource) {
4054
+ if (parseOwnerRepo(normalizedSource)) {
4055
+ if (await repoPrivacyPromise === false) selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
4056
+ } else selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
4057
+ }
3993
4058
  if (successful.length > 0 && installGlobally && (normalizedSource || parsed.type === "skillhub")) {
3994
4059
  const successfulSkillNames = new Set(successful.map((r) => r.skill));
3995
4060
  let cachedTree;
@@ -4006,7 +4071,8 @@ async function runAdd(args, options = {}) {
4006
4071
  const hash = getSkillFolderHashFromTree(cachedTree, skillPathValue);
4007
4072
  if (hash) skillFolderHash = hash;
4008
4073
  } else if (skillPathValue && tempDir) {
4009
- const hash = await computeSkillFolderHash(join(tempDir, dirname(skillPathValue)));
4074
+ const skillDir = join(tempDir, dirname(skillPathValue));
4075
+ const hash = await computeSkillFolderHash(skillDir);
4010
4076
  if (hash) skillFolderHash = hash;
4011
4077
  }
4012
4078
  await addSkillToLock(skill.name, {
@@ -4050,11 +4116,13 @@ async function runAdd(args, options = {}) {
4050
4116
  const skillResults = bySkill.get(r.skill) || [];
4051
4117
  skillResults.push(r);
4052
4118
  bySkill.set(r.skill, skillResults);
4053
- if (skillResults.length === 1) if (r.pluginName) {
4054
- const group = r.pluginName;
4055
- if (!groupedResults[group]) groupedResults[group] = [];
4056
- groupedResults[group].push(r);
4057
- } else ungroupedResults.push(r);
4119
+ if (skillResults.length === 1) {
4120
+ if (r.pluginName) {
4121
+ const group = r.pluginName;
4122
+ if (!groupedResults[group]) groupedResults[group] = [];
4123
+ groupedResults[group].push(r);
4124
+ } else ungroupedResults.push(r);
4125
+ }
4058
4126
  }
4059
4127
  const skillCount = bySkill.size;
4060
4128
  const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
@@ -4704,6 +4772,14 @@ async function removeCommand(skillNames, options) {
4704
4772
  targetAgents = Object.keys(agents);
4705
4773
  spinner$2.stop(`Targeting ${targetAgents.length} potential agent(s)`);
4706
4774
  }
4775
+ const { assertLegacyUnmanaged } = await import("./_chunks/ownership.mjs");
4776
+ await assertLegacyUnmanaged(selectedSkills.flatMap((skill) => [getCanonicalPath(skill, {
4777
+ global: isGlobal,
4778
+ cwd
4779
+ }), ...targetAgents.map((agent) => getInstallPath(skill, agent, {
4780
+ global: isGlobal,
4781
+ cwd
4782
+ }))]));
4707
4783
  if (!options.yes) {
4708
4784
  console.log();
4709
4785
  log.info("要卸载的技能:");
@@ -4750,12 +4826,15 @@ async function removeCommand(skillNames, options) {
4750
4826
  }
4751
4827
  const remainingAgents = (await detectInstalledAgents()).filter((a) => !targetAgents.includes(a));
4752
4828
  let isStillUsed = false;
4753
- for (const agentKey of remainingAgents) if (await lstat(getInstallPath(skillName, agentKey, {
4754
- global: isGlobal,
4755
- cwd
4756
- })).catch(() => null)) {
4757
- isStillUsed = true;
4758
- break;
4829
+ for (const agentKey of remainingAgents) {
4830
+ const path = getInstallPath(skillName, agentKey, {
4831
+ global: isGlobal,
4832
+ cwd
4833
+ });
4834
+ if (await lstat(path).catch(() => null)) {
4835
+ isStillUsed = true;
4836
+ break;
4837
+ }
4759
4838
  }
4760
4839
  if (!isStillUsed) await rm(canonicalPath, {
4761
4840
  recursive: true,
@@ -4895,12 +4974,14 @@ function parseUpdateOptions(args) {
4895
4974
  }
4896
4975
  function hasProjectSkills(cwd) {
4897
4976
  const dir = cwd || process.cwd();
4898
- if (existsSync(join(dir, "skills-lock.json"))) return true;
4977
+ const lockPath = join(dir, "skills-lock.json");
4978
+ if (existsSync(lockPath)) return true;
4899
4979
  const skillsDir = join(dir, ".agents", "skills");
4900
4980
  try {
4901
4981
  const entries = readdirSync(skillsDir, { withFileTypes: true });
4902
4982
  for (const entry of entries) if (entry.isDirectory()) {
4903
- if (existsSync(join(skillsDir, entry.name, "SKILL.md"))) return true;
4983
+ const skillMd = join(skillsDir, entry.name, "SKILL.md");
4984
+ if (existsSync(skillMd)) return true;
4904
4985
  }
4905
4986
  } catch {}
4906
4987
  return false;
@@ -5324,7 +5405,8 @@ async function updateProjectSkills(options = {}) {
5324
5405
  try {
5325
5406
  tempDir = await cloneRepo(sourceUrl, ref);
5326
5407
  const discoveredPaths = (await discoverSkills(tempDir)).map((s) => {
5327
- return join(relative(tempDir, s.path), "SKILL.md").split(sep).join("/");
5408
+ const relPath = relative(tempDir, s.path);
5409
+ return join(relPath, "SKILL.md").split(sep).join("/");
5328
5410
  });
5329
5411
  deletedSkills = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredPaths);
5330
5412
  } catch (error) {
@@ -5783,12 +5865,8 @@ function fail(message) {
5783
5865
  }
5784
5866
  var UseCommandError = class extends Error {};
5785
5867
  async function runLogin(args, globalOpts) {
5786
- const token = getOptionValue$3(args, "--token");
5868
+ const token = getOptionValue$3(args, "--token") || await promptForToken();
5787
5869
  const { registry } = resolveConfig(globalOpts);
5788
- if (!token) {
5789
- console.error("用法:bfi auth login --token <token>");
5790
- process.exit(1);
5791
- }
5792
5870
  const api = new SkillHubAPI(registry, token);
5793
5871
  try {
5794
5872
  const response = await api.whoami();
@@ -5821,7 +5899,7 @@ async function runLogout() {
5821
5899
  async function runWhoami(globalOpts) {
5822
5900
  const { registry, token } = resolveConfig(globalOpts);
5823
5901
  if (!token) {
5824
- console.error("未认证。运行:bfi auth login --token <token>");
5902
+ console.error("未认证。运行:bfi auth login(在隐藏提示中输入 Token)");
5825
5903
  process.exit(1);
5826
5904
  }
5827
5905
  const response = await new SkillHubAPI(registry, token).whoami();
@@ -5862,6 +5940,47 @@ async function runWhoami(globalOpts) {
5862
5940
  }
5863
5941
  console.log();
5864
5942
  }
5943
+ function promptForToken() {
5944
+ const input = process.stdin;
5945
+ if (!input.isTTY || typeof input.setRawMode !== "function") throw new Error("请在交互式终端运行 `bfi auth login`,Token 将以隐藏方式输入。");
5946
+ process.stderr.write("请输入 SkillHub Token(输入内容隐藏): ");
5947
+ return new Promise((resolve, reject) => {
5948
+ let token = "";
5949
+ let finished = false;
5950
+ const wasRaw = input.isRaw;
5951
+ const cleanup = () => {
5952
+ if (finished) return;
5953
+ finished = true;
5954
+ input.off("data", onData);
5955
+ input.setRawMode(Boolean(wasRaw));
5956
+ input.pause();
5957
+ process.stderr.write("\n");
5958
+ };
5959
+ const onData = (chunk) => {
5960
+ for (const character of chunk.toString("utf8")) {
5961
+ const code = character.charCodeAt(0);
5962
+ if (code === 3 || code === 4) {
5963
+ cleanup();
5964
+ reject(/* @__PURE__ */ new Error("登录已取消。"));
5965
+ return;
5966
+ }
5967
+ if (character === "\r" || character === "\n") {
5968
+ cleanup();
5969
+ const value = token.trim();
5970
+ if (value) resolve(value);
5971
+ else reject(/* @__PURE__ */ new Error("Token 不能为空。"));
5972
+ return;
5973
+ }
5974
+ if (code === 8 || code === 127) token = token.slice(0, -1);
5975
+ else if (code >= 32 && code <= 126) token += character;
5976
+ }
5977
+ };
5978
+ input.setRawMode(true);
5979
+ input.setEncoding("utf8");
5980
+ input.resume();
5981
+ input.on("data", onData);
5982
+ });
5983
+ }
5865
5984
  function getOptionValue$3(args, flag) {
5866
5985
  const idx = args.indexOf(flag);
5867
5986
  return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
@@ -6258,8 +6377,8 @@ const DIM$5 = "\x1B[38;5;102m";
6258
6377
  const TEXT$4 = "\x1B[38;5;145m";
6259
6378
  function formatFileSize(bytes) {
6260
6379
  if (bytes < 1024) return `${bytes} B`;
6261
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
6262
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
6380
+ if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
6381
+ return `${(bytes / 1048576).toFixed(1)} MB`;
6263
6382
  }
6264
6383
  async function runDocs(args, globalOpts) {
6265
6384
  switch (args[0]) {
@@ -6523,7 +6642,8 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
6523
6642
  console.log("需要包含 frontmatter(name, description)的 SKILL.md 文件。");
6524
6643
  process.exit(1);
6525
6644
  }
6526
- const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
6645
+ const skillMdContent = fs.readFileSync(skillMdPath, "utf-8");
6646
+ const { data: frontmatter } = parseFrontmatter(skillMdContent);
6527
6647
  const skillName = frontmatter.name || path$1.basename(resolvedDir);
6528
6648
  console.log(`${DIM$4}正在从目录发布技能...${RESET$4}`);
6529
6649
  console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
@@ -6541,7 +6661,7 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
6541
6661
  if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
6542
6662
  if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
6543
6663
  } catch (error) {
6544
- console.error(` ${RED$2}发布失败:${error.message}${RESET$4}`);
6664
+ printPublishFailure(error, api.registry);
6545
6665
  process.exit(1);
6546
6666
  }
6547
6667
  console.log();
@@ -6570,7 +6690,8 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
6570
6690
  }
6571
6691
  const skillDir = path$1.dirname(skillMdPath);
6572
6692
  console.log(` ${GREEN$3}在 ${path$1.relative(tmpDir, skillDir) || "."} 中找到 SKILL.md${RESET$4}`);
6573
- const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
6693
+ const skillMdContent = fs.readFileSync(skillMdPath, "utf-8");
6694
+ const { data: frontmatter } = parseFrontmatter(skillMdContent);
6574
6695
  const skillName = frontmatter.name || path$1.basename(skillDir);
6575
6696
  console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
6576
6697
  console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$3}${namespace}${RESET$4}`);
@@ -6585,7 +6706,7 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
6585
6706
  if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
6586
6707
  if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
6587
6708
  } catch (error) {
6588
- if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$2}错误:${error.message}${RESET$4}`);
6709
+ if (error.message && !error.message.includes("Publish failed")) printPublishFailure(error, api.registry);
6589
6710
  process.exit(1);
6590
6711
  } finally {
6591
6712
  try {
@@ -6695,7 +6816,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
6695
6816
  console.log(` ${GREEN$3}✓ 已发布:${namespace}--${res.slug || skill.name}${RESET$4}`);
6696
6817
  successCount++;
6697
6818
  } catch (error) {
6698
- console.error(` ${RED$2}✗ ${skill.name}:${error.message}${RESET$4}`);
6819
+ printPublishFailure(error, api.registry, `✗ ${skill.name}:`);
6699
6820
  }
6700
6821
  }
6701
6822
  console.log(`\n${GREEN$3}发布完成:${successCount}/${selectedSkills.length} 个技能${RESET$4}`);
@@ -6704,6 +6825,17 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
6704
6825
  await cleanupTempDir(tmpDir);
6705
6826
  } catch {}
6706
6827
  }
6828
+ function printPublishFailure(error, registry, label = "发布失败:") {
6829
+ const requiredScope = error?.body?.data?.requiredScope;
6830
+ if (error?.status === 403 && typeof requiredScope === "string" && requiredScope) {
6831
+ const settingsUrl = `${registry}/dashboard/tokens`;
6832
+ console.error(` ${RED$2}${label}当前 API Token 缺少「${requiredScope}」权限。${RESET$4}`);
6833
+ console.error(` ${DIM$4}修复:打开 ${settingsUrl} 新建 Token 并勾选「技能发布」,${RESET$4}`);
6834
+ console.error(` ${DIM$4}然后执行 bfi auth login --token <新 Token> 后重试。${RESET$4}`);
6835
+ return;
6836
+ }
6837
+ console.error(` ${RED$2}${label}${error.message}${RESET$4}`);
6838
+ }
6707
6839
  function getOptionValue$1(args, flag) {
6708
6840
  const idx = args.indexOf(flag);
6709
6841
  return idx !== -1 && idx < args.length - 1 ? args[idx + 1] : void 0;
@@ -6740,8 +6872,8 @@ function findSkillMd(dir) {
6740
6872
  }
6741
6873
  function formatSize(bytes) {
6742
6874
  if (bytes < 1024) return `${bytes} B`;
6743
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
6744
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
6875
+ if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
6876
+ return `${(bytes / 1048576).toFixed(1)} MB`;
6745
6877
  }
6746
6878
  function truncate(text, maxLen) {
6747
6879
  if (text.length <= maxLen) return text;
@@ -6830,107 +6962,6 @@ function maybePrintAnalyticsNotice(env = process.env) {
6830
6962
  console.log();
6831
6963
  } catch {}
6832
6964
  }
6833
- const pluginTargets = [
6834
- "claude-code",
6835
- "codex",
6836
- "zcode",
6837
- "codebuddy",
6838
- "qwen-code"
6839
- ];
6840
- const isPluginArchive = (value) => /\.(zip|tgz|tar\.gz)$/iu.test(value);
6841
- const isPluginVersion = (value) => /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u.test(value);
6842
- function parsePackageIntent(args) {
6843
- const output = [...args];
6844
- let mode = "skills";
6845
- const occurrences = args.filter((value) => value === "--as" || value.startsWith("--as="));
6846
- if (occurrences.length) {
6847
- const index = output.indexOf("--as");
6848
- const requested = output[index + 1];
6849
- if (occurrences.length !== 1 || index < 0 || !["skills", "plugin"].includes(requested ?? "")) throw new Error("Use --as skills or --as plugin exactly once; upgrade bfi if this option is unavailable");
6850
- mode = requested;
6851
- output.splice(index, 2);
6852
- }
6853
- if (mode === "skills" && (output.some((value) => [
6854
- "--plan",
6855
- "--export",
6856
- "--scope",
6857
- "--root",
6858
- "--operation"
6859
- ].includes(value) || /^(--plan|--export|--scope|--root|--operation)=/u.test(value)) || isPluginArchive(output[1] ?? ""))) throw new Error("Plugin archives and plugin options require explicit --as plugin; no skills were installed");
6860
- return {
6861
- mode,
6862
- args: output
6863
- };
6864
- }
6865
- function parsePluginArguments(args) {
6866
- const command = {
6867
- install: "install",
6868
- i: "install",
6869
- update: "update",
6870
- up: "update",
6871
- uninstall: "uninstall",
6872
- rm: "uninstall",
6873
- list: "list",
6874
- ls: "list"
6875
- }[args[0] ?? ""];
6876
- if (!command) throw new Error("Plugin intent supports install, update, uninstall and list");
6877
- const result = {
6878
- command,
6879
- scope: "project",
6880
- planOnly: false,
6881
- json: false
6882
- };
6883
- const seen = /* @__PURE__ */ new Set();
6884
- const values = /* @__PURE__ */ new Set([
6885
- "--agent",
6886
- "--version",
6887
- "--scope",
6888
- "--root",
6889
- "--export",
6890
- "--operation",
6891
- "--registry",
6892
- "--token",
6893
- "--namespace"
6894
- ]);
6895
- for (let index = 1; index < args.length; index++) {
6896
- const option = args[index];
6897
- if (!option.startsWith("-")) {
6898
- if (result.source) throw new Error("Specify exactly one plugin source");
6899
- result.source = option;
6900
- continue;
6901
- }
6902
- const flag = option === "-g" ? "--global" : option === "-y" ? "--yes" : option;
6903
- if (seen.has(flag)) throw new Error(`Duplicate plugin option: ${flag}`);
6904
- seen.add(flag);
6905
- if (values.has(flag)) {
6906
- const value = args[++index];
6907
- if (value === void 0 || value.startsWith("-") || /[\u0000-\u001f\u007f]/u.test(value)) throw new Error(`Missing or invalid value for ${flag}`);
6908
- if (flag === "--agent") {
6909
- if (!pluginTargets.includes(value)) throw new Error("Unknown native plugin target");
6910
- result.agent = value;
6911
- } else if (flag === "--version") {
6912
- if (!isPluginVersion(value)) throw new Error("Plugin version must be exact SemVer, not latest or a range");
6913
- result.version = value;
6914
- } else if (flag === "--scope") {
6915
- if (!["project", "user"].includes(value)) throw new Error("Plugin scope must be project or user");
6916
- result.scope = value;
6917
- } else if (flag === "--root") result.root = value;
6918
- else if (flag === "--export") result.exportPath = value;
6919
- else if (flag === "--operation") result.operationId = value;
6920
- } else if (flag === "--global") result.scope = "user";
6921
- else if (flag === "--plan") result.planOnly = true;
6922
- else if (flag === "--json") result.json = true;
6923
- else if (flag !== "--yes") throw new Error(`Unknown plugin option: ${flag}; upgrade bfi if needed`);
6924
- }
6925
- if (seen.has("--scope") && seen.has("--global")) throw new Error("Use --scope or --global, not both");
6926
- if (command !== "list" && !result.agent) throw new Error("Full plugin operations require an explicit --agent");
6927
- if (command !== "list" && !result.source && !(command === "uninstall" && result.operationId)) throw new Error("Specify @namespace/package or a local zip/tgz path");
6928
- if (result.source && !/^@[^/\s]+\/[^/\s]+$/u.test(result.source) && !isPluginArchive(result.source)) throw new Error("Use a namespace-qualified @namespace/package reference or local archive");
6929
- if (result.planOnly && result.exportPath) throw new Error("Plan and export are separate operations");
6930
- if (result.root !== void 0 && !isPluginArchive(result.source ?? "") || result.operationId && command !== "uninstall" || (result.exportPath || result.root !== void 0) && command !== "install") throw new Error("Plugin option is invalid for this operation");
6931
- if (command === "list" && (result.source || result.version || result.planOnly || result.exportPath || result.operationId)) throw new Error("Invalid plugin list options");
6932
- return result;
6933
- }
6934
6965
  var package_exports = /* @__PURE__ */ __exportAll({ runPackage: () => runPackage });
6935
6966
  const RESET$3 = "\x1B[0m";
6936
6967
  const BOLD$1 = "\x1B[1m";
@@ -7012,7 +7043,7 @@ function printPackageHelp() {
7012
7043
  console.log(`${BOLD$1}选项:${RESET$3}`);
7013
7044
  console.log(` --namespace <ns> 目标命名空间`);
7014
7045
  console.log(` --agent <a> 目标 agent(如 claude-code、codex)`);
7015
- console.log(` --as skills|plugin 默认 skills;完整插件必须显式 --as plugin --agent <a>`);
7046
+ console.log(` --as skills|plugin 默认 skills;完整插件必须显式 --as plugin,未指定 Agent/范围时交互选择`);
7016
7047
  console.log(` --version <v> 插件固定版本;省略只解析显式 stable,不回退 latest`);
7017
7048
  console.log(` --plan 插件模式只读计划,不写配置或安装记录`);
7018
7049
  console.log(` --export <archive> 插件模式校验后导出原始制品,不安装、不覆盖文件`);
@@ -7026,15 +7057,28 @@ function printPackageHelp() {
7026
7057
  console.log();
7027
7058
  }
7028
7059
  async function handleLocalList(args, globalOpts) {
7029
- const lock = loadLockfile(getWorkdir(globalOpts, args));
7060
+ const cwd = getWorkdir(globalOpts, args);
7061
+ const lock = loadLockfile(cwd);
7030
7062
  args.includes("-g") || args.includes("--global");
7031
7063
  const pkgs = lock.packages || {};
7032
7064
  const entries = Object.entries(pkgs);
7033
- if (args.includes("--json")) {
7034
- console.log(JSON.stringify(Object.values(pkgs), null, 2));
7065
+ const json = args.includes("--json");
7066
+ const { receipts } = await import("./_chunks/receipt.mjs");
7067
+ const installed = (await receipts()).filter((record) => record.plan.kind === "COLLECTION");
7068
+ if (json) {
7069
+ console.log(JSON.stringify([...Object.values(pkgs), ...installed.map((record) => ({
7070
+ ref: record.plan.ref,
7071
+ registry: record.plan.registry,
7072
+ state: record.state,
7073
+ operation: record.id,
7074
+ destination: record.destination,
7075
+ skills: record.plan.skills.length,
7076
+ mcps: record.plan.mcps.length
7077
+ }))], null, 2));
7035
7078
  return;
7036
7079
  }
7037
- if (entries.length === 0) {
7080
+ for (const record of installed) console.log(`${record.plan.ref} · ${record.state} · ${record.destination.agents.join(", ")} · ${record.destination.scope} · ${record.plan.skills.length} 技能 + ${record.plan.mcps.length} MCP · ${record.id}`);
7081
+ if (entries.length === 0 && installed.length === 0) {
7038
7082
  console.log("没有已安装的包。");
7039
7083
  console.log(`使用 ${DIM$3}bfi skills package install <ref>${RESET$3} 安装包。`);
7040
7084
  return;
@@ -7223,17 +7267,18 @@ async function handleInstall(args, api, globalOpts) {
7223
7267
  if (targetAgents.includes("eve")) {
7224
7268
  const availableSubagents = getEveSubagents(process.cwd());
7225
7269
  if (availableSubagents.length > 0) {
7270
+ const subagentChoices = [{
7271
+ value: "",
7272
+ label: "根 Agent",
7273
+ hint: "agent/skills"
7274
+ }, ...availableSubagents.map((name) => ({
7275
+ value: name,
7276
+ label: name,
7277
+ hint: `agent/subagents/${name}/skills`
7278
+ }))];
7226
7279
  const selectedSubagents = await multiselect$1({
7227
7280
  message: "Eve 技能安装位置?",
7228
- options: [{
7229
- value: "",
7230
- label: "根 Agent",
7231
- hint: "agent/skills"
7232
- }, ...availableSubagents.map((name) => ({
7233
- value: name,
7234
- label: name,
7235
- hint: `agent/subagents/${name}/skills`
7236
- }))],
7281
+ options: subagentChoices,
7237
7282
  initialValues: [""],
7238
7283
  required: true
7239
7284
  });
@@ -7428,7 +7473,8 @@ async function handleUninstall(args, globalOpts) {
7428
7473
  }
7429
7474
  async function handleUpdate(args, api, globalOpts) {
7430
7475
  const pkgRef = args[0];
7431
- const lock = loadLockfile(getWorkdir(globalOpts, args));
7476
+ const cwd = getWorkdir(globalOpts, args);
7477
+ const lock = loadLockfile(cwd);
7432
7478
  let packagesToUpdate = [];
7433
7479
  const pkgs = lock.packages || {};
7434
7480
  if (pkgRef) {
@@ -8761,7 +8807,6 @@ async function runYmc(args, globalOpts) {
8761
8807
  break;
8762
8808
  }
8763
8809
  default: {
8764
- if (sub === "cases" || sub.startsWith("cases")) console.error("案例库已迁移 WeKnora:检索/归档改用 weknora-bfi MCP 工具(kb_search / kb_archive_case)。");
8765
8810
  console.error(`未知子命令:${sub}`);
8766
8811
  const hint = SUBCOMMAND_HINTS[sub];
8767
8812
  if (hint) console.error(`提示:${hint}`);
@@ -8797,7 +8842,7 @@ const HELP = {
8797
8842
  禁止 * | SELECT * WHERE 字段=... (WHERE 全扫描必超时)
8798
8843
  -t <time> 时间简写 10m/2h/3d/4w(默认无时间=服务端超大窗兜底,会命中数天前记录,务必显式指定)
8799
8844
  --start-time/--end-time 精确窗口 yyyy-MM-dd HH:mm:ss 北京时间(优先于 -t)
8800
- --days N 按天数回溯(旧参数,等价 -t Nd)
8845
+ --days N 按天数回溯(等价 -t Nd)
8801
8846
  --line N 返回行数上限
8802
8847
  示例:
8803
8848
  bfi ymc sls-raw 'traceid: abc | SELECT * LIMIT 200' -e daily -t 1d
@@ -8831,8 +8876,8 @@ const HELP = {
8831
8876
  --app 服务维度过滤(裸 appCode)
8832
8877
  --method 按调用方法过滤
8833
8878
  --min-cost <ms> 最小耗时过滤(body.costTime=[ms, 999999])
8834
- (无 --profiler) 历史参数已移除映射——上游 enableProfiler 是"仅返回有 CPU 采样
8835
- 记录"的过滤器,实测任何组合均空集。CPU 热点走 dump-list。
8879
+ --profiler 透传上游 enableProfiler(仅返回有 CPU 采样记录的调用);
8880
+ 该过滤器常返回空集,常规取证勿带,CPU 热点走 dump-list。
8836
8881
  示例:
8837
8882
  bfi ymc slowcall -t 1d --trace-id abc -e core3 # 链路分层分解(跨服务根因链必用)
8838
8883
  bfi ymc slowcall -t 4w --app yonbip-scm-pu -e pre # 服务慢调用趋势`,
@@ -8857,12 +8902,6 @@ const HELP = {
8857
8902
  --size N 返回条数(默认 100,最大 5000)`,
8858
8903
  "dump-list": `bfi ymc dump-list -t <time> -e <env> [--app <appCode>] [--type profiler|heap|...] [--json]
8859
8904
  诊断文件列表(火焰图/JVM dump;CPU 热点取数的正确入口)。`,
8860
- cases: `bfi ymc cases <search|show|upload|index> [参数]
8861
- KEDB 案例库(内部诊断先验第一源)。
8862
- search [--type <t>] [--finger "<指纹>"] [--service <svc>] [--kw "<关键词>"] [--limit N]
8863
- show <case_id>
8864
- upload --file <报告.html> [--type slow-req] [--finger "词1,词2"] [--env] [--service]
8865
- index 案例库索引重建`,
8866
8905
  wiki: `bfi ymc wiki <search|show> [参数]
8867
8906
  知识库统一检索(confluence + jira + 文库 ecfile 三源——诊断先验的外部源,勿只查其一)。
8868
8907
  search "<关键词>" [--source confluence|jira|ecfile] [--limit N] [--titles]
@@ -8929,7 +8968,7 @@ bfi ymc — 观测查询(v2 API:YMC/Kibana/SLS/trace)
8929
8968
  sls-grep "<关键词>" -e <env> SLS 全文检索聚合(异常历史分布取证)
8930
8969
  slowsql -t <time> [-e] [--sql-str] [--md5] [--trace-id] [--app] [--no-profiler]
8931
8970
  slowcall -t <time> [-e] [--trace-id] [--app] [--method] [--min-cost ms]
8932
- [--profiler] # ⚠️ 采样过滤器实测常为空集,常规取证勿带
8971
+ [--profiler] # ⚠️ 采样过滤器常为空集,常规取证勿带
8933
8972
  exceptions -t <time> [-e] [--app]
8934
8973
  alerts -t <time> [-e] [--level error|warn|info]
8935
8974
  url-stats </uri> -t <time> -e <env> [--app <service_name>]
@@ -8941,7 +8980,6 @@ bfi ymc — 观测查询(v2 API:YMC/Kibana/SLS/trace)
8941
8980
  callstat / sqlstat 慢调用/慢SQL 统计版聚合
8942
8981
  rca <...> 官方智能诊断 RCA(长耗时)
8943
8982
  bpr-download <pk> BPR 报告下载
8944
- cases <search|show|upload|index> 案例库(KEDB)
8945
8983
  wiki search "<kw>" [--source] [--titles] 知识库统一检索(wiki/jira/文库)
8946
8984
  wiki show <id> [--source] 知识条目全文
8947
8985
  call <route> -e <env> [--data '<json>'] 通用透传(全部注册表端点)
@@ -9719,7 +9757,7 @@ ${BOLD}bfi — 用友大业财BFI统一命令行工具${RESET}
9719
9757
  ${BOLD}Usage:${RESET} bfi <command> [options]
9720
9758
 
9721
9759
  ${BOLD}━━━ 认证 ━━━${RESET}
9722
- auth login --token <token> 登录 SkillHub
9760
+ auth login [--token <token>] 登录 SkillHub(不带参数时隐藏输入 Token)
9723
9761
  auth logout 登出
9724
9762
  auth whoami 查看当前用户
9725
9763
 
@@ -9749,11 +9787,12 @@ ${BOLD}━━━ 技能包管理 ━━━${RESET}
9749
9787
  skills package list [--namespace <ns>] 列出技能包
9750
9788
  skills package info <ref> 查看包详情
9751
9789
  skills package create <slug> [--name] [--description] 创建包
9752
- skills package add <pkg> <skill...> 添加技能到包
9753
- skills package remove <pkg> <skill...> 从包移除技能
9790
+ skills package add-skills <pkg> <skill...> 添加技能到包
9791
+ skills package remove-skills <pkg> <skill...> 从包移除技能
9754
9792
  skills package install <ref> [--agent <a>] 安装技能组合(默认,不安装插件组件)
9755
9793
  --as plugin 显式完整插件模式;支持本地 zip/tgz,不兼容时不会降级安装
9756
9794
  --plan 只读校验安装计划;--export <archive> 仅导出,不安装
9795
+ --prepare-native <dir> 准备完整原生目录和市场索引,审阅后交给宿主安装;不注册、不转技能
9757
9796
  --agent <a> 目标 agent(如 claude-code、codex、zcode)
9758
9797
  --global 安装到主目录(跨项目可用)
9759
9798
 
@@ -9762,6 +9801,12 @@ ${BOLD}━━━ 文档 ━━━${RESET}
9762
9801
  docs list 列出文档
9763
9802
  docs info <id> 查看文档详情
9764
9803
 
9804
+ ${BOLD}━━━ MCP ━━━${RESET}
9805
+ mcp search / info 查询独立 MCP 市场
9806
+ mcp install <name...> --agent <agent> 保留已有配置,安装选定 MCP
9807
+ mcp list / remove / test --agent <agent> 管理或验证 MCP
9808
+ mcp --help 参数、支持范围与安全说明
9809
+
9765
9810
  ${BOLD}━━━ 观测查询(ymc)━━━${RESET}
9766
9811
  ymc status 登录会话状态
9767
9812
  ymc trace <trace_id> -e <env> traceId 三源合一(SLS+链路+ingress)
@@ -9778,6 +9823,7 @@ ${BOLD}━━━ DB 取证辅助(db)━━━${RESET}
9778
9823
  ytenant_id 条件且分库未知时,先定位再连)
9779
9824
 
9780
9825
  ${BOLD}━━━ 配置 ━━━${RESET}
9826
+ capabilities --json 只读查询此构建已实现的安装能力(不联网、不修改配置)
9781
9827
  config get [key] 查看配置
9782
9828
  config set <key> <value> [--global] 设置配置
9783
9829
 
@@ -9794,7 +9840,7 @@ ${BOLD}━━━ 技能引用格式 ━━━${RESET}
9794
9840
  owner/repo 从 GitHub 安装
9795
9841
 
9796
9842
  ${BOLD}━━━ 示例 ━━━${RESET}
9797
- ${DIM}$ bfi auth login --token sk-xxx${RESET}
9843
+ ${DIM}$ bfi auth login${RESET}
9798
9844
  ${DIM}$ bfi skills search react${RESET}
9799
9845
  ${DIM}$ bfi skills add @myteam/db-explore${RESET}
9800
9846
  ${DIM}$ bfi skills add vercel-labs/agent-skills --skill pr-review${RESET}
@@ -9848,7 +9894,7 @@ function runInit(args) {
9848
9894
  return;
9849
9895
  }
9850
9896
  if (hasName) mkdirSync(skillDir, { recursive: true });
9851
- writeFileSync(skillFile, `---
9897
+ const skillContent = `---
9852
9898
  name: ${skillName}
9853
9899
  description: A brief description of what this skill does
9854
9900
  ---
@@ -9866,7 +9912,8 @@ Describe when this skill should be used.
9866
9912
  1. First step
9867
9913
  2. Second step
9868
9914
  3. Additional steps as needed
9869
- `);
9915
+ `;
9916
+ writeFileSync(skillFile, skillContent);
9870
9917
  console.log(`${TEXT}已初始化技能:${DIM}${skillName}${RESET}`);
9871
9918
  console.log();
9872
9919
  console.log(`${DIM}已创建:${RESET}`);
@@ -10024,6 +10071,45 @@ async function main() {
10024
10071
  }
10025
10072
  const command = args[0];
10026
10073
  const restArgs = args.slice(1);
10074
+ if (command === "capabilities") {
10075
+ if (restArgs.some((argument) => argument !== "--json")) {
10076
+ console.error("用法:bfi capabilities [--json]");
10077
+ process.exitCode = 1;
10078
+ return;
10079
+ }
10080
+ console.log(JSON.stringify(getCapabilities(VERSION), null, 2));
10081
+ return;
10082
+ }
10083
+ const { dispatchInstallation } = await import("./_chunks/command.mjs");
10084
+ if (await dispatchInstallation(args)) return;
10085
+ const installationError = installationArgumentError(args);
10086
+ if (installationError) {
10087
+ console.error(installationError);
10088
+ process.exitCode = 1;
10089
+ return;
10090
+ }
10091
+ if (command === "mcp") {
10092
+ const { runMcp } = await import("./_chunks/mcp.mjs");
10093
+ await runMcp(restArgs);
10094
+ return;
10095
+ }
10096
+ const packageRoute = ["skills", "skill"].includes(command) ? restArgs : args;
10097
+ if (["package", "pkg"].includes(packageRoute[0] ?? "") && packageRoute.some((argument) => argument === "--as" || argument.startsWith("--as="))) {
10098
+ const intent = parsePackageIntent(packageRoute.slice(1));
10099
+ if (intent.mode === "plugin") {
10100
+ const pluginOpts = {
10101
+ workdir: process.cwd(),
10102
+ version: VERSION
10103
+ };
10104
+ for (const name of ["registry", "token"]) {
10105
+ const index = args.indexOf(`--${name}`);
10106
+ if (index >= 0) pluginOpts[name] = args[index + 1];
10107
+ }
10108
+ const { runPluginPackage } = await import("./_chunks/package-plugin.mjs");
10109
+ await runPluginPackage(intent.args, pluginOpts);
10110
+ return;
10111
+ }
10112
+ }
10027
10113
  try {
10028
10114
  const { maybePrintAnalyticsNotice } = await Promise.resolve().then(() => analytics_exports);
10029
10115
  maybePrintAnalyticsNotice();
@@ -10175,4 +10261,4 @@ main().catch((e) => {
10175
10261
  console.error(`✗ ${e?.message || e}`);
10176
10262
  process.exit(1);
10177
10263
  });
10178
- export { isPluginArchive, isPluginVersion, parsePluginArguments };
10264
+ export { SkillHubAPI, getAgentBaseDir };