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/README.md +117 -5
- package/dist/THIRD-PARTY-LICENSES.md.gz +0 -0
- package/dist/_chunks/agents.mjs +1 -1
- package/dist/_chunks/agents2.mjs +4 -1
- package/dist/_chunks/client.mjs +107 -0
- package/dist/_chunks/command.mjs +698 -0
- package/dist/_chunks/digest.mjs +132 -0
- package/dist/_chunks/files.mjs +89 -0
- package/dist/_chunks/{registry-client.mjs → intent.mjs} +163 -13
- package/dist/_chunks/libs/@clack/core.mjs +63 -58
- package/dist/_chunks/libs/@clack/prompts.mjs +46 -45
- package/dist/_chunks/libs/@kwsites/file-exists.mjs +10 -9
- package/dist/_chunks/libs/simple-git.mjs +4 -2
- package/dist/_chunks/manager.mjs +436 -0
- package/dist/_chunks/mcp.mjs +559 -0
- package/dist/_chunks/model.mjs +124 -0
- package/dist/_chunks/ownership.mjs +7 -0
- package/dist/_chunks/package-plugin.mjs +473 -36
- package/dist/_chunks/receipt.mjs +166 -0
- package/dist/_chunks/rolldown-runtime.mjs +1 -1
- package/dist/_chunks/skills.mjs +6 -3
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +319 -233
- package/dist/mcp-codesearch/main.d.mts +1 -1
- package/dist/mcp-db/main.d.mts +1 -1
- package/dist/mcp-weknora/main.d.mts +1 -1
- package/package.json +4 -2
- package/dist/THIRD-PARTY-LICENSES.md +0 -293
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/
|
|
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)
|
|
455
|
-
|
|
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)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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}
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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)
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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)
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
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
|
-
|
|
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)
|
|
3991
|
-
if (
|
|
3992
|
-
|
|
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
|
|
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)
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
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)
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 <
|
|
6262
|
-
return `${(bytes /
|
|
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
|
|
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
|
-
|
|
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
|
|
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"))
|
|
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
|
-
|
|
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 <
|
|
6744
|
-
return `${(bytes /
|
|
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
|
|
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
|
|
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
|
-
|
|
7034
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
8835
|
-
|
|
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>
|
|
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
|
|
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
|
-
|
|
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 {
|
|
10264
|
+
export { SkillHubAPI, getAgentBaseDir };
|