bfi-cli 0.1.89 → 0.1.91
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 +40 -0
- package/dist/THIRD-PARTY-LICENSES.md.gz +0 -0
- package/dist/_chunks/agents2.mjs +15 -1
- package/dist/_chunks/config.mjs +141 -0
- package/dist/_chunks/libs/@clack/core.mjs +63 -58
- package/dist/_chunks/libs/@clack/prompts.mjs +48 -45
- package/dist/_chunks/libs/@kwsites/file-exists.mjs +10 -9
- package/dist/_chunks/libs/simple-git.mjs +4 -2
- package/dist/_chunks/package-plugin.mjs +595 -0
- package/dist/_chunks/package-skill-identity.mjs +18 -0
- package/dist/_chunks/registry-client.mjs +535 -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 +876 -1095
- package/dist/mcp-codesearch/main.d.mts +1 -1
- package/dist/mcp-codesearch/main.mjs +16 -23
- package/dist/mcp-db/main.d.mts +1 -1
- package/package.json +6 -2
- package/dist/THIRD-PARTY-LICENSES.md +0 -293
package/dist/cli.mjs
CHANGED
|
@@ -4,13 +4,16 @@ 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";
|
|
8
|
+
import { loadGlobalConfig, loadLockfile, loadProjectConfig, resolveConfig, saveGlobalConfig as saveGlobalConfig$1, saveLockfile, setConfigValue } from "./_chunks/config.mjs";
|
|
7
9
|
import { esm_default } from "./_chunks/libs/simple-git.mjs";
|
|
8
10
|
import { EVE_SUBAGENTS_DIR, agents, detectInstalledAgents, getEveSubagents, getNonUniversalAgents, getUniversalAgents, getVisibleUniversalAgents, isUniversalAgent } from "./_chunks/agents.mjs";
|
|
11
|
+
import { packageSkillName } from "./_chunks/package-skill-identity.mjs";
|
|
9
12
|
import { createRequire } from "node:module";
|
|
10
13
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
11
14
|
import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
|
|
12
15
|
import { fileURLToPath } from "url";
|
|
13
|
-
import { createHash } from "node:crypto";
|
|
16
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
14
17
|
import { stripVTControlCharacters } from "node:util";
|
|
15
18
|
import fs from "node:fs";
|
|
16
19
|
import path$1 from "node:path";
|
|
@@ -19,8 +22,8 @@ import * as readline from "readline";
|
|
|
19
22
|
import { Writable } from "stream";
|
|
20
23
|
import https from "node:https";
|
|
21
24
|
import http from "node:http";
|
|
22
|
-
import os$1 from "node:os";
|
|
23
25
|
import zlib, { gunzipSync, inflateRawSync } from "node:zlib";
|
|
26
|
+
import os$1 from "node:os";
|
|
24
27
|
import { promisify } from "util";
|
|
25
28
|
import { execFile, spawn, spawnSync } from "child_process";
|
|
26
29
|
import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "fs/promises";
|
|
@@ -448,8 +451,10 @@ async function searchMultiselect(options) {
|
|
|
448
451
|
}
|
|
449
452
|
if (key.name === "space") {
|
|
450
453
|
const item = filtered[cursor];
|
|
451
|
-
if (item)
|
|
452
|
-
|
|
454
|
+
if (item) {
|
|
455
|
+
if (selected.has(item.value)) selected.delete(item.value);
|
|
456
|
+
else selected.add(item.value);
|
|
457
|
+
}
|
|
453
458
|
render();
|
|
454
459
|
return;
|
|
455
460
|
}
|
|
@@ -476,6 +481,9 @@ var SkillHubAPI = class {
|
|
|
476
481
|
token;
|
|
477
482
|
mockUserId;
|
|
478
483
|
rejectUnauthorized;
|
|
484
|
+
pluginRegistry() {
|
|
485
|
+
return new PluginRegistryClient(this.registry, this.token);
|
|
486
|
+
}
|
|
479
487
|
constructor(registry, token = null, mockUserId = null) {
|
|
480
488
|
this.registry = registry || "https://bfi.yyuap.com:811";
|
|
481
489
|
this.token = token;
|
|
@@ -506,12 +514,14 @@ var SkillHubAPI = class {
|
|
|
506
514
|
if (url.protocol === "https:") requestOptions.rejectUnauthorized = this.rejectUnauthorized;
|
|
507
515
|
let bodyStr;
|
|
508
516
|
let bodyBuf;
|
|
509
|
-
if (options.body !== void 0)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
517
|
+
if (options.body !== void 0) {
|
|
518
|
+
if (options.body instanceof Buffer) bodyBuf = options.body;
|
|
519
|
+
else if (typeof options.body === "string") bodyStr = options.body;
|
|
520
|
+
else if (typeof FormData !== "undefined" && options.body instanceof FormData) return this.fetchRequest(url, requestOptions, options.body);
|
|
521
|
+
else {
|
|
522
|
+
requestOptions.headers["Content-Type"] = "application/json";
|
|
523
|
+
bodyStr = JSON.stringify(options.body);
|
|
524
|
+
}
|
|
515
525
|
}
|
|
516
526
|
return new Promise((resolve, reject) => {
|
|
517
527
|
const req = (url.protocol === "https:" ? https : http).request(url, requestOptions, (res) => {
|
|
@@ -653,6 +663,13 @@ var SkillHubAPI = class {
|
|
|
653
663
|
async whoami() {
|
|
654
664
|
return (await this.request("GET", "/whoami")).body;
|
|
655
665
|
}
|
|
666
|
+
async reportInstallEvent(payload) {
|
|
667
|
+
return (await this.request("POST", "/install-events", { body: payload })).body;
|
|
668
|
+
}
|
|
669
|
+
async getBlocklist() {
|
|
670
|
+
const body = (await this.request("GET", "/registry/blocklist")).body;
|
|
671
|
+
return body.data || body;
|
|
672
|
+
}
|
|
656
673
|
async myNamespaces() {
|
|
657
674
|
return (await this.request("GET", "/me/namespaces")).body;
|
|
658
675
|
}
|
|
@@ -664,6 +681,30 @@ var SkillHubAPI = class {
|
|
|
664
681
|
const body = (await this.request("GET", `/namespaces/${namespace}/packages/${slug}`)).body;
|
|
665
682
|
return body.data || body;
|
|
666
683
|
}
|
|
684
|
+
async listComponentFiles(namespace, slug, componentId) {
|
|
685
|
+
const body = (await this.request("GET", `/namespaces/${namespace}/packages/${slug}/components/${componentId}/files`)).body;
|
|
686
|
+
return body.data || body;
|
|
687
|
+
}
|
|
688
|
+
async readComponentFile(namespace, slug, componentId, filePath) {
|
|
689
|
+
const target = `/namespaces/${namespace}/packages/${slug}/components/${componentId}/files/content?path=${encodeURIComponent(filePath)}`;
|
|
690
|
+
const url = new URL(this.buildUrl(target));
|
|
691
|
+
const isHttps = url.protocol === "https:";
|
|
692
|
+
const client = isHttps ? https : http;
|
|
693
|
+
const options = { headers: this.getHeaders() };
|
|
694
|
+
if (isHttps) options.rejectUnauthorized = this.rejectUnauthorized;
|
|
695
|
+
return new Promise((resolve, reject) => {
|
|
696
|
+
client.get(url.toString(), options, (res) => {
|
|
697
|
+
if (!res.statusCode || res.statusCode >= 400) {
|
|
698
|
+
reject(/* @__PURE__ */ new Error(`读取组件文件失败:HTTP ${res.statusCode}(${filePath})`));
|
|
699
|
+
res.resume();
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const chunks = [];
|
|
703
|
+
res.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
704
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
705
|
+
}).on("error", reject);
|
|
706
|
+
});
|
|
707
|
+
}
|
|
667
708
|
async downloadPackage(namespace, slug, targetPath) {
|
|
668
709
|
const path = `/namespaces/${namespace}/packages/${slug}/download`;
|
|
669
710
|
try {
|
|
@@ -682,72 +723,6 @@ var SkillHubAPI = class {
|
|
|
682
723
|
async removeSkillsFromPackage(namespace, slug, skills) {
|
|
683
724
|
return (await this.request("DELETE", `/namespaces/${namespace}/packages/${slug}/skills`, { body: { skills } })).body;
|
|
684
725
|
}
|
|
685
|
-
async listPlugins(params) {
|
|
686
|
-
const body = (await this.request("GET", "/plugins", { params })).body;
|
|
687
|
-
return body.data || body;
|
|
688
|
-
}
|
|
689
|
-
async getPlugin(pluginId) {
|
|
690
|
-
const body = (await this.request("GET", `/plugins/${pluginId}`)).body;
|
|
691
|
-
return body.data || body;
|
|
692
|
-
}
|
|
693
|
-
async createPlugin(data) {
|
|
694
|
-
return (await this.request("POST", "/plugins", { body: data })).body;
|
|
695
|
-
}
|
|
696
|
-
async updatePlugin(pluginId, data) {
|
|
697
|
-
return (await this.request("PUT", `/plugins/${pluginId}`, { body: data })).body;
|
|
698
|
-
}
|
|
699
|
-
async deletePlugin(pluginId) {
|
|
700
|
-
return (await this.request("DELETE", `/plugins/${pluginId}`)).body;
|
|
701
|
-
}
|
|
702
|
-
async listPluginVersions(pluginId) {
|
|
703
|
-
const body = (await this.request("GET", `/plugins/${pluginId}/versions`)).body;
|
|
704
|
-
return body.data || body;
|
|
705
|
-
}
|
|
706
|
-
async createPluginVersion(pluginId, data) {
|
|
707
|
-
return (await this.request("POST", `/plugins/${pluginId}/versions`, { body: data })).body;
|
|
708
|
-
}
|
|
709
|
-
async publishPluginVersion(pluginId, versionId) {
|
|
710
|
-
return (await this.request("POST", `/plugins/${pluginId}/versions/${versionId}/publish`)).body;
|
|
711
|
-
}
|
|
712
|
-
async yankPluginVersion(pluginId, versionId, reason) {
|
|
713
|
-
return (await this.request("POST", `/plugins/${pluginId}/versions/${versionId}/yank`, { body: reason ? { changelog: reason } : void 0 })).body;
|
|
714
|
-
}
|
|
715
|
-
async downloadPluginBundle(pluginId, versionId, targetPath) {
|
|
716
|
-
try {
|
|
717
|
-
return await this.downloadFile(`/plugins/${pluginId}/versions/${versionId}/download`, targetPath);
|
|
718
|
-
} catch (error) {
|
|
719
|
-
throw new Error(`Failed to download plugin bundle: ${error.message}`);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
async uploadPlugin(fileBuffer, filename, namespace, options) {
|
|
723
|
-
const form = new FormData();
|
|
724
|
-
form.append("file", new Blob([new Uint8Array(fileBuffer)]), filename);
|
|
725
|
-
form.append("namespace", namespace);
|
|
726
|
-
if (options?.visibility) form.append("visibility", options.visibility);
|
|
727
|
-
if (options?.summary) form.append("summary", options.summary);
|
|
728
|
-
if (options?.changelog) form.append("changelog", options.changelog);
|
|
729
|
-
return (await this.request("POST", "/plugins/upload", { body: form })).body;
|
|
730
|
-
}
|
|
731
|
-
async listMcpServers(params) {
|
|
732
|
-
const body = (await this.request("GET", "/mcp-servers", { params })).body;
|
|
733
|
-
return body.data || body;
|
|
734
|
-
}
|
|
735
|
-
async getMcpServer(mcpId) {
|
|
736
|
-
const body = (await this.request("GET", `/mcp-servers/${mcpId}`)).body;
|
|
737
|
-
return body.data || body;
|
|
738
|
-
}
|
|
739
|
-
async createMcpServer(data) {
|
|
740
|
-
return (await this.request("POST", "/mcp-servers", { body: data })).body;
|
|
741
|
-
}
|
|
742
|
-
async updateMcpServer(mcpId, data) {
|
|
743
|
-
return (await this.request("PUT", `/mcp-servers/${mcpId}`, { body: data })).body;
|
|
744
|
-
}
|
|
745
|
-
async deleteMcpServer(mcpId) {
|
|
746
|
-
return (await this.request("DELETE", `/mcp-servers/${mcpId}`)).body;
|
|
747
|
-
}
|
|
748
|
-
async getMcpConfig(mcpId) {
|
|
749
|
-
return (await this.request("GET", `/mcp-servers/${mcpId}/config`)).body.data?.config ?? "";
|
|
750
|
-
}
|
|
751
726
|
async gitParse(repositoryUrl, branch, directory) {
|
|
752
727
|
const body = {
|
|
753
728
|
repositoryUrl,
|
|
@@ -907,142 +882,6 @@ var SkillHubAPI = class {
|
|
|
907
882
|
});
|
|
908
883
|
}
|
|
909
884
|
};
|
|
910
|
-
var config_exports = /* @__PURE__ */ __exportAll({
|
|
911
|
-
GLOBAL_CONFIG_DIR: () => GLOBAL_CONFIG_DIR,
|
|
912
|
-
GLOBAL_CONFIG_FILE: () => GLOBAL_CONFIG_FILE,
|
|
913
|
-
LOCK_FILE: () => LOCK_FILE,
|
|
914
|
-
PROJECT_CONFIG_FILE: () => PROJECT_CONFIG_FILE,
|
|
915
|
-
getConfigValue: () => getConfigValue,
|
|
916
|
-
loadGlobalConfig: () => loadGlobalConfig,
|
|
917
|
-
loadLockfile: () => loadLockfile,
|
|
918
|
-
loadProjectConfig: () => loadProjectConfig,
|
|
919
|
-
resolveConfig: () => resolveConfig,
|
|
920
|
-
saveGlobalConfig: () => saveGlobalConfig,
|
|
921
|
-
saveLockfile: () => saveLockfile,
|
|
922
|
-
saveProjectConfig: () => saveProjectConfig,
|
|
923
|
-
setConfigValue: () => setConfigValue
|
|
924
|
-
});
|
|
925
|
-
const GLOBAL_CONFIG_DIR = path$1.join(os$1.homedir(), ".bfi");
|
|
926
|
-
const GLOBAL_CONFIG_FILE = path$1.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
927
|
-
const PROJECT_CONFIG_FILE = ".bfirc";
|
|
928
|
-
const LOCK_FILE_DIR = ".bfi";
|
|
929
|
-
const LOCK_FILE = path$1.join(LOCK_FILE_DIR, "lock.json");
|
|
930
|
-
function resolveConfig(globalOpts = {}) {
|
|
931
|
-
return {
|
|
932
|
-
registry: getConfigValue("registry", globalOpts) || "https://bfi.yyuap.com:811",
|
|
933
|
-
token: getConfigValue("token", globalOpts) || null,
|
|
934
|
-
namespace: getConfigValue("namespace", globalOpts) || "global",
|
|
935
|
-
ide: getConfigValue("ide", globalOpts) || null
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
function loadGlobalConfig() {
|
|
939
|
-
try {
|
|
940
|
-
if (fs.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
941
|
-
const content = fs.readFileSync(GLOBAL_CONFIG_FILE, "utf8");
|
|
942
|
-
return JSON.parse(content);
|
|
943
|
-
}
|
|
944
|
-
} catch (error) {
|
|
945
|
-
console.warn(`Warning: Failed to load global config: ${error.message}`);
|
|
946
|
-
}
|
|
947
|
-
return null;
|
|
948
|
-
}
|
|
949
|
-
function saveGlobalConfig(config) {
|
|
950
|
-
try {
|
|
951
|
-
if (!fs.existsSync(GLOBAL_CONFIG_DIR)) fs.mkdirSync(GLOBAL_CONFIG_DIR, {
|
|
952
|
-
recursive: true,
|
|
953
|
-
mode: 448
|
|
954
|
-
});
|
|
955
|
-
const content = JSON.stringify(config, null, 2);
|
|
956
|
-
fs.writeFileSync(GLOBAL_CONFIG_FILE, content, { mode: 384 });
|
|
957
|
-
return true;
|
|
958
|
-
} catch (error) {
|
|
959
|
-
throw new Error(`Failed to save global config: ${error.message}`);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
function loadProjectConfig(workdir) {
|
|
963
|
-
try {
|
|
964
|
-
const configPath = path$1.join(workdir, PROJECT_CONFIG_FILE);
|
|
965
|
-
if (fs.existsSync(configPath)) {
|
|
966
|
-
const content = fs.readFileSync(configPath, "utf8");
|
|
967
|
-
return JSON.parse(content);
|
|
968
|
-
}
|
|
969
|
-
} catch (error) {
|
|
970
|
-
console.warn(`Warning: Failed to load project config: ${error.message}`);
|
|
971
|
-
}
|
|
972
|
-
return null;
|
|
973
|
-
}
|
|
974
|
-
function saveProjectConfig(workdir, config) {
|
|
975
|
-
try {
|
|
976
|
-
const configPath = path$1.join(workdir, PROJECT_CONFIG_FILE);
|
|
977
|
-
const content = JSON.stringify(config, null, 2);
|
|
978
|
-
fs.writeFileSync(configPath, content);
|
|
979
|
-
return true;
|
|
980
|
-
} catch (error) {
|
|
981
|
-
throw new Error(`Failed to save project config: ${error.message}`);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
function loadLockfile(workdir) {
|
|
985
|
-
try {
|
|
986
|
-
const lockPath = path$1.join(workdir, LOCK_FILE);
|
|
987
|
-
if (fs.existsSync(lockPath)) {
|
|
988
|
-
const content = fs.readFileSync(lockPath, "utf8");
|
|
989
|
-
return JSON.parse(content);
|
|
990
|
-
}
|
|
991
|
-
} catch (error) {
|
|
992
|
-
console.warn(`Warning: Failed to load lockfile: ${error.message}`);
|
|
993
|
-
}
|
|
994
|
-
return {
|
|
995
|
-
version: 1,
|
|
996
|
-
skills: {},
|
|
997
|
-
packages: {}
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
function saveLockfile(workdir, lock) {
|
|
1001
|
-
try {
|
|
1002
|
-
const lockDir = path$1.join(workdir, LOCK_FILE_DIR);
|
|
1003
|
-
if (!fs.existsSync(lockDir)) fs.mkdirSync(lockDir, { recursive: true });
|
|
1004
|
-
const lockPath = path$1.join(workdir, LOCK_FILE);
|
|
1005
|
-
const content = JSON.stringify(lock, null, 2);
|
|
1006
|
-
fs.writeFileSync(lockPath, content);
|
|
1007
|
-
return true;
|
|
1008
|
-
} catch (error) {
|
|
1009
|
-
throw new Error(`Failed to save lockfile: ${error.message}`);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
function getConfigValue(key, globalOpts = {}) {
|
|
1013
|
-
const projectConfig = loadProjectConfig(globalOpts.workdir || process.cwd());
|
|
1014
|
-
const globalConfig = loadGlobalConfig();
|
|
1015
|
-
const configMap = {
|
|
1016
|
-
registry: globalOpts.registry || projectConfig && projectConfig.registry || globalConfig && globalConfig.registry || null,
|
|
1017
|
-
token: globalOpts.token || projectConfig && projectConfig.token || globalConfig && globalConfig.token || null,
|
|
1018
|
-
namespace: globalOpts.namespace || projectConfig && projectConfig.namespace || globalConfig && globalConfig.defaultNamespace || "global",
|
|
1019
|
-
ide: globalOpts.ide || projectConfig && projectConfig.ide || globalConfig && globalConfig.defaultIDE || null
|
|
1020
|
-
};
|
|
1021
|
-
return key ? configMap[key] : void 0;
|
|
1022
|
-
}
|
|
1023
|
-
function setConfigValue(key, value, isGlobal = false) {
|
|
1024
|
-
if (isGlobal) {
|
|
1025
|
-
const config = loadGlobalConfig() || { version: 1 };
|
|
1026
|
-
const configKey = {
|
|
1027
|
-
"registry": "registry",
|
|
1028
|
-
"token": "token",
|
|
1029
|
-
"default-namespace": "defaultNamespace",
|
|
1030
|
-
"default-ide": "defaultIDE"
|
|
1031
|
-
}[key] || key;
|
|
1032
|
-
config[configKey] = value;
|
|
1033
|
-
saveGlobalConfig(config);
|
|
1034
|
-
} else {
|
|
1035
|
-
const workdir = process.cwd();
|
|
1036
|
-
const config = loadProjectConfig(workdir) || {};
|
|
1037
|
-
const configKey = {
|
|
1038
|
-
"registry": "registry",
|
|
1039
|
-
"namespace": "namespace",
|
|
1040
|
-
"ide": "ide"
|
|
1041
|
-
}[key] || key;
|
|
1042
|
-
config[configKey] = value;
|
|
1043
|
-
saveProjectConfig(workdir, config);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
885
|
var skill_ref_exports = /* @__PURE__ */ __exportAll({
|
|
1047
886
|
parseSkillRef: () => parseSkillRef,
|
|
1048
887
|
toCanonicalSlug: () => toCanonicalSlug,
|
|
@@ -1458,10 +1297,12 @@ async function isDirEntryOrSymlinkToDir(entry, entryPath) {
|
|
|
1458
1297
|
}
|
|
1459
1298
|
}
|
|
1460
1299
|
function getCanonicalSkillsDir(global, cwd) {
|
|
1461
|
-
|
|
1300
|
+
const baseDir = global ? homedir() : cwd || process.cwd();
|
|
1301
|
+
return join(baseDir, AGENTS_DIR, SKILLS_SUBDIR);
|
|
1462
1302
|
}
|
|
1463
1303
|
function getEveSubagentSkillsDir(subagent, cwd) {
|
|
1464
|
-
|
|
1304
|
+
const baseDir = cwd || process.cwd();
|
|
1305
|
+
return join(baseDir, EVE_SUBAGENTS_DIR, sanitizeName(subagent), "skills");
|
|
1465
1306
|
}
|
|
1466
1307
|
function getAgentBaseDir(agentType, global, cwd, eveSubagent) {
|
|
1467
1308
|
if (isUniversalAgent(agentType)) return getCanonicalSkillsDir(global, cwd);
|
|
@@ -1539,7 +1380,8 @@ async function resolveParentSymlinks(path) {
|
|
|
1539
1380
|
const dir = dirname(resolved);
|
|
1540
1381
|
const base = basename(resolved);
|
|
1541
1382
|
try {
|
|
1542
|
-
|
|
1383
|
+
const realDir = await realpath(dir);
|
|
1384
|
+
return join(realDir, base);
|
|
1543
1385
|
} catch {
|
|
1544
1386
|
return resolved;
|
|
1545
1387
|
}
|
|
@@ -1563,7 +1405,8 @@ async function createSymlink(target, linkPath) {
|
|
|
1563
1405
|
}
|
|
1564
1406
|
const linkDir = dirname(linkPath);
|
|
1565
1407
|
await mkdir(linkDir, { recursive: true });
|
|
1566
|
-
const
|
|
1408
|
+
const realLinkDir = await resolveParentSymlinks(linkDir);
|
|
1409
|
+
const relativePath = relative(realLinkDir, target);
|
|
1567
1410
|
const symlinkType = platform() === "win32" ? "junction" : void 0;
|
|
1568
1411
|
await symlink(symlinkType === "junction" ? resolvedTarget : relativePath, linkPath, symlinkType);
|
|
1569
1412
|
return true;
|
|
@@ -1632,7 +1475,8 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
1632
1475
|
mode: "symlink"
|
|
1633
1476
|
};
|
|
1634
1477
|
if (!isGlobal && !isUniversalAgent(agentType)) {
|
|
1635
|
-
|
|
1478
|
+
const agentRootDir = join(cwd, agents[agentType].skillsDir.split("/")[0]);
|
|
1479
|
+
if (!existsSync(agentRootDir) && agentType !== "claude-code") return {
|
|
1636
1480
|
success: true,
|
|
1637
1481
|
path: canonicalDir,
|
|
1638
1482
|
canonicalPath: canonicalDir,
|
|
@@ -1706,7 +1550,8 @@ async function copyDirectory(src, dest, agentType) {
|
|
|
1706
1550
|
dereference: true,
|
|
1707
1551
|
recursive: true
|
|
1708
1552
|
});
|
|
1709
|
-
|
|
1553
|
+
const sourceStats = await stat(srcPath);
|
|
1554
|
+
await chmod(destPath, sourceStats.mode & 511);
|
|
1710
1555
|
} catch (err) {
|
|
1711
1556
|
if (err instanceof Error && "code" in err && err.code === "ENOENT" && entry.isSymbolicLink()) console.warn(`Skipping broken symlink: ${srcPath}`);
|
|
1712
1557
|
else throw err;
|
|
@@ -1923,7 +1768,8 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
|
|
|
1923
1768
|
mode: "symlink"
|
|
1924
1769
|
};
|
|
1925
1770
|
if (!isGlobal && !isUniversalAgent(agentType)) {
|
|
1926
|
-
|
|
1771
|
+
const agentRootDir = join(cwd, agents[agentType].skillsDir.split("/")[0]);
|
|
1772
|
+
if (!existsSync(agentRootDir)) return {
|
|
1927
1773
|
success: true,
|
|
1928
1774
|
path: canonicalDir,
|
|
1929
1775
|
canonicalPath: canonicalDir,
|
|
@@ -2151,7 +1997,7 @@ var ProviderRegistryImpl = class {
|
|
|
2151
1997
|
};
|
|
2152
1998
|
new ProviderRegistryImpl();
|
|
2153
1999
|
const DISCOVERY_SCHEMA_V2 = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
|
|
2154
|
-
const MAX_ARCHIVE_UNPACKED_BYTES =
|
|
2000
|
+
const MAX_ARCHIVE_UNPACKED_BYTES = 52428800;
|
|
2155
2001
|
const MAX_ARCHIVE_FILES = 1e3;
|
|
2156
2002
|
var WellKnownProvider = class {
|
|
2157
2003
|
id = "well-known";
|
|
@@ -3304,22 +3150,23 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
3304
3150
|
const installedAgents = await detectInstalledAgents();
|
|
3305
3151
|
const totalAgents = Object.keys(agents).length;
|
|
3306
3152
|
spinner.stop(`${totalAgents} agents`);
|
|
3307
|
-
if (installedAgents.length === 0)
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3153
|
+
if (installedAgents.length === 0) {
|
|
3154
|
+
if (options.yes) {
|
|
3155
|
+
targetAgents = validAgents;
|
|
3156
|
+
log.info("正在安装到全部 Agent");
|
|
3157
|
+
} else {
|
|
3158
|
+
log.info("请选择要安装技能的 Agent");
|
|
3159
|
+
const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).map(([key, config]) => ({
|
|
3160
|
+
value: key,
|
|
3161
|
+
label: config.displayName
|
|
3162
|
+
})));
|
|
3163
|
+
if (isCancel(selected)) {
|
|
3164
|
+
cancel("安装已取消");
|
|
3165
|
+
process.exit(0);
|
|
3166
|
+
}
|
|
3167
|
+
targetAgents = selected;
|
|
3319
3168
|
}
|
|
3320
|
-
|
|
3321
|
-
}
|
|
3322
|
-
else if (installedAgents.length === 1 || options.yes) {
|
|
3169
|
+
} else if (installedAgents.length === 1 || options.yes) {
|
|
3323
3170
|
targetAgents = ensureUniversalAgents(installedAgents);
|
|
3324
3171
|
if (installedAgents.length === 1) {
|
|
3325
3172
|
const firstAgent = installedAgents[0];
|
|
@@ -3513,7 +3360,7 @@ async function cloneWithGitHubFallback(url, ref, spinner) {
|
|
|
3513
3360
|
}
|
|
3514
3361
|
async function installFromSkillHub(parsed, options, spinner) {
|
|
3515
3362
|
let tempDir = null;
|
|
3516
|
-
const config = resolveConfig();
|
|
3363
|
+
const config = resolveConfig(options);
|
|
3517
3364
|
const api = new SkillHubAPI(config.registry, config.token);
|
|
3518
3365
|
const rawRef = parsed.url.startsWith("skillhub:") ? parsed.url.slice(9) : parsed.url;
|
|
3519
3366
|
const [firstPart, ...restParts] = rawRef.split("/");
|
|
@@ -3554,13 +3401,14 @@ async function installFromSkillHub(parsed, options, spinner) {
|
|
|
3554
3401
|
slug = hit.slug;
|
|
3555
3402
|
log.info(`自动选择:${import_picocolors.default.cyan(`@${namespace}/${slug}`)} — ${hit.description || hit.name}`);
|
|
3556
3403
|
} else {
|
|
3404
|
+
const choices = hits.map((h, idx) => ({
|
|
3405
|
+
value: idx,
|
|
3406
|
+
label: `@${h.namespace}/${h.slug}`,
|
|
3407
|
+
hint: h.description || h.name
|
|
3408
|
+
}));
|
|
3557
3409
|
const idx = await select({
|
|
3558
3410
|
message: "匹配到多个技能 — 请选择一个",
|
|
3559
|
-
options:
|
|
3560
|
-
value: idx,
|
|
3561
|
-
label: `@${h.namespace}/${h.slug}`,
|
|
3562
|
-
hint: h.description || h.name
|
|
3563
|
-
}))
|
|
3411
|
+
options: choices
|
|
3564
3412
|
});
|
|
3565
3413
|
if (isCancel(idx)) {
|
|
3566
3414
|
cancel("安装已取消");
|
|
@@ -3587,7 +3435,8 @@ async function installFromSkillHub(parsed, options, spinner) {
|
|
|
3587
3435
|
}
|
|
3588
3436
|
const meta = metadata;
|
|
3589
3437
|
spinner.stop(`Skill: ${import_picocolors.default.cyan(`@${namespace}/${slug}`)}${meta.description ? ` — ${import_picocolors.default.dim(meta.description)}` : ""}`);
|
|
3590
|
-
|
|
3438
|
+
const tmpRoot = tmpdir();
|
|
3439
|
+
tempDir = join(tmpRoot, `.skillhub-skillshub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
|
|
3591
3440
|
mkdirSync(tempDir, { recursive: true });
|
|
3592
3441
|
const zipPath = join(tempDir, "skill.zip");
|
|
3593
3442
|
spinner.start("正在下载技能...");
|
|
@@ -3638,6 +3487,21 @@ async function installFromSkillHub(parsed, options, spinner) {
|
|
|
3638
3487
|
} catch {}
|
|
3639
3488
|
process.exit(1);
|
|
3640
3489
|
}
|
|
3490
|
+
if (options.packageSkill) {
|
|
3491
|
+
const { scopePackageSkill } = await import("./_chunks/package-skill-identity.mjs").then((n) => n.package_skill_identity_exports);
|
|
3492
|
+
try {
|
|
3493
|
+
return {
|
|
3494
|
+
skills: scopePackageSkill(skills, namespace, slug),
|
|
3495
|
+
tempDir
|
|
3496
|
+
};
|
|
3497
|
+
} catch (error) {
|
|
3498
|
+
rmSync(tempDir, {
|
|
3499
|
+
recursive: true,
|
|
3500
|
+
force: true
|
|
3501
|
+
});
|
|
3502
|
+
throw error;
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3641
3505
|
return {
|
|
3642
3506
|
skills,
|
|
3643
3507
|
tempDir
|
|
@@ -3908,23 +3772,24 @@ async function runAdd(args, options = {}) {
|
|
|
3908
3772
|
}
|
|
3909
3773
|
targetAgents = selected;
|
|
3910
3774
|
}
|
|
3911
|
-
} else if (installedAgents.length === 0)
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3775
|
+
} else if (installedAgents.length === 0) {
|
|
3776
|
+
if (options.yes) {
|
|
3777
|
+
targetAgents = validAgents;
|
|
3778
|
+
log.info("正在安装到全部 Agent");
|
|
3779
|
+
} else {
|
|
3780
|
+
log.info("请选择要安装技能的 Agent");
|
|
3781
|
+
const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).filter(([key]) => key !== "eve").map(([key, config]) => ({
|
|
3782
|
+
value: key,
|
|
3783
|
+
label: config.displayName
|
|
3784
|
+
})));
|
|
3785
|
+
if (isCancel(selected)) {
|
|
3786
|
+
cancel("安装已取消");
|
|
3787
|
+
await cleanup(tempDir);
|
|
3788
|
+
process.exit(0);
|
|
3789
|
+
}
|
|
3790
|
+
targetAgents = selected;
|
|
3924
3791
|
}
|
|
3925
|
-
|
|
3926
|
-
}
|
|
3927
|
-
else if (installedAgents.length === 1 || options.yes) {
|
|
3792
|
+
} else if (installedAgents.length === 1 || options.yes) {
|
|
3928
3793
|
targetAgents = ensureUniversalAgents(installedAgents);
|
|
3929
3794
|
if (installedAgents.length === 1) {
|
|
3930
3795
|
const firstAgent = installedAgents[0];
|
|
@@ -3946,17 +3811,18 @@ async function runAdd(args, options = {}) {
|
|
|
3946
3811
|
const availableSubagents = getEveSubagents(process.cwd());
|
|
3947
3812
|
if (options.subagent && options.subagent.length > 0) eveSubagentTargets = options.subagent.map((s) => s === "root" || s === "." ? void 0 : s);
|
|
3948
3813
|
else if (availableSubagents.length > 0 && !options.yes) {
|
|
3814
|
+
const subagentChoices = [{
|
|
3815
|
+
value: "",
|
|
3816
|
+
label: "根 Agent",
|
|
3817
|
+
hint: "agent/skills"
|
|
3818
|
+
}, ...availableSubagents.map((name) => ({
|
|
3819
|
+
value: name,
|
|
3820
|
+
label: name,
|
|
3821
|
+
hint: `agent/subagents/${name}/skills`
|
|
3822
|
+
}))];
|
|
3949
3823
|
const selectedSubagents = await multiselect$1({
|
|
3950
3824
|
message: "Eve 技能安装位置?",
|
|
3951
|
-
options:
|
|
3952
|
-
value: "",
|
|
3953
|
-
label: "根 Agent",
|
|
3954
|
-
hint: "agent/skills"
|
|
3955
|
-
}, ...availableSubagents.map((name) => ({
|
|
3956
|
-
value: name,
|
|
3957
|
-
label: name,
|
|
3958
|
-
hint: `agent/subagents/${name}/skills`
|
|
3959
|
-
}))],
|
|
3825
|
+
options: subagentChoices,
|
|
3960
3826
|
initialValues: [""],
|
|
3961
3827
|
required: true
|
|
3962
3828
|
});
|
|
@@ -4134,9 +4000,11 @@ async function runAdd(args, options = {}) {
|
|
|
4134
4000
|
const normalizedSource = getOwnerRepo(parsed);
|
|
4135
4001
|
const lockSource = getLockSource(parsed.url, normalizedSource);
|
|
4136
4002
|
const projectLockSourceUrl = getProjectLockSourceUrl(parsed.type, parsed.url);
|
|
4137
|
-
if (normalizedSource)
|
|
4138
|
-
if (
|
|
4139
|
-
|
|
4003
|
+
if (normalizedSource) {
|
|
4004
|
+
if (parseOwnerRepo(normalizedSource)) {
|
|
4005
|
+
if (await repoPrivacyPromise === false) selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
|
|
4006
|
+
} else selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
|
|
4007
|
+
}
|
|
4140
4008
|
if (successful.length > 0 && installGlobally && (normalizedSource || parsed.type === "skillhub")) {
|
|
4141
4009
|
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4142
4010
|
let cachedTree;
|
|
@@ -4153,7 +4021,8 @@ async function runAdd(args, options = {}) {
|
|
|
4153
4021
|
const hash = getSkillFolderHashFromTree(cachedTree, skillPathValue);
|
|
4154
4022
|
if (hash) skillFolderHash = hash;
|
|
4155
4023
|
} else if (skillPathValue && tempDir) {
|
|
4156
|
-
const
|
|
4024
|
+
const skillDir = join(tempDir, dirname(skillPathValue));
|
|
4025
|
+
const hash = await computeSkillFolderHash(skillDir);
|
|
4157
4026
|
if (hash) skillFolderHash = hash;
|
|
4158
4027
|
}
|
|
4159
4028
|
await addSkillToLock(skill.name, {
|
|
@@ -4197,11 +4066,13 @@ async function runAdd(args, options = {}) {
|
|
|
4197
4066
|
const skillResults = bySkill.get(r.skill) || [];
|
|
4198
4067
|
skillResults.push(r);
|
|
4199
4068
|
bySkill.set(r.skill, skillResults);
|
|
4200
|
-
if (skillResults.length === 1)
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4069
|
+
if (skillResults.length === 1) {
|
|
4070
|
+
if (r.pluginName) {
|
|
4071
|
+
const group = r.pluginName;
|
|
4072
|
+
if (!groupedResults[group]) groupedResults[group] = [];
|
|
4073
|
+
groupedResults[group].push(r);
|
|
4074
|
+
} else ungroupedResults.push(r);
|
|
4075
|
+
}
|
|
4205
4076
|
}
|
|
4206
4077
|
const skillCount = bySkill.size;
|
|
4207
4078
|
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
@@ -4372,10 +4243,10 @@ function parseAddOptions(args) {
|
|
|
4372
4243
|
errors
|
|
4373
4244
|
};
|
|
4374
4245
|
}
|
|
4375
|
-
const RESET$
|
|
4376
|
-
const BOLD$
|
|
4377
|
-
const DIM$
|
|
4378
|
-
const TEXT$
|
|
4246
|
+
const RESET$11 = "\x1B[0m";
|
|
4247
|
+
const BOLD$8 = "\x1B[1m";
|
|
4248
|
+
const DIM$11 = "\x1B[38;5;102m";
|
|
4249
|
+
const TEXT$8 = "\x1B[38;5;145m";
|
|
4379
4250
|
const CYAN$2 = "\x1B[36m";
|
|
4380
4251
|
const SEARCH_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
4381
4252
|
function formatInstalls$1(count) {
|
|
@@ -4464,28 +4335,28 @@ async function runSearchPrompt(initialQuery = "", owner) {
|
|
|
4464
4335
|
if (lastRenderedLines > 0) process.stdout.write(MOVE_UP(lastRenderedLines) + MOVE_TO_COL(1));
|
|
4465
4336
|
process.stdout.write(CLEAR_DOWN);
|
|
4466
4337
|
const lines = [];
|
|
4467
|
-
const cursor = `${BOLD$
|
|
4468
|
-
lines.push(`${TEXT$
|
|
4338
|
+
const cursor = `${BOLD$8}_${RESET$11}`;
|
|
4339
|
+
lines.push(`${TEXT$8}搜索技能:${RESET$11} ${query}${cursor}`);
|
|
4469
4340
|
lines.push("");
|
|
4470
|
-
if (!query || query.length < 2) lines.push(`${DIM$
|
|
4471
|
-
else if (results.length === 0 && loading) lines.push(`${DIM$
|
|
4472
|
-
else if (results.length === 0) lines.push(`${DIM$
|
|
4341
|
+
if (!query || query.length < 2) lines.push(`${DIM$11}输入关键词搜索(最少 2 个字符)${RESET$11}`);
|
|
4342
|
+
else if (results.length === 0 && loading) lines.push(`${DIM$11}搜索中...${RESET$11}`);
|
|
4343
|
+
else if (results.length === 0) lines.push(`${DIM$11}未找到技能${RESET$11}`);
|
|
4473
4344
|
else {
|
|
4474
4345
|
const visible = results.slice(0, 8);
|
|
4475
4346
|
for (let i = 0; i < visible.length; i++) {
|
|
4476
4347
|
const skill = visible[i];
|
|
4477
4348
|
const isSelected = i === selectedIndex;
|
|
4478
|
-
const arrow = isSelected ? `${BOLD$
|
|
4479
|
-
const name = isSelected ? `${BOLD$
|
|
4480
|
-
const source = skill.source ? ` ${DIM$
|
|
4349
|
+
const arrow = isSelected ? `${BOLD$8}>${RESET$11}` : " ";
|
|
4350
|
+
const name = isSelected ? `${BOLD$8}${skill.name}${RESET$11}` : `${TEXT$8}${skill.name}${RESET$11}`;
|
|
4351
|
+
const source = skill.source ? ` ${DIM$11}${skill.source}${RESET$11}` : "";
|
|
4481
4352
|
const installs = formatInstalls$1(skill.installs);
|
|
4482
|
-
const installsBadge = installs ? ` ${CYAN$2}${installs}${RESET$
|
|
4483
|
-
const loadingIndicator = loading && i === 0 ? ` ${DIM$
|
|
4353
|
+
const installsBadge = installs ? ` ${CYAN$2}${installs}${RESET$11}` : "";
|
|
4354
|
+
const loadingIndicator = loading && i === 0 ? ` ${DIM$11}...${RESET$11}` : "";
|
|
4484
4355
|
lines.push(` ${arrow} ${name}${source}${installsBadge}${loadingIndicator}`);
|
|
4485
4356
|
}
|
|
4486
4357
|
}
|
|
4487
4358
|
lines.push("");
|
|
4488
|
-
lines.push(`${DIM$
|
|
4359
|
+
lines.push(`${DIM$11}上下键导航 | 回车选择 | ESC 取消${RESET$11}`);
|
|
4489
4360
|
for (const line of lines) process.stdout.write(line + "\n");
|
|
4490
4361
|
lastRenderedLines = lines.length;
|
|
4491
4362
|
}
|
|
@@ -4582,9 +4453,9 @@ async function runFind(args) {
|
|
|
4582
4453
|
const { query, options: findOptions, errors } = parseFindOptions(args);
|
|
4583
4454
|
const owner = findOptions.owner;
|
|
4584
4455
|
const isNonInteractive = !process.stdin.isTTY;
|
|
4585
|
-
const agentTip = `${DIM$
|
|
4586
|
-
${DIM$
|
|
4587
|
-
${DIM$
|
|
4456
|
+
const agentTip = `${DIM$11}Tip: if running in a coding agent, follow these steps:${RESET$11}
|
|
4457
|
+
${DIM$11} 1) npx skills find [query] [--owner <owner>]${RESET$11}
|
|
4458
|
+
${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
4588
4459
|
if (errors.length > 0) {
|
|
4589
4460
|
for (const error of errors) console.error(error);
|
|
4590
4461
|
console.error("Usage: npx skills find <query> [--owner <owner>]");
|
|
@@ -4595,16 +4466,16 @@ ${DIM$13} 2) npx skills add <owner/repo@skill>${RESET$13}`;
|
|
|
4595
4466
|
String(results.length);
|
|
4596
4467
|
if (results.length === 0) {
|
|
4597
4468
|
const ownerSuffix = owner ? ` from owner "${owner}"` : "";
|
|
4598
|
-
console.log(`${DIM$
|
|
4469
|
+
console.log(`${DIM$11}未找到 "${query}" 的技能${ownerSuffix}${RESET$11}`);
|
|
4599
4470
|
return;
|
|
4600
4471
|
}
|
|
4601
|
-
console.log(`${DIM$
|
|
4472
|
+
console.log(`${DIM$11}安装:${RESET$11} npx skills add <owner/repo@skill>`);
|
|
4602
4473
|
console.log();
|
|
4603
4474
|
for (const skill of results.slice(0, 6)) {
|
|
4604
4475
|
const pkg = skill.source || skill.slug;
|
|
4605
4476
|
const installs = formatInstalls$1(skill.installs);
|
|
4606
|
-
console.log(`${TEXT$
|
|
4607
|
-
console.log(`${DIM$
|
|
4477
|
+
console.log(`${TEXT$8}${pkg}@${skill.name}${RESET$11}${installs ? ` ${CYAN$2}${installs}${RESET$11}` : ""}`);
|
|
4478
|
+
console.log(`${DIM$11}└ https://skills.sh/${skill.slug}${RESET$11}`);
|
|
4608
4479
|
console.log();
|
|
4609
4480
|
}
|
|
4610
4481
|
return;
|
|
@@ -4612,19 +4483,19 @@ ${DIM$13} 2) npx skills add <owner/repo@skill>${RESET$13}`;
|
|
|
4612
4483
|
if (isNonInteractive || await isRunningInAgent()) {
|
|
4613
4484
|
console.log(agentTip);
|
|
4614
4485
|
console.log();
|
|
4615
|
-
console.log(`${DIM$
|
|
4486
|
+
console.log(`${DIM$11}Usage: npx skills find <query> [--owner <owner>]${RESET$11}`);
|
|
4616
4487
|
return;
|
|
4617
4488
|
}
|
|
4618
4489
|
const selected = await runSearchPrompt("", owner);
|
|
4619
4490
|
if (!selected) {
|
|
4620
|
-
console.log(`${DIM$
|
|
4491
|
+
console.log(`${DIM$11}搜索已取消${RESET$11}`);
|
|
4621
4492
|
console.log();
|
|
4622
4493
|
return;
|
|
4623
4494
|
}
|
|
4624
4495
|
const pkg = selected.source || selected.slug;
|
|
4625
4496
|
const skillName = selected.name;
|
|
4626
4497
|
console.log();
|
|
4627
|
-
console.log(`${TEXT$
|
|
4498
|
+
console.log(`${TEXT$8}正在从 ${DIM$11}${pkg}${RESET$11} 安装 ${BOLD$8}${skillName}${RESET$11}...`);
|
|
4628
4499
|
console.log();
|
|
4629
4500
|
const { source, options: addOptions } = parseAddOptions([
|
|
4630
4501
|
pkg,
|
|
@@ -4634,13 +4505,13 @@ ${DIM$13} 2) npx skills add <owner/repo@skill>${RESET$13}`;
|
|
|
4634
4505
|
await runAdd(source, addOptions);
|
|
4635
4506
|
console.log();
|
|
4636
4507
|
const info = getOwnerRepoFromString(pkg);
|
|
4637
|
-
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$
|
|
4638
|
-
else console.log(`${DIM$
|
|
4508
|
+
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$11}查看技能:${RESET$11} ${TEXT$8}https://skills.sh/${selected.slug}${RESET$11}`);
|
|
4509
|
+
else console.log(`${DIM$11}发现更多技能:${RESET$11} ${TEXT$8}https://skills.sh${RESET$11}`);
|
|
4639
4510
|
console.log();
|
|
4640
4511
|
}
|
|
4641
|
-
const RESET$
|
|
4642
|
-
const BOLD$
|
|
4643
|
-
const DIM$
|
|
4512
|
+
const RESET$10 = "\x1B[0m";
|
|
4513
|
+
const BOLD$7 = "\x1B[1m";
|
|
4514
|
+
const DIM$10 = "\x1B[38;5;102m";
|
|
4644
4515
|
const CYAN$1 = "\x1B[36m";
|
|
4645
4516
|
const YELLOW$4 = "\x1B[33m";
|
|
4646
4517
|
function shortenPath(fullPath, cwd) {
|
|
@@ -4676,8 +4547,8 @@ async function runList(args) {
|
|
|
4676
4547
|
const validAgents = Object.keys(agents);
|
|
4677
4548
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
4678
4549
|
if (invalidAgents.length > 0) {
|
|
4679
|
-
console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$
|
|
4680
|
-
console.log(`${DIM$
|
|
4550
|
+
console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$10}`);
|
|
4551
|
+
console.log(`${DIM$10}Valid agents: ${validAgents.join(", ")}${RESET$10}`);
|
|
4681
4552
|
process.exit(1);
|
|
4682
4553
|
}
|
|
4683
4554
|
agentFilter = options.agent;
|
|
@@ -4704,21 +4575,21 @@ async function runList(args) {
|
|
|
4704
4575
|
console.log("[]");
|
|
4705
4576
|
return;
|
|
4706
4577
|
}
|
|
4707
|
-
console.log(`${DIM$
|
|
4708
|
-
if (scope) console.log(`${DIM$
|
|
4709
|
-
else console.log(`${DIM$
|
|
4578
|
+
console.log(`${DIM$10}未找到${scopeLabel}技能。${RESET$10}`);
|
|
4579
|
+
if (scope) console.log(`${DIM$10}尝试不带 -g 参数列出项目技能${RESET$10}`);
|
|
4580
|
+
else console.log(`${DIM$10}尝试带 -g 参数列出全局技能${RESET$10}`);
|
|
4710
4581
|
return;
|
|
4711
4582
|
}
|
|
4712
4583
|
function printSkill(skill, indent = false, maxNameLength = 0, maxPathLength = 0) {
|
|
4713
4584
|
const prefix = indent ? " " : "";
|
|
4714
4585
|
const shortPath = shortenPath(skill.canonicalPath, cwd);
|
|
4715
4586
|
const agentNames = skill.agents.map((a) => agents[a].displayName);
|
|
4716
|
-
const agentInfo = skill.agents.length > 0 ? formatList$1(agentNames) : `${YELLOW$4}not linked${RESET$
|
|
4587
|
+
const agentInfo = skill.agents.length > 0 ? formatList$1(agentNames) : `${YELLOW$4}not linked${RESET$10}`;
|
|
4717
4588
|
const paddedName = sanitizeMetadata(skill.name).padEnd(maxNameLength);
|
|
4718
4589
|
const paddedPath = shortPath.padEnd(maxPathLength);
|
|
4719
|
-
console.log(`${prefix}${CYAN$1}${paddedName}${RESET$
|
|
4590
|
+
console.log(`${prefix}${CYAN$1}${paddedName}${RESET$10} ${DIM$10}${paddedPath}${RESET$10} ${DIM$10}Agents:${RESET$10} ${agentInfo}`);
|
|
4720
4591
|
}
|
|
4721
|
-
console.log(`${BOLD$
|
|
4592
|
+
console.log(`${BOLD$7}${scopeLabel}技能${RESET$10}`);
|
|
4722
4593
|
console.log();
|
|
4723
4594
|
const groupedSkills = {};
|
|
4724
4595
|
const ungroupedSkills = [];
|
|
@@ -4734,7 +4605,7 @@ async function runList(args) {
|
|
|
4734
4605
|
const sortedGroups = Object.keys(groupedSkills).sort();
|
|
4735
4606
|
for (const group of sortedGroups) {
|
|
4736
4607
|
const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
4737
|
-
console.log(`${BOLD$
|
|
4608
|
+
console.log(`${BOLD$7}${title}${RESET$10}`);
|
|
4738
4609
|
const skills = groupedSkills[group];
|
|
4739
4610
|
if (skills) {
|
|
4740
4611
|
let maxNameLength = 0;
|
|
@@ -4750,7 +4621,7 @@ async function runList(args) {
|
|
|
4750
4621
|
console.log();
|
|
4751
4622
|
}
|
|
4752
4623
|
if (ungroupedSkills.length > 0) {
|
|
4753
|
-
console.log(`${BOLD$
|
|
4624
|
+
console.log(`${BOLD$7}通用${RESET$10}`);
|
|
4754
4625
|
let maxNameLength = 0;
|
|
4755
4626
|
let maxPathLength = 0;
|
|
4756
4627
|
for (const skill of ungroupedSkills) {
|
|
@@ -4810,7 +4681,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4810
4681
|
spinner$2.stop(`发现 ${installedSkills.length} 个已安装技能`);
|
|
4811
4682
|
if (installedSkills.length === 0) {
|
|
4812
4683
|
outro(import_picocolors.default.yellow("没有可卸载的技能。"));
|
|
4813
|
-
return;
|
|
4684
|
+
return { removed: [] };
|
|
4814
4685
|
}
|
|
4815
4686
|
if (options.agent && options.agent.length > 0) {
|
|
4816
4687
|
const validAgents = Object.keys(agents);
|
|
@@ -4827,7 +4698,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4827
4698
|
selectedSkills = installedSkills.filter((s) => skillNames.some((name) => name.toLowerCase() === s.toLowerCase()));
|
|
4828
4699
|
if (selectedSkills.length === 0) {
|
|
4829
4700
|
log.error(`未找到匹配技能:${skillNames.join(", ")}`);
|
|
4830
|
-
return;
|
|
4701
|
+
return { removed: [] };
|
|
4831
4702
|
}
|
|
4832
4703
|
} else {
|
|
4833
4704
|
const choices = installedSkills.map((s) => ({
|
|
@@ -4891,17 +4762,21 @@ async function removeCommand(skillNames, options) {
|
|
|
4891
4762
|
});
|
|
4892
4763
|
} catch (err) {
|
|
4893
4764
|
log.warn(`Could not remove skill from ${agent.displayName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4765
|
+
throw err;
|
|
4894
4766
|
}
|
|
4895
4767
|
}
|
|
4896
4768
|
}
|
|
4897
4769
|
const remainingAgents = (await detectInstalledAgents()).filter((a) => !targetAgents.includes(a));
|
|
4898
4770
|
let isStillUsed = false;
|
|
4899
|
-
for (const agentKey of remainingAgents)
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4771
|
+
for (const agentKey of remainingAgents) {
|
|
4772
|
+
const path = getInstallPath(skillName, agentKey, {
|
|
4773
|
+
global: isGlobal,
|
|
4774
|
+
cwd
|
|
4775
|
+
});
|
|
4776
|
+
if (await lstat(path).catch(() => null)) {
|
|
4777
|
+
isStillUsed = true;
|
|
4778
|
+
break;
|
|
4779
|
+
}
|
|
4905
4780
|
}
|
|
4906
4781
|
if (!isStillUsed) await rm(canonicalPath, {
|
|
4907
4782
|
recursive: true,
|
|
@@ -4945,6 +4820,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4945
4820
|
}
|
|
4946
4821
|
console.log();
|
|
4947
4822
|
outro(import_picocolors.default.green("完成!"));
|
|
4823
|
+
return { removed: successful.map((result) => result.skill) };
|
|
4948
4824
|
}
|
|
4949
4825
|
function parseRemoveOptions(args) {
|
|
4950
4826
|
const options = {};
|
|
@@ -5024,10 +4900,10 @@ function buildLocalUpdateSource(entry) {
|
|
|
5024
4900
|
return appendFolderAndRef(source, entry.skillPath, entry.ref);
|
|
5025
4901
|
}
|
|
5026
4902
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
5027
|
-
const RESET$
|
|
5028
|
-
const BOLD$
|
|
5029
|
-
const DIM$
|
|
5030
|
-
const TEXT$
|
|
4903
|
+
const RESET$9 = "\x1B[0m";
|
|
4904
|
+
const BOLD$6 = "\x1B[1m";
|
|
4905
|
+
const DIM$9 = "\x1B[38;5;102m";
|
|
4906
|
+
const TEXT$7 = "\x1B[38;5;145m";
|
|
5031
4907
|
function parseUpdateOptions(args) {
|
|
5032
4908
|
const options = {};
|
|
5033
4909
|
const positional = [];
|
|
@@ -5040,12 +4916,14 @@ function parseUpdateOptions(args) {
|
|
|
5040
4916
|
}
|
|
5041
4917
|
function hasProjectSkills(cwd) {
|
|
5042
4918
|
const dir = cwd || process.cwd();
|
|
5043
|
-
|
|
4919
|
+
const lockPath = join(dir, "skills-lock.json");
|
|
4920
|
+
if (existsSync(lockPath)) return true;
|
|
5044
4921
|
const skillsDir = join(dir, ".agents", "skills");
|
|
5045
4922
|
try {
|
|
5046
4923
|
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
5047
4924
|
for (const entry of entries) if (entry.isDirectory()) {
|
|
5048
|
-
|
|
4925
|
+
const skillMd = join(skillsDir, entry.name, "SKILL.md");
|
|
4926
|
+
if (existsSync(skillMd)) return true;
|
|
5049
4927
|
}
|
|
5050
4928
|
} catch {}
|
|
5051
4929
|
return false;
|
|
@@ -5120,7 +4998,7 @@ function getInstallSource(skill) {
|
|
|
5120
4998
|
function printSkippedSkills(skipped) {
|
|
5121
4999
|
if (skipped.length === 0) return;
|
|
5122
5000
|
console.log();
|
|
5123
|
-
console.log(`${DIM$
|
|
5001
|
+
console.log(`${DIM$9}${skipped.length} 个技能无法自动检查:${RESET$9}`);
|
|
5124
5002
|
const grouped = /* @__PURE__ */ new Map();
|
|
5125
5003
|
for (const skill of skipped) {
|
|
5126
5004
|
const source = getInstallSource(skill);
|
|
@@ -5131,13 +5009,13 @@ function printSkippedSkills(skipped) {
|
|
|
5131
5009
|
for (const [source, skills] of grouped) {
|
|
5132
5010
|
if (skills.length === 1) {
|
|
5133
5011
|
const skill = skills[0];
|
|
5134
|
-
console.log(` ${TEXT$
|
|
5012
|
+
console.log(` ${TEXT$7}•${RESET$9} ${sanitizeMetadata(skill.name)} ${DIM$9}(${skill.reason})${RESET$9}`);
|
|
5135
5013
|
} else {
|
|
5136
5014
|
const reason = skills[0].reason;
|
|
5137
5015
|
const names = skills.map((s) => sanitizeMetadata(s.name)).join(", ");
|
|
5138
|
-
console.log(` ${TEXT$
|
|
5016
|
+
console.log(` ${TEXT$7}•${RESET$9} ${names} ${DIM$9}(${reason})${RESET$9}`);
|
|
5139
5017
|
}
|
|
5140
|
-
console.log(` ${DIM$
|
|
5018
|
+
console.log(` ${DIM$9}更新方法:${TEXT$7}npx skills add ${source} -g -y${RESET$9}`);
|
|
5141
5019
|
}
|
|
5142
5020
|
}
|
|
5143
5021
|
async function getProjectSkillsForUpdate(skillFilter) {
|
|
@@ -5162,13 +5040,13 @@ async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills
|
|
|
5162
5040
|
});
|
|
5163
5041
|
if (deletedSkills.length > 0) {
|
|
5164
5042
|
console.log();
|
|
5165
|
-
console.log(`${DIM$
|
|
5166
|
-
for (const s of deletedSkills) console.log(` ${DIM$
|
|
5167
|
-
if (options.yes || !process.stdin.isTTY) console.log(`${DIM$
|
|
5043
|
+
console.log(`${DIM$9}警告:${RESET$9} 以下来自 ${DIM$9}${source}${RESET$9} 的技能似乎已在源仓库中删除:`);
|
|
5044
|
+
for (const s of deletedSkills) console.log(` ${DIM$9}•${RESET$9} ${s}`);
|
|
5045
|
+
if (options.yes || !process.stdin.isTTY) console.log(`${DIM$9}非交互模式,跳过删除。${RESET$9}`);
|
|
5168
5046
|
else {
|
|
5169
5047
|
const confirmed = await confirm({ message: `是否删除这些已删除技能的本地副本?` });
|
|
5170
5048
|
if (confirmed && !isCancel(confirmed)) for (const s of deletedSkills) {
|
|
5171
|
-
console.log(`${DIM$
|
|
5049
|
+
console.log(`${DIM$9}正在卸载${RESET$9} ${s}...`);
|
|
5172
5050
|
await removeCommand([s], {
|
|
5173
5051
|
yes: true,
|
|
5174
5052
|
global: isGlobal
|
|
@@ -5185,8 +5063,8 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5185
5063
|
let failCount = 0;
|
|
5186
5064
|
if (skillNames.length === 0) {
|
|
5187
5065
|
if (!options.skills) {
|
|
5188
|
-
console.log(`${DIM$
|
|
5189
|
-
console.log(`${DIM$
|
|
5066
|
+
console.log(`${DIM$9}锁文件中没有跟踪的全局技能。${RESET$9}`);
|
|
5067
|
+
console.log(`${DIM$9}安装技能:${RESET$9} ${TEXT$7}npx skills add <package> -g${RESET$9}`);
|
|
5190
5068
|
}
|
|
5191
5069
|
return {
|
|
5192
5070
|
successCount,
|
|
@@ -5227,17 +5105,17 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5227
5105
|
const firstEntry = itemsForSource[0].entry;
|
|
5228
5106
|
const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
|
|
5229
5107
|
let tempDir = null;
|
|
5230
|
-
process.stdout.write(`\r${DIM$
|
|
5108
|
+
process.stdout.write(`\r${DIM$9}Checking skills from source: ${source}${RESET$9}\x1b[K\n`);
|
|
5231
5109
|
if (firstEntry.sourceType === "skillhub") {
|
|
5232
5110
|
const skillhubRef = source.replace(/^skillhub:/, "");
|
|
5233
5111
|
const { parseSkillRef, toCanonicalSlug } = await Promise.resolve().then(() => skill_ref_exports);
|
|
5234
5112
|
const { SkillHubAPI } = await Promise.resolve().then(() => api_exports);
|
|
5235
|
-
const { resolveConfig } = await
|
|
5113
|
+
const { resolveConfig } = await import("./_chunks/config.mjs").then((n) => n.config_exports);
|
|
5236
5114
|
const cfg = resolveConfig({});
|
|
5237
5115
|
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5238
5116
|
for (const item of itemsForSource) {
|
|
5239
5117
|
const safeName = sanitizeMetadata(item.name);
|
|
5240
|
-
console.log(`${TEXT$
|
|
5118
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
5241
5119
|
try {
|
|
5242
5120
|
const parts = skillhubRef.split("/");
|
|
5243
5121
|
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
@@ -5248,7 +5126,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5248
5126
|
});
|
|
5249
5127
|
successCount++;
|
|
5250
5128
|
} catch (error) {
|
|
5251
|
-
console.log(` ${DIM$
|
|
5129
|
+
console.log(` ${DIM$9}✗ ${safeName}: ${error.message}${RESET$9}`);
|
|
5252
5130
|
failCount++;
|
|
5253
5131
|
}
|
|
5254
5132
|
}
|
|
@@ -5258,7 +5136,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5258
5136
|
if (firstEntry.sourceType === "github") {
|
|
5259
5137
|
const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
|
|
5260
5138
|
if (!tree) {
|
|
5261
|
-
console.log(` ${DIM$
|
|
5139
|
+
console.log(` ${DIM$9}✗ Failed to fetch tree for ${source}${RESET$9}`);
|
|
5262
5140
|
continue;
|
|
5263
5141
|
}
|
|
5264
5142
|
const discoveredPaths = findSkillMdPaths(tree);
|
|
@@ -5293,7 +5171,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5293
5171
|
});
|
|
5294
5172
|
}
|
|
5295
5173
|
} catch (error) {
|
|
5296
|
-
console.log(` ${DIM$
|
|
5174
|
+
console.log(` ${DIM$9}✗ Failed to check skills from ${source}${RESET$9}`);
|
|
5297
5175
|
} finally {
|
|
5298
5176
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
5299
5177
|
}
|
|
@@ -5301,7 +5179,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5301
5179
|
if (checkable.length > 0) process.stdout.write("\r\x1B[K");
|
|
5302
5180
|
const checkedCount = checkable.length + skipped.length;
|
|
5303
5181
|
if (checkable.length === 0 && skipped.length === 0) {
|
|
5304
|
-
if (!options.skills) console.log(`${DIM$
|
|
5182
|
+
if (!options.skills) console.log(`${DIM$9}没有可检查的全局技能。${RESET$9}`);
|
|
5305
5183
|
return {
|
|
5306
5184
|
successCount,
|
|
5307
5185
|
failCount,
|
|
@@ -5317,28 +5195,28 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5317
5195
|
};
|
|
5318
5196
|
}
|
|
5319
5197
|
if (updates.length === 0) {
|
|
5320
|
-
console.log(`${TEXT$
|
|
5198
|
+
console.log(`${TEXT$7}✓ 所有全局技能已是最新${RESET$9}`);
|
|
5321
5199
|
return {
|
|
5322
5200
|
successCount,
|
|
5323
5201
|
failCount,
|
|
5324
5202
|
checkedCount
|
|
5325
5203
|
};
|
|
5326
5204
|
}
|
|
5327
|
-
console.log(`${TEXT$
|
|
5205
|
+
console.log(`${TEXT$7}发现 ${updates.length} 个可更新全局技能${RESET$9}`);
|
|
5328
5206
|
console.log();
|
|
5329
5207
|
for (const update of updates) {
|
|
5330
5208
|
const safeName = sanitizeMetadata(update.name);
|
|
5331
|
-
console.log(`${TEXT$
|
|
5209
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
5332
5210
|
const installUrl = buildUpdateInstallSource(update.entry);
|
|
5333
5211
|
if (!installUrl) {
|
|
5334
5212
|
failCount++;
|
|
5335
|
-
console.log(` ${DIM$
|
|
5213
|
+
console.log(` ${DIM$9}✗ Cannot update ${safeName}: lock file is missing sourceUrl for this generic Git source${RESET$9}`);
|
|
5336
5214
|
continue;
|
|
5337
5215
|
}
|
|
5338
5216
|
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
5339
5217
|
if (!existsSync(cliEntry)) {
|
|
5340
5218
|
failCount++;
|
|
5341
|
-
console.log(` ${DIM$
|
|
5219
|
+
console.log(` ${DIM$9}✗ Failed to update ${safeName}: CLI entrypoint not found at ${cliEntry}${RESET$9}`);
|
|
5342
5220
|
continue;
|
|
5343
5221
|
}
|
|
5344
5222
|
if (spawnSync(process.execPath, [
|
|
@@ -5357,10 +5235,10 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5357
5235
|
shell: false
|
|
5358
5236
|
}).status === 0) {
|
|
5359
5237
|
successCount++;
|
|
5360
|
-
console.log(` ${TEXT$
|
|
5238
|
+
console.log(` ${TEXT$7}✓${RESET$9} 已更新 ${safeName}`);
|
|
5361
5239
|
} else {
|
|
5362
5240
|
failCount++;
|
|
5363
|
-
console.log(` ${DIM$
|
|
5241
|
+
console.log(` ${DIM$9}✗ 更新 ${safeName} 失败${RESET$9}`);
|
|
5364
5242
|
}
|
|
5365
5243
|
}
|
|
5366
5244
|
printSkippedSkills(skipped);
|
|
@@ -5376,8 +5254,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5376
5254
|
let failCount = 0;
|
|
5377
5255
|
if (projectSkills.length === 0) {
|
|
5378
5256
|
if (!options.skills) {
|
|
5379
|
-
console.log(`${DIM$
|
|
5380
|
-
console.log(`${DIM$
|
|
5257
|
+
console.log(`${DIM$9}没有可更新的项目技能。${RESET$9}`);
|
|
5258
|
+
console.log(`${DIM$9}Install project skills with${RESET$9} ${TEXT$7}npx skills add <package>${RESET$9}`);
|
|
5381
5259
|
}
|
|
5382
5260
|
return {
|
|
5383
5261
|
successCount,
|
|
@@ -5388,7 +5266,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5388
5266
|
const updatable = projectSkills.filter((s) => s.entry.skillPath);
|
|
5389
5267
|
const legacy = projectSkills.filter((s) => !s.entry.skillPath);
|
|
5390
5268
|
if (updatable.length === 0) {
|
|
5391
|
-
console.log(`${DIM$
|
|
5269
|
+
console.log(`${DIM$9}No project skills can be updated in place.${RESET$9}`);
|
|
5392
5270
|
printLegacyProjectSkills(legacy);
|
|
5393
5271
|
return {
|
|
5394
5272
|
successCount,
|
|
@@ -5408,8 +5286,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5408
5286
|
const targetParts = [];
|
|
5409
5287
|
if (hasUniversal) targetParts.push("Universal");
|
|
5410
5288
|
targetParts.push(...targetAgentNames);
|
|
5411
|
-
if (targetParts.length > 0) console.log(`${TEXT$
|
|
5412
|
-
console.log(`${TEXT$
|
|
5289
|
+
if (targetParts.length > 0) console.log(`${TEXT$7}更新目标:${targetParts.join(", ")}${RESET$9}`);
|
|
5290
|
+
console.log(`${TEXT$7}正在刷新 ${updatable.length} 个技能...${RESET$9}`);
|
|
5413
5291
|
console.log();
|
|
5414
5292
|
const bySource = /* @__PURE__ */ new Map();
|
|
5415
5293
|
for (const skill of updatable) {
|
|
@@ -5421,7 +5299,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5421
5299
|
const localLock = await readLocalLock();
|
|
5422
5300
|
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
5423
5301
|
if (!existsSync(cliEntry)) {
|
|
5424
|
-
console.log(`${DIM$
|
|
5302
|
+
console.log(`${DIM$9}✗ CLI 入口未找到 ${cliEntry}${RESET$9}`);
|
|
5425
5303
|
return {
|
|
5426
5304
|
successCount,
|
|
5427
5305
|
failCount: updatable.length,
|
|
@@ -5436,12 +5314,12 @@ async function updateProjectSkills(options = {}) {
|
|
|
5436
5314
|
const skillhubRef = source.replace(/^skillhub:/, "");
|
|
5437
5315
|
const { parseSkillRef, toCanonicalSlug } = await Promise.resolve().then(() => skill_ref_exports);
|
|
5438
5316
|
const { SkillHubAPI } = await Promise.resolve().then(() => api_exports);
|
|
5439
|
-
const { resolveConfig } = await
|
|
5317
|
+
const { resolveConfig } = await import("./_chunks/config.mjs").then((n) => n.config_exports);
|
|
5440
5318
|
const cfg = resolveConfig({});
|
|
5441
5319
|
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5442
5320
|
for (const item of skillsForSource) {
|
|
5443
5321
|
const safeName = sanitizeMetadata(item.name);
|
|
5444
|
-
console.log(`${TEXT$
|
|
5322
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
5445
5323
|
try {
|
|
5446
5324
|
const parts = skillhubRef.split("/");
|
|
5447
5325
|
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
@@ -5452,7 +5330,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5452
5330
|
});
|
|
5453
5331
|
successCount++;
|
|
5454
5332
|
} catch (error) {
|
|
5455
|
-
console.log(` ${DIM$
|
|
5333
|
+
console.log(` ${DIM$9}✗ ${safeName}: ${error.message}${RESET$9}`);
|
|
5456
5334
|
failCount++;
|
|
5457
5335
|
}
|
|
5458
5336
|
}
|
|
@@ -5463,28 +5341,29 @@ async function updateProjectSkills(options = {}) {
|
|
|
5463
5341
|
let deletedSkills = [];
|
|
5464
5342
|
if (buildLocalUpdateSource(firstEntry) === null) {
|
|
5465
5343
|
failCount += skillsForSource.length;
|
|
5466
|
-
console.log(`${DIM$
|
|
5344
|
+
console.log(`${DIM$9}✗ Cannot update ${source}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$9}`);
|
|
5467
5345
|
continue;
|
|
5468
5346
|
}
|
|
5469
5347
|
try {
|
|
5470
5348
|
tempDir = await cloneRepo(sourceUrl, ref);
|
|
5471
5349
|
const discoveredPaths = (await discoverSkills(tempDir)).map((s) => {
|
|
5472
|
-
|
|
5350
|
+
const relPath = relative(tempDir, s.path);
|
|
5351
|
+
return join(relPath, "SKILL.md").split(sep).join("/");
|
|
5473
5352
|
});
|
|
5474
5353
|
deletedSkills = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredPaths);
|
|
5475
5354
|
} catch (error) {
|
|
5476
|
-
console.log(`${DIM$
|
|
5355
|
+
console.log(`${DIM$9}✗ 检查 ${source} 已删除技能失败${RESET$9}`);
|
|
5477
5356
|
} finally {
|
|
5478
5357
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
5479
5358
|
}
|
|
5480
5359
|
const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
|
|
5481
5360
|
for (const skill of remainingSkills) {
|
|
5482
5361
|
const safeName = sanitizeMetadata(skill.name);
|
|
5483
|
-
console.log(`${TEXT$
|
|
5362
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
5484
5363
|
const installUrl = buildLocalUpdateSource(skill.entry);
|
|
5485
5364
|
if (!installUrl) {
|
|
5486
5365
|
failCount++;
|
|
5487
|
-
console.log(` ${DIM$
|
|
5366
|
+
console.log(` ${DIM$9}✗ Cannot update ${safeName}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$9}`);
|
|
5488
5367
|
continue;
|
|
5489
5368
|
}
|
|
5490
5369
|
const subagentArgs = skill.entry.subagents?.length ? ["--subagent", ...skill.entry.subagents.map((s) => s === "" ? "root" : s)] : [];
|
|
@@ -5506,10 +5385,10 @@ async function updateProjectSkills(options = {}) {
|
|
|
5506
5385
|
shell: false
|
|
5507
5386
|
}).status === 0) {
|
|
5508
5387
|
successCount++;
|
|
5509
|
-
console.log(` ${TEXT$
|
|
5388
|
+
console.log(` ${TEXT$7}✓${RESET$9} 已更新 ${safeName}`);
|
|
5510
5389
|
} else {
|
|
5511
5390
|
failCount++;
|
|
5512
|
-
console.log(` ${DIM$
|
|
5391
|
+
console.log(` ${DIM$9}✗ 更新 ${safeName} 失败${RESET$9}`);
|
|
5513
5392
|
}
|
|
5514
5393
|
}
|
|
5515
5394
|
}
|
|
@@ -5523,25 +5402,25 @@ async function updateProjectSkills(options = {}) {
|
|
|
5523
5402
|
function printLegacyProjectSkills(legacy) {
|
|
5524
5403
|
if (legacy.length === 0) return;
|
|
5525
5404
|
console.log();
|
|
5526
|
-
console.log(`${DIM$
|
|
5405
|
+
console.log(`${DIM$9}${legacy.length} project skill(s) cannot be updated automatically (installed before skillPath tracking):${RESET$9}`);
|
|
5527
5406
|
for (const skill of legacy) {
|
|
5528
5407
|
const reinstall = buildLocalUpdateSource(skill.entry);
|
|
5529
|
-
console.log(` ${TEXT$
|
|
5530
|
-
if (reinstall) console.log(` ${DIM$
|
|
5531
|
-
else console.log(` ${DIM$
|
|
5408
|
+
console.log(` ${TEXT$7}•${RESET$9} ${sanitizeMetadata(skill.name)}`);
|
|
5409
|
+
if (reinstall) console.log(` ${DIM$9}刷新方法:${TEXT$7}npx skills add ${reinstall} -y${RESET$9}`);
|
|
5410
|
+
else console.log(` ${DIM$9}To refresh: reinstall using the original full Git URL; this lock entry only has an ambiguous shorthand.${RESET$9}`);
|
|
5532
5411
|
}
|
|
5533
5412
|
}
|
|
5534
5413
|
async function runUpdate(args = []) {
|
|
5535
5414
|
const options = parseUpdateOptions(args);
|
|
5536
5415
|
const scope = await resolveUpdateScope(options);
|
|
5537
|
-
if (options.skills) console.log(`${TEXT$
|
|
5538
|
-
else console.log(`${TEXT$
|
|
5416
|
+
if (options.skills) console.log(`${TEXT$7}正在更新 ${options.skills.join(", ")}...${RESET$9}`);
|
|
5417
|
+
else console.log(`${TEXT$7}正在检查技能更新...${RESET$9}`);
|
|
5539
5418
|
console.log();
|
|
5540
5419
|
let totalSuccess = 0;
|
|
5541
5420
|
let totalFail = 0;
|
|
5542
5421
|
let totalFound = 0;
|
|
5543
5422
|
if (scope === "global" || scope === "both") {
|
|
5544
|
-
if (scope === "both" && !options.skills) console.log(`${BOLD$
|
|
5423
|
+
if (scope === "both" && !options.skills) console.log(`${BOLD$6}全局技能${RESET$9}`);
|
|
5545
5424
|
const { successCount, failCount, checkedCount } = await updateGlobalSkills(options);
|
|
5546
5425
|
totalSuccess += successCount;
|
|
5547
5426
|
totalFail += failCount;
|
|
@@ -5549,17 +5428,17 @@ async function runUpdate(args = []) {
|
|
|
5549
5428
|
if (scope === "both" && !options.skills) console.log();
|
|
5550
5429
|
}
|
|
5551
5430
|
if (scope === "project" || scope === "both") {
|
|
5552
|
-
if (scope === "both" && !options.skills) console.log(`${BOLD$
|
|
5431
|
+
if (scope === "both" && !options.skills) console.log(`${BOLD$6}项目技能${RESET$9}`);
|
|
5553
5432
|
const { successCount, failCount, foundCount } = await updateProjectSkills(options);
|
|
5554
5433
|
totalSuccess += successCount;
|
|
5555
5434
|
totalFail += failCount;
|
|
5556
5435
|
totalFound += foundCount;
|
|
5557
5436
|
}
|
|
5558
|
-
if (options.skills && totalFound === 0) console.log(`${DIM$
|
|
5437
|
+
if (options.skills && totalFound === 0) console.log(`${DIM$9}未找到匹配的已安装技能:${options.skills.join(", ")}${RESET$9}`);
|
|
5559
5438
|
console.log();
|
|
5560
|
-
if (totalSuccess > 0) console.log(`${TEXT$
|
|
5439
|
+
if (totalSuccess > 0) console.log(`${TEXT$7}✓ 已更新 ${totalSuccess} 个技能${RESET$9}`);
|
|
5561
5440
|
if (totalFail > 0) {
|
|
5562
|
-
console.log(`${DIM$
|
|
5441
|
+
console.log(`${DIM$9}更新失败 ${totalFail} 个技能${RESET$9}`);
|
|
5563
5442
|
process.exitCode = 1;
|
|
5564
5443
|
}
|
|
5565
5444
|
String(totalSuccess + totalFail);
|
|
@@ -5946,7 +5825,7 @@ async function runLogin(args, globalOpts) {
|
|
|
5946
5825
|
name: String(userInfo.displayName || userInfo.name || ""),
|
|
5947
5826
|
handle: String(userInfo.handle || "")
|
|
5948
5827
|
};
|
|
5949
|
-
saveGlobalConfig(config);
|
|
5828
|
+
saveGlobalConfig$1(config);
|
|
5950
5829
|
console.log(`✓ 已登录为 ${userInfo.displayName || userInfo.name || "Unknown"}`);
|
|
5951
5830
|
console.log(` 注册中心:${registry}`);
|
|
5952
5831
|
} catch (error) {
|
|
@@ -5959,7 +5838,7 @@ async function runLogout() {
|
|
|
5959
5838
|
if (config?.token) {
|
|
5960
5839
|
delete config.token;
|
|
5961
5840
|
delete config.user;
|
|
5962
|
-
saveGlobalConfig(config);
|
|
5841
|
+
saveGlobalConfig$1(config);
|
|
5963
5842
|
console.log("✓ 已登出");
|
|
5964
5843
|
} else console.log("未登录。");
|
|
5965
5844
|
}
|
|
@@ -6011,10 +5890,10 @@ function getOptionValue$3(args, flag) {
|
|
|
6011
5890
|
const idx = args.indexOf(flag);
|
|
6012
5891
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
6013
5892
|
}
|
|
6014
|
-
const RESET$
|
|
6015
|
-
const BOLD$
|
|
6016
|
-
const DIM$
|
|
6017
|
-
const TEXT$
|
|
5893
|
+
const RESET$8 = "\x1B[0m";
|
|
5894
|
+
const BOLD$5 = "\x1B[1m";
|
|
5895
|
+
const DIM$8 = "\x1B[38;5;102m";
|
|
5896
|
+
const TEXT$6 = "\x1B[38;5;145m";
|
|
6018
5897
|
const CYAN = "\x1B[36m";
|
|
6019
5898
|
const MARKETPLACE_API = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
6020
5899
|
async function runSearch(args, globalOpts) {
|
|
@@ -6022,13 +5901,13 @@ async function runSearch(args, globalOpts) {
|
|
|
6022
5901
|
const namespace = args.includes("--namespace") ? getOptionValue$2(args, "--namespace") : void 0;
|
|
6023
5902
|
const json = args.includes("--json");
|
|
6024
5903
|
if (!query || query.trim() === "") {
|
|
6025
|
-
console.log(`${BOLD$
|
|
5904
|
+
console.log(`${BOLD$5}bfi skills search <关键词> [选项]${RESET$8}`);
|
|
6026
5905
|
console.log();
|
|
6027
|
-
console.log(`${DIM$
|
|
6028
|
-
console.log(` bfi skills search react ${DIM$
|
|
6029
|
-
console.log(` bfi skills search react --marketplace ${DIM$
|
|
6030
|
-
console.log(` bfi skills search react --all ${DIM$
|
|
6031
|
-
console.log(` bfi skills search react --json ${DIM$
|
|
5906
|
+
console.log(`${DIM$8}常用命令:${RESET$8}`);
|
|
5907
|
+
console.log(` bfi skills search react ${DIM$8}搜索 SkillHub${RESET$8}`);
|
|
5908
|
+
console.log(` bfi skills search react --marketplace ${DIM$8}搜索 skills.sh${RESET$8}`);
|
|
5909
|
+
console.log(` bfi skills search react --all ${DIM$8}搜索全部${RESET$8}`);
|
|
5910
|
+
console.log(` bfi skills search react --json ${DIM$8}JSON 输出${RESET$8}`);
|
|
6032
5911
|
return;
|
|
6033
5912
|
}
|
|
6034
5913
|
const useMarketplace = args.includes("--marketplace");
|
|
@@ -6038,9 +5917,9 @@ async function runSearch(args, globalOpts) {
|
|
|
6038
5917
|
return;
|
|
6039
5918
|
}
|
|
6040
5919
|
if (searchAll) {
|
|
6041
|
-
console.log(`${BOLD$
|
|
5920
|
+
console.log(`${BOLD$5}=== SkillHub 注册表 ===${RESET$8}\n`);
|
|
6042
5921
|
await searchSkillHub(query, namespace, globalOpts, json);
|
|
6043
|
-
console.log(`\n${BOLD$
|
|
5922
|
+
console.log(`\n${BOLD$5}=== skills.sh 市场 ===${RESET$8}\n`);
|
|
6044
5923
|
await searchMarketplace(query, json);
|
|
6045
5924
|
return;
|
|
6046
5925
|
}
|
|
@@ -6075,7 +5954,7 @@ async function searchSkillHub(query, namespace, globalOpts, json = false) {
|
|
|
6075
5954
|
count: 0,
|
|
6076
5955
|
results: []
|
|
6077
5956
|
}, null, 2));
|
|
6078
|
-
else console.log(`${DIM$
|
|
5957
|
+
else console.log(`${DIM$8}SkillHub 上未找到 "${query}" 的技能${RESET$8}`);
|
|
6079
5958
|
return;
|
|
6080
5959
|
}
|
|
6081
5960
|
if (json) {
|
|
@@ -6102,15 +5981,15 @@ async function searchSkillHub(query, namespace, globalOpts, json = false) {
|
|
|
6102
5981
|
const desc = skill.summary || skill.description || "";
|
|
6103
5982
|
const canonicalSlug = skill.slug || "";
|
|
6104
5983
|
const displaySlug = canonicalSlug.indexOf("--") > 0 ? canonicalSlug : `global--${canonicalSlug}`;
|
|
6105
|
-
console.log(` ${BOLD$
|
|
6106
|
-
if (desc) console.log(` ${DIM$
|
|
6107
|
-
console.log(` ${TEXT$
|
|
6108
|
-
if (skill.version) console.log(` ${DIM$
|
|
6109
|
-
console.log(` ${DIM$
|
|
5984
|
+
console.log(` ${BOLD$5}${name}${RESET$8}`);
|
|
5985
|
+
if (desc) console.log(` ${DIM$8}${truncate$2(desc, 100)}${RESET$8}`);
|
|
5986
|
+
console.log(` ${TEXT$6}slug: ${displaySlug}${RESET$8}`);
|
|
5987
|
+
if (skill.version) console.log(` ${DIM$8}version: ${skill.version}${RESET$8}`);
|
|
5988
|
+
console.log(` ${DIM$8}安装:${TEXT$6}bfi skills add ${displaySlug}${RESET$8}`);
|
|
6110
5989
|
console.log();
|
|
6111
5990
|
}
|
|
6112
5991
|
} catch (error) {
|
|
6113
|
-
console.error(`${DIM$
|
|
5992
|
+
console.error(`${DIM$8}SkillHub 搜索失败:${error.message}${RESET$8}`);
|
|
6114
5993
|
}
|
|
6115
5994
|
}
|
|
6116
5995
|
async function searchMarketplace(query, json = false) {
|
|
@@ -6127,7 +6006,7 @@ async function searchMarketplace(query, json = false) {
|
|
|
6127
6006
|
count: 0,
|
|
6128
6007
|
results: []
|
|
6129
6008
|
}, null, 2));
|
|
6130
|
-
else console.log(`${DIM$
|
|
6009
|
+
else console.log(`${DIM$8}skills.sh 市场无结果${RESET$8}`);
|
|
6131
6010
|
return;
|
|
6132
6011
|
}
|
|
6133
6012
|
const skills = (await res.json()).skills || [];
|
|
@@ -6137,7 +6016,7 @@ async function searchMarketplace(query, json = false) {
|
|
|
6137
6016
|
count: 0,
|
|
6138
6017
|
results: []
|
|
6139
6018
|
}, null, 2));
|
|
6140
|
-
else console.log(`${DIM$
|
|
6019
|
+
else console.log(`${DIM$8}skills.sh 上未找到 "${query}" 的技能${RESET$8}`);
|
|
6141
6020
|
return;
|
|
6142
6021
|
}
|
|
6143
6022
|
if (json) {
|
|
@@ -6158,15 +6037,15 @@ async function searchMarketplace(query, json = false) {
|
|
|
6158
6037
|
console.log(`skills.sh 上找到 ${skills.length} 个技能:\n`);
|
|
6159
6038
|
for (const skill of skills.slice(0, 12)) {
|
|
6160
6039
|
const installs = formatInstalls(skill.installs);
|
|
6161
|
-
console.log(` ${BOLD$
|
|
6162
|
-
if (skill.source) console.log(` ${DIM$
|
|
6163
|
-
if (installs) console.log(` ${CYAN}${installs}${RESET$
|
|
6164
|
-
console.log(` ${TEXT$
|
|
6165
|
-
if (skill.source) console.log(` ${DIM$
|
|
6040
|
+
console.log(` ${BOLD$5}${skill.name}${RESET$8}`);
|
|
6041
|
+
if (skill.source) console.log(` ${DIM$8}来源:${skill.source}${RESET$8}`);
|
|
6042
|
+
if (installs) console.log(` ${CYAN}${installs}${RESET$8}`);
|
|
6043
|
+
console.log(` ${TEXT$6}https://skills.sh/${skill.id}${RESET$8}`);
|
|
6044
|
+
if (skill.source) console.log(` ${DIM$8}安装:${TEXT$6}bfi skills add github:${skill.source}${RESET$8}`);
|
|
6166
6045
|
console.log();
|
|
6167
6046
|
}
|
|
6168
6047
|
} catch {
|
|
6169
|
-
console.log(`${DIM$
|
|
6048
|
+
console.log(`${DIM$8}skills.sh 市场不可用${RESET$8}`);
|
|
6170
6049
|
}
|
|
6171
6050
|
}
|
|
6172
6051
|
function formatInstalls(count) {
|
|
@@ -6183,13 +6062,13 @@ function getOptionValue$2(args, flag) {
|
|
|
6183
6062
|
const idx = args.indexOf(flag);
|
|
6184
6063
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
6185
6064
|
}
|
|
6186
|
-
const RESET$
|
|
6187
|
-
const BOLD$
|
|
6188
|
-
const DIM$
|
|
6189
|
-
const TEXT$
|
|
6065
|
+
const RESET$7 = "\x1B[0m";
|
|
6066
|
+
const BOLD$4 = "\x1B[1m";
|
|
6067
|
+
const DIM$7 = "\x1B[38;5;102m";
|
|
6068
|
+
const TEXT$5 = "\x1B[38;5;145m";
|
|
6190
6069
|
const YELLOW$3 = "\x1B[33m";
|
|
6191
|
-
const RED$
|
|
6192
|
-
const GREEN$
|
|
6070
|
+
const RED$3 = "\x1B[31m";
|
|
6071
|
+
const GREEN$4 = "\x1B[32m";
|
|
6193
6072
|
async function runInfo(args, globalOpts) {
|
|
6194
6073
|
const ref = args[0];
|
|
6195
6074
|
if (!ref) {
|
|
@@ -6205,35 +6084,35 @@ async function runInfo(args, globalOpts) {
|
|
|
6205
6084
|
const result = await api.getSkill(canonical);
|
|
6206
6085
|
const skill = result.data || result.skill || result;
|
|
6207
6086
|
if (!skill || !skill.slug) {
|
|
6208
|
-
console.log(`${DIM$
|
|
6087
|
+
console.log(`${DIM$7}未找到技能:${ref}${RESET$7}`);
|
|
6209
6088
|
process.exit(1);
|
|
6210
6089
|
}
|
|
6211
6090
|
console.log();
|
|
6212
|
-
console.log(`${BOLD$
|
|
6213
|
-
console.log(`${DIM$
|
|
6214
|
-
console.log(` ${BOLD$
|
|
6215
|
-
if (skill.summary || skill.description) console.log(` ${BOLD$
|
|
6091
|
+
console.log(`${BOLD$4}${skill.displayName || skill.name || skill.slug}${RESET$7}`);
|
|
6092
|
+
console.log(`${DIM$7}${"─".repeat(48)}${RESET$7}`);
|
|
6093
|
+
console.log(` ${BOLD$4}Slug:${RESET$7} ${TEXT$5}${skill.slug}${RESET$7}`);
|
|
6094
|
+
if (skill.summary || skill.description) console.log(` ${BOLD$4}描述:${RESET$7} ${TEXT$5}${skill.summary || skill.description}${RESET$7}`);
|
|
6216
6095
|
const owner = skill.owner;
|
|
6217
6096
|
if (owner) {
|
|
6218
|
-
console.log(` ${BOLD$
|
|
6219
|
-
if (owner.handle) console.log(` ${BOLD$
|
|
6097
|
+
console.log(` ${BOLD$4}所有者:${RESET$7} ${TEXT$5}${owner.displayName || owner.handle}${RESET$7}`);
|
|
6098
|
+
if (owner.handle) console.log(` ${BOLD$4}句柄:${RESET$7} ${TEXT$5}${owner.handle}${RESET$7}`);
|
|
6220
6099
|
}
|
|
6221
6100
|
const latestVersion = skill.latestVersion;
|
|
6222
6101
|
if (latestVersion) {
|
|
6223
|
-
console.log(` ${BOLD$
|
|
6224
|
-
if (latestVersion.license) console.log(` ${BOLD$
|
|
6225
|
-
if (latestVersion.changelog) console.log(` ${BOLD$
|
|
6102
|
+
console.log(` ${BOLD$4}版本:${RESET$7} ${TEXT$5}${latestVersion.version || "N/A"}${RESET$7}`);
|
|
6103
|
+
if (latestVersion.license) console.log(` ${BOLD$4}许可证:${RESET$7} ${TEXT$5}${latestVersion.license}${RESET$7}`);
|
|
6104
|
+
if (latestVersion.changelog) console.log(` ${BOLD$4}变更日志:${RESET$7} ${DIM$7}${truncate$1(latestVersion.changelog, 80)}${RESET$7}`);
|
|
6226
6105
|
if (latestVersion.createdAt) {
|
|
6227
6106
|
const date = new Date(latestVersion.createdAt).toISOString().split("T")[0];
|
|
6228
|
-
console.log(` ${BOLD$
|
|
6107
|
+
console.log(` ${BOLD$4}发布时间:${RESET$7} ${TEXT$5}${date}${RESET$7}`);
|
|
6229
6108
|
}
|
|
6230
6109
|
}
|
|
6231
6110
|
const stats = skill.stats;
|
|
6232
6111
|
if (stats) {
|
|
6233
6112
|
console.log();
|
|
6234
|
-
console.log(` ${BOLD$
|
|
6235
|
-
if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$
|
|
6236
|
-
if (stats.views !== void 0) console.log(` 浏览量:${TEXT$
|
|
6113
|
+
console.log(` ${BOLD$4}统计:${RESET$7}`);
|
|
6114
|
+
if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$5}${stats.downloads}${RESET$7}`);
|
|
6115
|
+
if (stats.views !== void 0) console.log(` 浏览量:${TEXT$5}${stats.views}${RESET$7}`);
|
|
6237
6116
|
}
|
|
6238
6117
|
const tags = skill.tags;
|
|
6239
6118
|
if (Array.isArray(tags) && tags.length > 0) {
|
|
@@ -6241,28 +6120,28 @@ async function runInfo(args, globalOpts) {
|
|
|
6241
6120
|
const tag = t;
|
|
6242
6121
|
return tag.name || tag;
|
|
6243
6122
|
}).join(", ");
|
|
6244
|
-
console.log(` ${BOLD$
|
|
6123
|
+
console.log(` ${BOLD$4}标签:${RESET$7} ${TEXT$5}${tagList}${RESET$7}`);
|
|
6245
6124
|
}
|
|
6246
6125
|
const moderation = skill.moderation;
|
|
6247
6126
|
if (moderation) {
|
|
6248
6127
|
const verdict = moderation.verdict;
|
|
6249
6128
|
if (verdict === "MALWARE_BLOCKED" || moderation.isMalwareBlocked) {
|
|
6250
6129
|
console.log();
|
|
6251
|
-
console.log(` ${RED$
|
|
6252
|
-
if (moderation.summary) console.log(` ${RED$
|
|
6130
|
+
console.log(` ${RED$3}${BOLD$4}⚠ 已封禁:检测到恶意软件${RESET$7}`);
|
|
6131
|
+
if (moderation.summary) console.log(` ${RED$3}${moderation.summary}${RESET$7}`);
|
|
6253
6132
|
} else if (verdict === "SUSPICIOUS" || moderation.isSuspicious) {
|
|
6254
6133
|
console.log();
|
|
6255
|
-
console.log(` ${YELLOW$3}⚠ 可疑技能${RESET$
|
|
6256
|
-
if (moderation.summary) console.log(` ${YELLOW$3}${moderation.summary}${RESET$
|
|
6257
|
-
} else if (verdict === "SAFE") console.log(` ${BOLD$
|
|
6134
|
+
console.log(` ${YELLOW$3}⚠ 可疑技能${RESET$7}`);
|
|
6135
|
+
if (moderation.summary) console.log(` ${YELLOW$3}${moderation.summary}${RESET$7}`);
|
|
6136
|
+
} else if (verdict === "SAFE") console.log(` ${BOLD$4}安全:${RESET$7} ${GREEN$4}已验证安全${RESET$7}`);
|
|
6258
6137
|
}
|
|
6259
6138
|
if (skill.createdAt) {
|
|
6260
6139
|
const created = new Date(skill.createdAt).toISOString().split("T")[0];
|
|
6261
|
-
console.log(` ${BOLD$
|
|
6140
|
+
console.log(` ${BOLD$4}创建时间:${RESET$7} ${TEXT$5}${created}${RESET$7}`);
|
|
6262
6141
|
}
|
|
6263
6142
|
if (skill.updatedAt) {
|
|
6264
6143
|
const updated = new Date(skill.updatedAt).toISOString().split("T")[0];
|
|
6265
|
-
console.log(` ${BOLD$
|
|
6144
|
+
console.log(` ${BOLD$4}更新时间:${RESET$7} ${TEXT$5}${updated}${RESET$7}`);
|
|
6266
6145
|
}
|
|
6267
6146
|
console.log();
|
|
6268
6147
|
} catch (error) {
|
|
@@ -6275,8 +6154,8 @@ function truncate$1(text, maxLen) {
|
|
|
6275
6154
|
if (text.length <= maxLen) return text;
|
|
6276
6155
|
return text.slice(0, maxLen) + "...";
|
|
6277
6156
|
}
|
|
6278
|
-
const DIM$
|
|
6279
|
-
const RESET$
|
|
6157
|
+
const DIM$6 = "\x1B[38;5;102m";
|
|
6158
|
+
const RESET$6 = "\x1B[0m";
|
|
6280
6159
|
async function runConfig(args, globalOpts) {
|
|
6281
6160
|
switch (args[0]) {
|
|
6282
6161
|
case "get":
|
|
@@ -6285,6 +6164,9 @@ async function runConfig(args, globalOpts) {
|
|
|
6285
6164
|
case "set":
|
|
6286
6165
|
await handleSet(args, globalOpts);
|
|
6287
6166
|
break;
|
|
6167
|
+
case "analytics":
|
|
6168
|
+
handleAnalytics(args);
|
|
6169
|
+
break;
|
|
6288
6170
|
default: printConfigHelp();
|
|
6289
6171
|
}
|
|
6290
6172
|
}
|
|
@@ -6303,6 +6185,9 @@ function printConfigHelp() {
|
|
|
6303
6185
|
console.log(" namespace Project namespace");
|
|
6304
6186
|
console.log(" ide Project IDE");
|
|
6305
6187
|
console.log();
|
|
6188
|
+
console.log("匿名上报:");
|
|
6189
|
+
console.log(" bfi config analytics on|off|status 匿名安装上报开关(默认开)");
|
|
6190
|
+
console.log();
|
|
6306
6191
|
console.log("标志:");
|
|
6307
6192
|
console.log(" --global 全局应用设置(默认:项目)");
|
|
6308
6193
|
}
|
|
@@ -6371,16 +6256,34 @@ async function handleSet(args, _globalOpts) {
|
|
|
6371
6256
|
}
|
|
6372
6257
|
const isGlobal = args.includes("--global");
|
|
6373
6258
|
setConfigValue(key, value, isGlobal);
|
|
6374
|
-
console.log(`${DIM$
|
|
6259
|
+
console.log(`${DIM$6}✓${RESET$6} 已设置 ${key} = ${value}(${(isGlobal ? "global" : "project") === "global" ? "全局" : "项目"})`);
|
|
6260
|
+
}
|
|
6261
|
+
function handleAnalytics(args) {
|
|
6262
|
+
const action = args[1];
|
|
6263
|
+
if (action === "status") {
|
|
6264
|
+
const enabled = (loadGlobalConfig() || {}).analytics !== false;
|
|
6265
|
+
console.log(`匿名安装上报:${enabled ? "开" : "关"}(默认开)`);
|
|
6266
|
+
console.log("只上报本地随机安装 id 与技能 id,不含用户名、主机名、IP。");
|
|
6267
|
+
console.log("也可用环境变量 BFI_ANALYTICS=off 临时关闭。");
|
|
6268
|
+
return;
|
|
6269
|
+
}
|
|
6270
|
+
if (action !== "on" && action !== "off") {
|
|
6271
|
+
console.error("用法:bfi config analytics on|off|status");
|
|
6272
|
+
process.exit(1);
|
|
6273
|
+
}
|
|
6274
|
+
const config = loadGlobalConfig() || { version: 1 };
|
|
6275
|
+
config.analytics = action === "on";
|
|
6276
|
+
saveGlobalConfig(config);
|
|
6277
|
+
console.log(action === "on" ? "已开启匿名安装上报(bfi config analytics off 可关闭)。" : "已关闭匿名安装上报(bfi config analytics on 可重新开启)。");
|
|
6375
6278
|
}
|
|
6376
|
-
const RESET$
|
|
6377
|
-
const BOLD$
|
|
6378
|
-
const DIM$
|
|
6379
|
-
const TEXT$
|
|
6279
|
+
const RESET$5 = "\x1B[0m";
|
|
6280
|
+
const BOLD$3 = "\x1B[1m";
|
|
6281
|
+
const DIM$5 = "\x1B[38;5;102m";
|
|
6282
|
+
const TEXT$4 = "\x1B[38;5;145m";
|
|
6380
6283
|
function formatFileSize(bytes) {
|
|
6381
6284
|
if (bytes < 1024) return `${bytes} B`;
|
|
6382
|
-
if (bytes <
|
|
6383
|
-
return `${(bytes /
|
|
6285
|
+
if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
6286
|
+
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
6384
6287
|
}
|
|
6385
6288
|
async function runDocs(args, globalOpts) {
|
|
6386
6289
|
switch (args[0]) {
|
|
@@ -6407,11 +6310,11 @@ function printDocsHelp() {
|
|
|
6407
6310
|
console.log(" bfi docs download <id> 下载原始文档文件");
|
|
6408
6311
|
console.log();
|
|
6409
6312
|
console.log("示例:");
|
|
6410
|
-
console.log(` ${DIM$
|
|
6411
|
-
console.log(` ${DIM$
|
|
6412
|
-
console.log(` ${DIM$
|
|
6413
|
-
console.log(` ${DIM$
|
|
6414
|
-
console.log(` ${DIM$
|
|
6313
|
+
console.log(` ${DIM$5}$ ${RESET$5}bfi docs upload notes.md`);
|
|
6314
|
+
console.log(` ${DIM$5}$ ${RESET$5}bfi docs upload bundle.zip`);
|
|
6315
|
+
console.log(` ${DIM$5}$ ${RESET$5}bfi docs list`);
|
|
6316
|
+
console.log(` ${DIM$5}$ ${RESET$5}bfi docs info 42`);
|
|
6317
|
+
console.log(` ${DIM$5}$ ${RESET$5}bfi docs download 42`);
|
|
6415
6318
|
}
|
|
6416
6319
|
async function handleUpload(args, globalOpts) {
|
|
6417
6320
|
const filePath = args[0];
|
|
@@ -6434,18 +6337,18 @@ async function handleUpload(args, globalOpts) {
|
|
|
6434
6337
|
const api = new SkillHubAPI(registry, token);
|
|
6435
6338
|
const fileName = path$1.basename(resolvedPath);
|
|
6436
6339
|
const fileSizeStr = formatFileSize(stats.size);
|
|
6437
|
-
console.log(`${DIM$
|
|
6340
|
+
console.log(`${DIM$5}正在上传 ${fileName} (${fileSizeStr})...${RESET$5}`);
|
|
6438
6341
|
try {
|
|
6439
6342
|
const fileBuffer = fs.readFileSync(resolvedPath);
|
|
6440
6343
|
const result = await api.uploadDoc(fileBuffer, fileName);
|
|
6441
6344
|
const doc = result.data || result;
|
|
6442
|
-
console.log(`${TEXT$
|
|
6345
|
+
console.log(`${TEXT$4}✓ 上传成功${RESET$5}`);
|
|
6443
6346
|
console.log();
|
|
6444
|
-
if (doc.id) console.log(` ${BOLD$
|
|
6445
|
-
if (doc.status) console.log(` ${BOLD$
|
|
6446
|
-
if (doc.fileName) console.log(` ${BOLD$
|
|
6347
|
+
if (doc.id) console.log(` ${BOLD$3}ID:${RESET$5} ${TEXT$4}${doc.id}${RESET$5}`);
|
|
6348
|
+
if (doc.status) console.log(` ${BOLD$3}状态:${RESET$5} ${TEXT$4}${doc.status}${RESET$5}`);
|
|
6349
|
+
if (doc.fileName) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$4}${doc.fileName}${RESET$5}`);
|
|
6447
6350
|
console.log();
|
|
6448
|
-
if (doc.id) console.log(`${DIM$
|
|
6351
|
+
if (doc.id) console.log(`${DIM$5}查看状态:bfi docs info ${doc.id}${RESET$5}`);
|
|
6449
6352
|
} catch (error) {
|
|
6450
6353
|
console.error(`上传失败:${error.message}`);
|
|
6451
6354
|
process.exit(1);
|
|
@@ -6461,14 +6364,14 @@ async function handleList(args, globalOpts) {
|
|
|
6461
6364
|
const data = result.data || result;
|
|
6462
6365
|
const docs = data.items || data.content || data.docs || (Array.isArray(data) ? data : []);
|
|
6463
6366
|
if (!Array.isArray(docs) || docs.length === 0) {
|
|
6464
|
-
console.log(`${DIM$
|
|
6367
|
+
console.log(`${DIM$5}未找到文档。${RESET$5}`);
|
|
6465
6368
|
return;
|
|
6466
6369
|
}
|
|
6467
6370
|
const total = data.total || data.totalElements || result.total || result.totalElements;
|
|
6468
6371
|
console.log();
|
|
6469
|
-
if (total) console.log(`${BOLD$
|
|
6470
|
-
else console.log(`${BOLD$
|
|
6471
|
-
console.log(`${DIM$
|
|
6372
|
+
if (total) console.log(`${BOLD$3}文档${RESET$5}(第 ${page + 1} 页,共 ${total} 条):`);
|
|
6373
|
+
else console.log(`${BOLD$3}文档${RESET$5}(第 ${page + 1} 页,共 ${docs.length} 条结果):`);
|
|
6374
|
+
console.log(`${DIM$5}${"─".repeat(64)}${RESET$5}`);
|
|
6472
6375
|
for (const doc of docs) {
|
|
6473
6376
|
const id = doc.id || "?";
|
|
6474
6377
|
const fileName = doc.fileName || doc.file_name || "unknown";
|
|
@@ -6478,11 +6381,11 @@ async function handleList(args, globalOpts) {
|
|
|
6478
6381
|
const ip = doc.clientIp || "N/A";
|
|
6479
6382
|
const createdAt = doc.createdAt || doc.created_at;
|
|
6480
6383
|
const dateStr = createdAt ? new Date(createdAt).toISOString().split("T")[0] : "N/A";
|
|
6481
|
-
console.log(` ${BOLD$
|
|
6482
|
-
console.log(` ${DIM$
|
|
6384
|
+
console.log(` ${BOLD$3}#${id}${RESET$5} ${TEXT$4}${fileName}${RESET$5}`);
|
|
6385
|
+
console.log(` ${DIM$5}${sizeStr} | ${user} | ${ip} | ${dateStr}${RESET$5}`);
|
|
6483
6386
|
console.log();
|
|
6484
6387
|
}
|
|
6485
|
-
if (total && Number(total) > (page + 1) * size) console.log(`${DIM$
|
|
6388
|
+
if (total && Number(total) > (page + 1) * size) console.log(`${DIM$5}下一页:bfi docs list ${page + 1}${RESET$5}`);
|
|
6486
6389
|
console.log();
|
|
6487
6390
|
} catch (error) {
|
|
6488
6391
|
console.error(`列出文档失败:${error.message}`);
|
|
@@ -6506,46 +6409,46 @@ async function handleInfo(args, globalOpts) {
|
|
|
6506
6409
|
const result = await api.getDoc(id);
|
|
6507
6410
|
const doc = result.data || result;
|
|
6508
6411
|
if (!doc || !doc.id) {
|
|
6509
|
-
console.log(`${DIM$
|
|
6412
|
+
console.log(`${DIM$5}未找到文档:${id}${RESET$5}`);
|
|
6510
6413
|
process.exit(1);
|
|
6511
6414
|
}
|
|
6512
6415
|
const content = doc.content;
|
|
6513
6416
|
const hasContent = typeof content === "string" && content.trim().length > 0;
|
|
6514
6417
|
console.log();
|
|
6515
|
-
console.log(`${BOLD$
|
|
6516
|
-
console.log(`${DIM$
|
|
6517
|
-
if (doc.fileName || doc.file_name) console.log(` ${BOLD$
|
|
6418
|
+
console.log(`${BOLD$3}文档 #${doc.id}${RESET$5}`);
|
|
6419
|
+
console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
|
|
6420
|
+
if (doc.fileName || doc.file_name) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$4}${doc.fileName || doc.file_name}${RESET$5}`);
|
|
6518
6421
|
const fileSize = doc.fileSize || doc.file_size;
|
|
6519
|
-
if (fileSize) console.log(` ${BOLD$
|
|
6422
|
+
if (fileSize) console.log(` ${BOLD$3}大小:${RESET$5} ${TEXT$4}${formatFileSize(Number(fileSize))}${RESET$5}`);
|
|
6520
6423
|
const user = doc.userDisplayName || doc.userId;
|
|
6521
|
-
if (user) console.log(` ${BOLD$
|
|
6522
|
-
if (doc.clientIp) console.log(` ${BOLD$
|
|
6424
|
+
if (user) console.log(` ${BOLD$3}上传者:${RESET$5} ${TEXT$4}${user}${RESET$5}`);
|
|
6425
|
+
if (doc.clientIp) console.log(` ${BOLD$3}IP:${RESET$5} ${TEXT$4}${doc.clientIp}${RESET$5}`);
|
|
6523
6426
|
if (doc.status) {
|
|
6524
|
-
const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$
|
|
6525
|
-
console.log(` ${BOLD$
|
|
6427
|
+
const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$4;
|
|
6428
|
+
console.log(` ${BOLD$3}状态:${RESET$5} ${statusColor}${doc.status}${RESET$5}`);
|
|
6526
6429
|
}
|
|
6527
6430
|
if (doc.createdAt || doc.created_at) {
|
|
6528
6431
|
const date = new Date(doc.createdAt || doc.created_at).toISOString().replace("T", " ").slice(0, 19);
|
|
6529
|
-
console.log(` ${BOLD$
|
|
6432
|
+
console.log(` ${BOLD$3}创建时间:${RESET$5} ${TEXT$4}${date}${RESET$5}`);
|
|
6530
6433
|
}
|
|
6531
6434
|
if (doc.updatedAt || doc.updated_at) {
|
|
6532
6435
|
const date = new Date(doc.updatedAt || doc.updated_at).toISOString().replace("T", " ").slice(0, 19);
|
|
6533
|
-
console.log(` ${BOLD$
|
|
6436
|
+
console.log(` ${BOLD$3}更新时间:${RESET$5} ${TEXT$4}${date}${RESET$5}`);
|
|
6534
6437
|
}
|
|
6535
6438
|
if (doc.error || doc.errorMessage) {
|
|
6536
6439
|
console.log();
|
|
6537
|
-
console.log(` \x1b[31m${BOLD$
|
|
6538
|
-
console.log(` \x1b[31m${doc.error || doc.errorMessage}${RESET$
|
|
6440
|
+
console.log(` \x1b[31m${BOLD$3}错误:${RESET$5}`);
|
|
6441
|
+
console.log(` \x1b[31m${doc.error || doc.errorMessage}${RESET$5}`);
|
|
6539
6442
|
}
|
|
6540
6443
|
if (hasContent) {
|
|
6541
6444
|
console.log();
|
|
6542
|
-
console.log(` ${BOLD$
|
|
6543
|
-
console.log(`${DIM$
|
|
6544
|
-
for (const line of content.split("\n")) console.log(` ${TEXT$
|
|
6545
|
-
console.log(`${DIM$
|
|
6445
|
+
console.log(` ${BOLD$3}内容:${RESET$5}`);
|
|
6446
|
+
console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
|
|
6447
|
+
for (const line of content.split("\n")) console.log(` ${TEXT$4}${line}${RESET$5}`);
|
|
6448
|
+
console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
|
|
6546
6449
|
} else if (doc.content === null || doc.content === void 0) {
|
|
6547
6450
|
console.log();
|
|
6548
|
-
console.log(` ${DIM$
|
|
6451
|
+
console.log(` ${DIM$5}该文件类型不支持内容预览,可用 bfi docs download ${doc.id} 获取原始文件${RESET$5}`);
|
|
6549
6452
|
}
|
|
6550
6453
|
console.log();
|
|
6551
6454
|
} catch (error) {
|
|
@@ -6572,20 +6475,20 @@ async function handleDownload(args, globalOpts) {
|
|
|
6572
6475
|
const safeName = path$1.basename(fileName);
|
|
6573
6476
|
const targetPath = path$1.resolve(safeName);
|
|
6574
6477
|
fs.writeFileSync(targetPath, buffer);
|
|
6575
|
-
console.log(`${TEXT$
|
|
6478
|
+
console.log(`${TEXT$4}✓ 已保存 ${safeName} (${formatFileSize(buffer.length)})${RESET$5}`);
|
|
6576
6479
|
} catch (error) {
|
|
6577
6480
|
if (error.status === 404) console.error(`未找到文档:${id}`);
|
|
6578
6481
|
else console.error(`下载失败:${error.message}`);
|
|
6579
6482
|
process.exit(1);
|
|
6580
6483
|
}
|
|
6581
6484
|
}
|
|
6582
|
-
const RESET$
|
|
6583
|
-
const BOLD$
|
|
6584
|
-
const DIM$
|
|
6585
|
-
const TEXT$
|
|
6586
|
-
const GREEN$
|
|
6485
|
+
const RESET$4 = "\x1B[0m";
|
|
6486
|
+
const BOLD$2 = "\x1B[1m";
|
|
6487
|
+
const DIM$4 = "\x1B[38;5;102m";
|
|
6488
|
+
const TEXT$3 = "\x1B[38;5;145m";
|
|
6489
|
+
const GREEN$3 = "\x1B[32m";
|
|
6587
6490
|
const YELLOW$2 = "\x1B[33m";
|
|
6588
|
-
const RED$
|
|
6491
|
+
const RED$2 = "\x1B[31m";
|
|
6589
6492
|
async function runPublish(args, globalOpts) {
|
|
6590
6493
|
const fromIdx = args.indexOf("--from");
|
|
6591
6494
|
const mode = fromIdx !== -1 ? args[fromIdx + 1] : "dir";
|
|
@@ -6644,25 +6547,26 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
|
|
|
6644
6547
|
console.log("需要包含 frontmatter(name, description)的 SKILL.md 文件。");
|
|
6645
6548
|
process.exit(1);
|
|
6646
6549
|
}
|
|
6647
|
-
const
|
|
6550
|
+
const skillMdContent = fs.readFileSync(skillMdPath, "utf-8");
|
|
6551
|
+
const { data: frontmatter } = parseFrontmatter(skillMdContent);
|
|
6648
6552
|
const skillName = frontmatter.name || path$1.basename(resolvedDir);
|
|
6649
|
-
console.log(`${DIM$
|
|
6650
|
-
console.log(` ${BOLD$
|
|
6651
|
-
console.log(` ${BOLD$
|
|
6652
|
-
console.log(` ${BOLD$
|
|
6653
|
-
console.log(` ${BOLD$
|
|
6553
|
+
console.log(`${DIM$4}正在从目录发布技能...${RESET$4}`);
|
|
6554
|
+
console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
|
|
6555
|
+
console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$3}${namespace}${RESET$4}`);
|
|
6556
|
+
console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$3}${visibility}${RESET$4}`);
|
|
6557
|
+
console.log(` ${BOLD$2}目录:${RESET$4} ${TEXT$3}${resolvedDir}${RESET$4}`);
|
|
6654
6558
|
console.log();
|
|
6655
|
-
console.log(`${DIM$
|
|
6559
|
+
console.log(`${DIM$4}正在创建 zip...${RESET$4}`);
|
|
6656
6560
|
const zipBuffer = createZipFromDir(resolvedDir);
|
|
6657
|
-
console.log(` ${GREEN$
|
|
6658
|
-
console.log(`${DIM$
|
|
6561
|
+
console.log(` ${GREEN$3}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$4}`);
|
|
6562
|
+
console.log(`${DIM$4}正在上传到 SkillHub...${RESET$4}`);
|
|
6659
6563
|
try {
|
|
6660
6564
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6661
|
-
console.log(` ${GREEN$
|
|
6662
|
-
if (res.slug) console.log(` ${BOLD$
|
|
6663
|
-
if (res.url) console.log(` ${BOLD$
|
|
6565
|
+
console.log(` ${GREEN$3}发布成功!${RESET$4}`);
|
|
6566
|
+
if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
|
|
6567
|
+
if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
|
|
6664
6568
|
} catch (error) {
|
|
6665
|
-
console.error(` ${RED$
|
|
6569
|
+
console.error(` ${RED$2}发布失败:${error.message}${RESET$4}`);
|
|
6666
6570
|
process.exit(1);
|
|
6667
6571
|
}
|
|
6668
6572
|
console.log();
|
|
@@ -6679,34 +6583,35 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
|
|
|
6679
6583
|
console.error(`文件未找到:${resolvedZip}`);
|
|
6680
6584
|
process.exit(1);
|
|
6681
6585
|
}
|
|
6682
|
-
console.log(`${DIM$
|
|
6586
|
+
console.log(`${DIM$4}正在解压 zip...${RESET$4}`);
|
|
6683
6587
|
const tmpDir = path$1.join(os$1.tmpdir(), `.bfi-publish-${Date.now()}`);
|
|
6684
6588
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
6685
6589
|
try {
|
|
6686
6590
|
await extractZip(resolvedZip, tmpDir);
|
|
6687
6591
|
const skillMdPath = findSkillMd(tmpDir);
|
|
6688
6592
|
if (!skillMdPath) {
|
|
6689
|
-
console.error(` ${RED$
|
|
6593
|
+
console.error(` ${RED$2}zip 文件中未找到 SKILL.md${RESET$4}`);
|
|
6690
6594
|
process.exit(1);
|
|
6691
6595
|
}
|
|
6692
6596
|
const skillDir = path$1.dirname(skillMdPath);
|
|
6693
|
-
console.log(` ${GREEN$
|
|
6694
|
-
const
|
|
6597
|
+
console.log(` ${GREEN$3}在 ${path$1.relative(tmpDir, skillDir) || "."} 中找到 SKILL.md${RESET$4}`);
|
|
6598
|
+
const skillMdContent = fs.readFileSync(skillMdPath, "utf-8");
|
|
6599
|
+
const { data: frontmatter } = parseFrontmatter(skillMdContent);
|
|
6695
6600
|
const skillName = frontmatter.name || path$1.basename(skillDir);
|
|
6696
|
-
console.log(` ${BOLD$
|
|
6697
|
-
console.log(` ${BOLD$
|
|
6698
|
-
console.log(` ${BOLD$
|
|
6601
|
+
console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
|
|
6602
|
+
console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$3}${namespace}${RESET$4}`);
|
|
6603
|
+
console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$3}${visibility}${RESET$4}`);
|
|
6699
6604
|
console.log();
|
|
6700
|
-
console.log(`${DIM$
|
|
6605
|
+
console.log(`${DIM$4}正在重新打包...${RESET$4}`);
|
|
6701
6606
|
const zipBuffer = createZipFromDir(skillDir);
|
|
6702
|
-
console.log(` ${GREEN$
|
|
6703
|
-
console.log(`${DIM$
|
|
6607
|
+
console.log(` ${GREEN$3}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$4}`);
|
|
6608
|
+
console.log(`${DIM$4}正在上传到 SkillHub...${RESET$4}`);
|
|
6704
6609
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6705
|
-
console.log(` ${GREEN$
|
|
6706
|
-
if (res.slug) console.log(` ${BOLD$
|
|
6707
|
-
if (res.url) console.log(` ${BOLD$
|
|
6610
|
+
console.log(` ${GREEN$3}发布成功!${RESET$4}`);
|
|
6611
|
+
if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
|
|
6612
|
+
if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
|
|
6708
6613
|
} catch (error) {
|
|
6709
|
-
if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$
|
|
6614
|
+
if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$2}错误:${error.message}${RESET$4}`);
|
|
6710
6615
|
process.exit(1);
|
|
6711
6616
|
} finally {
|
|
6712
6617
|
try {
|
|
@@ -6735,18 +6640,18 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6735
6640
|
}
|
|
6736
6641
|
const effectiveBranch = branch || parsedRef || "main";
|
|
6737
6642
|
const effectiveSubpath = directory || null;
|
|
6738
|
-
console.log(`${DIM$
|
|
6739
|
-
console.log(` ${BOLD$
|
|
6740
|
-
console.log(` ${BOLD$
|
|
6643
|
+
console.log(`${DIM$4}正在从 Git 仓库发现技能...${RESET$4}`);
|
|
6644
|
+
console.log(` ${BOLD$2}仓库:${RESET$4} ${TEXT$3}${repoUrl}${RESET$4}`);
|
|
6645
|
+
console.log(` ${BOLD$2}分支:${RESET$4} ${TEXT$3}${effectiveBranch}${RESET$4}`);
|
|
6741
6646
|
console.log();
|
|
6742
6647
|
let tmpDir = null;
|
|
6743
6648
|
let skills = [];
|
|
6744
6649
|
try {
|
|
6745
|
-
console.log(`${DIM$
|
|
6650
|
+
console.log(`${DIM$4}正在克隆仓库...${RESET$4}`);
|
|
6746
6651
|
const { cloneRepo } = await Promise.resolve().then(() => git_exports);
|
|
6747
6652
|
tmpDir = await cloneRepo(repoUrl, effectiveBranch);
|
|
6748
|
-
console.log(` ${GREEN$
|
|
6749
|
-
console.log(`${DIM$
|
|
6653
|
+
console.log(` ${GREEN$3}仓库克隆完成${RESET$4}`);
|
|
6654
|
+
console.log(`${DIM$4}正在发现技能...${RESET$4}`);
|
|
6750
6655
|
const { discoverSkills } = await import("./_chunks/skills.mjs").then((n) => n.skills_exports);
|
|
6751
6656
|
skills = (await discoverSkills(tmpDir, effectiveSubpath || void 0)).map((s) => ({
|
|
6752
6657
|
path: s.path,
|
|
@@ -6754,7 +6659,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6754
6659
|
description: s.description || ""
|
|
6755
6660
|
}));
|
|
6756
6661
|
} catch (error) {
|
|
6757
|
-
console.error(` ${RED$
|
|
6662
|
+
console.error(` ${RED$2}克隆或发现失败:${error.message}${RESET$4}`);
|
|
6758
6663
|
if (tmpDir) try {
|
|
6759
6664
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6760
6665
|
await cleanupTempDir(tmpDir);
|
|
@@ -6762,7 +6667,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6762
6667
|
process.exit(1);
|
|
6763
6668
|
}
|
|
6764
6669
|
if (skills.length === 0) {
|
|
6765
|
-
console.log(`${YELLOW$2}仓库中未发现技能。${RESET$
|
|
6670
|
+
console.log(`${YELLOW$2}仓库中未发现技能。${RESET$4}`);
|
|
6766
6671
|
if (tmpDir) try {
|
|
6767
6672
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6768
6673
|
await cleanupTempDir(tmpDir);
|
|
@@ -6774,7 +6679,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6774
6679
|
const filters = skillFilter.split(",").map((s) => s.trim().toLowerCase());
|
|
6775
6680
|
selectedSkills = skills.filter((s) => filters.some((f) => s.name.toLowerCase().includes(f)));
|
|
6776
6681
|
if (selectedSkills.length === 0) {
|
|
6777
|
-
console.log(`${YELLOW$2}未找到匹配 "${skillFilter}" 的技能${RESET$
|
|
6682
|
+
console.log(`${YELLOW$2}未找到匹配 "${skillFilter}" 的技能${RESET$4}`);
|
|
6778
6683
|
console.log(`可用:${skills.map((s) => s.name).join(", ")}`);
|
|
6779
6684
|
try {
|
|
6780
6685
|
fs.rmSync(tmpDir, {
|
|
@@ -6785,12 +6690,12 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6785
6690
|
process.exit(1);
|
|
6786
6691
|
}
|
|
6787
6692
|
}
|
|
6788
|
-
console.log(`\n${BOLD$
|
|
6693
|
+
console.log(`\n${BOLD$2}发现 ${skills.length} 个技能,将发布 ${selectedSkills.length} 个:${RESET$4}\n`);
|
|
6789
6694
|
for (const skill of selectedSkills) {
|
|
6790
6695
|
const relPath = path$1.relative(tmpDir, skill.path) || ".";
|
|
6791
|
-
console.log(` ${BOLD$
|
|
6792
|
-
console.log(` ${DIM$
|
|
6793
|
-
if (skill.description) console.log(` ${DIM$
|
|
6696
|
+
console.log(` ${BOLD$2}${skill.name}${RESET$4}`);
|
|
6697
|
+
console.log(` ${DIM$4}📁 ${relPath.split("/").join(" ▸ ")}${RESET$4}`);
|
|
6698
|
+
if (skill.description) console.log(` ${DIM$4}${truncate(skill.description, 80)}${RESET$4}`);
|
|
6794
6699
|
console.log();
|
|
6795
6700
|
}
|
|
6796
6701
|
if (!yes) {
|
|
@@ -6809,17 +6714,17 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6809
6714
|
const { createZipFromDir } = await Promise.resolve().then(() => zip_exports);
|
|
6810
6715
|
let successCount = 0;
|
|
6811
6716
|
for (const skill of selectedSkills) {
|
|
6812
|
-
console.log(`${DIM$
|
|
6717
|
+
console.log(`${DIM$4}正在发布 ${skill.name}...${RESET$4}`);
|
|
6813
6718
|
try {
|
|
6814
6719
|
const zipBuffer = createZipFromDir(skill.path);
|
|
6815
6720
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6816
|
-
console.log(` ${GREEN$
|
|
6721
|
+
console.log(` ${GREEN$3}✓ 已发布:${namespace}--${res.slug || skill.name}${RESET$4}`);
|
|
6817
6722
|
successCount++;
|
|
6818
6723
|
} catch (error) {
|
|
6819
|
-
console.error(` ${RED$
|
|
6724
|
+
console.error(` ${RED$2}✗ ${skill.name}:${error.message}${RESET$4}`);
|
|
6820
6725
|
}
|
|
6821
6726
|
}
|
|
6822
|
-
console.log(`\n${GREEN$
|
|
6727
|
+
console.log(`\n${GREEN$3}发布完成:${successCount}/${selectedSkills.length} 个技能${RESET$4}`);
|
|
6823
6728
|
if (tmpDir) try {
|
|
6824
6729
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6825
6730
|
await cleanupTempDir(tmpDir);
|
|
@@ -6861,21 +6766,218 @@ function findSkillMd(dir) {
|
|
|
6861
6766
|
}
|
|
6862
6767
|
function formatSize(bytes) {
|
|
6863
6768
|
if (bytes < 1024) return `${bytes} B`;
|
|
6864
|
-
if (bytes <
|
|
6865
|
-
return `${(bytes /
|
|
6769
|
+
if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
6770
|
+
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
6866
6771
|
}
|
|
6867
6772
|
function truncate(text, maxLen) {
|
|
6868
6773
|
if (text.length <= maxLen) return text;
|
|
6869
6774
|
return text.slice(0, maxLen) + "...";
|
|
6870
6775
|
}
|
|
6776
|
+
var analytics_exports = /* @__PURE__ */ __exportAll({
|
|
6777
|
+
buildPayload: () => buildPayload,
|
|
6778
|
+
getInstallId: () => getInstallId,
|
|
6779
|
+
isCi: () => isCi,
|
|
6780
|
+
maybePrintAnalyticsNotice: () => maybePrintAnalyticsNotice,
|
|
6781
|
+
reportInstall: () => reportInstall,
|
|
6782
|
+
reportInstallEventForSkill: () => reportInstallEventForSkill,
|
|
6783
|
+
shouldReport: () => shouldReport
|
|
6784
|
+
});
|
|
6785
|
+
const INSTALL_ID_FILE = path$1.join(os$1.homedir(), ".bfi", "install-id");
|
|
6786
|
+
const ANALYTICS_NOTICE_FILE = path$1.join(os$1.homedir(), ".bfi", "analytics-notice-shown");
|
|
6787
|
+
function buildPayload(input, installId) {
|
|
6788
|
+
return {
|
|
6789
|
+
installId: installId ?? "anonymous",
|
|
6790
|
+
skillId: input.skillId,
|
|
6791
|
+
versionId: input.versionId,
|
|
6792
|
+
agentTarget: input.agentTarget,
|
|
6793
|
+
ci: input.ci ?? isCi(),
|
|
6794
|
+
action: input.action ?? "INSTALL"
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
function isCi(env = process.env) {
|
|
6798
|
+
return Boolean(env.CI || env.CONTINUOUS_INTEGRATION || env.BUILD_NUMBER);
|
|
6799
|
+
}
|
|
6800
|
+
function shouldReport(config, env = process.env) {
|
|
6801
|
+
if (env.BFI_ANALYTICS === "off" || env.BFI_ANALYTICS === "false") return false;
|
|
6802
|
+
if (env.BFI_ANALYTICS === "on" || env.BFI_ANALYTICS === "true") return true;
|
|
6803
|
+
return config.analytics !== false;
|
|
6804
|
+
}
|
|
6805
|
+
function getInstallId(store) {
|
|
6806
|
+
const cached = store.read();
|
|
6807
|
+
if (cached && cached.trim().length > 0) return cached.trim();
|
|
6808
|
+
const id = randomUUID();
|
|
6809
|
+
store.write(id);
|
|
6810
|
+
return id;
|
|
6811
|
+
}
|
|
6812
|
+
function defaultInstallIdStore() {
|
|
6813
|
+
return {
|
|
6814
|
+
read: () => {
|
|
6815
|
+
try {
|
|
6816
|
+
if (fs.existsSync(INSTALL_ID_FILE)) return fs.readFileSync(INSTALL_ID_FILE, "utf8");
|
|
6817
|
+
} catch {}
|
|
6818
|
+
return null;
|
|
6819
|
+
},
|
|
6820
|
+
write: (value) => {
|
|
6821
|
+
try {
|
|
6822
|
+
fs.mkdirSync(path$1.dirname(INSTALL_ID_FILE), {
|
|
6823
|
+
recursive: true,
|
|
6824
|
+
mode: 448
|
|
6825
|
+
});
|
|
6826
|
+
fs.writeFileSync(INSTALL_ID_FILE, value, { mode: 384 });
|
|
6827
|
+
} catch {}
|
|
6828
|
+
}
|
|
6829
|
+
};
|
|
6830
|
+
}
|
|
6831
|
+
async function reportInstall(api, payload) {
|
|
6832
|
+
try {
|
|
6833
|
+
await api.reportInstallEvent(payload);
|
|
6834
|
+
} catch {}
|
|
6835
|
+
}
|
|
6836
|
+
async function reportInstallEventForSkill(api, input) {
|
|
6837
|
+
if (!api || typeof input.skillId !== "number") return;
|
|
6838
|
+
if (!shouldReport(loadGlobalConfig() || {})) return;
|
|
6839
|
+
const installId = getInstallId(defaultInstallIdStore());
|
|
6840
|
+
await reportInstall(api, buildPayload({
|
|
6841
|
+
skillId: input.skillId,
|
|
6842
|
+
agentTarget: input.agentTarget
|
|
6843
|
+
}, installId));
|
|
6844
|
+
}
|
|
6845
|
+
function maybePrintAnalyticsNotice(env = process.env) {
|
|
6846
|
+
if (env.BFI_ANALYTICS === "off" || env.BFI_ANALYTICS === "false") return;
|
|
6847
|
+
try {
|
|
6848
|
+
if (fs.existsSync(ANALYTICS_NOTICE_FILE)) return;
|
|
6849
|
+
fs.mkdirSync(path$1.dirname(ANALYTICS_NOTICE_FILE), {
|
|
6850
|
+
recursive: true,
|
|
6851
|
+
mode: 448
|
|
6852
|
+
});
|
|
6853
|
+
fs.writeFileSync(ANALYTICS_NOTICE_FILE, (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
|
|
6854
|
+
console.log("bfi 会匿名上报技能安装事件(仅安装 id 与技能 id,不含用户与 IP),用于市场统计。");
|
|
6855
|
+
console.log("关闭:bfi config analytics off");
|
|
6856
|
+
console.log();
|
|
6857
|
+
} catch {}
|
|
6858
|
+
}
|
|
6859
|
+
const pluginTargets = [
|
|
6860
|
+
"claude-code",
|
|
6861
|
+
"codex",
|
|
6862
|
+
"zcode",
|
|
6863
|
+
"codebuddy",
|
|
6864
|
+
"qwen-code"
|
|
6865
|
+
];
|
|
6866
|
+
const isPluginArchive = (value) => /\.(zip|tgz|tar\.gz)$/iu.test(value);
|
|
6867
|
+
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);
|
|
6868
|
+
function parsePackageIntent(args) {
|
|
6869
|
+
const output = [...args];
|
|
6870
|
+
let mode = "skills";
|
|
6871
|
+
const occurrences = args.filter((value) => value === "--as" || value.startsWith("--as="));
|
|
6872
|
+
if (occurrences.length) {
|
|
6873
|
+
const index = output.indexOf("--as");
|
|
6874
|
+
const requested = output[index + 1];
|
|
6875
|
+
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");
|
|
6876
|
+
mode = requested;
|
|
6877
|
+
output.splice(index, 2);
|
|
6878
|
+
}
|
|
6879
|
+
if (mode === "skills" && (output.some((value) => [
|
|
6880
|
+
"--plan",
|
|
6881
|
+
"--export",
|
|
6882
|
+
"--scope",
|
|
6883
|
+
"--root",
|
|
6884
|
+
"--operation"
|
|
6885
|
+
].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");
|
|
6886
|
+
return {
|
|
6887
|
+
mode,
|
|
6888
|
+
args: output
|
|
6889
|
+
};
|
|
6890
|
+
}
|
|
6891
|
+
function parsePluginArguments(args) {
|
|
6892
|
+
const command = {
|
|
6893
|
+
install: "install",
|
|
6894
|
+
i: "install",
|
|
6895
|
+
update: "update",
|
|
6896
|
+
up: "update",
|
|
6897
|
+
uninstall: "uninstall",
|
|
6898
|
+
rm: "uninstall",
|
|
6899
|
+
list: "list",
|
|
6900
|
+
ls: "list"
|
|
6901
|
+
}[args[0] ?? ""];
|
|
6902
|
+
if (!command) throw new Error("Plugin intent supports install, update, uninstall and list");
|
|
6903
|
+
const result = {
|
|
6904
|
+
command,
|
|
6905
|
+
scope: "project",
|
|
6906
|
+
planOnly: false,
|
|
6907
|
+
json: false
|
|
6908
|
+
};
|
|
6909
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6910
|
+
const values = /* @__PURE__ */ new Set([
|
|
6911
|
+
"--agent",
|
|
6912
|
+
"--version",
|
|
6913
|
+
"--scope",
|
|
6914
|
+
"--root",
|
|
6915
|
+
"--export",
|
|
6916
|
+
"--operation",
|
|
6917
|
+
"--registry",
|
|
6918
|
+
"--token",
|
|
6919
|
+
"--namespace"
|
|
6920
|
+
]);
|
|
6921
|
+
for (let index = 1; index < args.length; index++) {
|
|
6922
|
+
const option = args[index];
|
|
6923
|
+
if (!option.startsWith("-")) {
|
|
6924
|
+
if (result.source) throw new Error("Specify exactly one plugin source");
|
|
6925
|
+
result.source = option;
|
|
6926
|
+
continue;
|
|
6927
|
+
}
|
|
6928
|
+
const flag = option === "-g" ? "--global" : option === "-y" ? "--yes" : option;
|
|
6929
|
+
if (seen.has(flag)) throw new Error(`Duplicate plugin option: ${flag}`);
|
|
6930
|
+
seen.add(flag);
|
|
6931
|
+
if (values.has(flag)) {
|
|
6932
|
+
const value = args[++index];
|
|
6933
|
+
if (value === void 0 || value.startsWith("-") || /[\u0000-\u001f\u007f]/u.test(value)) throw new Error(`Missing or invalid value for ${flag}`);
|
|
6934
|
+
if (flag === "--agent") {
|
|
6935
|
+
if (!pluginTargets.includes(value)) throw new Error("Unknown native plugin target");
|
|
6936
|
+
result.agent = value;
|
|
6937
|
+
} else if (flag === "--version") {
|
|
6938
|
+
if (!isPluginVersion(value)) throw new Error("Plugin version must be exact SemVer, not latest or a range");
|
|
6939
|
+
result.version = value;
|
|
6940
|
+
} else if (flag === "--scope") {
|
|
6941
|
+
if (!["project", "user"].includes(value)) throw new Error("Plugin scope must be project or user");
|
|
6942
|
+
result.scope = value;
|
|
6943
|
+
} else if (flag === "--root") result.root = value;
|
|
6944
|
+
else if (flag === "--export") result.exportPath = value;
|
|
6945
|
+
else if (flag === "--operation") result.operationId = value;
|
|
6946
|
+
} else if (flag === "--global") result.scope = "user";
|
|
6947
|
+
else if (flag === "--plan") result.planOnly = true;
|
|
6948
|
+
else if (flag === "--json") result.json = true;
|
|
6949
|
+
else if (flag !== "--yes") throw new Error(`Unknown plugin option: ${flag}; upgrade bfi if needed`);
|
|
6950
|
+
}
|
|
6951
|
+
if (seen.has("--scope") && seen.has("--global")) throw new Error("Use --scope or --global, not both");
|
|
6952
|
+
if (command !== "list" && !result.agent) throw new Error("Full plugin operations require an explicit --agent");
|
|
6953
|
+
if (command !== "list" && !result.source && !(command === "uninstall" && result.operationId)) throw new Error("Specify @namespace/package or a local zip/tgz path");
|
|
6954
|
+
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");
|
|
6955
|
+
if (result.planOnly && result.exportPath) throw new Error("Plan and export are separate operations");
|
|
6956
|
+
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");
|
|
6957
|
+
if (command === "list" && (result.source || result.version || result.planOnly || result.exportPath || result.operationId)) throw new Error("Invalid plugin list options");
|
|
6958
|
+
return result;
|
|
6959
|
+
}
|
|
6871
6960
|
var package_exports = /* @__PURE__ */ __exportAll({ runPackage: () => runPackage });
|
|
6872
|
-
const RESET$
|
|
6873
|
-
const BOLD$
|
|
6874
|
-
const DIM$
|
|
6875
|
-
const GREEN$
|
|
6876
|
-
const RED$
|
|
6961
|
+
const RESET$3 = "\x1B[0m";
|
|
6962
|
+
const BOLD$1 = "\x1B[1m";
|
|
6963
|
+
const DIM$3 = "\x1B[38;5;102m";
|
|
6964
|
+
const GREEN$2 = "\x1B[32m";
|
|
6965
|
+
const RED$1 = "\x1B[31m";
|
|
6877
6966
|
const YELLOW$1 = "\x1B[33m";
|
|
6878
6967
|
async function runPackage(args, globalOpts) {
|
|
6968
|
+
const intent = parsePackageIntent(args);
|
|
6969
|
+
const previousDirectory = process.cwd();
|
|
6970
|
+
try {
|
|
6971
|
+
if (globalOpts.workdir) process.chdir(globalOpts.workdir);
|
|
6972
|
+
if (intent.mode === "plugin") {
|
|
6973
|
+
const { runPluginPackage } = await import("./_chunks/package-plugin.mjs");
|
|
6974
|
+
await runPluginPackage(intent.args, globalOpts);
|
|
6975
|
+
} else await dispatchPackage(intent.args, globalOpts);
|
|
6976
|
+
} finally {
|
|
6977
|
+
process.chdir(previousDirectory);
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
async function dispatchPackage(args, globalOpts) {
|
|
6879
6981
|
const subcommand = args[0];
|
|
6880
6982
|
const { registry, token } = resolveConfig(globalOpts);
|
|
6881
6983
|
const api = new SkillHubAPI(registry, token);
|
|
@@ -6919,28 +7021,39 @@ function getWorkdir(globalOpts, args) {
|
|
|
6919
7021
|
return globalOpts.workdir || process.cwd();
|
|
6920
7022
|
}
|
|
6921
7023
|
function printPackageHelp() {
|
|
6922
|
-
console.log(`${BOLD$
|
|
7024
|
+
console.log(`${BOLD$1}包命令:${RESET$3}`);
|
|
6923
7025
|
console.log();
|
|
6924
|
-
console.log(`${BOLD$
|
|
6925
|
-
console.log(` ${DIM$
|
|
6926
|
-
console.log(` ${DIM$
|
|
6927
|
-
console.log(` ${DIM$
|
|
6928
|
-
console.log(` ${DIM$
|
|
6929
|
-
console.log(` ${DIM$
|
|
7026
|
+
console.log(`${BOLD$1}本地操作:${RESET$3}`);
|
|
7027
|
+
console.log(` ${DIM$3}bfi skills package list${RESET$3} 列出已安装的包`);
|
|
7028
|
+
console.log(` ${DIM$3}bfi skills package install <ref>${RESET$3} 安装包中的技能组合`);
|
|
7029
|
+
console.log(` ${DIM$3}bfi skills package uninstall <ref>${RESET$3} 卸载包(含全部技能)`);
|
|
7030
|
+
console.log(` ${DIM$3}bfi skills package update [ref]${RESET$3} 更新包中技能`);
|
|
7031
|
+
console.log(` ${DIM$3}bfi skills package search <query>${RESET$3} 搜索包`);
|
|
6930
7032
|
console.log();
|
|
6931
|
-
console.log(`${BOLD$
|
|
6932
|
-
console.log(` ${DIM$
|
|
6933
|
-
console.log(` ${DIM$
|
|
6934
|
-
console.log(` ${DIM$
|
|
7033
|
+
console.log(`${BOLD$1}服务端管理:${RESET$3}`);
|
|
7034
|
+
console.log(` ${DIM$3}bfi skills package create <slug>${RESET$3} 创建包`);
|
|
7035
|
+
console.log(` ${DIM$3}bfi skills package add-skills <ref> [skills...]${RESET$3} 添加技能到包`);
|
|
7036
|
+
console.log(` ${DIM$3}bfi skills package remove-skills <ref> [skills...]${RESET$3} 从包移除技能`);
|
|
6935
7037
|
console.log();
|
|
6936
|
-
console.log(`${BOLD$
|
|
7038
|
+
console.log(`${BOLD$1}选项:${RESET$3}`);
|
|
6937
7039
|
console.log(` --namespace <ns> 目标命名空间`);
|
|
7040
|
+
console.log(` --agent <a> 目标 agent(如 claude-code、codex)`);
|
|
7041
|
+
console.log(` --as skills|plugin 默认 skills;完整插件必须显式 --as plugin --agent <a>`);
|
|
7042
|
+
console.log(` --version <v> 插件固定版本;省略只解析显式 stable,不回退 latest`);
|
|
7043
|
+
console.log(` --plan 插件模式只读计划,不写配置或安装记录`);
|
|
7044
|
+
console.log(` --export <archive> 插件模式校验后导出原始制品,不安装、不覆盖文件`);
|
|
7045
|
+
console.log(` --scope user|project 插件安装范围,默认 project`);
|
|
7046
|
+
console.log(` --root <path> 本地 zip/tgz 多插件根必须明确选择`);
|
|
7047
|
+
console.log(` --operation <id> 按本机精确安装记录卸载插件,不删除额外用户文件`);
|
|
7048
|
+
console.log(` -g, --global 安装到主目录而非当前项目`);
|
|
6938
7049
|
console.log(` --name <name> 显示名称(create 时)`);
|
|
6939
7050
|
console.log(` --description <t> 描述(create 时)`);
|
|
6940
7051
|
console.log(` --json JSON 输出`);
|
|
7052
|
+
console.log();
|
|
6941
7053
|
}
|
|
6942
7054
|
async function handleLocalList(args, globalOpts) {
|
|
6943
|
-
const
|
|
7055
|
+
const cwd = getWorkdir(globalOpts, args);
|
|
7056
|
+
const lock = loadLockfile(cwd);
|
|
6944
7057
|
args.includes("-g") || args.includes("--global");
|
|
6945
7058
|
const pkgs = lock.packages || {};
|
|
6946
7059
|
const entries = Object.entries(pkgs);
|
|
@@ -6950,18 +7063,18 @@ async function handleLocalList(args, globalOpts) {
|
|
|
6950
7063
|
}
|
|
6951
7064
|
if (entries.length === 0) {
|
|
6952
7065
|
console.log("没有已安装的包。");
|
|
6953
|
-
console.log(`使用 ${DIM$
|
|
7066
|
+
console.log(`使用 ${DIM$3}bfi skills package install <ref>${RESET$3} 安装包。`);
|
|
6954
7067
|
return;
|
|
6955
7068
|
}
|
|
6956
7069
|
console.log();
|
|
6957
|
-
console.log(`${BOLD$
|
|
7070
|
+
console.log(`${BOLD$1}已安装的包(${entries.length} 个):${RESET$3}`);
|
|
6958
7071
|
console.log();
|
|
6959
7072
|
for (const [ref, info] of entries) {
|
|
6960
7073
|
const pkg = info;
|
|
6961
7074
|
const skills = lock.skills || {};
|
|
6962
7075
|
const pkgSkills = Object.entries(skills).filter(([, s]) => s.package?.slug === pkg.slug);
|
|
6963
|
-
console.log(` ${BOLD$
|
|
6964
|
-
console.log(` ${DIM$
|
|
7076
|
+
console.log(` ${BOLD$1}${pkg.name || pkg.slug}${RESET$3}`);
|
|
7077
|
+
console.log(` ${DIM$3}命名空间:@${pkg.namespace} | 版本:${pkg.version || "latest"} | 技能数:${pkgSkills.length}${RESET$3}`);
|
|
6965
7078
|
console.log();
|
|
6966
7079
|
}
|
|
6967
7080
|
}
|
|
@@ -7018,41 +7131,72 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7018
7131
|
process.exit(1);
|
|
7019
7132
|
}
|
|
7020
7133
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7134
|
+
try {
|
|
7135
|
+
const blocklist = await api.getBlocklist();
|
|
7136
|
+
const ref = `@${namespace}/${slug}`;
|
|
7137
|
+
const critical = (blocklist?.critical ?? []).find((entry) => entry.ref === ref);
|
|
7138
|
+
if (critical) {
|
|
7139
|
+
console.error(`${RED$1}该技能包已被紧急下架:${critical.reason}${RESET$3}`);
|
|
7140
|
+
process.exit(1);
|
|
7141
|
+
}
|
|
7142
|
+
const removed = (blocklist?.removed ?? []).find((entry) => entry.ref === ref);
|
|
7143
|
+
if (removed) {
|
|
7144
|
+
console.error(`${RED$1}该技能包已被移除:${removed.reason}${RESET$3}`);
|
|
7145
|
+
if (removed.replacedBy) console.error(`${DIM$3}建议改用:${removed.replacedBy}${RESET$3}`);
|
|
7146
|
+
process.exit(1);
|
|
7147
|
+
}
|
|
7148
|
+
} catch {}
|
|
7021
7149
|
const isYes = args.includes("-y") || args.includes("--yes") || globalOpts.yes;
|
|
7022
7150
|
const isGlobal = args.includes("-g") || args.includes("--global");
|
|
7023
|
-
|
|
7151
|
+
const agentOption = getOptionValue(args, "--agent");
|
|
7152
|
+
const requestedAgents = agentOption ? agentOption.split(",").map((value) => value.trim()).filter(Boolean) : void 0;
|
|
7153
|
+
console.log(`${DIM$3}正在获取包信息...${RESET$3}`);
|
|
7024
7154
|
let pkgInfo;
|
|
7025
7155
|
try {
|
|
7026
7156
|
pkgInfo = await api.getPackage(namespace, slug);
|
|
7027
7157
|
pkgInfo = pkgInfo.data || pkgInfo;
|
|
7028
7158
|
} catch (error) {
|
|
7029
|
-
console.error(`${RED$
|
|
7159
|
+
console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
|
|
7030
7160
|
process.exit(1);
|
|
7031
7161
|
}
|
|
7162
|
+
if (pkgInfo.kind === "PLUGIN") throw new Error("这是完整插件,不是技能组合;请使用 --as plugin --agent <目标Agent> 安装,插件可能包含 MCP 和 Hooks。");
|
|
7032
7163
|
const skills = pkgInfo.skills || [];
|
|
7033
7164
|
if (skills.length === 0) {
|
|
7034
|
-
console.log(`${YELLOW$1}
|
|
7165
|
+
console.log(`${YELLOW$1}包中没有可独立安装的技能。${RESET$3}`);
|
|
7035
7166
|
return;
|
|
7036
7167
|
}
|
|
7037
|
-
console.log(`${BOLD$
|
|
7168
|
+
console.log(`${BOLD$1}${pkgInfo.name || slug}${RESET$3}(${skills.length} 个技能):`);
|
|
7038
7169
|
console.log();
|
|
7039
7170
|
for (const skill of skills) {
|
|
7040
7171
|
const ref = `@${skill.namespace || namespace}/${skill.slug}`;
|
|
7041
|
-
console.log(` ${DIM$
|
|
7172
|
+
console.log(` ${DIM$3}• ${skill.displayName || skill.slug}${RESET$3} ${DIM$3}${ref}${RESET$3}`);
|
|
7042
7173
|
}
|
|
7043
7174
|
console.log();
|
|
7044
7175
|
let targetAgents;
|
|
7045
7176
|
let installGlobally;
|
|
7046
7177
|
let installMode;
|
|
7047
7178
|
let eveSubagentTargets = [void 0];
|
|
7048
|
-
if (
|
|
7179
|
+
if (requestedAgents) {
|
|
7180
|
+
const validAgents = Object.keys(agents);
|
|
7181
|
+
const invalid = requestedAgents.filter((value) => !validAgents.includes(value));
|
|
7182
|
+
if (invalid.length > 0) {
|
|
7183
|
+
console.error(`${RED$1}未知 agent:${invalid.join(", ")}${RESET$3}`);
|
|
7184
|
+
process.exit(1);
|
|
7185
|
+
}
|
|
7186
|
+
targetAgents = requestedAgents;
|
|
7187
|
+
installGlobally = isGlobal;
|
|
7188
|
+
installMode = "copy";
|
|
7189
|
+
console.log(`${DIM$3}安装到:${targetAgents.map((a) => agents[a].displayName).join(", ")}${RESET$3}`);
|
|
7190
|
+
console.log(`${DIM$3}范围:${installGlobally ? "全局" : "项目"}${RESET$3}`);
|
|
7191
|
+
console.log();
|
|
7192
|
+
} else if (isYes) {
|
|
7049
7193
|
const installed = await detectInstalledAgents();
|
|
7050
7194
|
const validAgents = Object.keys(agents);
|
|
7051
7195
|
targetAgents = ensureUniversalAgents(installed.length > 0 ? installed : validAgents);
|
|
7052
7196
|
installGlobally = isGlobal;
|
|
7053
7197
|
installMode = "symlink";
|
|
7054
|
-
console.log(`${DIM$
|
|
7055
|
-
console.log(`${DIM$
|
|
7198
|
+
console.log(`${DIM$3}安装到:${targetAgents.map((a) => agents[a].displayName).join(", ")}${RESET$3}`);
|
|
7199
|
+
console.log(`${DIM$3}范围:${installGlobally ? "全局" : "项目"} | 方式:${installMode === "symlink" ? "符号链接" : "复制"}${RESET$3}`);
|
|
7056
7200
|
console.log();
|
|
7057
7201
|
} else {
|
|
7058
7202
|
const spinner$1 = spinner();
|
|
@@ -7073,7 +7217,7 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7073
7217
|
}
|
|
7074
7218
|
if (useEve) {
|
|
7075
7219
|
targetAgents = ["eve"];
|
|
7076
|
-
console.log(`${DIM$
|
|
7220
|
+
console.log(`${DIM$3}安装到:eve agent${RESET$3}`);
|
|
7077
7221
|
} else {
|
|
7078
7222
|
const selected = await selectAgentsInteractive({ global: false });
|
|
7079
7223
|
if (isCancel(selected)) {
|
|
@@ -7095,7 +7239,7 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7095
7239
|
} else if (installedAgents.length === 1) {
|
|
7096
7240
|
targetAgents = ensureUniversalAgents(installedAgents);
|
|
7097
7241
|
const firstAgent = installedAgents[0];
|
|
7098
|
-
console.log(`${DIM$
|
|
7242
|
+
console.log(`${DIM$3}安装到:${agents[firstAgent].displayName}${RESET$3}`);
|
|
7099
7243
|
} else {
|
|
7100
7244
|
const selected = await selectAgentsInteractive({ global: false });
|
|
7101
7245
|
if (isCancel(selected)) {
|
|
@@ -7107,17 +7251,18 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7107
7251
|
if (targetAgents.includes("eve")) {
|
|
7108
7252
|
const availableSubagents = getEveSubagents(process.cwd());
|
|
7109
7253
|
if (availableSubagents.length > 0) {
|
|
7254
|
+
const subagentChoices = [{
|
|
7255
|
+
value: "",
|
|
7256
|
+
label: "根 Agent",
|
|
7257
|
+
hint: "agent/skills"
|
|
7258
|
+
}, ...availableSubagents.map((name) => ({
|
|
7259
|
+
value: name,
|
|
7260
|
+
label: name,
|
|
7261
|
+
hint: `agent/subagents/${name}/skills`
|
|
7262
|
+
}))];
|
|
7110
7263
|
const selectedSubagents = await multiselect$1({
|
|
7111
7264
|
message: "Eve 技能安装位置?",
|
|
7112
|
-
options:
|
|
7113
|
-
value: "",
|
|
7114
|
-
label: "根 Agent",
|
|
7115
|
-
hint: "agent/skills"
|
|
7116
|
-
}, ...availableSubagents.map((name) => ({
|
|
7117
|
-
value: name,
|
|
7118
|
-
label: name,
|
|
7119
|
-
hint: `agent/subagents/${name}/skills`
|
|
7120
|
-
}))],
|
|
7265
|
+
options: subagentChoices,
|
|
7121
7266
|
initialValues: [""],
|
|
7122
7267
|
required: true
|
|
7123
7268
|
});
|
|
@@ -7173,7 +7318,7 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7173
7318
|
const overwriteChecks = await Promise.all(skills.flatMap((skill) => installTargets.map(async (target) => ({
|
|
7174
7319
|
skillName: skill.slug,
|
|
7175
7320
|
target,
|
|
7176
|
-
installed: await isSkillInstalled(skill.slug, target.agent, {
|
|
7321
|
+
installed: await isSkillInstalled(packageSkillName(skill.namespace || namespace, skill.slug), target.agent, {
|
|
7177
7322
|
global: installGlobally,
|
|
7178
7323
|
eveSubagent: target.subagent
|
|
7179
7324
|
})
|
|
@@ -7204,6 +7349,9 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7204
7349
|
const installedSkills = [];
|
|
7205
7350
|
const nonEveAgents = targetAgents.filter((a) => a !== "eve");
|
|
7206
7351
|
const runAddBaseOpts = {
|
|
7352
|
+
registry: globalOpts.registry,
|
|
7353
|
+
token: globalOpts.token,
|
|
7354
|
+
packageSkill: true,
|
|
7207
7355
|
global: installGlobally,
|
|
7208
7356
|
copy: installMode === "copy",
|
|
7209
7357
|
yes: true
|
|
@@ -7212,16 +7360,32 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7212
7360
|
if (targetAgents.includes("eve")) runAddBaseOpts.subagent = eveSubagentTargets.map((s) => s ?? "root");
|
|
7213
7361
|
for (const skill of skills) {
|
|
7214
7362
|
const skillRef = `@${skill.namespace || namespace}/${skill.slug}`;
|
|
7215
|
-
console.log(` ${DIM$
|
|
7363
|
+
console.log(` ${DIM$3}正在安装 ${skillRef}...${RESET$3}`);
|
|
7216
7364
|
try {
|
|
7217
7365
|
await runAdd([skillRef], runAddBaseOpts);
|
|
7218
|
-
|
|
7219
|
-
|
|
7366
|
+
const targets = targetAgents.flatMap((agent) => agent === "eve" ? eveSubagentTargets.map((subagent) => ({
|
|
7367
|
+
agent,
|
|
7368
|
+
subagent
|
|
7369
|
+
})) : [{
|
|
7370
|
+
agent,
|
|
7371
|
+
subagent: void 0
|
|
7372
|
+
}]);
|
|
7373
|
+
const verified = await Promise.all(targets.map((target) => isSkillInstalled(packageSkillName(skill.namespace || namespace, skill.slug), target.agent, {
|
|
7374
|
+
global: installGlobally,
|
|
7375
|
+
eveSubagent: target.subagent
|
|
7376
|
+
})));
|
|
7377
|
+
if (verified.length === 0 || verified.some((result) => !result)) throw new Error("Skill installation could not be verified");
|
|
7378
|
+
console.log(` ${GREEN$2}✓ ${skill.slug}${RESET$3}`);
|
|
7379
|
+
installedSkills.push(skillRef);
|
|
7220
7380
|
} catch (error) {
|
|
7221
|
-
console.log(` ${RED$
|
|
7381
|
+
console.log(` ${RED$1}✗ ${skill.slug}:${error.message}${RESET$3}`);
|
|
7222
7382
|
}
|
|
7223
7383
|
}
|
|
7224
|
-
|
|
7384
|
+
if (installedSkills.length === 0) {
|
|
7385
|
+
console.log(`${YELLOW$1}没有确认安装成功的技能,未创建包安装记录${RESET$3}`);
|
|
7386
|
+
return;
|
|
7387
|
+
}
|
|
7388
|
+
const cwd = installGlobally ? os$1.homedir() : getWorkdir(globalOpts, args);
|
|
7225
7389
|
const lock = loadLockfile(cwd);
|
|
7226
7390
|
const pkgKey = `@${namespace}/${slug}`;
|
|
7227
7391
|
if (!lock.packages) lock.packages = {};
|
|
@@ -7232,20 +7396,28 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7232
7396
|
version: pkgInfo.version || "latest",
|
|
7233
7397
|
skillCount: installedSkills.length
|
|
7234
7398
|
};
|
|
7235
|
-
for (const
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
namespace,
|
|
7243
|
-
|
|
7399
|
+
for (const skillRef of installedSkills) {
|
|
7400
|
+
const identity = parseSkillRef(skillRef);
|
|
7401
|
+
const existing = lock.skills[skillRef];
|
|
7402
|
+
lock.skills[skillRef] = {
|
|
7403
|
+
...existing,
|
|
7404
|
+
namespace: identity.namespace,
|
|
7405
|
+
slug: identity.slug,
|
|
7406
|
+
installedName: packageSkillName(identity.namespace, identity.slug),
|
|
7407
|
+
version: existing?.version || "latest",
|
|
7408
|
+
package: {
|
|
7409
|
+
namespace,
|
|
7410
|
+
slug
|
|
7411
|
+
}
|
|
7244
7412
|
};
|
|
7245
7413
|
}
|
|
7246
7414
|
saveLockfile(cwd, lock);
|
|
7415
|
+
for (const skillRef of installedSkills) await reportInstallEventForSkill(api, {
|
|
7416
|
+
skillId: skills.find((entry) => `@${entry.namespace || namespace}/${entry.slug}` === skillRef)?.skillId,
|
|
7417
|
+
agentTarget: targetAgents[0]
|
|
7418
|
+
});
|
|
7247
7419
|
console.log();
|
|
7248
|
-
console.log(`${GREEN$
|
|
7420
|
+
console.log(installedSkills.length === skills.length ? `${GREEN$2}✓ 包安装完成${RESET$3}` : `${YELLOW$1}包仅部分安装:${installedSkills.length}/${skills.length},仅记录已确认成功的技能${RESET$3}`);
|
|
7249
7421
|
}
|
|
7250
7422
|
async function handleUninstall(args, globalOpts) {
|
|
7251
7423
|
const pkgRef = args[0];
|
|
@@ -7255,10 +7427,10 @@ async function handleUninstall(args, globalOpts) {
|
|
|
7255
7427
|
}
|
|
7256
7428
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7257
7429
|
const pkgKey = `@${namespace}/${slug}`;
|
|
7258
|
-
const cwd = getWorkdir(globalOpts, args);
|
|
7430
|
+
const cwd = args.includes("-g") || args.includes("--global") ? os$1.homedir() : getWorkdir(globalOpts, args);
|
|
7259
7431
|
const lock = loadLockfile(cwd);
|
|
7260
7432
|
if (!lock.packages || !lock.packages[pkgKey]) {
|
|
7261
|
-
console.log(`${YELLOW$1}包未安装:${pkgKey}${RESET$
|
|
7433
|
+
console.log(`${YELLOW$1}包未安装:${pkgKey}${RESET$3}`);
|
|
7262
7434
|
return;
|
|
7263
7435
|
}
|
|
7264
7436
|
const skills = lock.skills || {};
|
|
@@ -7268,28 +7440,31 @@ async function handleUninstall(args, globalOpts) {
|
|
|
7268
7440
|
if (skillInfo.package?.slug === slug && skillInfo.package?.namespace === namespace) {
|
|
7269
7441
|
const { removeCommand } = await Promise.resolve().then(() => remove_exports);
|
|
7270
7442
|
try {
|
|
7271
|
-
|
|
7272
|
-
|
|
7443
|
+
const installedName = skillInfo.installedName || (skillName.startsWith("@") ? packageSkillName(skillInfo.namespace, skillInfo.slug) : skillName);
|
|
7444
|
+
if (!(await removeCommand([installedName], {
|
|
7445
|
+
global: args.includes("-g") || args.includes("--global"),
|
|
7273
7446
|
yes: true
|
|
7274
|
-
});
|
|
7447
|
+
})).removed.includes(installedName)) throw new Error("Removal could not be verified");
|
|
7448
|
+
delete lock.skills[skillName];
|
|
7275
7449
|
removed++;
|
|
7276
7450
|
} catch {}
|
|
7277
7451
|
}
|
|
7278
7452
|
}
|
|
7279
|
-
delete lock.packages[pkgKey];
|
|
7280
|
-
const { saveLockfile } = await
|
|
7453
|
+
if (!Object.values(lock.skills).some((info) => info.package?.slug === slug && info.package?.namespace === namespace)) delete lock.packages[pkgKey];
|
|
7454
|
+
const { saveLockfile } = await import("./_chunks/config.mjs").then((n) => n.config_exports);
|
|
7281
7455
|
saveLockfile(cwd, lock);
|
|
7282
|
-
console.log(`${GREEN$
|
|
7456
|
+
console.log(lock.packages[pkgKey] ? `${YELLOW$1}包未完全卸载:移除 ${removed} 个技能,未确认移除的记录已保留${RESET$3}` : `${GREEN$2}✓ 已卸载包,移除 ${removed} 个技能${RESET$3}`);
|
|
7283
7457
|
}
|
|
7284
7458
|
async function handleUpdate(args, api, globalOpts) {
|
|
7285
7459
|
const pkgRef = args[0];
|
|
7286
|
-
const
|
|
7460
|
+
const cwd = getWorkdir(globalOpts, args);
|
|
7461
|
+
const lock = loadLockfile(cwd);
|
|
7287
7462
|
let packagesToUpdate = [];
|
|
7288
7463
|
const pkgs = lock.packages || {};
|
|
7289
7464
|
if (pkgRef) {
|
|
7290
7465
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7291
7466
|
if (!pkgs[`@${namespace}/${slug}`]) {
|
|
7292
|
-
console.log(`${YELLOW$1}包未安装:@${namespace}/${slug}${RESET$
|
|
7467
|
+
console.log(`${YELLOW$1}包未安装:@${namespace}/${slug}${RESET$3}`);
|
|
7293
7468
|
return;
|
|
7294
7469
|
}
|
|
7295
7470
|
packagesToUpdate.push({
|
|
@@ -7304,25 +7479,29 @@ async function handleUpdate(args, api, globalOpts) {
|
|
|
7304
7479
|
console.log("没有已安装的包需要更新。");
|
|
7305
7480
|
return;
|
|
7306
7481
|
}
|
|
7307
|
-
console.log(`${DIM$
|
|
7482
|
+
console.log(`${DIM$3}正在更新 ${packagesToUpdate.length} 个包...${RESET$3}`);
|
|
7308
7483
|
for (const pkg of packagesToUpdate) try {
|
|
7309
7484
|
const pkgInfo = await api.getPackage(pkg.namespace, pkg.slug);
|
|
7310
|
-
const
|
|
7311
|
-
|
|
7485
|
+
const info = pkgInfo.data || pkgInfo;
|
|
7486
|
+
if (info.kind === "PLUGIN") throw new Error("这是完整插件,请使用 --as plugin --agent <目标Agent> 更新;不会按技能组合安装。");
|
|
7487
|
+
const skills = info.skills || [];
|
|
7488
|
+
console.log(` ${DIM$3}更新 @${pkg.namespace}/${pkg.slug}(${skills.length} 个技能)...${RESET$3}`);
|
|
7312
7489
|
for (const skill of skills) {
|
|
7313
7490
|
const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
|
|
7314
7491
|
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
7315
7492
|
try {
|
|
7316
7493
|
await runAdd([skillRef], {
|
|
7317
7494
|
...globalOpts,
|
|
7318
|
-
yes: true
|
|
7495
|
+
yes: true,
|
|
7496
|
+
packageSkill: true,
|
|
7497
|
+
global: args.includes("-g") || args.includes("--global")
|
|
7319
7498
|
});
|
|
7320
7499
|
} catch {}
|
|
7321
7500
|
}
|
|
7322
7501
|
} catch (error) {
|
|
7323
|
-
console.log(` ${RED$
|
|
7502
|
+
console.log(` ${RED$1}✗ @${pkg.namespace}/${pkg.slug}:${error.message}${RESET$3}`);
|
|
7324
7503
|
}
|
|
7325
|
-
console.log(`${GREEN$
|
|
7504
|
+
console.log(`${GREEN$2}✓ 包更新完成${RESET$3}`);
|
|
7326
7505
|
}
|
|
7327
7506
|
async function handleSearch(args, api, globalOpts) {
|
|
7328
7507
|
const query = args.filter((a) => !a.startsWith("--")).join(" ") || void 0;
|
|
@@ -7342,11 +7521,11 @@ async function handleSearch(args, api, globalOpts) {
|
|
|
7342
7521
|
}, null, 2));
|
|
7343
7522
|
return;
|
|
7344
7523
|
}
|
|
7345
|
-
console.log(`\n${BOLD$
|
|
7524
|
+
console.log(`\n${BOLD$1}找到 ${skills.length} 个相关技能(按命名空间分组):${RESET$3}\n`);
|
|
7346
7525
|
for (const [ns, sk] of namespaces) {
|
|
7347
|
-
console.log(` ${BOLD$
|
|
7348
|
-
for (const s of sk.slice(0, 5)) console.log(` ${DIM$
|
|
7349
|
-
if (sk.length > 5) console.log(` ${DIM$
|
|
7526
|
+
console.log(` ${BOLD$1}@${ns}${RESET$3}(${sk.length} 个技能)`);
|
|
7527
|
+
for (const s of sk.slice(0, 5)) console.log(` ${DIM$3}- ${s.displayName || s.name} (${s.slug})${RESET$3}`);
|
|
7528
|
+
if (sk.length > 5) console.log(` ${DIM$3}... 还有 ${sk.length - 5} 个${RESET$3}`);
|
|
7350
7529
|
console.log();
|
|
7351
7530
|
}
|
|
7352
7531
|
} else {
|
|
@@ -7385,11 +7564,11 @@ async function handleSearch(args, api, globalOpts) {
|
|
|
7385
7564
|
};
|
|
7386
7565
|
const maxName = Math.max(...allPackages.map((p) => dw(p.displayName || p.name || p.slug || "")), 4);
|
|
7387
7566
|
const maxSlug = Math.max(...allPackages.map((p) => dw(`@${p._namespace}/${p.slug}`)), 4);
|
|
7388
|
-
console.log(`\n${BOLD$
|
|
7567
|
+
console.log(`\n${BOLD$1}所有技能包(${allPackages.length} 个):${RESET$3}\n`);
|
|
7389
7568
|
for (const pkg of allPackages) {
|
|
7390
7569
|
const name = pad(pkg.displayName || pkg.name || pkg.slug || "", maxName);
|
|
7391
7570
|
const ref = pad(`@${pkg._namespace}/${pkg.slug}`, maxSlug);
|
|
7392
|
-
console.log(` ${BOLD$
|
|
7571
|
+
console.log(` ${BOLD$1}${name}${RESET$3} ${DIM$3}${ref} ${pkg.visibility === "PUBLIC" ? "公开" : "私有"}${RESET$3}`);
|
|
7393
7572
|
}
|
|
7394
7573
|
}
|
|
7395
7574
|
}
|
|
@@ -7410,11 +7589,11 @@ async function handleCreate(args, api, globalOpts) {
|
|
|
7410
7589
|
description: description || "",
|
|
7411
7590
|
visibility: visibility.toUpperCase()
|
|
7412
7591
|
});
|
|
7413
|
-
console.log(`${GREEN$
|
|
7592
|
+
console.log(`${GREEN$2}✓ 已创建包 ${toSkillRefString(namespace, slug)}${RESET$3}`);
|
|
7414
7593
|
const pkg = result.package || result.data || result;
|
|
7415
|
-
if (pkg) console.log(` ${DIM$
|
|
7594
|
+
if (pkg) console.log(` ${DIM$3}名称:${pkg.name || slug} | 可见性:${pkg.visibility || visibility}${RESET$3}`);
|
|
7416
7595
|
} catch (error) {
|
|
7417
|
-
console.error(`${RED$
|
|
7596
|
+
console.error(`${RED$1}创建包失败:${error.message}${RESET$3}`);
|
|
7418
7597
|
process.exit(1);
|
|
7419
7598
|
}
|
|
7420
7599
|
}
|
|
@@ -7440,11 +7619,11 @@ async function handleAddSkills(args, api, globalOpts) {
|
|
|
7440
7619
|
};
|
|
7441
7620
|
});
|
|
7442
7621
|
if (skills.length === 0) {
|
|
7443
|
-
console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$
|
|
7622
|
+
console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$3}`);
|
|
7444
7623
|
return;
|
|
7445
7624
|
}
|
|
7446
7625
|
} catch (error) {
|
|
7447
|
-
console.error(`${RED$
|
|
7626
|
+
console.error(`${RED$1}获取技能列表失败:${error.message}${RESET$3}`);
|
|
7448
7627
|
process.exit(1);
|
|
7449
7628
|
}
|
|
7450
7629
|
else skills = skillRefs.map((ref) => {
|
|
@@ -7456,9 +7635,9 @@ async function handleAddSkills(args, api, globalOpts) {
|
|
|
7456
7635
|
});
|
|
7457
7636
|
try {
|
|
7458
7637
|
await api.addSkillsToPackage(namespace, slug, skills);
|
|
7459
|
-
console.log(`${GREEN$
|
|
7638
|
+
console.log(`${GREEN$2}✓ 已添加 ${skills.length} 个技能到 ${toSkillRefString(namespace, slug)}${RESET$3}`);
|
|
7460
7639
|
} catch (error) {
|
|
7461
|
-
console.error(`${RED$
|
|
7640
|
+
console.error(`${RED$1}添加失败:${error.message}${RESET$3}`);
|
|
7462
7641
|
process.exit(1);
|
|
7463
7642
|
}
|
|
7464
7643
|
}
|
|
@@ -7483,7 +7662,7 @@ async function handleRemoveSkills(args, api, globalOpts) {
|
|
|
7483
7662
|
return;
|
|
7484
7663
|
}
|
|
7485
7664
|
} catch (error) {
|
|
7486
|
-
console.error(`${RED$
|
|
7665
|
+
console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
|
|
7487
7666
|
process.exit(1);
|
|
7488
7667
|
}
|
|
7489
7668
|
else skills = skillRefs.map((ref) => {
|
|
@@ -7495,9 +7674,9 @@ async function handleRemoveSkills(args, api, globalOpts) {
|
|
|
7495
7674
|
});
|
|
7496
7675
|
try {
|
|
7497
7676
|
await api.removeSkillsFromPackage(namespace, slug, skills);
|
|
7498
|
-
console.log(`${GREEN$
|
|
7677
|
+
console.log(`${GREEN$2}✓ 已从包移除 ${skills.length} 个技能${RESET$3}`);
|
|
7499
7678
|
} catch (error) {
|
|
7500
|
-
console.error(`${RED$
|
|
7679
|
+
console.error(`${RED$1}移除失败:${error.message}${RESET$3}`);
|
|
7501
7680
|
process.exit(1);
|
|
7502
7681
|
}
|
|
7503
7682
|
}
|
|
@@ -7516,19 +7695,19 @@ async function handleServerInfo(args, api, globalOpts) {
|
|
|
7516
7695
|
console.log(JSON.stringify(pkg, null, 2));
|
|
7517
7696
|
return;
|
|
7518
7697
|
}
|
|
7519
|
-
console.log(`\n${BOLD$
|
|
7520
|
-
console.log(`${DIM$
|
|
7521
|
-
console.log(` ${BOLD$
|
|
7522
|
-
console.log(` ${BOLD$
|
|
7523
|
-
console.log(` ${BOLD$
|
|
7698
|
+
console.log(`\n${BOLD$1}${pkg.displayName || pkg.name || slug}${RESET$3}`);
|
|
7699
|
+
console.log(`${DIM$3}${"=".repeat(48)}${RESET$3}`);
|
|
7700
|
+
console.log(` ${BOLD$1}Slug:${RESET$3} ${toCanonicalSlug(namespace, slug)}`);
|
|
7701
|
+
console.log(` ${BOLD$1}命名空间:${RESET$3} @${namespace}`);
|
|
7702
|
+
console.log(` ${BOLD$1}可见性:${RESET$3} ${pkg.visibility || "PRIVATE"}`);
|
|
7524
7703
|
const skills = pkg.skills || [];
|
|
7525
7704
|
if (skills.length > 0) {
|
|
7526
|
-
console.log(`\n ${BOLD$
|
|
7527
|
-
for (const s of skills) console.log(` ${DIM$
|
|
7705
|
+
console.log(`\n ${BOLD$1}技能(${skills.length} 个):${RESET$3}`);
|
|
7706
|
+
for (const s of skills) console.log(` ${DIM$3}- ${s.displayName || s.slug} (${s.namespace || namespace}--${s.slug})${RESET$3}`);
|
|
7528
7707
|
}
|
|
7529
7708
|
console.log();
|
|
7530
7709
|
} catch (error) {
|
|
7531
|
-
console.error(`${RED$
|
|
7710
|
+
console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
|
|
7532
7711
|
process.exit(1);
|
|
7533
7712
|
}
|
|
7534
7713
|
}
|
|
@@ -7536,418 +7715,6 @@ function getOptionValue(args, flag) {
|
|
|
7536
7715
|
const idx = args.indexOf(flag);
|
|
7537
7716
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
7538
7717
|
}
|
|
7539
|
-
const RESET$4 = "\x1B[0m";
|
|
7540
|
-
const BOLD$2 = "\x1B[1m";
|
|
7541
|
-
const DIM$4 = "\x1B[38;5;102m";
|
|
7542
|
-
const TEXT$4 = "\x1B[38;5;145m";
|
|
7543
|
-
const GREEN$3 = "\x1B[32m";
|
|
7544
|
-
const RED$2 = "\x1B[31m";
|
|
7545
|
-
async function runPlugin(args, globalOpts) {
|
|
7546
|
-
const sub = args[0];
|
|
7547
|
-
const subArgs = args.slice(1);
|
|
7548
|
-
switch (sub) {
|
|
7549
|
-
case "create":
|
|
7550
|
-
case "new":
|
|
7551
|
-
await createScaffold(subArgs);
|
|
7552
|
-
break;
|
|
7553
|
-
case "validate":
|
|
7554
|
-
await validate(subArgs);
|
|
7555
|
-
break;
|
|
7556
|
-
case "publish":
|
|
7557
|
-
await publish(subArgs, globalOpts);
|
|
7558
|
-
break;
|
|
7559
|
-
case "list":
|
|
7560
|
-
case "ls":
|
|
7561
|
-
await list$1(subArgs, globalOpts);
|
|
7562
|
-
break;
|
|
7563
|
-
case "info":
|
|
7564
|
-
await info$1(subArgs, globalOpts);
|
|
7565
|
-
break;
|
|
7566
|
-
case "install":
|
|
7567
|
-
await install(subArgs, globalOpts);
|
|
7568
|
-
break;
|
|
7569
|
-
case "--help":
|
|
7570
|
-
case "-h":
|
|
7571
|
-
case void 0:
|
|
7572
|
-
showHelp$2();
|
|
7573
|
-
break;
|
|
7574
|
-
default:
|
|
7575
|
-
console.log(`未知 plugin 命令:${sub}`);
|
|
7576
|
-
showHelp$2();
|
|
7577
|
-
}
|
|
7578
|
-
}
|
|
7579
|
-
function showHelp$2() {
|
|
7580
|
-
console.log();
|
|
7581
|
-
console.log(`${BOLD$2}bfi plugin <command>${RESET$4} — Claude Code plugin 管理`);
|
|
7582
|
-
console.log();
|
|
7583
|
-
console.log(`${BOLD$2}Commands:${RESET$4}`);
|
|
7584
|
-
console.log(` create <name> Scaffold a plugin directory (skills/, agents/, hooks/, mcp/)`);
|
|
7585
|
-
console.log(` validate [path] Validate plugin.json and directory layout`);
|
|
7586
|
-
console.log(` publish <path> Publish a plugin to SkillHub`);
|
|
7587
|
-
console.log(` list, ls List plugins on SkillHub`);
|
|
7588
|
-
console.log(` info <id> Show plugin details`);
|
|
7589
|
-
console.log(` install <ref> Install a plugin (slug, plugin id, or local .zip)`);
|
|
7590
|
-
console.log();
|
|
7591
|
-
}
|
|
7592
|
-
async function createScaffold(args) {
|
|
7593
|
-
const name = args.find((a, i) => i === 0 || args[i - 1] !== "--with" && !a.startsWith("-"));
|
|
7594
|
-
if (!name || name.startsWith("-")) {
|
|
7595
|
-
console.error("用法:bfi plugin create <name> [--with skills agents hooks mcp]");
|
|
7596
|
-
process.exit(1);
|
|
7597
|
-
}
|
|
7598
|
-
const withIdx = args.indexOf("--with");
|
|
7599
|
-
const components = withIdx !== -1 ? args.slice(withIdx + 1) : [];
|
|
7600
|
-
const target = path$1.resolve(name);
|
|
7601
|
-
if (fs.existsSync(path$1.join(target, ".claude-plugin", "plugin.json"))) {
|
|
7602
|
-
console.error(`plugin 已存在:${target}`);
|
|
7603
|
-
process.exit(1);
|
|
7604
|
-
}
|
|
7605
|
-
fs.mkdirSync(path$1.join(target, ".claude-plugin"), { recursive: true });
|
|
7606
|
-
const manifest = {
|
|
7607
|
-
$schema: "https://anthropic.com/claude-code/plugin.schema.json",
|
|
7608
|
-
name: path$1.basename(target),
|
|
7609
|
-
version: "0.1.0",
|
|
7610
|
-
description: "",
|
|
7611
|
-
author: { name: "" },
|
|
7612
|
-
...components.includes("skills") ? { skills: ["./skills"] } : {}
|
|
7613
|
-
};
|
|
7614
|
-
fs.writeFileSync(path$1.join(target, ".claude-plugin", "plugin.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
7615
|
-
if (components.includes("skills")) fs.mkdirSync(path$1.join(target, "skills"), { recursive: true });
|
|
7616
|
-
if (components.includes("agents")) fs.mkdirSync(path$1.join(target, "agents"), { recursive: true });
|
|
7617
|
-
if (components.includes("hooks")) {
|
|
7618
|
-
fs.mkdirSync(path$1.join(target, "hooks"), { recursive: true });
|
|
7619
|
-
fs.writeFileSync(path$1.join(target, "hooks", "hooks.json"), JSON.stringify({ hooks: {} }, null, 2) + "\n");
|
|
7620
|
-
}
|
|
7621
|
-
if (components.includes("mcp")) fs.writeFileSync(path$1.join(target, ".mcp.json"), JSON.stringify({ mcpServers: {} }, null, 2) + "\n");
|
|
7622
|
-
console.log(`${GREEN$3}✓${RESET$4} Created plugin ${TEXT$4}${path$1.basename(target)}${RESET$4}`);
|
|
7623
|
-
console.log(` ${DIM$4}${target}${RESET$4}`);
|
|
7624
|
-
}
|
|
7625
|
-
async function validate(args) {
|
|
7626
|
-
const target = path$1.resolve(args[0] ?? ".");
|
|
7627
|
-
const standardManifest = path$1.join(target, ".claude-plugin", "plugin.json");
|
|
7628
|
-
const bfiManifest = path$1.join(target, "plugin.json");
|
|
7629
|
-
const manifestPath = fs.existsSync(standardManifest) ? standardManifest : fs.existsSync(bfiManifest) ? bfiManifest : standardManifest;
|
|
7630
|
-
const isBfiLayout = manifestPath === bfiManifest;
|
|
7631
|
-
const errors = [];
|
|
7632
|
-
const warnings = [];
|
|
7633
|
-
if (!fs.existsSync(manifestPath)) errors.push(`未找到 ${manifestPath}`);
|
|
7634
|
-
else try {
|
|
7635
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
7636
|
-
if (!manifest.name) errors.push("plugin.json 缺少 name 字段");
|
|
7637
|
-
if (!manifest.version) warnings.push("plugin.json 缺少 version 字段");
|
|
7638
|
-
if (!manifest.description) warnings.push("plugin.json 缺少 description 字段");
|
|
7639
|
-
const skillPaths = manifest.skills ?? manifest.provides?.skills ?? [];
|
|
7640
|
-
for (const skillPath of skillPaths) {
|
|
7641
|
-
const normalized = skillPath.startsWith("./") ? skillPath.slice(2) : skillPath;
|
|
7642
|
-
if (!isBfiLayout && !skillPath.startsWith("./")) errors.push(`skills 路径必须以 ./ 开头:${skillPath}`);
|
|
7643
|
-
else if (!fs.existsSync(path$1.join(target, normalized))) errors.push(`skills 路径不存在:${skillPath}`);
|
|
7644
|
-
}
|
|
7645
|
-
} catch (e) {
|
|
7646
|
-
errors.push(`plugin.json 解析失败:${e.message}`);
|
|
7647
|
-
}
|
|
7648
|
-
if (fs.existsSync(path$1.join(target, ".mcp.json"))) try {
|
|
7649
|
-
JSON.parse(fs.readFileSync(path$1.join(target, ".mcp.json"), "utf-8"));
|
|
7650
|
-
} catch (e) {
|
|
7651
|
-
errors.push(`.mcp.json 解析失败:${e.message}`);
|
|
7652
|
-
}
|
|
7653
|
-
if (fs.existsSync(path$1.join(target, "hooks", "hooks.json"))) try {
|
|
7654
|
-
JSON.parse(fs.readFileSync(path$1.join(target, "hooks", "hooks.json"), "utf-8"));
|
|
7655
|
-
} catch (e) {
|
|
7656
|
-
errors.push(`hooks/hooks.json 解析失败:${e.message}`);
|
|
7657
|
-
}
|
|
7658
|
-
for (const error of errors) console.log(`${RED$2}✗${RESET$4} ${error}`);
|
|
7659
|
-
for (const warning of warnings) console.log(`${DIM$4}⚠${RESET$4} ${warning}`);
|
|
7660
|
-
if (errors.length === 0) console.log(`${GREEN$3}✓${RESET$4} Validation passed${warnings.length > 0 ? ` with ${warnings.length} warning(s)` : ""}`);
|
|
7661
|
-
else process.exitCode = 1;
|
|
7662
|
-
}
|
|
7663
|
-
async function publish(args, globalOpts) {
|
|
7664
|
-
const target = args[0];
|
|
7665
|
-
if (!target) {
|
|
7666
|
-
console.error("用法:bfi plugin publish <directory|file.zip|file.tgz>");
|
|
7667
|
-
process.exit(1);
|
|
7668
|
-
}
|
|
7669
|
-
const resolved = path$1.resolve(target);
|
|
7670
|
-
const namespace = getOption$1(args, "--namespace") || globalOpts.namespace || "global";
|
|
7671
|
-
const visibility = getOption$1(args, "--visibility") || "public";
|
|
7672
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7673
|
-
if (!token) {
|
|
7674
|
-
console.error("未登录。请先运行:bfi auth login --token <token>");
|
|
7675
|
-
process.exit(1);
|
|
7676
|
-
}
|
|
7677
|
-
const api = new SkillHubAPI(registry, token);
|
|
7678
|
-
if (/\.(tgz|tar\.gz|zip)$/i.test(resolved)) {
|
|
7679
|
-
if (!fs.existsSync(resolved)) {
|
|
7680
|
-
console.error(`文件未找到:${resolved}`);
|
|
7681
|
-
process.exit(1);
|
|
7682
|
-
}
|
|
7683
|
-
console.log(`上传 ${TEXT$4}${path$1.basename(resolved)}${RESET$4} → ${DIM$4}${registry}${RESET$4}`);
|
|
7684
|
-
try {
|
|
7685
|
-
const created = await api.uploadPlugin(fs.readFileSync(resolved), path$1.basename(resolved), namespace, { visibility: visibility.toUpperCase() });
|
|
7686
|
-
const plugin = created?.data ?? created;
|
|
7687
|
-
console.log(`${GREEN$3}✓${RESET$4} Published ${TEXT$4}${plugin.slug}${RESET$4} (id=${plugin.id})`);
|
|
7688
|
-
} catch (e) {
|
|
7689
|
-
console.error(`发布失败:${e.message}`);
|
|
7690
|
-
process.exit(1);
|
|
7691
|
-
}
|
|
7692
|
-
return;
|
|
7693
|
-
}
|
|
7694
|
-
const standardManifest = path$1.join(resolved, ".claude-plugin", "plugin.json");
|
|
7695
|
-
const bfiManifest = path$1.join(resolved, "plugin.json");
|
|
7696
|
-
const manifestPath = fs.existsSync(standardManifest) ? standardManifest : fs.existsSync(bfiManifest) ? bfiManifest : null;
|
|
7697
|
-
if (!manifestPath) {
|
|
7698
|
-
console.error(`不是有效 plugin 目录(缺少 .claude-plugin/plugin.json 或 plugin.json):${resolved}`);
|
|
7699
|
-
process.exit(1);
|
|
7700
|
-
}
|
|
7701
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
7702
|
-
const namespaces = await api.myNamespaces();
|
|
7703
|
-
const ns = (Array.isArray(namespaces) ? namespaces : namespaces?.data ?? []).find((n) => n.slug === namespace);
|
|
7704
|
-
if (!ns) {
|
|
7705
|
-
console.error(`命名空间不存在:${namespace}`);
|
|
7706
|
-
process.exit(1);
|
|
7707
|
-
}
|
|
7708
|
-
const zipBuffer = createZipFromDir(resolved);
|
|
7709
|
-
const versionMatch = manifest.version ?? "0.1.0";
|
|
7710
|
-
console.log(`发布 ${TEXT$4}${manifest.name}@${versionMatch}${RESET$4} → ${DIM$4}${registry}${RESET$4}`);
|
|
7711
|
-
const created = await api.createPlugin({
|
|
7712
|
-
namespaceId: ns.id,
|
|
7713
|
-
slug: manifest.name,
|
|
7714
|
-
displayName: manifest.name,
|
|
7715
|
-
summary: manifest.description ?? "",
|
|
7716
|
-
description: manifest.description ?? "",
|
|
7717
|
-
visibility: visibility.toUpperCase()
|
|
7718
|
-
});
|
|
7719
|
-
const plugin = created?.data ?? created;
|
|
7720
|
-
const version = await api.createPluginVersion(plugin.id, {
|
|
7721
|
-
version: versionMatch,
|
|
7722
|
-
changelog: "Published via bfi plugin publish"
|
|
7723
|
-
});
|
|
7724
|
-
const versionBody = version?.data ?? version;
|
|
7725
|
-
const tmpZip = path$1.join(os$1.tmpdir(), `${manifest.name}-${versionMatch}.zip`);
|
|
7726
|
-
fs.writeFileSync(tmpZip, zipBuffer);
|
|
7727
|
-
console.log(`${GREEN$3}✓${RESET$4} Plugin created (id=${plugin.id}, version=${versionBody.version})`);
|
|
7728
|
-
console.log(` ${DIM$4}zip: ${tmpZip}${RESET$4}`);
|
|
7729
|
-
}
|
|
7730
|
-
async function list$1(_args, globalOpts) {
|
|
7731
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7732
|
-
const result = await new SkillHubAPI(registry, token).listPlugins();
|
|
7733
|
-
const plugins = Array.isArray(result) ? result : result?.content ?? result?.data ?? [];
|
|
7734
|
-
if (plugins.length === 0) {
|
|
7735
|
-
console.log("暂无 plugin。");
|
|
7736
|
-
return;
|
|
7737
|
-
}
|
|
7738
|
-
console.log();
|
|
7739
|
-
for (const p of plugins) {
|
|
7740
|
-
console.log(` ${TEXT$4}${p.slug}${RESET$4} ${DIM$4}${p.latestVersionId ? "v" + p.latestVersionId : ""}${RESET$4}`);
|
|
7741
|
-
console.log(` ${p.summary ?? ""}`);
|
|
7742
|
-
console.log(` ${DIM$4}下载 ${p.downloadCount ?? 0} · star ${p.starCount ?? 0}${RESET$4}`);
|
|
7743
|
-
}
|
|
7744
|
-
console.log();
|
|
7745
|
-
}
|
|
7746
|
-
async function info$1(args, globalOpts) {
|
|
7747
|
-
const idArg = args[0];
|
|
7748
|
-
if (!idArg) {
|
|
7749
|
-
console.error("用法:bfi plugin info <pluginId>");
|
|
7750
|
-
process.exit(1);
|
|
7751
|
-
}
|
|
7752
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7753
|
-
const plugin = await new SkillHubAPI(registry, token).getPlugin(Number(idArg));
|
|
7754
|
-
console.log();
|
|
7755
|
-
console.log(`${BOLD$2}${plugin.displayName ?? plugin.slug}${RESET$4} (id=${plugin.id})`);
|
|
7756
|
-
console.log(` ${plugin.summary ?? ""}`);
|
|
7757
|
-
console.log(` ${DIM$4}visibility=${plugin.visibility} status=${plugin.status}${RESET$4}`);
|
|
7758
|
-
console.log(` ${DIM$4}下载 ${plugin.downloadCount ?? 0} · star ${plugin.starCount ?? 0}${RESET$4}`);
|
|
7759
|
-
console.log();
|
|
7760
|
-
}
|
|
7761
|
-
async function install(args, globalOpts) {
|
|
7762
|
-
const ref = args[0];
|
|
7763
|
-
if (!ref) {
|
|
7764
|
-
console.error("用法:bfi plugin install <slug|插件ID|./plugin.zip>");
|
|
7765
|
-
process.exit(1);
|
|
7766
|
-
}
|
|
7767
|
-
const homeSkills = path$1.join(os$1.homedir(), ".claude", "skills");
|
|
7768
|
-
if (ref.endsWith(".zip") || fs.existsSync(ref)) {
|
|
7769
|
-
const resolved = path$1.resolve(ref);
|
|
7770
|
-
extractZip(resolved, homeSkills);
|
|
7771
|
-
console.log(`${GREEN$3}✓${RESET$4} Installed from ${resolved}`);
|
|
7772
|
-
console.log(` ${DIM$4}${homeSkills}${RESET$4}`);
|
|
7773
|
-
console.log(` ${DIM$4}重启 Claude Code 会话后生效${RESET$4}`);
|
|
7774
|
-
return;
|
|
7775
|
-
}
|
|
7776
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7777
|
-
const api = new SkillHubAPI(registry, token);
|
|
7778
|
-
let plugin;
|
|
7779
|
-
if (/^\d+$/.test(ref)) plugin = await api.getPlugin(Number(ref));
|
|
7780
|
-
else {
|
|
7781
|
-
const list = await api.listPlugins();
|
|
7782
|
-
const match = (Array.isArray(list) ? list : list?.content ?? list?.data ?? []).find((p) => p.slug === ref || p.displayName === ref);
|
|
7783
|
-
if (!match) {
|
|
7784
|
-
console.error(`未找到插件:${ref}(支持传 slug 或插件 ID)`);
|
|
7785
|
-
process.exit(1);
|
|
7786
|
-
}
|
|
7787
|
-
plugin = await api.getPlugin(match.id);
|
|
7788
|
-
}
|
|
7789
|
-
const slug = plugin.slug ?? `plugin-${plugin.id}`;
|
|
7790
|
-
if (!plugin.latestVersionId) {
|
|
7791
|
-
console.error(`插件 ${slug} 尚无已发布版本,无法安装`);
|
|
7792
|
-
process.exit(1);
|
|
7793
|
-
}
|
|
7794
|
-
const tmpZip = path$1.join(os$1.tmpdir(), `${slug}-install.zip`);
|
|
7795
|
-
console.log(`下载 ${TEXT$4}${slug}${RESET$4} ← ${DIM$4}${registry}${RESET$4}`);
|
|
7796
|
-
await api.downloadPluginBundle(plugin.id, plugin.latestVersionId, tmpZip);
|
|
7797
|
-
extractZip(tmpZip, homeSkills);
|
|
7798
|
-
fs.unlinkSync(tmpZip);
|
|
7799
|
-
console.log(`${GREEN$3}✓${RESET$4} Installed ${TEXT$4}${slug}${RESET$4}`);
|
|
7800
|
-
console.log(` ${DIM$4}${homeSkills}${RESET$4}`);
|
|
7801
|
-
console.log(` ${DIM$4}重启 Claude Code 会话后生效${RESET$4}`);
|
|
7802
|
-
}
|
|
7803
|
-
function getOption$1(args, flag) {
|
|
7804
|
-
const idx = args.indexOf(flag);
|
|
7805
|
-
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
7806
|
-
}
|
|
7807
|
-
const RESET$3 = "\x1B[0m";
|
|
7808
|
-
const BOLD$1 = "\x1B[1m";
|
|
7809
|
-
const DIM$3 = "\x1B[38;5;102m";
|
|
7810
|
-
const TEXT$3 = "\x1B[38;5;145m";
|
|
7811
|
-
const GREEN$2 = "\x1B[32m";
|
|
7812
|
-
const RED$1 = "\x1B[31m";
|
|
7813
|
-
async function runMcp(args, globalOpts) {
|
|
7814
|
-
const sub = args[0];
|
|
7815
|
-
const subArgs = args.slice(1);
|
|
7816
|
-
switch (sub) {
|
|
7817
|
-
case "list":
|
|
7818
|
-
case "ls":
|
|
7819
|
-
await list(subArgs, globalOpts);
|
|
7820
|
-
break;
|
|
7821
|
-
case "info":
|
|
7822
|
-
await info(subArgs, globalOpts);
|
|
7823
|
-
break;
|
|
7824
|
-
case "config":
|
|
7825
|
-
await config(subArgs, globalOpts);
|
|
7826
|
-
break;
|
|
7827
|
-
case "add":
|
|
7828
|
-
await add(subArgs, globalOpts);
|
|
7829
|
-
break;
|
|
7830
|
-
case "remove":
|
|
7831
|
-
case "rm":
|
|
7832
|
-
await remove(subArgs);
|
|
7833
|
-
break;
|
|
7834
|
-
case "--help":
|
|
7835
|
-
case "-h":
|
|
7836
|
-
case void 0:
|
|
7837
|
-
showHelp$1();
|
|
7838
|
-
break;
|
|
7839
|
-
default:
|
|
7840
|
-
console.log(`未知 mcp 命令:${sub}`);
|
|
7841
|
-
showHelp$1();
|
|
7842
|
-
}
|
|
7843
|
-
}
|
|
7844
|
-
function showHelp$1() {
|
|
7845
|
-
console.log();
|
|
7846
|
-
console.log(`${BOLD$1}bfi mcp <command>${RESET$3} — MCP 服务器管理`);
|
|
7847
|
-
console.log();
|
|
7848
|
-
console.log(`${BOLD$1}Commands:${RESET$3}`);
|
|
7849
|
-
console.log(` list, ls List MCP servers on SkillHub`);
|
|
7850
|
-
console.log(` info <id> Show MCP server details`);
|
|
7851
|
-
console.log(` config <id> Print .mcp.json config fragment`);
|
|
7852
|
-
console.log(` add <id> Write server into local .mcp.json (user scope)`);
|
|
7853
|
-
console.log(` remove <name> Remove a server from local .mcp.json`);
|
|
7854
|
-
console.log();
|
|
7855
|
-
}
|
|
7856
|
-
async function list(_args, globalOpts) {
|
|
7857
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7858
|
-
const result = await new SkillHubAPI(registry, token).listMcpServers();
|
|
7859
|
-
const servers = Array.isArray(result) ? result : result?.content ?? result?.data ?? [];
|
|
7860
|
-
if (servers.length === 0) {
|
|
7861
|
-
console.log("暂无 MCP 服务器。");
|
|
7862
|
-
return;
|
|
7863
|
-
}
|
|
7864
|
-
console.log();
|
|
7865
|
-
for (const s of servers) {
|
|
7866
|
-
console.log(` ${TEXT$3}${s.slug}${RESET$3} ${DIM$3}[${s.transportType}]${RESET$3}`);
|
|
7867
|
-
console.log(` ${s.summary ?? ""}`);
|
|
7868
|
-
console.log(` ${DIM$3}下载 ${s.downloadCount ?? 0} · star ${s.starCount ?? 0}${RESET$3}`);
|
|
7869
|
-
}
|
|
7870
|
-
console.log();
|
|
7871
|
-
}
|
|
7872
|
-
async function info(args, globalOpts) {
|
|
7873
|
-
const idArg = args[0];
|
|
7874
|
-
if (!idArg) {
|
|
7875
|
-
console.error("用法:bfi mcp info <mcpId>");
|
|
7876
|
-
process.exit(1);
|
|
7877
|
-
}
|
|
7878
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7879
|
-
const server = await new SkillHubAPI(registry, token).getMcpServer(Number(idArg));
|
|
7880
|
-
console.log();
|
|
7881
|
-
console.log(`${BOLD$1}${server.displayName ?? server.slug}${RESET$3} (id=${server.id})`);
|
|
7882
|
-
console.log(` transport: ${server.transportType}`);
|
|
7883
|
-
if (server.serverUrl) console.log(` url: ${server.serverUrl}`);
|
|
7884
|
-
if (server.command) console.log(` command: ${server.command}`);
|
|
7885
|
-
console.log(` ${DIM$3}visibility=${server.visibility} status=${server.status}${RESET$3}`);
|
|
7886
|
-
console.log();
|
|
7887
|
-
}
|
|
7888
|
-
async function config(args, globalOpts) {
|
|
7889
|
-
const idArg = args[0];
|
|
7890
|
-
if (!idArg) {
|
|
7891
|
-
console.error("用法:bfi mcp config <mcpId>");
|
|
7892
|
-
process.exit(1);
|
|
7893
|
-
}
|
|
7894
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7895
|
-
const configJson = await new SkillHubAPI(registry, token).getMcpConfig(Number(idArg));
|
|
7896
|
-
console.log(configJson);
|
|
7897
|
-
}
|
|
7898
|
-
async function add(args, globalOpts) {
|
|
7899
|
-
const idArg = args[0];
|
|
7900
|
-
if (!idArg) {
|
|
7901
|
-
console.error("用法:bfi mcp add <mcpId>");
|
|
7902
|
-
process.exit(1);
|
|
7903
|
-
}
|
|
7904
|
-
const { registry, token } = resolveConfig(globalOpts);
|
|
7905
|
-
const api = new SkillHubAPI(registry, token);
|
|
7906
|
-
const server = await api.getMcpServer(Number(idArg));
|
|
7907
|
-
const configJson = JSON.parse(await api.getMcpConfig(Number(idArg)));
|
|
7908
|
-
const mcpPath = resolveMcpPath(getOption(args, "--scope") ?? "user");
|
|
7909
|
-
let existing = { mcpServers: {} };
|
|
7910
|
-
if (fs.existsSync(mcpPath)) {
|
|
7911
|
-
existing = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
7912
|
-
if (!existing.mcpServers) existing.mcpServers = {};
|
|
7913
|
-
}
|
|
7914
|
-
const serverName = server.slug ?? `mcp-${server.id}`;
|
|
7915
|
-
existing.mcpServers[serverName] = configJson.mcpServers[Object.keys(configJson.mcpServers)[0]];
|
|
7916
|
-
fs.mkdirSync(path$1.dirname(mcpPath), { recursive: true });
|
|
7917
|
-
fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
7918
|
-
console.log(`${GREEN$2}✓${RESET$3} Added ${TEXT$3}${serverName}${RESET$3} → ${DIM$3}${mcpPath}${RESET$3}`);
|
|
7919
|
-
}
|
|
7920
|
-
async function remove(args) {
|
|
7921
|
-
const name = args[0];
|
|
7922
|
-
if (!name) {
|
|
7923
|
-
console.error("用法:bfi mcp remove <name> [--scope user|project|local]");
|
|
7924
|
-
process.exit(1);
|
|
7925
|
-
}
|
|
7926
|
-
const mcpPath = resolveMcpPath(getOption(args, "--scope") ?? "user");
|
|
7927
|
-
if (!fs.existsSync(mcpPath)) {
|
|
7928
|
-
console.error(`配置文件不存在:${mcpPath}`);
|
|
7929
|
-
process.exit(1);
|
|
7930
|
-
}
|
|
7931
|
-
const existing = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
7932
|
-
if (!existing.mcpServers?.[name]) {
|
|
7933
|
-
console.error(`${RED$1}未找到服务器:${name}${RESET$3}`);
|
|
7934
|
-
process.exit(1);
|
|
7935
|
-
}
|
|
7936
|
-
delete existing.mcpServers[name];
|
|
7937
|
-
fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
7938
|
-
console.log(`${GREEN$2}✓${RESET$3} Removed ${TEXT$3}${name}${RESET$3}`);
|
|
7939
|
-
}
|
|
7940
|
-
function resolveMcpPath(scope) {
|
|
7941
|
-
switch (scope) {
|
|
7942
|
-
case "project": return path$1.join(process.cwd(), ".mcp.json");
|
|
7943
|
-
case "local": return path$1.join(process.cwd(), ".claude", ".mcp.json.local");
|
|
7944
|
-
default: return path$1.join(os$1.homedir(), ".claude", ".mcp.json");
|
|
7945
|
-
}
|
|
7946
|
-
}
|
|
7947
|
-
function getOption(args, flag) {
|
|
7948
|
-
const idx = args.indexOf(flag);
|
|
7949
|
-
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
7950
|
-
}
|
|
7951
7718
|
const RESET$2 = "\x1B[0m";
|
|
7952
7719
|
const DIM$2 = "\x1B[38;5;102m";
|
|
7953
7720
|
const TEXT$2 = "\x1B[38;5;145m";
|
|
@@ -9291,22 +9058,37 @@ async function runEvent(args, globalOpts) {
|
|
|
9291
9058
|
process.exit(1);
|
|
9292
9059
|
}
|
|
9293
9060
|
function printHelp$1() {
|
|
9294
|
-
console.log(`bfi ms —
|
|
9061
|
+
console.log(`bfi ms — 微服务/二方包 查询(编码 → 仓库列表 + 归属画像)
|
|
9295
9062
|
|
|
9296
9063
|
用法:
|
|
9297
|
-
bfi ms
|
|
9298
|
-
bfi ms --keyword <名称关键词> # 按微服务中文名模糊查询
|
|
9064
|
+
bfi ms <关键词> # 编码或名称片段,双列 OR 命中(微服务 + 二方包一并返回)
|
|
9299
9065
|
全局: --json / --dry-run / --token <tok>
|
|
9066
|
+
可选: --limit <n>(默认 50)
|
|
9300
9067
|
|
|
9301
|
-
输出:
|
|
9302
|
-
|
|
9068
|
+
输出: 分类(微服务/二方包)/编码/名称/类型/领域云/领域/部门/数据库/rpcDomain/简介/
|
|
9069
|
+
流水线名称+编码/脚本流水线+路径/仓库列表/人员信息/关联前端/关联后端
|
|
9303
9070
|
|
|
9304
9071
|
示例:
|
|
9305
|
-
bfi ms yonbip-
|
|
9306
|
-
bfi ms
|
|
9072
|
+
bfi ms yonbip-am-aum
|
|
9073
|
+
bfi ms 库存
|
|
9307
9074
|
|
|
9308
9075
|
提示: 事件监听的 microServiceCode 字段 / 异常栈的包名线索都可用本命令反查仓库。`);
|
|
9309
9076
|
}
|
|
9077
|
+
const KIND_LABEL = {
|
|
9078
|
+
MS: "微服务",
|
|
9079
|
+
SPL: "二方包"
|
|
9080
|
+
};
|
|
9081
|
+
const STATUS_LABEL = {
|
|
9082
|
+
0: "已废弃",
|
|
9083
|
+
1: "在用",
|
|
9084
|
+
2: "已停用"
|
|
9085
|
+
};
|
|
9086
|
+
function peopleLine(maintainers) {
|
|
9087
|
+
return maintainers.map((m) => `${m.name || "-"}(${m.role || "-"})`).join(", ");
|
|
9088
|
+
}
|
|
9089
|
+
function relateLine(items) {
|
|
9090
|
+
return items.map((x) => `${x.name || "-"}(${x.code || "-"})`).join(", ");
|
|
9091
|
+
}
|
|
9310
9092
|
async function runMs(args, globalOpts) {
|
|
9311
9093
|
const rest = args;
|
|
9312
9094
|
if (!args[0] || flag(rest, "--help") || flag(rest, "-h")) {
|
|
@@ -9320,32 +9102,41 @@ async function runMs(args, globalOpts) {
|
|
|
9320
9102
|
console.error("未认证。先运行:bfi auth login --token <sk-token>(或传 --token)");
|
|
9321
9103
|
process.exit(1);
|
|
9322
9104
|
}
|
|
9323
|
-
const keyword =
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
console.error("缺少微服务编码或 --keyword。运行 bfi ms --help 查看用法。");
|
|
9105
|
+
const keyword = positionals(rest)[0] ?? "";
|
|
9106
|
+
if (!keyword.trim()) {
|
|
9107
|
+
console.error("缺少检索词。运行 bfi ms --help 查看用法。");
|
|
9327
9108
|
process.exit(1);
|
|
9328
9109
|
}
|
|
9329
9110
|
const body = {
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
use_status: opt(rest, "--use-status") ?? "1",
|
|
9333
|
-
page: Number(opt(rest, "--page") ?? 1),
|
|
9334
|
-
size: Number(opt(rest, "--size") ?? 20)
|
|
9111
|
+
keyword,
|
|
9112
|
+
limit: Number(opt(rest, "--limit") ?? 50)
|
|
9335
9113
|
};
|
|
9336
9114
|
emit(await api.call("code/microservice", body), jsonMode, (data) => {
|
|
9337
9115
|
const d = data;
|
|
9338
9116
|
const rows = d.rows ?? [];
|
|
9339
|
-
if (!rows.length) return
|
|
9340
|
-
return rows.map((r) => {
|
|
9117
|
+
if (!rows.length) return `无匹配(total=${d.total ?? 0})`;
|
|
9118
|
+
return (d.truncated ? `⚠ 命中 ${d.total} 行,仅展示 ${rows.length} 行,请收窄关键词\n\n` : "") + rows.map((r) => {
|
|
9341
9119
|
const repos = r.repos ?? [];
|
|
9342
|
-
const
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
`
|
|
9346
|
-
`
|
|
9347
|
-
|
|
9348
|
-
]
|
|
9120
|
+
const kind = KIND_LABEL[String(r.kind)] ?? String(r.kind ?? "");
|
|
9121
|
+
const status = STATUS_LABEL[String(r.use_status)] ?? String(r.use_status ?? "-");
|
|
9122
|
+
const lines = [
|
|
9123
|
+
`[${kind}] ${r.code} ${r.name} (${r.type || "-"}, ${status})`,
|
|
9124
|
+
` 领域云: ${r.domain_cloud || "-"} | 领域: ${r.bfi_domain || "-"} | 部门: ${r.department || "-"}`,
|
|
9125
|
+
` 数据库: ${r.schema || "-"}${r.rpc_domain ? ` | rpcDomain: ${r.rpc_domain}` : ""}`
|
|
9126
|
+
];
|
|
9127
|
+
if (r.description) lines.push(` 简介: ${r.description}`);
|
|
9128
|
+
if (r.pipeline_name || r.pipeline_code || r.bfi_pipeline_code) lines.push(` 流水线: ${r.pipeline_name || "-"} (${r.pipeline_code || "-"}) | 脚本: ${r.bfi_pipeline_code || "-"} @ ${r.bfi_script_path || "-"}`);
|
|
9129
|
+
lines.push(` 仓库(${repos.length}):`);
|
|
9130
|
+
lines.push(repos.length ? repos.map((x) => ` - ${x.repo} (${x.url}${x.branch ? ` @${x.branch}` : ""})`).join("\n") : " (无 git 地址)");
|
|
9131
|
+
const people = r.maintainers ?? [];
|
|
9132
|
+
if (people.length) lines.push(` 人员: ${peopleLine(people)}`);
|
|
9133
|
+
const rf = r.related_front ?? [];
|
|
9134
|
+
const rb = r.related_backend ?? [];
|
|
9135
|
+
const rfLabel = r.type === "isFe" ? "关联前端二方包" : "关联前端";
|
|
9136
|
+
if (rf.length) lines.push(` ${rfLabel}: ${relateLine(rf)}`);
|
|
9137
|
+
if (rb.length) lines.push(` 关联后端: ${relateLine(rb)}`);
|
|
9138
|
+
if (r.product_name || r.msc_code) lines.push(` 所属应用: ${r.product_name || "-"} | 提供方微服务: ${r.msc_code || "-"}`);
|
|
9139
|
+
return lines.join("\n");
|
|
9349
9140
|
}).join("\n\n");
|
|
9350
9141
|
});
|
|
9351
9142
|
}
|
|
@@ -9769,21 +9560,11 @@ ${BOLD}━━━ 技能包管理 ━━━${RESET}
|
|
|
9769
9560
|
skills package create <slug> [--name] [--description] 创建包
|
|
9770
9561
|
skills package add <pkg> <skill...> 添加技能到包
|
|
9771
9562
|
skills package remove <pkg> <skill...> 从包移除技能
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
plugin list 列出插件
|
|
9778
|
-
plugin info <id> 查看插件详情
|
|
9779
|
-
plugin install <slug|id|file.zip> 安装插件(名称 / ID / 本地压缩包)
|
|
9780
|
-
|
|
9781
|
-
${BOLD}━━━ MCP 服务器 ━━━${RESET}
|
|
9782
|
-
mcp list 列出 MCP 服务器
|
|
9783
|
-
mcp info <id> 查看详情
|
|
9784
|
-
mcp config <id> 打印 .mcp.json 配置
|
|
9785
|
-
mcp add <id> 写入本地 .mcp.json
|
|
9786
|
-
mcp remove <name> 从本地 .mcp.json 移除
|
|
9563
|
+
skills package install <ref> [--agent <a>] 安装技能组合(默认,不安装插件组件)
|
|
9564
|
+
--as plugin 显式完整插件模式;支持本地 zip/tgz,不兼容时不会降级安装
|
|
9565
|
+
--plan 只读校验安装计划;--export <archive> 仅导出,不安装
|
|
9566
|
+
--agent <a> 目标 agent(如 claude-code、codex、zcode)
|
|
9567
|
+
--global 安装到主目录(跨项目可用)
|
|
9787
9568
|
|
|
9788
9569
|
${BOLD}━━━ 文档 ━━━${RESET}
|
|
9789
9570
|
docs upload <file> 上传文档
|
|
@@ -9876,7 +9657,7 @@ function runInit(args) {
|
|
|
9876
9657
|
return;
|
|
9877
9658
|
}
|
|
9878
9659
|
if (hasName) mkdirSync(skillDir, { recursive: true });
|
|
9879
|
-
|
|
9660
|
+
const skillContent = `---
|
|
9880
9661
|
name: ${skillName}
|
|
9881
9662
|
description: A brief description of what this skill does
|
|
9882
9663
|
---
|
|
@@ -9894,7 +9675,8 @@ Describe when this skill should be used.
|
|
|
9894
9675
|
1. First step
|
|
9895
9676
|
2. Second step
|
|
9896
9677
|
3. Additional steps as needed
|
|
9897
|
-
|
|
9678
|
+
`;
|
|
9679
|
+
writeFileSync(skillFile, skillContent);
|
|
9898
9680
|
console.log(`${TEXT}已初始化技能:${DIM}${skillName}${RESET}`);
|
|
9899
9681
|
console.log();
|
|
9900
9682
|
console.log(`${DIM}已创建:${RESET}`);
|
|
@@ -10052,6 +9834,10 @@ async function main() {
|
|
|
10052
9834
|
}
|
|
10053
9835
|
const command = args[0];
|
|
10054
9836
|
const restArgs = args.slice(1);
|
|
9837
|
+
try {
|
|
9838
|
+
const { maybePrintAnalyticsNotice } = await Promise.resolve().then(() => analytics_exports);
|
|
9839
|
+
maybePrintAnalyticsNotice();
|
|
9840
|
+
} catch {}
|
|
10055
9841
|
const globalOpts = {};
|
|
10056
9842
|
const nsIdx = args.indexOf("--namespace");
|
|
10057
9843
|
if (nsIdx !== -1 && nsIdx + 1 < args.length) globalOpts.namespace = args[nsIdx + 1];
|
|
@@ -10060,8 +9846,9 @@ async function main() {
|
|
|
10060
9846
|
const tokIdx = args.indexOf("--token");
|
|
10061
9847
|
if (tokIdx !== -1 && tokIdx + 1 < args.length) globalOpts.token = args[tokIdx + 1];
|
|
10062
9848
|
globalOpts.workdir = process.cwd();
|
|
9849
|
+
globalOpts.version = VERSION;
|
|
10063
9850
|
try {
|
|
10064
|
-
const { resolveConfig } = await
|
|
9851
|
+
const { resolveConfig } = await import("./_chunks/config.mjs").then((n) => n.config_exports);
|
|
10065
9852
|
const { resolveAgents } = await import("./_chunks/agents2.mjs");
|
|
10066
9853
|
const { SkillHubAPI } = await Promise.resolve().then(() => api_exports);
|
|
10067
9854
|
const { setAgents } = await import("./_chunks/agents.mjs").then((n) => n.agents_exports);
|
|
@@ -10116,12 +9903,6 @@ async function main() {
|
|
|
10116
9903
|
case "pkg":
|
|
10117
9904
|
await runPackage(restArgs, globalOpts);
|
|
10118
9905
|
break;
|
|
10119
|
-
case "plugin":
|
|
10120
|
-
await runPlugin(restArgs, globalOpts);
|
|
10121
|
-
break;
|
|
10122
|
-
case "mcp":
|
|
10123
|
-
await runMcp(restArgs, globalOpts);
|
|
10124
|
-
break;
|
|
10125
9906
|
case "log":
|
|
10126
9907
|
await runLog(restArgs, globalOpts);
|
|
10127
9908
|
break;
|
|
@@ -10204,4 +9985,4 @@ main().catch((e) => {
|
|
|
10204
9985
|
console.error(`✗ ${e?.message || e}`);
|
|
10205
9986
|
process.exit(1);
|
|
10206
9987
|
});
|
|
10207
|
-
export {
|
|
9988
|
+
export { isPluginArchive, isPluginVersion, parsePluginArguments };
|