bfi-cli 0.1.26 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/agents.mjs +733 -57
- package/dist/_chunks/agents2.mjs +66 -0
- package/dist/_chunks/libs/@clack/prompts.mjs +44 -1
- package/dist/_chunks/skills.mjs +445 -0
- package/dist/cli.mjs +693 -1608
- package/package.json +1 -1
- package/skills/skillhub-skills/SKILL.md +187 -0
- package/skills/bfi-skill/SKILL.md +0 -176
package/dist/cli.mjs
CHANGED
|
@@ -3,8 +3,9 @@ import { __exportAll, __toESM } from "./_chunks/rolldown-runtime.mjs";
|
|
|
3
3
|
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
|
+
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";
|
|
6
7
|
import { esm_default } from "./_chunks/libs/simple-git.mjs";
|
|
7
|
-
import {
|
|
8
|
+
import { EVE_SUBAGENTS_DIR, agents, detectInstalledAgents, getEveSubagents, getNonUniversalAgents, getUniversalAgents, getVisibleUniversalAgents, isUniversalAgent } from "./_chunks/agents.mjs";
|
|
8
9
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
9
10
|
import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
|
|
10
11
|
import { fileURLToPath } from "url";
|
|
@@ -20,10 +21,8 @@ import http from "node:http";
|
|
|
20
21
|
import os$1 from "node:os";
|
|
21
22
|
import zlib, { gunzipSync, inflateRawSync } from "node:zlib";
|
|
22
23
|
import { promisify } from "util";
|
|
23
|
-
import { execFile,
|
|
24
|
+
import { execFile, spawn, spawnSync } from "child_process";
|
|
24
25
|
import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
25
|
-
import { parse } from "yaml";
|
|
26
|
-
import { createHash as createHash$1 } from "crypto";
|
|
27
26
|
import { determineAgent } from "@vercel/detect-agent";
|
|
28
27
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
29
28
|
const GIT_HOST = process.env.BFI_GIT_HOST || "git.yyrd.com";
|
|
@@ -283,18 +282,6 @@ function isWellKnownUrl(input) {
|
|
|
283
282
|
return false;
|
|
284
283
|
}
|
|
285
284
|
}
|
|
286
|
-
const CSI_RE = /\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g;
|
|
287
|
-
const OSC_RE = /\x1b\][\s\S]*?(?:\x07|\x1b\\)/g;
|
|
288
|
-
const DCS_PM_APC_RE = /\x1b[P^_][\s\S]*?(?:\x1b\\)/g;
|
|
289
|
-
const SIMPLE_ESC_RE = /\x1b[\x20-\x7e]/g;
|
|
290
|
-
const C1_RE = /[\x80-\x9f]/g;
|
|
291
|
-
const CONTROL_RE = /[\x00-\x06\x07\x08\x0b\x0c\x0d-\x1a\x1c-\x1f\x7f]/g;
|
|
292
|
-
function stripTerminalEscapes(str) {
|
|
293
|
-
return str.replace(OSC_RE, "").replace(DCS_PM_APC_RE, "").replace(CSI_RE, "").replace(SIMPLE_ESC_RE, "").replace(C1_RE, "").replace(CONTROL_RE, "");
|
|
294
|
-
}
|
|
295
|
-
function sanitizeMetadata(str) {
|
|
296
|
-
return stripTerminalEscapes(str).replace(/[\r\n]+/g, " ").trim();
|
|
297
|
-
}
|
|
298
285
|
const silentOutput = new Writable({ write(_chunk, _encoding, callback) {
|
|
299
286
|
callback();
|
|
300
287
|
} });
|
|
@@ -513,7 +500,9 @@ var SkillHubAPI = class {
|
|
|
513
500
|
};
|
|
514
501
|
if (url.protocol === "https:") requestOptions.rejectUnauthorized = this.rejectUnauthorized;
|
|
515
502
|
let bodyStr;
|
|
516
|
-
|
|
503
|
+
let bodyBuf;
|
|
504
|
+
if (options.body !== void 0) if (options.body instanceof Buffer) bodyBuf = options.body;
|
|
505
|
+
else if (options.body instanceof FormData || typeof options.body === "string") bodyStr = options.body;
|
|
517
506
|
else {
|
|
518
507
|
requestOptions.headers["Content-Type"] = "application/json";
|
|
519
508
|
bodyStr = JSON.stringify(options.body);
|
|
@@ -552,7 +541,8 @@ var SkillHubAPI = class {
|
|
|
552
541
|
});
|
|
553
542
|
});
|
|
554
543
|
req.on("error", reject);
|
|
555
|
-
if (
|
|
544
|
+
if (bodyBuf) req.write(bodyBuf);
|
|
545
|
+
else if (bodyStr) req.write(bodyStr);
|
|
556
546
|
req.end();
|
|
557
547
|
});
|
|
558
548
|
}
|
|
@@ -657,12 +647,13 @@ var SkillHubAPI = class {
|
|
|
657
647
|
return (await this.request("DELETE", `/namespaces/${namespace}/packages/${slug}/skills`, { body: { skills } })).body;
|
|
658
648
|
}
|
|
659
649
|
async gitParse(repositoryUrl, branch, directory) {
|
|
660
|
-
|
|
650
|
+
const body = {
|
|
661
651
|
repositoryUrl,
|
|
662
652
|
branch,
|
|
663
|
-
directory,
|
|
664
653
|
token: this.token
|
|
665
|
-
}
|
|
654
|
+
};
|
|
655
|
+
if (directory) body.directory = directory;
|
|
656
|
+
return (await this.request("POST", "/skills/git/parse", { body })).body;
|
|
666
657
|
}
|
|
667
658
|
async gitPublish(sessionId, namespace, visibility, selectedSkills, confirmWarnings) {
|
|
668
659
|
return (await this.request("POST", "/skills/git/publish", { body: {
|
|
@@ -739,51 +730,36 @@ var SkillHubAPI = class {
|
|
|
739
730
|
contentSummary
|
|
740
731
|
} })).body;
|
|
741
732
|
}
|
|
742
|
-
async publishSkill(namespace, zipBuffer, visibility = "
|
|
743
|
-
const url = new URL(this.buildUrl("/skills/publish"));
|
|
744
|
-
const isHttps = url.protocol === "https:";
|
|
745
|
-
const client = isHttps ? https : http;
|
|
746
|
-
const boundary = `----FormBoundary${Date.now()}${Math.random().toString(36).slice(2)}`;
|
|
747
|
-
const header = [
|
|
748
|
-
`--${boundary}`,
|
|
749
|
-
"Content-Disposition: form-data; name=\"file\"; filename=\"skill.zip\"",
|
|
750
|
-
"Content-Type: application/zip",
|
|
751
|
-
"",
|
|
752
|
-
""
|
|
753
|
-
].join("\r\n");
|
|
754
|
-
const footer = `\r\n--${boundary}--\r\n`;
|
|
755
|
-
const body = Buffer.concat([
|
|
756
|
-
Buffer.from(header, "utf8"),
|
|
757
|
-
zipBuffer,
|
|
758
|
-
Buffer.from(footer, "utf8")
|
|
759
|
-
]);
|
|
760
|
-
const requestOptions = {
|
|
761
|
-
method: "POST",
|
|
762
|
-
headers: {
|
|
763
|
-
...this.getHeaders(),
|
|
764
|
-
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
765
|
-
"Content-Length": String(body.length)
|
|
766
|
-
}
|
|
767
|
-
};
|
|
768
|
-
if (isHttps) requestOptions.rejectUnauthorized = this.rejectUnauthorized;
|
|
769
|
-
url.searchParams.set("namespace", namespace);
|
|
770
|
-
url.searchParams.set("visibility", visibility);
|
|
733
|
+
async publishSkill(namespace, zipBuffer, visibility = "public", confirmWarnings = false) {
|
|
771
734
|
return new Promise((resolve, reject) => {
|
|
772
|
-
const
|
|
735
|
+
const crlf = "\r\n";
|
|
736
|
+
const boundary = `----FormBoundary${Date.now()}${Math.random().toString(36).slice(2)}`;
|
|
737
|
+
const header = Buffer.from(`--${boundary}${crlf}Content-Disposition: form-data; name="file"; filename="skill.zip"${crlf}Content-Type: application/zip${crlf}${crlf}`, "utf8");
|
|
738
|
+
const footer = Buffer.from(`${crlf}--${boundary}--${crlf}`, "utf8");
|
|
739
|
+
const body = Buffer.concat([
|
|
740
|
+
header,
|
|
741
|
+
zipBuffer,
|
|
742
|
+
footer
|
|
743
|
+
]);
|
|
744
|
+
const cfm = confirmWarnings ? "true" : "false";
|
|
745
|
+
const url = new URL(this.buildUrl(`/skills/${namespace}/publish?visibility=${visibility}&confirmWarnings=${cfm}`));
|
|
746
|
+
const h = this.getHeaders();
|
|
747
|
+
h["Content-Type"] = `multipart/form-data; boundary=${boundary}`;
|
|
748
|
+
h["Content-Length"] = String(body.length);
|
|
749
|
+
const req = https.request(url, {
|
|
750
|
+
method: "POST",
|
|
751
|
+
headers: h,
|
|
752
|
+
rejectUnauthorized: this.rejectUnauthorized
|
|
753
|
+
}, (res) => {
|
|
773
754
|
let data = "";
|
|
774
755
|
res.on("data", (chunk) => data += chunk.toString());
|
|
775
756
|
res.on("end", () => {
|
|
776
757
|
try {
|
|
777
758
|
const parsed = JSON.parse(data);
|
|
778
759
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) resolve(parsed);
|
|
779
|
-
else {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
error.body = parsed;
|
|
783
|
-
reject(error);
|
|
784
|
-
}
|
|
785
|
-
} catch (err) {
|
|
786
|
-
reject(err);
|
|
760
|
+
else reject(new Error(parsed.msg || parsed.message || `HTTP ${res.statusCode}`));
|
|
761
|
+
} catch {
|
|
762
|
+
reject(new Error(data || `HTTP ${res.statusCode}`));
|
|
787
763
|
}
|
|
788
764
|
});
|
|
789
765
|
});
|
|
@@ -804,13 +780,15 @@ var config_exports = /* @__PURE__ */ __exportAll({
|
|
|
804
780
|
loadProjectConfig: () => loadProjectConfig,
|
|
805
781
|
resolveConfig: () => resolveConfig,
|
|
806
782
|
saveGlobalConfig: () => saveGlobalConfig,
|
|
783
|
+
saveLockfile: () => saveLockfile,
|
|
807
784
|
saveProjectConfig: () => saveProjectConfig,
|
|
808
785
|
setConfigValue: () => setConfigValue
|
|
809
786
|
});
|
|
810
787
|
const GLOBAL_CONFIG_DIR = path$1.join(os$1.homedir(), ".bfi");
|
|
811
788
|
const GLOBAL_CONFIG_FILE = path$1.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
812
789
|
const PROJECT_CONFIG_FILE = ".bfirc";
|
|
813
|
-
const
|
|
790
|
+
const LOCK_FILE_DIR = ".bfi";
|
|
791
|
+
const LOCK_FILE = path$1.join(LOCK_FILE_DIR, "lock.json");
|
|
814
792
|
function resolveConfig(globalOpts = {}) {
|
|
815
793
|
return {
|
|
816
794
|
registry: getConfigValue("registry", globalOpts) || "https://bfi.yyuap.com:811",
|
|
@@ -881,6 +859,18 @@ function loadLockfile(workdir) {
|
|
|
881
859
|
packages: {}
|
|
882
860
|
};
|
|
883
861
|
}
|
|
862
|
+
function saveLockfile(workdir, lock) {
|
|
863
|
+
try {
|
|
864
|
+
const lockDir = path$1.join(workdir, LOCK_FILE_DIR);
|
|
865
|
+
if (!fs.existsSync(lockDir)) fs.mkdirSync(lockDir, { recursive: true });
|
|
866
|
+
const lockPath = path$1.join(workdir, LOCK_FILE);
|
|
867
|
+
const content = JSON.stringify(lock, null, 2);
|
|
868
|
+
fs.writeFileSync(lockPath, content);
|
|
869
|
+
return true;
|
|
870
|
+
} catch (error) {
|
|
871
|
+
throw new Error(`Failed to save lockfile: ${error.message}`);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
884
874
|
function getConfigValue(key, globalOpts = {}) {
|
|
885
875
|
const projectConfig = loadProjectConfig(globalOpts.workdir || process.cwd());
|
|
886
876
|
const globalConfig = loadGlobalConfig();
|
|
@@ -947,6 +937,11 @@ function toCanonicalSlug(namespace, slug) {
|
|
|
947
937
|
function toSkillRefString(namespace, slug) {
|
|
948
938
|
return `@${namespace}/${slug}`;
|
|
949
939
|
}
|
|
940
|
+
var zip_exports = /* @__PURE__ */ __exportAll({
|
|
941
|
+
createZipFromDir: () => createZipFromDir,
|
|
942
|
+
extractZip: () => extractZip,
|
|
943
|
+
extractZipBuffer: () => extractZipBuffer
|
|
944
|
+
});
|
|
950
945
|
async function extractZip(zipPath, targetDir) {
|
|
951
946
|
return new Promise((resolve, reject) => {
|
|
952
947
|
const chunks = [];
|
|
@@ -1011,13 +1006,13 @@ function extractZipBuffer(buffer, targetDir) {
|
|
|
1011
1006
|
} else throw new Error(`Unsupported compression method: ${entry.compressionMethod}`);
|
|
1012
1007
|
}
|
|
1013
1008
|
}
|
|
1014
|
-
function collectFiles
|
|
1009
|
+
function collectFiles(dir, baseDir) {
|
|
1015
1010
|
const result = [];
|
|
1016
1011
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1017
1012
|
for (const entry of entries) {
|
|
1018
1013
|
const fullPath = path$1.join(dir, entry.name);
|
|
1019
1014
|
const relativePath = path$1.relative(baseDir, fullPath);
|
|
1020
|
-
if (entry.isDirectory()) result.push(...collectFiles
|
|
1015
|
+
if (entry.isDirectory()) result.push(...collectFiles(fullPath, baseDir));
|
|
1021
1016
|
else if (entry.isFile()) result.push({
|
|
1022
1017
|
relativePath,
|
|
1023
1018
|
buffer: fs.readFileSync(fullPath)
|
|
@@ -1026,7 +1021,7 @@ function collectFiles$1(dir, baseDir) {
|
|
|
1026
1021
|
return result;
|
|
1027
1022
|
}
|
|
1028
1023
|
function createZipFromDir(sourceDir) {
|
|
1029
|
-
const normalizedFiles = collectFiles
|
|
1024
|
+
const normalizedFiles = collectFiles(sourceDir, sourceDir).map((f) => ({
|
|
1030
1025
|
...f,
|
|
1031
1026
|
relativePath: f.relativePath.replace(/\\/g, "/")
|
|
1032
1027
|
}));
|
|
@@ -1103,6 +1098,14 @@ function crc32(data) {
|
|
|
1103
1098
|
}
|
|
1104
1099
|
return (crc ^ 4294967295) >>> 0;
|
|
1105
1100
|
}
|
|
1101
|
+
var git_exports = /* @__PURE__ */ __exportAll({
|
|
1102
|
+
GitCloneError: () => GitCloneError,
|
|
1103
|
+
cleanupTempDir: () => cleanupTempDir,
|
|
1104
|
+
cloneRepo: () => cloneRepo,
|
|
1105
|
+
isGitHubHttpsCloneUrl: () => isGitHubHttpsCloneUrl,
|
|
1106
|
+
isGitHubSsoAuthError: () => isGitHubSsoAuthError,
|
|
1107
|
+
parseGitHubRepoUrl: () => parseGitHubRepoUrl
|
|
1108
|
+
});
|
|
1106
1109
|
const DEFAULT_CLONE_TIMEOUT_MS = 3e5;
|
|
1107
1110
|
const CLONE_TIMEOUT_MS = (() => {
|
|
1108
1111
|
const raw = process.env.SKILLS_CLONE_TIMEOUT_MS;
|
|
@@ -1236,1208 +1239,58 @@ function buildGitHubAuthError(url, repo, message) {
|
|
|
1236
1239
|
if (repo && isGitHubSsoAuthError(message)) return `GitHub 阻止了 ${url} 的 HTTPS 访问,因为组织强制使用 SAML SSO。\n skills 已尝试您现有的 git 凭据和可用回退方案,但均未成功。\n - 重新授权您的 GitHub 凭据/应用以符合该组织的 SSO 策略\n - 或使用 SSH 重新运行:npx skills add ${repo.sshUrl}\n - 验证访问权限:gh auth status -h github.com 或 ssh -T git@github.com`;
|
|
1237
1240
|
if (repo) return `身份验证失败:${url}。\n - 私有仓库请确保您有访问权限\n - 使用 SSH 重试:npx skills add ${repo.sshUrl}\n - 检查访问权限:gh auth status -h github.com 或 ssh -T git@github.com`;
|
|
1238
1241
|
return `身份验证失败:${url}。\n - 私有仓库请确保您有访问权限\n - SSH:使用 'ssh -T git@github.com' 检查您的密钥\n - HTTPS:运行 'gh auth login' 或配置 git 凭据`;
|
|
1239
|
-
}
|
|
1240
|
-
async function cloneRepo(url, ref) {
|
|
1241
|
-
if (/^ext::/i.test(url)) throw new GitCloneError("不支持的 Git 传输协议:ext", url);
|
|
1242
|
-
const tempDir = await mkdtemp(join(tmpdir(), "skills-"));
|
|
1243
|
-
const cloneOptions = ref ? [
|
|
1244
|
-
"--depth",
|
|
1245
|
-
"1",
|
|
1246
|
-
"--branch",
|
|
1247
|
-
ref
|
|
1248
|
-
] : ["--depth", "1"];
|
|
1249
|
-
const repo = parseGitHubRepoUrl(url);
|
|
1250
|
-
try {
|
|
1251
|
-
await createGitClient().clone(url, tempDir, cloneOptions);
|
|
1252
|
-
return tempDir;
|
|
1253
|
-
} catch (error) {
|
|
1254
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1255
|
-
const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
|
|
1256
|
-
const isAuthError = isAuthFailure(errorMessage);
|
|
1257
|
-
if (isTimeout) {
|
|
1258
|
-
await rm(tempDir, {
|
|
1259
|
-
recursive: true,
|
|
1260
|
-
force: true
|
|
1261
|
-
}).catch(() => {});
|
|
1262
|
-
throw new GitCloneError(`克隆超时(${Math.round(CLONE_TIMEOUT_MS / 1e3)} 秒)。常见原因:\n - 仓库过大:使用 SKILLS_CLONE_TIMEOUT_MS=600000 (10 分钟) 提高超时时间\n - 网络较慢:重试,或手动克隆后将本地路径传给 'skills add'\n - 私有仓库未配置凭据:请确保身份验证已配置\n - SSH:ssh-add -l(检查已加载的密钥)\n - HTTPS:gh auth status(如果使用 GitHub CLI)`, url, true, false);
|
|
1263
|
-
}
|
|
1264
|
-
if (isAuthError && repo && isGitHubHttpsCloneUrl(url)) {
|
|
1265
|
-
try {
|
|
1266
|
-
await resetTempDir(tempDir);
|
|
1267
|
-
if (await tryGhClone(repo, tempDir, ref)) return tempDir;
|
|
1268
|
-
} catch {}
|
|
1269
|
-
try {
|
|
1270
|
-
await resetTempDir(tempDir);
|
|
1271
|
-
await createGitClient({ GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND ?? "ssh -o BatchMode=yes" }).clone(repo.sshUrl, tempDir, cloneOptions);
|
|
1272
|
-
return tempDir;
|
|
1273
|
-
} catch {}
|
|
1274
|
-
}
|
|
1275
|
-
await rm(tempDir, {
|
|
1276
|
-
recursive: true,
|
|
1277
|
-
force: true
|
|
1278
|
-
}).catch(() => {});
|
|
1279
|
-
if (isAuthError) throw new GitCloneError(buildGitHubAuthError(url, repo, errorMessage), url, false, true);
|
|
1280
|
-
throw new GitCloneError(`克隆失败 ${url}:${errorMessage}`, url, false, false);
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
async function cleanupTempDir(dir) {
|
|
1284
|
-
const normalizedDir = normalize(resolve(dir));
|
|
1285
|
-
const normalizedTmpDir = normalize(resolve(tmpdir()));
|
|
1286
|
-
if (!normalizedDir.startsWith(normalizedTmpDir + sep) && normalizedDir !== normalizedTmpDir) throw new Error("Attempted to clean up directory outside of temp directory");
|
|
1287
|
-
await rm(dir, {
|
|
1288
|
-
recursive: true,
|
|
1289
|
-
force: true
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
function parseFrontmatter(raw) {
|
|
1293
|
-
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
1294
|
-
if (!match) return {
|
|
1295
|
-
data: {},
|
|
1296
|
-
content: raw
|
|
1297
|
-
};
|
|
1298
|
-
return {
|
|
1299
|
-
data: parse(match[1]) ?? {},
|
|
1300
|
-
content: match[2] ?? ""
|
|
1301
|
-
};
|
|
1302
|
-
}
|
|
1303
|
-
function isContainedIn(targetPath, basePath) {
|
|
1304
|
-
const normalizedBase = normalize(resolve(basePath));
|
|
1305
|
-
const normalizedTarget = normalize(resolve(targetPath));
|
|
1306
|
-
return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
|
|
1307
|
-
}
|
|
1308
|
-
function isValidRelativePath(path) {
|
|
1309
|
-
return path.startsWith("./");
|
|
1310
|
-
}
|
|
1311
|
-
async function getPluginSkillPaths(basePath) {
|
|
1312
|
-
const searchDirs = [];
|
|
1313
|
-
const addPluginSkillPaths = (pluginBase, skills) => {
|
|
1314
|
-
if (!isContainedIn(pluginBase, basePath)) return;
|
|
1315
|
-
if (skills && skills.length > 0) for (const skillPath of skills) {
|
|
1316
|
-
if (!isValidRelativePath(skillPath)) continue;
|
|
1317
|
-
const skillDir = dirname(join(pluginBase, skillPath));
|
|
1318
|
-
if (isContainedIn(skillDir, basePath)) searchDirs.push(skillDir);
|
|
1319
|
-
}
|
|
1320
|
-
searchDirs.push(join(pluginBase, "skills"));
|
|
1321
|
-
};
|
|
1322
|
-
try {
|
|
1323
|
-
const content = await readFile(join(basePath, ".claude-plugin/marketplace.json"), "utf-8");
|
|
1324
|
-
const manifest = JSON.parse(content);
|
|
1325
|
-
const pluginRoot = manifest.metadata?.pluginRoot;
|
|
1326
|
-
if (pluginRoot === void 0 || isValidRelativePath(pluginRoot)) for (const plugin of manifest.plugins ?? []) {
|
|
1327
|
-
if (typeof plugin.source !== "string" && plugin.source !== void 0) continue;
|
|
1328
|
-
if (plugin.source !== void 0 && !isValidRelativePath(plugin.source)) continue;
|
|
1329
|
-
addPluginSkillPaths(join(basePath, pluginRoot ?? "", plugin.source ?? ""), plugin.skills);
|
|
1330
|
-
}
|
|
1331
|
-
} catch {}
|
|
1332
|
-
try {
|
|
1333
|
-
const content = await readFile(join(basePath, ".claude-plugin/plugin.json"), "utf-8");
|
|
1334
|
-
addPluginSkillPaths(basePath, JSON.parse(content).skills);
|
|
1335
|
-
} catch {}
|
|
1336
|
-
return searchDirs;
|
|
1337
|
-
}
|
|
1338
|
-
async function getPluginGroupings(basePath) {
|
|
1339
|
-
const groupings = /* @__PURE__ */ new Map();
|
|
1340
|
-
try {
|
|
1341
|
-
const content = await readFile(join(basePath, ".claude-plugin/marketplace.json"), "utf-8");
|
|
1342
|
-
const manifest = JSON.parse(content);
|
|
1343
|
-
const pluginRoot = manifest.metadata?.pluginRoot;
|
|
1344
|
-
if (pluginRoot === void 0 || isValidRelativePath(pluginRoot)) for (const plugin of manifest.plugins ?? []) {
|
|
1345
|
-
if (!plugin.name) continue;
|
|
1346
|
-
if (typeof plugin.source !== "string" && plugin.source !== void 0) continue;
|
|
1347
|
-
if (plugin.source !== void 0 && !isValidRelativePath(plugin.source)) continue;
|
|
1348
|
-
const pluginBase = join(basePath, pluginRoot ?? "", plugin.source ?? "");
|
|
1349
|
-
if (!isContainedIn(pluginBase, basePath)) continue;
|
|
1350
|
-
if (plugin.skills && plugin.skills.length > 0) for (const skillPath of plugin.skills) {
|
|
1351
|
-
if (!isValidRelativePath(skillPath)) continue;
|
|
1352
|
-
const skillDir = join(pluginBase, skillPath);
|
|
1353
|
-
if (isContainedIn(skillDir, basePath)) groupings.set(resolve(skillDir), plugin.name);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
} catch {}
|
|
1357
|
-
try {
|
|
1358
|
-
const content = await readFile(join(basePath, ".claude-plugin/plugin.json"), "utf-8");
|
|
1359
|
-
const manifest = JSON.parse(content);
|
|
1360
|
-
if (manifest.name && manifest.skills && manifest.skills.length > 0) for (const skillPath of manifest.skills) {
|
|
1361
|
-
if (!isValidRelativePath(skillPath)) continue;
|
|
1362
|
-
const skillDir = join(basePath, skillPath);
|
|
1363
|
-
if (isContainedIn(skillDir, basePath)) groupings.set(resolve(skillDir), manifest.name);
|
|
1364
|
-
}
|
|
1365
|
-
} catch {}
|
|
1366
|
-
return groupings;
|
|
1367
|
-
}
|
|
1368
|
-
const AGENTS_DIR$1 = ".agents";
|
|
1369
|
-
const SKILL_LOCK_FILE = ".skill-lock.json";
|
|
1370
|
-
const SKILL_LOCK_CURRENT_VERSION = 3;
|
|
1371
|
-
const LOCAL_LOCK_FILE = "skills-lock.json";
|
|
1372
|
-
const LOCAL_LOCK_CURRENT_VERSION = 1;
|
|
1373
|
-
function getSkillLockPath() {
|
|
1374
|
-
const xdgStateHome = process.env.XDG_STATE_HOME;
|
|
1375
|
-
if (xdgStateHome) return join(xdgStateHome, "skills", SKILL_LOCK_FILE);
|
|
1376
|
-
return join(homedir(), AGENTS_DIR$1, SKILL_LOCK_FILE);
|
|
1377
|
-
}
|
|
1378
|
-
function createEmptySkillLockFile() {
|
|
1379
|
-
return {
|
|
1380
|
-
version: SKILL_LOCK_CURRENT_VERSION,
|
|
1381
|
-
skills: {},
|
|
1382
|
-
dismissed: {}
|
|
1383
|
-
};
|
|
1384
|
-
}
|
|
1385
|
-
async function readSkillLock() {
|
|
1386
|
-
const lockPath = getSkillLockPath();
|
|
1387
|
-
try {
|
|
1388
|
-
const content = await readFile(lockPath, "utf-8");
|
|
1389
|
-
const parsed = JSON.parse(content);
|
|
1390
|
-
if (typeof parsed.version !== "number" || !parsed.skills) return createEmptySkillLockFile();
|
|
1391
|
-
if (parsed.version < SKILL_LOCK_CURRENT_VERSION) return createEmptySkillLockFile();
|
|
1392
|
-
return parsed;
|
|
1393
|
-
} catch {
|
|
1394
|
-
return createEmptySkillLockFile();
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
async function writeSkillLock(lock) {
|
|
1398
|
-
const lockPath = getSkillLockPath();
|
|
1399
|
-
await mkdir(dirname(lockPath), { recursive: true });
|
|
1400
|
-
await writeFile(lockPath, JSON.stringify(lock, null, 2), "utf-8");
|
|
1401
|
-
}
|
|
1402
|
-
let _ghWarningShown = false;
|
|
1403
|
-
function getGitHubToken() {
|
|
1404
|
-
if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
|
|
1405
|
-
if (process.env.GH_TOKEN) return process.env.GH_TOKEN;
|
|
1406
|
-
if (!_ghWarningShown) {
|
|
1407
|
-
process.stderr.write(`${import_picocolors.default.yellow("│")} ${import_picocolors.default.yellow("GitHub rate limit reached")} — using your ${import_picocolors.default.cyan("gh")} login to continue.\n${import_picocolors.default.yellow("│")} ${import_picocolors.default.dim(`Tip: set ${import_picocolors.default.cyan("GITHUB_TOKEN")} to avoid this prompt, or use ${import_picocolors.default.cyan("--full-depth")} to clone instead.\n`)}`);
|
|
1408
|
-
_ghWarningShown = true;
|
|
1409
|
-
}
|
|
1410
|
-
try {
|
|
1411
|
-
const token = execSync("gh auth token", {
|
|
1412
|
-
encoding: "utf-8",
|
|
1413
|
-
stdio: [
|
|
1414
|
-
"pipe",
|
|
1415
|
-
"pipe",
|
|
1416
|
-
"pipe"
|
|
1417
|
-
]
|
|
1418
|
-
}).trim();
|
|
1419
|
-
if (token) return token;
|
|
1420
|
-
} catch {}
|
|
1421
|
-
return null;
|
|
1422
|
-
}
|
|
1423
|
-
async function addSkillToLock(skillName, entry) {
|
|
1424
|
-
const lock = await readSkillLock();
|
|
1425
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1426
|
-
const existingEntry = lock.skills[skillName];
|
|
1427
|
-
lock.skills[skillName] = {
|
|
1428
|
-
...entry,
|
|
1429
|
-
installedAt: existingEntry?.installedAt ?? now,
|
|
1430
|
-
updatedAt: now
|
|
1431
|
-
};
|
|
1432
|
-
await writeSkillLock(lock);
|
|
1433
|
-
}
|
|
1434
|
-
async function removeSkillFromLock(skillName) {
|
|
1435
|
-
const lock = await readSkillLock();
|
|
1436
|
-
if (!(skillName in lock.skills)) return false;
|
|
1437
|
-
delete lock.skills[skillName];
|
|
1438
|
-
await writeSkillLock(lock);
|
|
1439
|
-
return true;
|
|
1440
|
-
}
|
|
1441
|
-
async function getSkillFromLock(skillName) {
|
|
1442
|
-
return (await readSkillLock()).skills[skillName] ?? null;
|
|
1443
|
-
}
|
|
1444
|
-
async function getAllLockedSkills() {
|
|
1445
|
-
return (await readSkillLock()).skills;
|
|
1446
|
-
}
|
|
1447
|
-
async function isPromptDismissed(promptKey) {
|
|
1448
|
-
return (await readSkillLock()).dismissed?.[promptKey] === true;
|
|
1449
|
-
}
|
|
1450
|
-
async function dismissPrompt(promptKey) {
|
|
1451
|
-
const lock = await readSkillLock();
|
|
1452
|
-
if (!lock.dismissed) lock.dismissed = {};
|
|
1453
|
-
lock.dismissed[promptKey] = true;
|
|
1454
|
-
await writeSkillLock(lock);
|
|
1455
|
-
}
|
|
1456
|
-
async function getLastSelectedAgents() {
|
|
1457
|
-
return (await readSkillLock()).lastSelectedAgents;
|
|
1458
|
-
}
|
|
1459
|
-
async function saveSelectedAgents(agents) {
|
|
1460
|
-
const lock = await readSkillLock();
|
|
1461
|
-
lock.lastSelectedAgents = agents;
|
|
1462
|
-
await writeSkillLock(lock);
|
|
1463
|
-
}
|
|
1464
|
-
function getLocalLockPath(cwd) {
|
|
1465
|
-
return join(cwd || process.cwd(), LOCAL_LOCK_FILE);
|
|
1466
|
-
}
|
|
1467
|
-
function createEmptyLocalLock() {
|
|
1468
|
-
return {
|
|
1469
|
-
version: LOCAL_LOCK_CURRENT_VERSION,
|
|
1470
|
-
skills: {}
|
|
1471
|
-
};
|
|
1472
|
-
}
|
|
1473
|
-
async function readLocalLock(cwd) {
|
|
1474
|
-
const lockPath = getLocalLockPath(cwd);
|
|
1475
|
-
try {
|
|
1476
|
-
const content = await readFile(lockPath, "utf-8");
|
|
1477
|
-
const parsed = JSON.parse(content);
|
|
1478
|
-
if (typeof parsed.version !== "number" || !parsed.skills) return createEmptyLocalLock();
|
|
1479
|
-
if (parsed.version < LOCAL_LOCK_CURRENT_VERSION) return createEmptyLocalLock();
|
|
1480
|
-
return parsed;
|
|
1481
|
-
} catch {
|
|
1482
|
-
return createEmptyLocalLock();
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
async function writeLocalLock(lock, cwd) {
|
|
1486
|
-
const lockPath = getLocalLockPath(cwd);
|
|
1487
|
-
const sortedSkills = {};
|
|
1488
|
-
for (const key of Object.keys(lock.skills).sort()) sortedSkills[key] = lock.skills[key];
|
|
1489
|
-
const sorted = {
|
|
1490
|
-
version: lock.version,
|
|
1491
|
-
skills: sortedSkills
|
|
1492
|
-
};
|
|
1493
|
-
await writeFile(lockPath, JSON.stringify(sorted, null, 2) + "\n", "utf-8");
|
|
1494
|
-
}
|
|
1495
|
-
async function computeSkillFolderHash(skillDir) {
|
|
1496
|
-
const files = [];
|
|
1497
|
-
await collectFiles(skillDir, skillDir, files);
|
|
1498
|
-
files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
1499
|
-
const hash = createHash$1("sha256");
|
|
1500
|
-
for (const file of files) {
|
|
1501
|
-
hash.update(file.relativePath);
|
|
1502
|
-
hash.update(file.content);
|
|
1503
|
-
}
|
|
1504
|
-
return hash.digest("hex");
|
|
1505
|
-
}
|
|
1506
|
-
async function collectFiles(baseDir, currentDir, results) {
|
|
1507
|
-
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
1508
|
-
await Promise.all(entries.map(async (entry) => {
|
|
1509
|
-
const fullPath = join(currentDir, entry.name);
|
|
1510
|
-
if (entry.isDirectory()) {
|
|
1511
|
-
if (entry.name === ".git" || entry.name === "node_modules") return;
|
|
1512
|
-
await collectFiles(baseDir, fullPath, results);
|
|
1513
|
-
} else if (entry.isFile()) {
|
|
1514
|
-
const content = await readFile(fullPath);
|
|
1515
|
-
const relativePath = relative(baseDir, fullPath).split("\\").join("/");
|
|
1516
|
-
results.push({
|
|
1517
|
-
relativePath,
|
|
1518
|
-
content
|
|
1519
|
-
});
|
|
1520
|
-
}
|
|
1521
|
-
}));
|
|
1522
|
-
}
|
|
1523
|
-
async function addSkillToLocalLock(skillName, entry, cwd) {
|
|
1524
|
-
const lock = await readLocalLock(cwd);
|
|
1525
|
-
lock.skills[skillName] = entry;
|
|
1526
|
-
await writeLocalLock(lock, cwd);
|
|
1527
|
-
}
|
|
1528
|
-
const SKIP_DIRS = [
|
|
1529
|
-
"node_modules",
|
|
1530
|
-
".git",
|
|
1531
|
-
"dist",
|
|
1532
|
-
"build",
|
|
1533
|
-
"__pycache__"
|
|
1534
|
-
];
|
|
1535
|
-
const AGENT_PROJECT_SKILL_DIRS = [
|
|
1536
|
-
".agents/skills",
|
|
1537
|
-
".claude/skills",
|
|
1538
|
-
".cline/skills",
|
|
1539
|
-
".codebuddy/skills",
|
|
1540
|
-
".codex/skills",
|
|
1541
|
-
".commandcode/skills",
|
|
1542
|
-
".continue/skills",
|
|
1543
|
-
".github/skills",
|
|
1544
|
-
".goose/skills",
|
|
1545
|
-
".iflow/skills",
|
|
1546
|
-
".junie/skills",
|
|
1547
|
-
".kilocode/skills",
|
|
1548
|
-
".kiro/skills",
|
|
1549
|
-
".mux/skills",
|
|
1550
|
-
".neovate/skills",
|
|
1551
|
-
".opencode/skills",
|
|
1552
|
-
".openhands/skills",
|
|
1553
|
-
".pi/skills",
|
|
1554
|
-
".qoder/skills",
|
|
1555
|
-
".roo/skills",
|
|
1556
|
-
".trae/skills",
|
|
1557
|
-
".windsurf/skills",
|
|
1558
|
-
".zcode/skills",
|
|
1559
|
-
".zencoder/skills"
|
|
1560
|
-
];
|
|
1561
|
-
function normalizeSkillName(name) {
|
|
1562
|
-
return name.toLowerCase().replace(/[\s_]+/g, "-");
|
|
1563
|
-
}
|
|
1564
|
-
function normalizeRelativePath(path) {
|
|
1565
|
-
return path.split(sep).join("/").replace(/\/+/g, "/");
|
|
1566
|
-
}
|
|
1567
|
-
function shouldInstallInternalSkills() {
|
|
1568
|
-
const envValue = process.env.INSTALL_INTERNAL_SKILLS;
|
|
1569
|
-
return envValue === "1" || envValue === "true";
|
|
1570
|
-
}
|
|
1571
|
-
async function hasSkillMd(dir) {
|
|
1572
|
-
try {
|
|
1573
|
-
return (await stat(join(dir, "SKILL.md"))).isFile();
|
|
1574
|
-
} catch {
|
|
1575
|
-
return false;
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
async function parseSkillMd(skillMdPath, options) {
|
|
1579
|
-
try {
|
|
1580
|
-
const content = await readFile(skillMdPath, "utf-8");
|
|
1581
|
-
const { data } = parseFrontmatter(content);
|
|
1582
|
-
if (!data.name || !data.description) return null;
|
|
1583
|
-
if (typeof data.name !== "string" || typeof data.description !== "string") return null;
|
|
1584
|
-
if (data.metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) return null;
|
|
1585
|
-
return {
|
|
1586
|
-
name: sanitizeMetadata(data.name),
|
|
1587
|
-
description: sanitizeMetadata(data.description),
|
|
1588
|
-
path: dirname(skillMdPath),
|
|
1589
|
-
rawContent: content,
|
|
1590
|
-
metadata: data.metadata
|
|
1591
|
-
};
|
|
1592
|
-
} catch {
|
|
1593
|
-
return null;
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
async function findSkillDirs(dir, depth = 0, maxDepth = 5) {
|
|
1597
|
-
if (depth > maxDepth) return [];
|
|
1598
|
-
try {
|
|
1599
|
-
const [hasSkill, entries] = await Promise.all([hasSkillMd(dir), readdir(dir, { withFileTypes: true }).catch(() => [])]);
|
|
1600
|
-
const currentDir = hasSkill ? [dir] : [];
|
|
1601
|
-
const subDirResults = await Promise.all(entries.filter((entry) => entry.isDirectory() && !SKIP_DIRS.includes(entry.name)).map((entry) => findSkillDirs(join(dir, entry.name), depth + 1, maxDepth)));
|
|
1602
|
-
return [...currentDir, ...subDirResults.flat()];
|
|
1603
|
-
} catch {
|
|
1604
|
-
return [];
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
function isSubpathSafe(basePath, subpath) {
|
|
1608
|
-
const normalizedBase = normalize(resolve(basePath));
|
|
1609
|
-
const normalizedTarget = normalize(resolve(join(basePath, subpath)));
|
|
1610
|
-
return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
|
|
1611
|
-
}
|
|
1612
|
-
async function discoverSkills(basePath, subpath, options) {
|
|
1613
|
-
const skills = [];
|
|
1614
|
-
const seenNames = /* @__PURE__ */ new Set();
|
|
1615
|
-
const localLock = await readLocalLock(basePath);
|
|
1616
|
-
const lockedSkillNames = new Set(Object.keys(localLock.skills).map(normalizeSkillName));
|
|
1617
|
-
if (subpath && !isSubpathSafe(basePath, subpath)) throw new Error(`Invalid subpath: "${subpath}" resolves outside the repository directory. Subpath must not contain ".." segments that escape the base path.`);
|
|
1618
|
-
const searchPath = subpath ? join(basePath, subpath) : basePath;
|
|
1619
|
-
const pluginGroupings = await getPluginGroupings(searchPath);
|
|
1620
|
-
const enhanceSkill = (skill) => {
|
|
1621
|
-
const resolvedPath = resolve(skill.path);
|
|
1622
|
-
if (pluginGroupings.has(resolvedPath)) skill.pluginName = pluginGroupings.get(resolvedPath);
|
|
1623
|
-
return skill;
|
|
1624
|
-
};
|
|
1625
|
-
const isInstalledProjectSkill = (skill) => {
|
|
1626
|
-
if (lockedSkillNames.size === 0) return false;
|
|
1627
|
-
const relativeDir = normalizeRelativePath(relative(basePath, skill.path));
|
|
1628
|
-
if (!AGENT_PROJECT_SKILL_DIRS.some((dir) => relativeDir === dir || relativeDir.startsWith(`${dir}/`))) return false;
|
|
1629
|
-
const skillName = normalizeSkillName(skill.name);
|
|
1630
|
-
const directoryName = normalizeSkillName(basename(skill.path));
|
|
1631
|
-
return lockedSkillNames.has(skillName) || lockedSkillNames.has(directoryName);
|
|
1632
|
-
};
|
|
1633
|
-
if (await hasSkillMd(searchPath)) {
|
|
1634
|
-
let skill = await parseSkillMd(join(searchPath, "SKILL.md"), options);
|
|
1635
|
-
if (skill) {
|
|
1636
|
-
if (!isInstalledProjectSkill(skill)) {
|
|
1637
|
-
skill = enhanceSkill(skill);
|
|
1638
|
-
skills.push(skill);
|
|
1639
|
-
seenNames.add(skill.name);
|
|
1640
|
-
if (!options?.fullDepth) return skills;
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
const prioritySearchDirs = [
|
|
1645
|
-
searchPath,
|
|
1646
|
-
join(searchPath, "skills"),
|
|
1647
|
-
join(searchPath, "skills/.curated"),
|
|
1648
|
-
join(searchPath, "skills/.experimental"),
|
|
1649
|
-
join(searchPath, "skills/.system"),
|
|
1650
|
-
...AGENT_PROJECT_SKILL_DIRS.map((dir) => join(searchPath, dir))
|
|
1651
|
-
];
|
|
1652
|
-
const deepContainerDirs = new Set(prioritySearchDirs.slice(1));
|
|
1653
|
-
prioritySearchDirs.push(...await getPluginSkillPaths(searchPath));
|
|
1654
|
-
const tryAddSkillAt = async (skillDir) => {
|
|
1655
|
-
if (!await hasSkillMd(skillDir)) return false;
|
|
1656
|
-
let skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
|
|
1657
|
-
if (!skill || seenNames.has(skill.name)) return true;
|
|
1658
|
-
if (isInstalledProjectSkill(skill)) return true;
|
|
1659
|
-
skill = enhanceSkill(skill);
|
|
1660
|
-
skills.push(skill);
|
|
1661
|
-
seenNames.add(skill.name);
|
|
1662
|
-
return true;
|
|
1663
|
-
};
|
|
1664
|
-
for (const dir of prioritySearchDirs) {
|
|
1665
|
-
const walkDeep = deepContainerDirs.has(dir);
|
|
1666
|
-
try {
|
|
1667
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
1668
|
-
for (const entry of entries) {
|
|
1669
|
-
if (!entry.isDirectory()) continue;
|
|
1670
|
-
const childDir = join(dir, entry.name);
|
|
1671
|
-
if (await tryAddSkillAt(childDir) || !walkDeep) continue;
|
|
1672
|
-
if (SKIP_DIRS.includes(entry.name)) continue;
|
|
1673
|
-
try {
|
|
1674
|
-
const grandEntries = await readdir(childDir, { withFileTypes: true });
|
|
1675
|
-
for (const grand of grandEntries) {
|
|
1676
|
-
if (!grand.isDirectory() || SKIP_DIRS.includes(grand.name)) continue;
|
|
1677
|
-
await tryAddSkillAt(join(childDir, grand.name));
|
|
1678
|
-
}
|
|
1679
|
-
} catch {}
|
|
1680
|
-
}
|
|
1681
|
-
} catch {}
|
|
1682
|
-
}
|
|
1683
|
-
if (skills.length === 0 || options?.fullDepth) {
|
|
1684
|
-
const allSkillDirs = await findSkillDirs(searchPath);
|
|
1685
|
-
for (const skillDir of allSkillDirs) {
|
|
1686
|
-
let skill = await parseSkillMd(join(skillDir, "SKILL.md"), options);
|
|
1687
|
-
if (skill && !seenNames.has(skill.name) && !isInstalledProjectSkill(skill)) {
|
|
1688
|
-
skill = enhanceSkill(skill);
|
|
1689
|
-
skills.push(skill);
|
|
1690
|
-
seenNames.add(skill.name);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
return skills;
|
|
1695
|
-
}
|
|
1696
|
-
function getSkillDisplayName(skill) {
|
|
1697
|
-
return skill.name || basename(skill.path);
|
|
1698
|
-
}
|
|
1699
|
-
function filterSkills(skills, inputNames) {
|
|
1700
|
-
const normalizedInputs = inputNames.map((n) => n.toLowerCase());
|
|
1701
|
-
return skills.filter((skill) => {
|
|
1702
|
-
const name = skill.name.toLowerCase();
|
|
1703
|
-
const displayName = getSkillDisplayName(skill).toLowerCase();
|
|
1704
|
-
return normalizedInputs.some((input) => input === name || input === displayName);
|
|
1705
|
-
});
|
|
1706
|
-
}
|
|
1707
|
-
var agents_exports = /* @__PURE__ */ __exportAll({
|
|
1708
|
-
EVE_SUBAGENTS_DIR: () => EVE_SUBAGENTS_DIR,
|
|
1709
|
-
agents: () => agents,
|
|
1710
|
-
detectInstalledAgents: () => detectInstalledAgents,
|
|
1711
|
-
getEveSubagents: () => getEveSubagents,
|
|
1712
|
-
getNonUniversalAgents: () => getNonUniversalAgents,
|
|
1713
|
-
getOpenClawGlobalSkillsDir: () => getOpenClawGlobalSkillsDir,
|
|
1714
|
-
getUniversalAgents: () => getUniversalAgents,
|
|
1715
|
-
getVisibleUniversalAgents: () => getVisibleUniversalAgents,
|
|
1716
|
-
isUniversalAgent: () => isUniversalAgent,
|
|
1717
|
-
isZCodeInstalled: () => isZCodeInstalled,
|
|
1718
|
-
setAgents: () => setAgents
|
|
1719
|
-
});
|
|
1720
|
-
const home = homedir();
|
|
1721
|
-
const configHome = xdgConfig ?? join(home, ".config");
|
|
1722
|
-
const codexHome = process.env.CODEX_HOME?.trim() || join(home, ".codex");
|
|
1723
|
-
const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join(home, ".claude");
|
|
1724
|
-
const vibeHome = process.env.VIBE_HOME?.trim() || join(home, ".vibe");
|
|
1725
|
-
const hermesHome = process.env.HERMES_HOME?.trim() || join(home, ".hermes");
|
|
1726
|
-
const autohandHome = process.env.AUTOHAND_HOME?.trim() || join(home, ".autohand");
|
|
1727
|
-
const zedAppDataHome = process.env.APPDATA?.trim();
|
|
1728
|
-
const zedFlatpakConfigHome = process.env.FLATPAK_XDG_CONFIG_HOME?.trim();
|
|
1729
|
-
function packageJsonHasDependency(packageJsonPath, dependencyName) {
|
|
1730
|
-
try {
|
|
1731
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
1732
|
-
return !!(packageJson.dependencies?.[dependencyName] || packageJson.devDependencies?.[dependencyName]);
|
|
1733
|
-
} catch {
|
|
1734
|
-
return false;
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
function getOpenClawGlobalSkillsDir(homeDir = home, pathExists = existsSync) {
|
|
1738
|
-
if (pathExists(join(homeDir, ".openclaw"))) return join(homeDir, ".openclaw/skills");
|
|
1739
|
-
if (pathExists(join(homeDir, ".clawdbot"))) return join(homeDir, ".clawdbot/skills");
|
|
1740
|
-
if (pathExists(join(homeDir, ".moltbot"))) return join(homeDir, ".moltbot/skills");
|
|
1741
|
-
return join(homeDir, ".openclaw/skills");
|
|
1742
|
-
}
|
|
1743
|
-
function isZCodeInstalled(homeDir = home, pathExists = existsSync) {
|
|
1744
|
-
return pathExists(join(homeDir, ".zcode")) || pathExists("/Applications/ZCode.app");
|
|
1745
|
-
}
|
|
1746
|
-
const agents = {
|
|
1747
|
-
"aider-desk": {
|
|
1748
|
-
name: "aider-desk",
|
|
1749
|
-
displayName: "AiderDesk",
|
|
1750
|
-
skillsDir: ".aider-desk/skills",
|
|
1751
|
-
globalSkillsDir: join(home, ".aider-desk/skills"),
|
|
1752
|
-
detectInstalled: async () => {
|
|
1753
|
-
return existsSync(join(home, ".aider-desk"));
|
|
1754
|
-
}
|
|
1755
|
-
},
|
|
1756
|
-
amp: {
|
|
1757
|
-
name: "amp",
|
|
1758
|
-
displayName: "Amp",
|
|
1759
|
-
skillsDir: ".agents/skills",
|
|
1760
|
-
globalSkillsDir: join(configHome, "agents/skills"),
|
|
1761
|
-
detectInstalled: async () => {
|
|
1762
|
-
return existsSync(join(configHome, "amp"));
|
|
1763
|
-
}
|
|
1764
|
-
},
|
|
1765
|
-
antigravity: {
|
|
1766
|
-
name: "antigravity",
|
|
1767
|
-
displayName: "Antigravity",
|
|
1768
|
-
skillsDir: ".agents/skills",
|
|
1769
|
-
globalSkillsDir: join(home, ".gemini/antigravity/skills"),
|
|
1770
|
-
detectInstalled: async () => {
|
|
1771
|
-
return existsSync(join(home, ".gemini/antigravity"));
|
|
1772
|
-
}
|
|
1773
|
-
},
|
|
1774
|
-
"antigravity-cli": {
|
|
1775
|
-
name: "antigravity-cli",
|
|
1776
|
-
displayName: "Antigravity CLI",
|
|
1777
|
-
skillsDir: ".agents/skills",
|
|
1778
|
-
globalSkillsDir: join(home, ".gemini/antigravity-cli/skills"),
|
|
1779
|
-
detectInstalled: async () => {
|
|
1780
|
-
return existsSync(join(home, ".gemini/antigravity-cli"));
|
|
1781
|
-
}
|
|
1782
|
-
},
|
|
1783
|
-
astrbot: {
|
|
1784
|
-
name: "astrbot",
|
|
1785
|
-
displayName: "AstrBot",
|
|
1786
|
-
skillsDir: "data/skills",
|
|
1787
|
-
globalSkillsDir: join(home, ".astrbot/data/skills"),
|
|
1788
|
-
detectInstalled: async () => {
|
|
1789
|
-
return existsSync(join(process.cwd(), "data/skills")) || existsSync(join(home, ".astrbot"));
|
|
1790
|
-
}
|
|
1791
|
-
},
|
|
1792
|
-
"autohand-code": {
|
|
1793
|
-
name: "autohand-code",
|
|
1794
|
-
displayName: "Autohand Code CLI",
|
|
1795
|
-
skillsDir: ".autohand/skills",
|
|
1796
|
-
globalSkillsDir: join(autohandHome, "skills"),
|
|
1797
|
-
detectInstalled: async () => {
|
|
1798
|
-
return existsSync(autohandHome);
|
|
1799
|
-
}
|
|
1800
|
-
},
|
|
1801
|
-
augment: {
|
|
1802
|
-
name: "augment",
|
|
1803
|
-
displayName: "Augment",
|
|
1804
|
-
skillsDir: ".augment/skills",
|
|
1805
|
-
globalSkillsDir: join(home, ".augment/skills"),
|
|
1806
|
-
detectInstalled: async () => {
|
|
1807
|
-
return existsSync(join(home, ".augment"));
|
|
1808
|
-
}
|
|
1809
|
-
},
|
|
1810
|
-
bob: {
|
|
1811
|
-
name: "bob",
|
|
1812
|
-
displayName: "IBM Bob",
|
|
1813
|
-
skillsDir: ".bob/skills",
|
|
1814
|
-
globalSkillsDir: join(home, ".bob/skills"),
|
|
1815
|
-
detectInstalled: async () => {
|
|
1816
|
-
return existsSync(join(home, ".bob"));
|
|
1817
|
-
}
|
|
1818
|
-
},
|
|
1819
|
-
"claude-code": {
|
|
1820
|
-
name: "claude-code",
|
|
1821
|
-
displayName: "Claude Code",
|
|
1822
|
-
skillsDir: ".claude/skills",
|
|
1823
|
-
globalSkillsDir: join(claudeHome, "skills"),
|
|
1824
|
-
detectInstalled: async () => {
|
|
1825
|
-
return existsSync(claudeHome);
|
|
1826
|
-
}
|
|
1827
|
-
},
|
|
1828
|
-
openclaw: {
|
|
1829
|
-
name: "openclaw",
|
|
1830
|
-
displayName: "OpenClaw",
|
|
1831
|
-
skillsDir: "skills",
|
|
1832
|
-
globalSkillsDir: getOpenClawGlobalSkillsDir(),
|
|
1833
|
-
detectInstalled: async () => {
|
|
1834
|
-
return existsSync(join(home, ".openclaw")) || existsSync(join(home, ".clawdbot")) || existsSync(join(home, ".moltbot"));
|
|
1835
|
-
}
|
|
1836
|
-
},
|
|
1837
|
-
cline: {
|
|
1838
|
-
name: "cline",
|
|
1839
|
-
displayName: "Cline",
|
|
1840
|
-
skillsDir: ".agents/skills",
|
|
1841
|
-
globalSkillsDir: join(home, ".agents", "skills"),
|
|
1842
|
-
detectInstalled: async () => {
|
|
1843
|
-
return existsSync(join(home, ".cline"));
|
|
1844
|
-
}
|
|
1845
|
-
},
|
|
1846
|
-
"codearts-agent": {
|
|
1847
|
-
name: "codearts-agent",
|
|
1848
|
-
displayName: "CodeArts Agent",
|
|
1849
|
-
skillsDir: ".codeartsdoer/skills",
|
|
1850
|
-
globalSkillsDir: join(home, ".codeartsdoer/skills"),
|
|
1851
|
-
detectInstalled: async () => {
|
|
1852
|
-
return existsSync(join(home, ".codeartsdoer"));
|
|
1853
|
-
}
|
|
1854
|
-
},
|
|
1855
|
-
codebuddy: {
|
|
1856
|
-
name: "codebuddy",
|
|
1857
|
-
displayName: "CodeBuddy",
|
|
1858
|
-
skillsDir: ".codebuddy/skills",
|
|
1859
|
-
globalSkillsDir: join(home, ".codebuddy/skills"),
|
|
1860
|
-
detectInstalled: async () => {
|
|
1861
|
-
return existsSync(join(process.cwd(), ".codebuddy")) || existsSync(join(home, ".codebuddy"));
|
|
1862
|
-
}
|
|
1863
|
-
},
|
|
1864
|
-
codemaker: {
|
|
1865
|
-
name: "codemaker",
|
|
1866
|
-
displayName: "Codemaker",
|
|
1867
|
-
skillsDir: ".codemaker/skills",
|
|
1868
|
-
globalSkillsDir: join(home, ".codemaker/skills"),
|
|
1869
|
-
detectInstalled: async () => {
|
|
1870
|
-
return existsSync(join(home, ".codemaker"));
|
|
1871
|
-
}
|
|
1872
|
-
},
|
|
1873
|
-
codestudio: {
|
|
1874
|
-
name: "codestudio",
|
|
1875
|
-
displayName: "Code Studio",
|
|
1876
|
-
skillsDir: ".codestudio/skills",
|
|
1877
|
-
globalSkillsDir: join(home, ".codestudio/skills"),
|
|
1878
|
-
detectInstalled: async () => {
|
|
1879
|
-
return existsSync(join(home, ".codestudio"));
|
|
1880
|
-
}
|
|
1881
|
-
},
|
|
1882
|
-
codex: {
|
|
1883
|
-
name: "codex",
|
|
1884
|
-
displayName: "Codex",
|
|
1885
|
-
skillsDir: ".agents/skills",
|
|
1886
|
-
globalSkillsDir: join(codexHome, "skills"),
|
|
1887
|
-
detectInstalled: async () => {
|
|
1888
|
-
return existsSync(codexHome) || existsSync("/etc/codex");
|
|
1889
|
-
}
|
|
1890
|
-
},
|
|
1891
|
-
"command-code": {
|
|
1892
|
-
name: "command-code",
|
|
1893
|
-
displayName: "Command Code",
|
|
1894
|
-
skillsDir: ".commandcode/skills",
|
|
1895
|
-
globalSkillsDir: join(home, ".commandcode/skills"),
|
|
1896
|
-
detectInstalled: async () => {
|
|
1897
|
-
return existsSync(join(home, ".commandcode"));
|
|
1898
|
-
}
|
|
1899
|
-
},
|
|
1900
|
-
continue: {
|
|
1901
|
-
name: "continue",
|
|
1902
|
-
displayName: "Continue",
|
|
1903
|
-
skillsDir: ".continue/skills",
|
|
1904
|
-
globalSkillsDir: join(home, ".continue/skills"),
|
|
1905
|
-
detectInstalled: async () => {
|
|
1906
|
-
return existsSync(join(process.cwd(), ".continue")) || existsSync(join(home, ".continue"));
|
|
1907
|
-
}
|
|
1908
|
-
},
|
|
1909
|
-
cortex: {
|
|
1910
|
-
name: "cortex",
|
|
1911
|
-
displayName: "Cortex Code",
|
|
1912
|
-
skillsDir: ".cortex/skills",
|
|
1913
|
-
globalSkillsDir: join(home, ".snowflake/cortex/skills"),
|
|
1914
|
-
detectInstalled: async () => {
|
|
1915
|
-
return existsSync(join(home, ".snowflake/cortex"));
|
|
1916
|
-
}
|
|
1917
|
-
},
|
|
1918
|
-
crush: {
|
|
1919
|
-
name: "crush",
|
|
1920
|
-
displayName: "Crush",
|
|
1921
|
-
skillsDir: ".crush/skills",
|
|
1922
|
-
globalSkillsDir: join(home, ".config/crush/skills"),
|
|
1923
|
-
detectInstalled: async () => {
|
|
1924
|
-
return existsSync(join(home, ".config/crush"));
|
|
1925
|
-
}
|
|
1926
|
-
},
|
|
1927
|
-
cursor: {
|
|
1928
|
-
name: "cursor",
|
|
1929
|
-
displayName: "Cursor",
|
|
1930
|
-
skillsDir: ".agents/skills",
|
|
1931
|
-
globalSkillsDir: join(home, ".cursor/skills"),
|
|
1932
|
-
detectInstalled: async () => {
|
|
1933
|
-
return existsSync(join(home, ".cursor"));
|
|
1934
|
-
}
|
|
1935
|
-
},
|
|
1936
|
-
deepagents: {
|
|
1937
|
-
name: "deepagents",
|
|
1938
|
-
displayName: "Deep Agents",
|
|
1939
|
-
skillsDir: ".agents/skills",
|
|
1940
|
-
globalSkillsDir: join(home, ".deepagents/agent/skills"),
|
|
1941
|
-
detectInstalled: async () => {
|
|
1942
|
-
return existsSync(join(home, ".deepagents"));
|
|
1943
|
-
}
|
|
1944
|
-
},
|
|
1945
|
-
devin: {
|
|
1946
|
-
name: "devin",
|
|
1947
|
-
displayName: "Devin for Terminal",
|
|
1948
|
-
skillsDir: ".devin/skills",
|
|
1949
|
-
globalSkillsDir: join(configHome, "devin/skills"),
|
|
1950
|
-
detectInstalled: async () => {
|
|
1951
|
-
return existsSync(join(configHome, "devin"));
|
|
1952
|
-
}
|
|
1953
|
-
},
|
|
1954
|
-
dexto: {
|
|
1955
|
-
name: "dexto",
|
|
1956
|
-
displayName: "Dexto",
|
|
1957
|
-
skillsDir: ".agents/skills",
|
|
1958
|
-
globalSkillsDir: join(home, ".agents/skills"),
|
|
1959
|
-
showInUniversalPrompt: false,
|
|
1960
|
-
detectInstalled: async () => {
|
|
1961
|
-
return existsSync(join(home, ".dexto"));
|
|
1962
|
-
}
|
|
1963
|
-
},
|
|
1964
|
-
droid: {
|
|
1965
|
-
name: "droid",
|
|
1966
|
-
displayName: "Droid",
|
|
1967
|
-
skillsDir: ".factory/skills",
|
|
1968
|
-
globalSkillsDir: join(home, ".factory/skills"),
|
|
1969
|
-
detectInstalled: async () => {
|
|
1970
|
-
return existsSync(join(home, ".factory"));
|
|
1971
|
-
}
|
|
1972
|
-
},
|
|
1973
|
-
eve: {
|
|
1974
|
-
name: "eve",
|
|
1975
|
-
displayName: "Eve",
|
|
1976
|
-
skillsDir: "agent/skills",
|
|
1977
|
-
globalSkillsDir: void 0,
|
|
1978
|
-
detectInstalled: async () => {
|
|
1979
|
-
const cwd = process.cwd();
|
|
1980
|
-
return existsSync(join(cwd, "agent")) && packageJsonHasDependency(join(cwd, "package.json"), "eve");
|
|
1981
|
-
}
|
|
1982
|
-
},
|
|
1983
|
-
firebender: {
|
|
1984
|
-
name: "firebender",
|
|
1985
|
-
displayName: "Firebender",
|
|
1986
|
-
skillsDir: ".agents/skills",
|
|
1987
|
-
globalSkillsDir: join(home, ".firebender/skills"),
|
|
1988
|
-
showInUniversalPrompt: false,
|
|
1989
|
-
detectInstalled: async () => {
|
|
1990
|
-
return existsSync(join(home, ".firebender"));
|
|
1991
|
-
}
|
|
1992
|
-
},
|
|
1993
|
-
forgecode: {
|
|
1994
|
-
name: "forgecode",
|
|
1995
|
-
displayName: "ForgeCode",
|
|
1996
|
-
skillsDir: ".forge/skills",
|
|
1997
|
-
globalSkillsDir: join(home, ".forge/skills"),
|
|
1998
|
-
detectInstalled: async () => {
|
|
1999
|
-
return existsSync(join(home, ".forge"));
|
|
2000
|
-
}
|
|
2001
|
-
},
|
|
2002
|
-
"gemini-cli": {
|
|
2003
|
-
name: "gemini-cli",
|
|
2004
|
-
displayName: "Gemini CLI",
|
|
2005
|
-
skillsDir: ".agents/skills",
|
|
2006
|
-
globalSkillsDir: join(home, ".gemini/skills"),
|
|
2007
|
-
detectInstalled: async () => {
|
|
2008
|
-
return existsSync(join(home, ".gemini"));
|
|
2009
|
-
}
|
|
2010
|
-
},
|
|
2011
|
-
"github-copilot": {
|
|
2012
|
-
name: "github-copilot",
|
|
2013
|
-
displayName: "GitHub Copilot",
|
|
2014
|
-
skillsDir: ".agents/skills",
|
|
2015
|
-
globalSkillsDir: join(home, ".copilot/skills"),
|
|
2016
|
-
detectInstalled: async () => {
|
|
2017
|
-
return existsSync(join(home, ".copilot"));
|
|
2018
|
-
}
|
|
2019
|
-
},
|
|
2020
|
-
goose: {
|
|
2021
|
-
name: "goose",
|
|
2022
|
-
displayName: "Goose",
|
|
2023
|
-
skillsDir: ".goose/skills",
|
|
2024
|
-
globalSkillsDir: join(configHome, "goose/skills"),
|
|
2025
|
-
detectInstalled: async () => {
|
|
2026
|
-
return existsSync(join(configHome, "goose"));
|
|
2027
|
-
}
|
|
2028
|
-
},
|
|
2029
|
-
"hermes-agent": {
|
|
2030
|
-
name: "hermes-agent",
|
|
2031
|
-
displayName: "Hermes Agent",
|
|
2032
|
-
skillsDir: ".hermes/skills",
|
|
2033
|
-
globalSkillsDir: join(hermesHome, "skills"),
|
|
2034
|
-
detectInstalled: async () => {
|
|
2035
|
-
return existsSync(hermesHome);
|
|
2036
|
-
}
|
|
2037
|
-
},
|
|
2038
|
-
"inference-sh": {
|
|
2039
|
-
name: "inference-sh",
|
|
2040
|
-
displayName: "inference.sh",
|
|
2041
|
-
skillsDir: ".inferencesh/skills",
|
|
2042
|
-
globalSkillsDir: join(home, ".inferencesh/skills"),
|
|
2043
|
-
detectInstalled: async () => {
|
|
2044
|
-
return existsSync(join(home, ".inferencesh"));
|
|
2045
|
-
}
|
|
2046
|
-
},
|
|
2047
|
-
jazz: {
|
|
2048
|
-
name: "jazz",
|
|
2049
|
-
displayName: "Jazz",
|
|
2050
|
-
skillsDir: ".jazz/skills",
|
|
2051
|
-
globalSkillsDir: join(home, ".jazz/skills"),
|
|
2052
|
-
detectInstalled: async () => {
|
|
2053
|
-
return existsSync(join(home, ".jazz")) || existsSync(join(process.cwd(), ".jazz"));
|
|
2054
|
-
}
|
|
2055
|
-
},
|
|
2056
|
-
junie: {
|
|
2057
|
-
name: "junie",
|
|
2058
|
-
displayName: "Junie",
|
|
2059
|
-
skillsDir: ".junie/skills",
|
|
2060
|
-
globalSkillsDir: join(home, ".junie/skills"),
|
|
2061
|
-
detectInstalled: async () => {
|
|
2062
|
-
return existsSync(join(home, ".junie"));
|
|
2063
|
-
}
|
|
2064
|
-
},
|
|
2065
|
-
"iflow-cli": {
|
|
2066
|
-
name: "iflow-cli",
|
|
2067
|
-
displayName: "iFlow CLI",
|
|
2068
|
-
skillsDir: ".iflow/skills",
|
|
2069
|
-
globalSkillsDir: join(home, ".iflow/skills"),
|
|
2070
|
-
detectInstalled: async () => {
|
|
2071
|
-
return existsSync(join(home, ".iflow"));
|
|
2072
|
-
}
|
|
2073
|
-
},
|
|
2074
|
-
kilo: {
|
|
2075
|
-
name: "kilo",
|
|
2076
|
-
displayName: "Kilo Code",
|
|
2077
|
-
skillsDir: ".kilocode/skills",
|
|
2078
|
-
globalSkillsDir: join(home, ".kilocode/skills"),
|
|
2079
|
-
detectInstalled: async () => {
|
|
2080
|
-
return existsSync(join(home, ".kilocode"));
|
|
2081
|
-
}
|
|
2082
|
-
},
|
|
2083
|
-
"kimi-code-cli": {
|
|
2084
|
-
name: "kimi-code-cli",
|
|
2085
|
-
displayName: "Kimi Code CLI",
|
|
2086
|
-
skillsDir: ".agents/skills",
|
|
2087
|
-
globalSkillsDir: join(home, ".agents/skills"),
|
|
2088
|
-
detectInstalled: async () => {
|
|
2089
|
-
return existsSync(join(home, ".kimi-code")) || existsSync(join(home, ".kimi"));
|
|
2090
|
-
}
|
|
2091
|
-
},
|
|
2092
|
-
"kiro-cli": {
|
|
2093
|
-
name: "kiro-cli",
|
|
2094
|
-
displayName: "Kiro CLI",
|
|
2095
|
-
skillsDir: ".kiro/skills",
|
|
2096
|
-
globalSkillsDir: join(home, ".kiro/skills"),
|
|
2097
|
-
detectInstalled: async () => {
|
|
2098
|
-
return existsSync(join(home, ".kiro"));
|
|
2099
|
-
}
|
|
2100
|
-
},
|
|
2101
|
-
kode: {
|
|
2102
|
-
name: "kode",
|
|
2103
|
-
displayName: "Kode",
|
|
2104
|
-
skillsDir: ".kode/skills",
|
|
2105
|
-
globalSkillsDir: join(home, ".kode/skills"),
|
|
2106
|
-
detectInstalled: async () => {
|
|
2107
|
-
return existsSync(join(home, ".kode"));
|
|
2108
|
-
}
|
|
2109
|
-
},
|
|
2110
|
-
lingma: {
|
|
2111
|
-
name: "lingma",
|
|
2112
|
-
displayName: "Lingma",
|
|
2113
|
-
skillsDir: ".lingma/skills",
|
|
2114
|
-
globalSkillsDir: join(home, ".lingma/skills"),
|
|
2115
|
-
detectInstalled: async () => {
|
|
2116
|
-
return existsSync(join(home, ".lingma"));
|
|
2117
|
-
}
|
|
2118
|
-
},
|
|
2119
|
-
loaf: {
|
|
2120
|
-
name: "loaf",
|
|
2121
|
-
displayName: "Loaf",
|
|
2122
|
-
skillsDir: ".agents/skills",
|
|
2123
|
-
globalSkillsDir: join(home, ".agents/skills"),
|
|
2124
|
-
showInUniversalPrompt: false,
|
|
2125
|
-
detectInstalled: async () => {
|
|
2126
|
-
return existsSync(join(home, ".loaf"));
|
|
2127
|
-
}
|
|
2128
|
-
},
|
|
2129
|
-
mcpjam: {
|
|
2130
|
-
name: "mcpjam",
|
|
2131
|
-
displayName: "MCPJam",
|
|
2132
|
-
skillsDir: ".mcpjam/skills",
|
|
2133
|
-
globalSkillsDir: join(home, ".mcpjam/skills"),
|
|
2134
|
-
detectInstalled: async () => {
|
|
2135
|
-
return existsSync(join(home, ".mcpjam"));
|
|
2136
|
-
}
|
|
2137
|
-
},
|
|
2138
|
-
"mistral-vibe": {
|
|
2139
|
-
name: "mistral-vibe",
|
|
2140
|
-
displayName: "Mistral Vibe",
|
|
2141
|
-
skillsDir: ".vibe/skills",
|
|
2142
|
-
globalSkillsDir: join(vibeHome, "skills"),
|
|
2143
|
-
detectInstalled: async () => {
|
|
2144
|
-
return existsSync(vibeHome);
|
|
2145
|
-
}
|
|
2146
|
-
},
|
|
2147
|
-
moxby: {
|
|
2148
|
-
name: "moxby",
|
|
2149
|
-
displayName: "Moxby",
|
|
2150
|
-
skillsDir: ".moxby/skills",
|
|
2151
|
-
globalSkillsDir: join(home, ".moxby/skills"),
|
|
2152
|
-
detectInstalled: async () => {
|
|
2153
|
-
return existsSync(join(home, ".moxby"));
|
|
2154
|
-
}
|
|
2155
|
-
},
|
|
2156
|
-
mux: {
|
|
2157
|
-
name: "mux",
|
|
2158
|
-
displayName: "Mux",
|
|
2159
|
-
skillsDir: ".mux/skills",
|
|
2160
|
-
globalSkillsDir: join(home, ".mux/skills"),
|
|
2161
|
-
detectInstalled: async () => {
|
|
2162
|
-
return existsSync(join(home, ".mux"));
|
|
2163
|
-
}
|
|
2164
|
-
},
|
|
2165
|
-
opencode: {
|
|
2166
|
-
name: "opencode",
|
|
2167
|
-
displayName: "OpenCode",
|
|
2168
|
-
skillsDir: ".agents/skills",
|
|
2169
|
-
globalSkillsDir: join(configHome, "opencode/skills"),
|
|
2170
|
-
detectInstalled: async () => {
|
|
2171
|
-
return existsSync(join(configHome, "opencode"));
|
|
2172
|
-
}
|
|
2173
|
-
},
|
|
2174
|
-
openhands: {
|
|
2175
|
-
name: "openhands",
|
|
2176
|
-
displayName: "OpenHands",
|
|
2177
|
-
skillsDir: ".openhands/skills",
|
|
2178
|
-
globalSkillsDir: join(home, ".openhands/skills"),
|
|
2179
|
-
detectInstalled: async () => {
|
|
2180
|
-
return existsSync(join(home, ".openhands"));
|
|
2181
|
-
}
|
|
2182
|
-
},
|
|
2183
|
-
ona: {
|
|
2184
|
-
name: "ona",
|
|
2185
|
-
displayName: "Ona",
|
|
2186
|
-
skillsDir: ".ona/skills",
|
|
2187
|
-
globalSkillsDir: join(home, ".ona/skills"),
|
|
2188
|
-
detectInstalled: async () => {
|
|
2189
|
-
return existsSync(join(home, ".ona"));
|
|
2190
|
-
}
|
|
2191
|
-
},
|
|
2192
|
-
pi: {
|
|
2193
|
-
name: "pi",
|
|
2194
|
-
displayName: "Pi",
|
|
2195
|
-
skillsDir: ".pi/skills",
|
|
2196
|
-
globalSkillsDir: join(home, ".pi/agent/skills"),
|
|
2197
|
-
detectInstalled: async () => {
|
|
2198
|
-
return existsSync(join(home, ".pi/agent"));
|
|
2199
|
-
}
|
|
2200
|
-
},
|
|
2201
|
-
qoder: {
|
|
2202
|
-
name: "qoder",
|
|
2203
|
-
displayName: "Qoder",
|
|
2204
|
-
skillsDir: ".qoder/skills",
|
|
2205
|
-
globalSkillsDir: join(home, ".qoder/skills"),
|
|
2206
|
-
detectInstalled: async () => {
|
|
2207
|
-
return existsSync(join(home, ".qoder"));
|
|
2208
|
-
}
|
|
2209
|
-
},
|
|
2210
|
-
"qoder-cn": {
|
|
2211
|
-
name: "qoder-cn",
|
|
2212
|
-
displayName: "Qoder CN",
|
|
2213
|
-
skillsDir: ".qoder/skills",
|
|
2214
|
-
globalSkillsDir: join(home, ".qoder-cn/skills"),
|
|
2215
|
-
detectInstalled: async () => {
|
|
2216
|
-
return existsSync(join(home, ".qoder-cn"));
|
|
2217
|
-
}
|
|
2218
|
-
},
|
|
2219
|
-
"qwen-code": {
|
|
2220
|
-
name: "qwen-code",
|
|
2221
|
-
displayName: "Qwen Code",
|
|
2222
|
-
skillsDir: ".qwen/skills",
|
|
2223
|
-
globalSkillsDir: join(home, ".qwen/skills"),
|
|
2224
|
-
detectInstalled: async () => {
|
|
2225
|
-
return existsSync(join(home, ".qwen"));
|
|
2226
|
-
}
|
|
2227
|
-
},
|
|
2228
|
-
replit: {
|
|
2229
|
-
name: "replit",
|
|
2230
|
-
displayName: "Replit",
|
|
2231
|
-
skillsDir: ".agents/skills",
|
|
2232
|
-
globalSkillsDir: join(configHome, "agents/skills"),
|
|
2233
|
-
showInUniversalList: false,
|
|
2234
|
-
detectInstalled: async () => {
|
|
2235
|
-
return existsSync(join(process.cwd(), ".replit"));
|
|
2236
|
-
}
|
|
2237
|
-
},
|
|
2238
|
-
reasonix: {
|
|
2239
|
-
name: "reasonix",
|
|
2240
|
-
displayName: "Reasonix",
|
|
2241
|
-
skillsDir: ".reasonix/skills",
|
|
2242
|
-
globalSkillsDir: join(home, ".reasonix/skills"),
|
|
2243
|
-
detectInstalled: async () => {
|
|
2244
|
-
return existsSync(join(home, ".reasonix"));
|
|
2245
|
-
}
|
|
2246
|
-
},
|
|
2247
|
-
rovodev: {
|
|
2248
|
-
name: "rovodev",
|
|
2249
|
-
displayName: "Rovo Dev",
|
|
2250
|
-
skillsDir: ".rovodev/skills",
|
|
2251
|
-
globalSkillsDir: join(home, ".rovodev/skills"),
|
|
2252
|
-
detectInstalled: async () => {
|
|
2253
|
-
return existsSync(join(home, ".rovodev"));
|
|
2254
|
-
}
|
|
2255
|
-
},
|
|
2256
|
-
roo: {
|
|
2257
|
-
name: "roo",
|
|
2258
|
-
displayName: "Roo Code",
|
|
2259
|
-
skillsDir: ".roo/skills",
|
|
2260
|
-
globalSkillsDir: join(home, ".roo/skills"),
|
|
2261
|
-
detectInstalled: async () => {
|
|
2262
|
-
return existsSync(join(home, ".roo"));
|
|
2263
|
-
}
|
|
2264
|
-
},
|
|
2265
|
-
"tabnine-cli": {
|
|
2266
|
-
name: "tabnine-cli",
|
|
2267
|
-
displayName: "Tabnine CLI",
|
|
2268
|
-
skillsDir: ".tabnine/agent/skills",
|
|
2269
|
-
globalSkillsDir: join(home, ".tabnine/agent/skills"),
|
|
2270
|
-
detectInstalled: async () => {
|
|
2271
|
-
return existsSync(join(home, ".tabnine"));
|
|
2272
|
-
}
|
|
2273
|
-
},
|
|
2274
|
-
terramind: {
|
|
2275
|
-
name: "terramind",
|
|
2276
|
-
displayName: "Terramind",
|
|
2277
|
-
skillsDir: ".terramind/skills",
|
|
2278
|
-
globalSkillsDir: join(home, ".terramind/skills"),
|
|
2279
|
-
detectInstalled: async () => {
|
|
2280
|
-
return existsSync(join(home, ".terramind"));
|
|
2281
|
-
}
|
|
2282
|
-
},
|
|
2283
|
-
tinycloud: {
|
|
2284
|
-
name: "tinycloud",
|
|
2285
|
-
displayName: "Tinycloud",
|
|
2286
|
-
skillsDir: ".tinycloud/skills",
|
|
2287
|
-
globalSkillsDir: join(home, ".tinycloud/skills"),
|
|
2288
|
-
detectInstalled: async () => {
|
|
2289
|
-
return existsSync(join(home, ".tinycloud"));
|
|
2290
|
-
}
|
|
2291
|
-
},
|
|
2292
|
-
trae: {
|
|
2293
|
-
name: "trae",
|
|
2294
|
-
displayName: "Trae",
|
|
2295
|
-
skillsDir: ".trae/skills",
|
|
2296
|
-
globalSkillsDir: join(home, ".trae/skills"),
|
|
2297
|
-
detectInstalled: async () => {
|
|
2298
|
-
return existsSync(join(home, ".trae"));
|
|
2299
|
-
}
|
|
2300
|
-
},
|
|
2301
|
-
"trae-cn": {
|
|
2302
|
-
name: "trae-cn",
|
|
2303
|
-
displayName: "Trae CN",
|
|
2304
|
-
skillsDir: ".trae/skills",
|
|
2305
|
-
globalSkillsDir: join(home, ".trae-cn/skills"),
|
|
2306
|
-
detectInstalled: async () => {
|
|
2307
|
-
return existsSync(join(home, ".trae-cn"));
|
|
2308
|
-
}
|
|
2309
|
-
},
|
|
2310
|
-
warp: {
|
|
2311
|
-
name: "warp",
|
|
2312
|
-
displayName: "Warp",
|
|
2313
|
-
skillsDir: ".agents/skills",
|
|
2314
|
-
globalSkillsDir: join(home, ".agents/skills"),
|
|
2315
|
-
detectInstalled: async () => {
|
|
2316
|
-
return existsSync(join(home, ".warp"));
|
|
2317
|
-
}
|
|
2318
|
-
},
|
|
2319
|
-
windsurf: {
|
|
2320
|
-
name: "windsurf",
|
|
2321
|
-
displayName: "Windsurf",
|
|
2322
|
-
skillsDir: ".windsurf/skills",
|
|
2323
|
-
globalSkillsDir: join(home, ".codeium/windsurf/skills"),
|
|
2324
|
-
detectInstalled: async () => {
|
|
2325
|
-
return existsSync(join(home, ".codeium/windsurf"));
|
|
2326
|
-
}
|
|
2327
|
-
},
|
|
2328
|
-
zed: {
|
|
2329
|
-
name: "zed",
|
|
2330
|
-
displayName: "Zed",
|
|
2331
|
-
skillsDir: ".agents/skills",
|
|
2332
|
-
globalSkillsDir: join(home, ".agents/skills"),
|
|
2333
|
-
detectInstalled: async () => {
|
|
2334
|
-
return existsSync(join(configHome, "zed")) || !!zedAppDataHome && existsSync(join(zedAppDataHome, "Zed")) || !!zedFlatpakConfigHome && existsSync(join(zedFlatpakConfigHome, "zed"));
|
|
2335
|
-
}
|
|
2336
|
-
},
|
|
2337
|
-
zcode: {
|
|
2338
|
-
name: "zcode",
|
|
2339
|
-
displayName: "ZCode",
|
|
2340
|
-
skillsDir: ".zcode/skills",
|
|
2341
|
-
globalSkillsDir: join(home, ".zcode/skills"),
|
|
2342
|
-
detectInstalled: async () => {
|
|
2343
|
-
return isZCodeInstalled();
|
|
2344
|
-
}
|
|
2345
|
-
},
|
|
2346
|
-
zencoder: {
|
|
2347
|
-
name: "zencoder",
|
|
2348
|
-
displayName: "Zencoder",
|
|
2349
|
-
skillsDir: ".zencoder/skills",
|
|
2350
|
-
globalSkillsDir: join(home, ".zencoder/skills"),
|
|
2351
|
-
detectInstalled: async () => {
|
|
2352
|
-
return existsSync(join(home, ".zencoder"));
|
|
2353
|
-
}
|
|
2354
|
-
},
|
|
2355
|
-
zenflow: {
|
|
2356
|
-
name: "zenflow",
|
|
2357
|
-
displayName: "Zenflow",
|
|
2358
|
-
skillsDir: ".zencoder/skills",
|
|
2359
|
-
globalSkillsDir: join(home, ".zencoder/skills"),
|
|
2360
|
-
detectInstalled: async () => {
|
|
2361
|
-
return existsSync(join(home, ".zencoder"));
|
|
2362
|
-
}
|
|
2363
|
-
},
|
|
2364
|
-
neovate: {
|
|
2365
|
-
name: "neovate",
|
|
2366
|
-
displayName: "Neovate",
|
|
2367
|
-
skillsDir: ".neovate/skills",
|
|
2368
|
-
globalSkillsDir: join(home, ".neovate/skills"),
|
|
2369
|
-
detectInstalled: async () => {
|
|
2370
|
-
return existsSync(join(home, ".neovate"));
|
|
2371
|
-
}
|
|
2372
|
-
},
|
|
2373
|
-
pochi: {
|
|
2374
|
-
name: "pochi",
|
|
2375
|
-
displayName: "Pochi",
|
|
2376
|
-
skillsDir: ".pochi/skills",
|
|
2377
|
-
globalSkillsDir: join(home, ".pochi/skills"),
|
|
2378
|
-
detectInstalled: async () => {
|
|
2379
|
-
return existsSync(join(home, ".pochi"));
|
|
2380
|
-
}
|
|
2381
|
-
},
|
|
2382
|
-
promptscript: {
|
|
2383
|
-
name: "promptscript",
|
|
2384
|
-
displayName: "PromptScript",
|
|
2385
|
-
skillsDir: ".agents/skills",
|
|
2386
|
-
globalSkillsDir: void 0,
|
|
2387
|
-
showInUniversalPrompt: false,
|
|
2388
|
-
detectInstalled: async () => {
|
|
2389
|
-
return existsSync(join(process.cwd(), ".promptscript")) || existsSync(join(process.cwd(), "promptscript.yaml"));
|
|
1242
|
+
}
|
|
1243
|
+
async function cloneRepo(url, ref) {
|
|
1244
|
+
if (/^ext::/i.test(url)) throw new GitCloneError("不支持的 Git 传输协议:ext", url);
|
|
1245
|
+
const tempDir = await mkdtemp(join(tmpdir(), "skills-"));
|
|
1246
|
+
const cloneOptions = ref ? [
|
|
1247
|
+
"--depth",
|
|
1248
|
+
"1",
|
|
1249
|
+
"--branch",
|
|
1250
|
+
ref
|
|
1251
|
+
] : ["--depth", "1"];
|
|
1252
|
+
const repo = parseGitHubRepoUrl(url);
|
|
1253
|
+
try {
|
|
1254
|
+
await createGitClient().clone(url, tempDir, cloneOptions);
|
|
1255
|
+
return tempDir;
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1258
|
+
const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
|
|
1259
|
+
const isAuthError = isAuthFailure(errorMessage);
|
|
1260
|
+
if (isTimeout) {
|
|
1261
|
+
await rm(tempDir, {
|
|
1262
|
+
recursive: true,
|
|
1263
|
+
force: true
|
|
1264
|
+
}).catch(() => {});
|
|
1265
|
+
throw new GitCloneError(`克隆超时(${Math.round(CLONE_TIMEOUT_MS / 1e3)} 秒)。常见原因:\n - 仓库过大:使用 SKILLS_CLONE_TIMEOUT_MS=600000 (10 分钟) 提高超时时间\n - 网络较慢:重试,或手动克隆后将本地路径传给 'skills add'\n - 私有仓库未配置凭据:请确保身份验证已配置\n - SSH:ssh-add -l(检查已加载的密钥)\n - HTTPS:gh auth status(如果使用 GitHub CLI)`, url, true, false);
|
|
2390
1266
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
1267
|
+
if (isAuthError && repo && isGitHubHttpsCloneUrl(url)) {
|
|
1268
|
+
try {
|
|
1269
|
+
await resetTempDir(tempDir);
|
|
1270
|
+
if (await tryGhClone(repo, tempDir, ref)) return tempDir;
|
|
1271
|
+
} catch {}
|
|
1272
|
+
try {
|
|
1273
|
+
await resetTempDir(tempDir);
|
|
1274
|
+
await createGitClient({ GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND ?? "ssh -o BatchMode=yes" }).clone(repo.sshUrl, tempDir, cloneOptions);
|
|
1275
|
+
return tempDir;
|
|
1276
|
+
} catch {}
|
|
2399
1277
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
showInUniversalList: false,
|
|
2407
|
-
detectInstalled: async () => false
|
|
2408
|
-
}
|
|
2409
|
-
};
|
|
2410
|
-
function setAgents(next) {
|
|
2411
|
-
for (const key of Object.keys(agents)) delete agents[key];
|
|
2412
|
-
Object.assign(agents, next);
|
|
2413
|
-
}
|
|
2414
|
-
async function detectInstalledAgents() {
|
|
2415
|
-
return (await Promise.all(Object.entries(agents).map(async ([type, config]) => ({
|
|
2416
|
-
type,
|
|
2417
|
-
installed: await config.detectInstalled()
|
|
2418
|
-
})))).filter((r) => r.installed).map((r) => r.type);
|
|
2419
|
-
}
|
|
2420
|
-
const EVE_SUBAGENTS_DIR = join("agent", "subagents");
|
|
2421
|
-
function getEveSubagents(cwd = process.cwd()) {
|
|
2422
|
-
const dir = join(cwd, EVE_SUBAGENTS_DIR);
|
|
2423
|
-
if (!existsSync(dir)) return [];
|
|
2424
|
-
try {
|
|
2425
|
-
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
2426
|
-
} catch {
|
|
2427
|
-
return [];
|
|
1278
|
+
await rm(tempDir, {
|
|
1279
|
+
recursive: true,
|
|
1280
|
+
force: true
|
|
1281
|
+
}).catch(() => {});
|
|
1282
|
+
if (isAuthError) throw new GitCloneError(buildGitHubAuthError(url, repo, errorMessage), url, false, true);
|
|
1283
|
+
throw new GitCloneError(`克隆失败 ${url}:${errorMessage}`, url, false, false);
|
|
2428
1284
|
}
|
|
2429
1285
|
}
|
|
2430
|
-
function
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
}
|
|
2439
|
-
function isUniversalAgent(type) {
|
|
2440
|
-
return agents[type].skillsDir === ".agents/skills";
|
|
1286
|
+
async function cleanupTempDir(dir) {
|
|
1287
|
+
const normalizedDir = normalize(resolve(dir));
|
|
1288
|
+
const normalizedTmpDir = normalize(resolve(tmpdir()));
|
|
1289
|
+
if (!normalizedDir.startsWith(normalizedTmpDir + sep) && normalizedDir !== normalizedTmpDir) throw new Error("Attempted to clean up directory outside of temp directory");
|
|
1290
|
+
await rm(dir, {
|
|
1291
|
+
recursive: true,
|
|
1292
|
+
force: true
|
|
1293
|
+
});
|
|
2441
1294
|
}
|
|
2442
1295
|
const AGENTS_DIR = ".agents";
|
|
2443
1296
|
const SKILLS_SUBDIR = "skills";
|
|
@@ -3931,6 +2784,14 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
3931
2784
|
tree
|
|
3932
2785
|
};
|
|
3933
2786
|
}
|
|
2787
|
+
var add_exports = /* @__PURE__ */ __exportAll({
|
|
2788
|
+
formatEveInstallPromptMessage: () => formatEveInstallPromptMessage,
|
|
2789
|
+
getLockSource: () => getLockSource,
|
|
2790
|
+
getProjectLockSourceUrl: () => getProjectLockSourceUrl,
|
|
2791
|
+
parseAddOptions: () => parseAddOptions,
|
|
2792
|
+
promptForAgents: () => promptForAgents,
|
|
2793
|
+
runAdd: () => runAdd
|
|
2794
|
+
});
|
|
3934
2795
|
const isCancelled = (value) => typeof value === "symbol";
|
|
3935
2796
|
const EVE_AGENT_LABEL = "eve agent";
|
|
3936
2797
|
async function isSourcePrivate(source) {
|
|
@@ -4456,7 +3317,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4456
3317
|
}
|
|
4457
3318
|
console.log();
|
|
4458
3319
|
outro(import_picocolors.default.green("完成!") + import_picocolors.default.dim(" 使用前请审阅技能;技能以 Agent 的全部权限运行。"));
|
|
4459
|
-
await
|
|
3320
|
+
await promptForBfiSkill(options, targetAgents);
|
|
4460
3321
|
}
|
|
4461
3322
|
async function cloneWithGitHubFallback(url, ref, spinner) {
|
|
4462
3323
|
try {
|
|
@@ -4471,6 +3332,7 @@ async function cloneWithGitHubFallback(url, ref, spinner) {
|
|
|
4471
3332
|
}
|
|
4472
3333
|
}
|
|
4473
3334
|
async function installFromSkillHub(parsed, options, spinner) {
|
|
3335
|
+
let tempDir = null;
|
|
4474
3336
|
const config = resolveConfig();
|
|
4475
3337
|
const api = new SkillHubAPI(config.registry, config.token);
|
|
4476
3338
|
const rawRef = parsed.url.startsWith("skillhub:") ? parsed.url.slice(9) : parsed.url;
|
|
@@ -4492,6 +3354,15 @@ async function installFromSkillHub(parsed, options, spinner) {
|
|
|
4492
3354
|
return (b.score || 0) - (a.score || 0);
|
|
4493
3355
|
}).slice(0, 20);
|
|
4494
3356
|
if (hits.length === 0) {
|
|
3357
|
+
spinner.stop("SkillHub 未找到,尝试作为 Git 仓库...");
|
|
3358
|
+
const gitSource = parseSource(rawRef);
|
|
3359
|
+
if (gitSource.type === "git" || gitSource.type === "github" || gitSource.type === "gitlab") {
|
|
3360
|
+
tempDir = await cloneWithGitHubFallback(gitSource.url, gitSource.ref, spinner);
|
|
3361
|
+
return {
|
|
3362
|
+
skills: await discoverSkills(tempDir, gitSource.subpath, { includeInternal: true }),
|
|
3363
|
+
tempDir
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
4495
3366
|
spinner.stop(import_picocolors.default.red("无结果"));
|
|
4496
3367
|
outro(import_picocolors.default.red(`No skills found on SkillHub for "${rawRef}".`));
|
|
4497
3368
|
process.exit(1);
|
|
@@ -4536,7 +3407,7 @@ async function installFromSkillHub(parsed, options, spinner) {
|
|
|
4536
3407
|
}
|
|
4537
3408
|
const meta = metadata;
|
|
4538
3409
|
spinner.stop(`Skill: ${import_picocolors.default.cyan(`@${namespace}/${slug}`)}${meta.description ? ` — ${import_picocolors.default.dim(meta.description)}` : ""}`);
|
|
4539
|
-
|
|
3410
|
+
tempDir = join(tmpdir(), `.skillhub-skillshub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
|
|
4540
3411
|
mkdirSync(tempDir, { recursive: true });
|
|
4541
3412
|
const zipPath = join(tempDir, "skill.zip");
|
|
4542
3413
|
spinner.start("正在下载技能...");
|
|
@@ -4694,7 +3565,7 @@ async function runAdd(args, options = {}) {
|
|
|
4694
3565
|
skills = blobResult.skills;
|
|
4695
3566
|
spinner$2.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
4696
3567
|
} else {
|
|
4697
|
-
tempDir = await cloneWithGitHubFallback(parsed.url, parsed.ref, spinner$2);
|
|
3568
|
+
tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$2);
|
|
4698
3569
|
spinner$2.stop("仓库克隆完成");
|
|
4699
3570
|
spinner$2.start("正在发现技能...");
|
|
4700
3571
|
skills = await discoverSkills(tempDir, parsed.subpath, {
|
|
@@ -4703,7 +3574,7 @@ async function runAdd(args, options = {}) {
|
|
|
4703
3574
|
});
|
|
4704
3575
|
}
|
|
4705
3576
|
} else {
|
|
4706
|
-
tempDir = await cloneWithGitHubFallback(parsed.url, parsed.ref, spinner$2);
|
|
3577
|
+
tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$2);
|
|
4707
3578
|
spinner$2.stop("仓库克隆完成");
|
|
4708
3579
|
spinner$2.start("正在发现技能...");
|
|
4709
3580
|
skills = await discoverSkills(tempDir, parsed.subpath, {
|
|
@@ -5203,7 +4074,7 @@ async function runAdd(args, options = {}) {
|
|
|
5203
4074
|
}
|
|
5204
4075
|
console.log();
|
|
5205
4076
|
outro(import_picocolors.default.green("完成!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
|
|
5206
|
-
await
|
|
4077
|
+
await promptForBfiSkill(options, targetAgents);
|
|
5207
4078
|
} catch (error) {
|
|
5208
4079
|
if (error instanceof GitCloneError) {
|
|
5209
4080
|
log.error(import_picocolors.default.red("克隆仓库失败"));
|
|
@@ -5221,42 +4092,42 @@ async function cleanup(tempDir) {
|
|
|
5221
4092
|
await cleanupTempDir(tempDir);
|
|
5222
4093
|
} catch {}
|
|
5223
4094
|
}
|
|
5224
|
-
async function
|
|
4095
|
+
async function promptForBfiSkill(options, targetAgents) {
|
|
5225
4096
|
if (!process.stdin.isTTY) return;
|
|
5226
4097
|
if (options?.yes) return;
|
|
5227
4098
|
try {
|
|
5228
|
-
if (await isPromptDismissed("
|
|
5229
|
-
if (await isSkillInstalled("
|
|
5230
|
-
await dismissPrompt("
|
|
4099
|
+
if (await isPromptDismissed("bfiSkillPrompt")) return;
|
|
4100
|
+
if (await isSkillInstalled("skillhub-skills", "claude-code", { global: true })) {
|
|
4101
|
+
await dismissPrompt("bfiSkillPrompt");
|
|
5231
4102
|
return;
|
|
5232
4103
|
}
|
|
5233
4104
|
console.log();
|
|
5234
4105
|
log.message(import_picocolors.default.dim("一次性提示 - 关闭后将不再询问。"));
|
|
5235
|
-
const install = await confirm({ message:
|
|
4106
|
+
const install = await confirm({ message: `安装 ${import_picocolors.default.cyan("skillhub-skills")} 技能?它能帮助 AI Agent 自动发现和使用 bfi CLI 的全部功能。` });
|
|
5236
4107
|
if (isCancel(install)) {
|
|
5237
|
-
await dismissPrompt("
|
|
4108
|
+
await dismissPrompt("bfiSkillPrompt");
|
|
5238
4109
|
return;
|
|
5239
4110
|
}
|
|
5240
4111
|
if (install) {
|
|
5241
|
-
await dismissPrompt("
|
|
5242
|
-
const
|
|
5243
|
-
if (!
|
|
4112
|
+
await dismissPrompt("bfiSkillPrompt");
|
|
4113
|
+
const bfiSkillAgents = targetAgents?.filter((a) => a !== "replit");
|
|
4114
|
+
if (!bfiSkillAgents || bfiSkillAgents.length === 0) return;
|
|
5244
4115
|
console.log();
|
|
5245
|
-
log.step("正在安装
|
|
4116
|
+
log.step("正在安装 skillhub-skills 技能...");
|
|
5246
4117
|
try {
|
|
5247
|
-
await runAdd(["
|
|
5248
|
-
skill: ["
|
|
4118
|
+
await runAdd(["bfi/skillhub-skills"], {
|
|
4119
|
+
skill: ["skillhub-skills"],
|
|
5249
4120
|
global: true,
|
|
5250
4121
|
yes: true,
|
|
5251
|
-
agent:
|
|
4122
|
+
agent: bfiSkillAgents
|
|
5252
4123
|
});
|
|
5253
4124
|
} catch {
|
|
5254
|
-
log.warn("安装
|
|
5255
|
-
log.message(import_picocolors.default.dim("
|
|
4125
|
+
log.warn("安装 skillhub-skills 失败。你可以稍后重试:");
|
|
4126
|
+
log.message(import_picocolors.default.dim(" bfi skills add bfi/skillhub-skills --skill skillhub-skills -g -y"));
|
|
5256
4127
|
}
|
|
5257
4128
|
} else {
|
|
5258
|
-
await dismissPrompt("
|
|
5259
|
-
log.message(import_picocolors.default.dim("你可以稍后通过以下命令安装:
|
|
4129
|
+
await dismissPrompt("bfiSkillPrompt");
|
|
4130
|
+
log.message(import_picocolors.default.dim("你可以稍后通过以下命令安装:bfi skills add bfi/skillhub-skills --skill skillhub-skills -g -y"));
|
|
5260
4131
|
}
|
|
5261
4132
|
} catch {}
|
|
5262
4133
|
}
|
|
@@ -5301,7 +4172,10 @@ function parseAddOptions(args) {
|
|
|
5301
4172
|
}
|
|
5302
4173
|
} else if (arg === "--full-depth") options.fullDepth = true;
|
|
5303
4174
|
else if (arg === "--copy") options.copy = true;
|
|
5304
|
-
else if (arg === "--
|
|
4175
|
+
else if (arg === "--branch") {
|
|
4176
|
+
i++;
|
|
4177
|
+
if (args[i] && !args[i].startsWith("-")) options.branch = args[i];
|
|
4178
|
+
} else if (arg === "--subagent") {
|
|
5305
4179
|
options.subagent = options.subagent || [];
|
|
5306
4180
|
i++;
|
|
5307
4181
|
let nextArg = args[i];
|
|
@@ -5322,7 +4196,7 @@ function parseAddOptions(args) {
|
|
|
5322
4196
|
const RESET$11 = "\x1B[0m";
|
|
5323
4197
|
const BOLD$8 = "\x1B[1m";
|
|
5324
4198
|
const DIM$11 = "\x1B[38;5;102m";
|
|
5325
|
-
const TEXT$
|
|
4199
|
+
const TEXT$8 = "\x1B[38;5;145m";
|
|
5326
4200
|
const CYAN$2 = "\x1B[36m";
|
|
5327
4201
|
const SEARCH_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
5328
4202
|
function formatInstalls$1(count) {
|
|
@@ -5412,7 +4286,7 @@ async function runSearchPrompt(initialQuery = "", owner) {
|
|
|
5412
4286
|
process.stdout.write(CLEAR_DOWN);
|
|
5413
4287
|
const lines = [];
|
|
5414
4288
|
const cursor = `${BOLD$8}_${RESET$11}`;
|
|
5415
|
-
lines.push(`${TEXT$
|
|
4289
|
+
lines.push(`${TEXT$8}搜索技能:${RESET$11} ${query}${cursor}`);
|
|
5416
4290
|
lines.push("");
|
|
5417
4291
|
if (!query || query.length < 2) lines.push(`${DIM$11}输入关键词搜索(最少 2 个字符)${RESET$11}`);
|
|
5418
4292
|
else if (results.length === 0 && loading) lines.push(`${DIM$11}搜索中...${RESET$11}`);
|
|
@@ -5423,7 +4297,7 @@ async function runSearchPrompt(initialQuery = "", owner) {
|
|
|
5423
4297
|
const skill = visible[i];
|
|
5424
4298
|
const isSelected = i === selectedIndex;
|
|
5425
4299
|
const arrow = isSelected ? `${BOLD$8}>${RESET$11}` : " ";
|
|
5426
|
-
const name = isSelected ? `${BOLD$8}${skill.name}${RESET$11}` : `${TEXT$
|
|
4300
|
+
const name = isSelected ? `${BOLD$8}${skill.name}${RESET$11}` : `${TEXT$8}${skill.name}${RESET$11}`;
|
|
5427
4301
|
const source = skill.source ? ` ${DIM$11}${skill.source}${RESET$11}` : "";
|
|
5428
4302
|
const installs = formatInstalls$1(skill.installs);
|
|
5429
4303
|
const installsBadge = installs ? ` ${CYAN$2}${installs}${RESET$11}` : "";
|
|
@@ -5550,7 +4424,7 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
5550
4424
|
for (const skill of results.slice(0, 6)) {
|
|
5551
4425
|
const pkg = skill.source || skill.slug;
|
|
5552
4426
|
const installs = formatInstalls$1(skill.installs);
|
|
5553
|
-
console.log(`${TEXT$
|
|
4427
|
+
console.log(`${TEXT$8}${pkg}@${skill.name}${RESET$11}${installs ? ` ${CYAN$2}${installs}${RESET$11}` : ""}`);
|
|
5554
4428
|
console.log(`${DIM$11}└ https://skills.sh/${skill.slug}${RESET$11}`);
|
|
5555
4429
|
console.log();
|
|
5556
4430
|
}
|
|
@@ -5571,7 +4445,7 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
5571
4445
|
const pkg = selected.source || selected.slug;
|
|
5572
4446
|
const skillName = selected.name;
|
|
5573
4447
|
console.log();
|
|
5574
|
-
console.log(`${TEXT$
|
|
4448
|
+
console.log(`${TEXT$8}正在从 ${DIM$11}${pkg}${RESET$11} 安装 ${BOLD$8}${skillName}${RESET$11}...`);
|
|
5575
4449
|
console.log();
|
|
5576
4450
|
const { source, options: addOptions } = parseAddOptions([
|
|
5577
4451
|
pkg,
|
|
@@ -5581,15 +4455,15 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
5581
4455
|
await runAdd(source, addOptions);
|
|
5582
4456
|
console.log();
|
|
5583
4457
|
const info = getOwnerRepoFromString(pkg);
|
|
5584
|
-
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$11}查看技能:${RESET$11} ${TEXT$
|
|
5585
|
-
else console.log(`${DIM$11}发现更多技能:${RESET$11} ${TEXT$
|
|
4458
|
+
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$11}查看技能:${RESET$11} ${TEXT$8}https://skills.sh/${selected.slug}${RESET$11}`);
|
|
4459
|
+
else console.log(`${DIM$11}发现更多技能:${RESET$11} ${TEXT$8}https://skills.sh${RESET$11}`);
|
|
5586
4460
|
console.log();
|
|
5587
4461
|
}
|
|
5588
4462
|
const RESET$10 = "\x1B[0m";
|
|
5589
4463
|
const BOLD$7 = "\x1B[1m";
|
|
5590
4464
|
const DIM$10 = "\x1B[38;5;102m";
|
|
5591
4465
|
const CYAN$1 = "\x1B[36m";
|
|
5592
|
-
const YELLOW$
|
|
4466
|
+
const YELLOW$4 = "\x1B[33m";
|
|
5593
4467
|
function shortenPath(fullPath, cwd) {
|
|
5594
4468
|
const home = homedir();
|
|
5595
4469
|
if (fullPath.startsWith(home)) return fullPath.replace(home, "~");
|
|
@@ -5623,7 +4497,7 @@ async function runList(args) {
|
|
|
5623
4497
|
const validAgents = Object.keys(agents);
|
|
5624
4498
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
5625
4499
|
if (invalidAgents.length > 0) {
|
|
5626
|
-
console.log(`${YELLOW$
|
|
4500
|
+
console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$10}`);
|
|
5627
4501
|
console.log(`${DIM$10}Valid agents: ${validAgents.join(", ")}${RESET$10}`);
|
|
5628
4502
|
process.exit(1);
|
|
5629
4503
|
}
|
|
@@ -5660,7 +4534,7 @@ async function runList(args) {
|
|
|
5660
4534
|
const prefix = indent ? " " : "";
|
|
5661
4535
|
const shortPath = shortenPath(skill.canonicalPath, cwd);
|
|
5662
4536
|
const agentNames = skill.agents.map((a) => agents[a].displayName);
|
|
5663
|
-
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$
|
|
4537
|
+
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$4}not linked${RESET$10}`;
|
|
5664
4538
|
const paddedName = sanitizeMetadata(skill.name).padEnd(maxNameLength);
|
|
5665
4539
|
const paddedPath = shortPath.padEnd(maxPathLength);
|
|
5666
4540
|
console.log(`${prefix}${CYAN$1}${paddedName}${RESET$10} ${DIM$10}${paddedPath}${RESET$10} ${DIM$10}Agents:${RESET$10} ${agentInfo}`);
|
|
@@ -5722,6 +4596,10 @@ async function runList(args) {
|
|
|
5722
4596
|
console.log();
|
|
5723
4597
|
}
|
|
5724
4598
|
}
|
|
4599
|
+
var remove_exports = /* @__PURE__ */ __exportAll({
|
|
4600
|
+
parseRemoveOptions: () => parseRemoveOptions,
|
|
4601
|
+
removeCommand: () => removeCommand
|
|
4602
|
+
});
|
|
5725
4603
|
async function removeCommand(skillNames, options) {
|
|
5726
4604
|
const agentResult = await detectAgent();
|
|
5727
4605
|
if (agentResult.isAgent) {
|
|
@@ -5970,7 +4848,7 @@ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
|
5970
4848
|
const RESET$9 = "\x1B[0m";
|
|
5971
4849
|
const BOLD$6 = "\x1B[1m";
|
|
5972
4850
|
const DIM$9 = "\x1B[38;5;102m";
|
|
5973
|
-
const TEXT$
|
|
4851
|
+
const TEXT$7 = "\x1B[38;5;145m";
|
|
5974
4852
|
function parseUpdateOptions(args) {
|
|
5975
4853
|
const options = {};
|
|
5976
4854
|
const positional = [];
|
|
@@ -6064,13 +4942,13 @@ function printSkippedSkills(skipped) {
|
|
|
6064
4942
|
for (const [source, skills] of grouped) {
|
|
6065
4943
|
if (skills.length === 1) {
|
|
6066
4944
|
const skill = skills[0];
|
|
6067
|
-
console.log(` ${TEXT$
|
|
4945
|
+
console.log(` ${TEXT$7}•${RESET$9} ${sanitizeMetadata(skill.name)} ${DIM$9}(${skill.reason})${RESET$9}`);
|
|
6068
4946
|
} else {
|
|
6069
4947
|
const reason = skills[0].reason;
|
|
6070
4948
|
const names = skills.map((s) => sanitizeMetadata(s.name)).join(", ");
|
|
6071
|
-
console.log(` ${TEXT$
|
|
4949
|
+
console.log(` ${TEXT$7}•${RESET$9} ${names} ${DIM$9}(${reason})${RESET$9}`);
|
|
6072
4950
|
}
|
|
6073
|
-
console.log(` ${DIM$9}更新方法:${TEXT$
|
|
4951
|
+
console.log(` ${DIM$9}更新方法:${TEXT$7}npx skills add ${source} -g -y${RESET$9}`);
|
|
6074
4952
|
}
|
|
6075
4953
|
}
|
|
6076
4954
|
async function getProjectSkillsForUpdate(skillFilter) {
|
|
@@ -6119,7 +4997,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
6119
4997
|
if (skillNames.length === 0) {
|
|
6120
4998
|
if (!options.skills) {
|
|
6121
4999
|
console.log(`${DIM$9}锁文件中没有跟踪的全局技能。${RESET$9}`);
|
|
6122
|
-
console.log(`${DIM$9}安装技能:${RESET$9} ${TEXT$
|
|
5000
|
+
console.log(`${DIM$9}安装技能:${RESET$9} ${TEXT$7}npx skills add <package> -g${RESET$9}`);
|
|
6123
5001
|
}
|
|
6124
5002
|
return {
|
|
6125
5003
|
successCount,
|
|
@@ -6161,6 +5039,10 @@ async function updateGlobalSkills(options = {}) {
|
|
|
6161
5039
|
const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
|
|
6162
5040
|
let tempDir = null;
|
|
6163
5041
|
process.stdout.write(`\r${DIM$9}Checking skills from source: ${source}${RESET$9}\x1b[K\n`);
|
|
5042
|
+
if (firstEntry.sourceType === "skillhub") {
|
|
5043
|
+
console.log(` ${DIM$9}SkillHub 技能请使用 bfi skills add 重新安装来更新${RESET$9}`);
|
|
5044
|
+
continue;
|
|
5045
|
+
}
|
|
6164
5046
|
try {
|
|
6165
5047
|
if (firstEntry.sourceType === "github") {
|
|
6166
5048
|
const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
|
|
@@ -6224,25 +5106,25 @@ async function updateGlobalSkills(options = {}) {
|
|
|
6224
5106
|
};
|
|
6225
5107
|
}
|
|
6226
5108
|
if (updates.length === 0) {
|
|
6227
|
-
console.log(`${TEXT$
|
|
5109
|
+
console.log(`${TEXT$7}✓ 所有全局技能已是最新${RESET$9}`);
|
|
6228
5110
|
return {
|
|
6229
5111
|
successCount,
|
|
6230
5112
|
failCount,
|
|
6231
5113
|
checkedCount
|
|
6232
5114
|
};
|
|
6233
5115
|
}
|
|
6234
|
-
console.log(`${TEXT$
|
|
5116
|
+
console.log(`${TEXT$7}发现 ${updates.length} 个可更新全局技能${RESET$9}`);
|
|
6235
5117
|
console.log();
|
|
6236
5118
|
for (const update of updates) {
|
|
6237
5119
|
const safeName = sanitizeMetadata(update.name);
|
|
6238
|
-
console.log(`${TEXT$
|
|
5120
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
6239
5121
|
const installUrl = buildUpdateInstallSource(update.entry);
|
|
6240
5122
|
if (!installUrl) {
|
|
6241
5123
|
failCount++;
|
|
6242
5124
|
console.log(` ${DIM$9}✗ Cannot update ${safeName}: lock file is missing sourceUrl for this generic Git source${RESET$9}`);
|
|
6243
5125
|
continue;
|
|
6244
5126
|
}
|
|
6245
|
-
const cliEntry = join(__dirname$1, "..", "bin", "
|
|
5127
|
+
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
6246
5128
|
if (!existsSync(cliEntry)) {
|
|
6247
5129
|
failCount++;
|
|
6248
5130
|
console.log(` ${DIM$9}✗ Failed to update ${safeName}: CLI entrypoint not found at ${cliEntry}${RESET$9}`);
|
|
@@ -6264,7 +5146,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
6264
5146
|
shell: false
|
|
6265
5147
|
}).status === 0) {
|
|
6266
5148
|
successCount++;
|
|
6267
|
-
console.log(` ${TEXT$
|
|
5149
|
+
console.log(` ${TEXT$7}✓${RESET$9} 已更新 ${safeName}`);
|
|
6268
5150
|
} else {
|
|
6269
5151
|
failCount++;
|
|
6270
5152
|
console.log(` ${DIM$9}✗ 更新 ${safeName} 失败${RESET$9}`);
|
|
@@ -6284,7 +5166,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6284
5166
|
if (projectSkills.length === 0) {
|
|
6285
5167
|
if (!options.skills) {
|
|
6286
5168
|
console.log(`${DIM$9}没有可更新的项目技能。${RESET$9}`);
|
|
6287
|
-
console.log(`${DIM$9}Install project skills with${RESET$9} ${TEXT$
|
|
5169
|
+
console.log(`${DIM$9}Install project skills with${RESET$9} ${TEXT$7}npx skills add <package>${RESET$9}`);
|
|
6288
5170
|
}
|
|
6289
5171
|
return {
|
|
6290
5172
|
successCount,
|
|
@@ -6315,8 +5197,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
6315
5197
|
const targetParts = [];
|
|
6316
5198
|
if (hasUniversal) targetParts.push("Universal");
|
|
6317
5199
|
targetParts.push(...targetAgentNames);
|
|
6318
|
-
if (targetParts.length > 0) console.log(`${TEXT$
|
|
6319
|
-
console.log(`${TEXT$
|
|
5200
|
+
if (targetParts.length > 0) console.log(`${TEXT$7}更新目标:${targetParts.join(", ")}${RESET$9}`);
|
|
5201
|
+
console.log(`${TEXT$7}正在刷新 ${updatable.length} 个技能...${RESET$9}`);
|
|
6320
5202
|
console.log();
|
|
6321
5203
|
const bySource = /* @__PURE__ */ new Map();
|
|
6322
5204
|
for (const skill of updatable) {
|
|
@@ -6326,7 +5208,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6326
5208
|
bySource.set(source, existing);
|
|
6327
5209
|
}
|
|
6328
5210
|
const localLock = await readLocalLock();
|
|
6329
|
-
const cliEntry = join(__dirname$1, "..", "bin", "
|
|
5211
|
+
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
6330
5212
|
if (!existsSync(cliEntry)) {
|
|
6331
5213
|
console.log(`${DIM$9}✗ CLI 入口未找到 ${cliEntry}${RESET$9}`);
|
|
6332
5214
|
return {
|
|
@@ -6361,7 +5243,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6361
5243
|
const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
|
|
6362
5244
|
for (const skill of remainingSkills) {
|
|
6363
5245
|
const safeName = sanitizeMetadata(skill.name);
|
|
6364
|
-
console.log(`${TEXT$
|
|
5246
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
6365
5247
|
const installUrl = buildLocalUpdateSource(skill.entry);
|
|
6366
5248
|
if (!installUrl) {
|
|
6367
5249
|
failCount++;
|
|
@@ -6387,7 +5269,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6387
5269
|
shell: false
|
|
6388
5270
|
}).status === 0) {
|
|
6389
5271
|
successCount++;
|
|
6390
|
-
console.log(` ${TEXT$
|
|
5272
|
+
console.log(` ${TEXT$7}✓${RESET$9} 已更新 ${safeName}`);
|
|
6391
5273
|
} else {
|
|
6392
5274
|
failCount++;
|
|
6393
5275
|
console.log(` ${DIM$9}✗ 更新 ${safeName} 失败${RESET$9}`);
|
|
@@ -6407,16 +5289,16 @@ function printLegacyProjectSkills(legacy) {
|
|
|
6407
5289
|
console.log(`${DIM$9}${legacy.length} project skill(s) cannot be updated automatically (installed before skillPath tracking):${RESET$9}`);
|
|
6408
5290
|
for (const skill of legacy) {
|
|
6409
5291
|
const reinstall = buildLocalUpdateSource(skill.entry);
|
|
6410
|
-
console.log(` ${TEXT$
|
|
6411
|
-
if (reinstall) console.log(` ${DIM$9}刷新方法:${TEXT$
|
|
5292
|
+
console.log(` ${TEXT$7}•${RESET$9} ${sanitizeMetadata(skill.name)}`);
|
|
5293
|
+
if (reinstall) console.log(` ${DIM$9}刷新方法:${TEXT$7}npx skills add ${reinstall} -y${RESET$9}`);
|
|
6412
5294
|
else console.log(` ${DIM$9}To refresh: reinstall using the original full Git URL; this lock entry only has an ambiguous shorthand.${RESET$9}`);
|
|
6413
5295
|
}
|
|
6414
5296
|
}
|
|
6415
5297
|
async function runUpdate(args = []) {
|
|
6416
5298
|
const options = parseUpdateOptions(args);
|
|
6417
5299
|
const scope = await resolveUpdateScope(options);
|
|
6418
|
-
if (options.skills) console.log(`${TEXT$
|
|
6419
|
-
else console.log(`${TEXT$
|
|
5300
|
+
if (options.skills) console.log(`${TEXT$7}正在更新 ${options.skills.join(", ")}...${RESET$9}`);
|
|
5301
|
+
else console.log(`${TEXT$7}正在检查技能更新...${RESET$9}`);
|
|
6420
5302
|
console.log();
|
|
6421
5303
|
let totalSuccess = 0;
|
|
6422
5304
|
let totalFail = 0;
|
|
@@ -6438,7 +5320,7 @@ async function runUpdate(args = []) {
|
|
|
6438
5320
|
}
|
|
6439
5321
|
if (options.skills && totalFound === 0) console.log(`${DIM$9}未找到匹配的已安装技能:${options.skills.join(", ")}${RESET$9}`);
|
|
6440
5322
|
console.log();
|
|
6441
|
-
if (totalSuccess > 0) console.log(`${TEXT$
|
|
5323
|
+
if (totalSuccess > 0) console.log(`${TEXT$7}✓ 已更新 ${totalSuccess} 个技能${RESET$9}`);
|
|
6442
5324
|
if (totalFail > 0) {
|
|
6443
5325
|
console.log(`${DIM$9}更新失败 ${totalFail} 个技能${RESET$9}`);
|
|
6444
5326
|
process.exitCode = 1;
|
|
@@ -6895,42 +5777,44 @@ function getOptionValue$3(args, flag) {
|
|
|
6895
5777
|
const RESET$8 = "\x1B[0m";
|
|
6896
5778
|
const BOLD$5 = "\x1B[1m";
|
|
6897
5779
|
const DIM$8 = "\x1B[38;5;102m";
|
|
6898
|
-
const TEXT$
|
|
5780
|
+
const TEXT$6 = "\x1B[38;5;145m";
|
|
6899
5781
|
const CYAN = "\x1B[36m";
|
|
6900
5782
|
const MARKETPLACE_API = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
6901
5783
|
async function runSearch(args, globalOpts) {
|
|
6902
5784
|
const query = parseSearchQuery(args);
|
|
6903
5785
|
const namespace = args.includes("--namespace") ? getOptionValue$2(args, "--namespace") : void 0;
|
|
5786
|
+
const json = args.includes("--json");
|
|
6904
5787
|
if (!query) {
|
|
6905
|
-
console.log("用法:bfi search <query> [--namespace <ns>] [--marketplace]");
|
|
5788
|
+
console.log("用法:bfi search <query> [--namespace <ns>] [--marketplace] [--json]");
|
|
6906
5789
|
console.log();
|
|
6907
5790
|
console.log("选项:");
|
|
6908
5791
|
console.log(" --namespace <ns> 按 SkillHub 命名空间筛选");
|
|
6909
5792
|
console.log(" --marketplace 仅搜索 skills.sh 市场");
|
|
6910
5793
|
console.log(" --all 同时搜索 SkillHub 和市场");
|
|
5794
|
+
console.log(" --json 以 JSON 格式输出结果");
|
|
6911
5795
|
return;
|
|
6912
5796
|
}
|
|
6913
5797
|
const useMarketplace = args.includes("--marketplace");
|
|
6914
5798
|
const searchAll = args.includes("--all");
|
|
6915
5799
|
if (useMarketplace) {
|
|
6916
|
-
await searchMarketplace(query);
|
|
5800
|
+
await searchMarketplace(query, json);
|
|
6917
5801
|
return;
|
|
6918
5802
|
}
|
|
6919
5803
|
if (searchAll) {
|
|
6920
5804
|
console.log(`${BOLD$5}=== SkillHub 注册表 ===${RESET$8}\n`);
|
|
6921
|
-
await searchSkillHub(query, namespace, globalOpts);
|
|
5805
|
+
await searchSkillHub(query, namespace, globalOpts, json);
|
|
6922
5806
|
console.log(`\n${BOLD$5}=== skills.sh 市场 ===${RESET$8}\n`);
|
|
6923
|
-
await searchMarketplace(query);
|
|
5807
|
+
await searchMarketplace(query, json);
|
|
6924
5808
|
return;
|
|
6925
5809
|
}
|
|
6926
|
-
await searchSkillHub(query, namespace, globalOpts);
|
|
5810
|
+
await searchSkillHub(query, namespace, globalOpts, json);
|
|
6927
5811
|
}
|
|
6928
5812
|
function parseSearchQuery(args) {
|
|
6929
5813
|
const parts = [];
|
|
6930
5814
|
for (let i = 0; i < args.length; i++) {
|
|
6931
5815
|
const arg = args[i];
|
|
6932
|
-
if (arg === "--namespace" || arg === "--marketplace" || arg === "--all") {
|
|
6933
|
-
if (arg
|
|
5816
|
+
if (arg === "--namespace" || arg === "--marketplace" || arg === "--all" || arg === "--registry" || arg === "--token" || arg === "--json" || arg === "--limit") {
|
|
5817
|
+
if (arg !== "--marketplace" && arg !== "--all" && arg !== "--json") i++;
|
|
6934
5818
|
continue;
|
|
6935
5819
|
}
|
|
6936
5820
|
if (arg.startsWith("--")) continue;
|
|
@@ -6938,7 +5822,7 @@ function parseSearchQuery(args) {
|
|
|
6938
5822
|
}
|
|
6939
5823
|
return parts.join(" ");
|
|
6940
5824
|
}
|
|
6941
|
-
async function searchSkillHub(query, namespace, globalOpts) {
|
|
5825
|
+
async function searchSkillHub(query, namespace, globalOpts, json = false) {
|
|
6942
5826
|
const { registry, token } = resolveConfig(globalOpts);
|
|
6943
5827
|
const api = new SkillHubAPI(registry, token);
|
|
6944
5828
|
try {
|
|
@@ -6949,7 +5833,30 @@ async function searchSkillHub(query, namespace, globalOpts) {
|
|
|
6949
5833
|
});
|
|
6950
5834
|
const skills = result.results || result.data || [];
|
|
6951
5835
|
if (skills.length === 0) {
|
|
6952
|
-
console.log(
|
|
5836
|
+
if (json) console.log(JSON.stringify({
|
|
5837
|
+
source: "skillhub",
|
|
5838
|
+
count: 0,
|
|
5839
|
+
results: []
|
|
5840
|
+
}, null, 2));
|
|
5841
|
+
else console.log(`${DIM$8}SkillHub 上未找到 "${query}" 的技能${RESET$8}`);
|
|
5842
|
+
return;
|
|
5843
|
+
}
|
|
5844
|
+
if (json) {
|
|
5845
|
+
const results = skills.map((skill) => {
|
|
5846
|
+
const canonicalSlug = skill.slug || "";
|
|
5847
|
+
const displaySlug = canonicalSlug.indexOf("--") > 0 ? canonicalSlug : `global--${canonicalSlug}`;
|
|
5848
|
+
return {
|
|
5849
|
+
name: skill.displayName || skill.name || skill.slug,
|
|
5850
|
+
slug: displaySlug,
|
|
5851
|
+
description: skill.summary || skill.description || "",
|
|
5852
|
+
version: skill.version || null
|
|
5853
|
+
};
|
|
5854
|
+
});
|
|
5855
|
+
console.log(JSON.stringify({
|
|
5856
|
+
source: "skillhub",
|
|
5857
|
+
count: results.length,
|
|
5858
|
+
results
|
|
5859
|
+
}, null, 2));
|
|
6953
5860
|
return;
|
|
6954
5861
|
}
|
|
6955
5862
|
console.log(`SkillHub 上找到 ${skills.length} 个技能:\n`);
|
|
@@ -6960,16 +5867,16 @@ async function searchSkillHub(query, namespace, globalOpts) {
|
|
|
6960
5867
|
const displaySlug = canonicalSlug.indexOf("--") > 0 ? canonicalSlug : `global--${canonicalSlug}`;
|
|
6961
5868
|
console.log(` ${BOLD$5}${name}${RESET$8}`);
|
|
6962
5869
|
if (desc) console.log(` ${DIM$8}${truncate$2(desc, 100)}${RESET$8}`);
|
|
6963
|
-
console.log(` ${TEXT$
|
|
5870
|
+
console.log(` ${TEXT$6}slug: ${displaySlug}${RESET$8}`);
|
|
6964
5871
|
if (skill.version) console.log(` ${DIM$8}version: ${skill.version}${RESET$8}`);
|
|
6965
|
-
console.log(` ${DIM$8}安装:${TEXT$
|
|
5872
|
+
console.log(` ${DIM$8}安装:${TEXT$6}bfi skills add ${displaySlug}${RESET$8}`);
|
|
6966
5873
|
console.log();
|
|
6967
5874
|
}
|
|
6968
5875
|
} catch (error) {
|
|
6969
5876
|
console.error(`${DIM$8}SkillHub 搜索失败:${error.message}${RESET$8}`);
|
|
6970
5877
|
}
|
|
6971
5878
|
}
|
|
6972
|
-
async function searchMarketplace(query) {
|
|
5879
|
+
async function searchMarketplace(query, json = false) {
|
|
6973
5880
|
try {
|
|
6974
5881
|
const params = new URLSearchParams({
|
|
6975
5882
|
q: query,
|
|
@@ -6978,12 +5885,37 @@ async function searchMarketplace(query) {
|
|
|
6978
5885
|
const url = `${MARKETPLACE_API}/api/search?${params.toString()}`;
|
|
6979
5886
|
const res = await fetch(url);
|
|
6980
5887
|
if (!res.ok) {
|
|
6981
|
-
console.log(
|
|
5888
|
+
if (json) console.log(JSON.stringify({
|
|
5889
|
+
source: "marketplace",
|
|
5890
|
+
count: 0,
|
|
5891
|
+
results: []
|
|
5892
|
+
}, null, 2));
|
|
5893
|
+
else console.log(`${DIM$8}skills.sh 市场无结果${RESET$8}`);
|
|
6982
5894
|
return;
|
|
6983
5895
|
}
|
|
6984
5896
|
const skills = (await res.json()).skills || [];
|
|
6985
5897
|
if (skills.length === 0) {
|
|
6986
|
-
console.log(
|
|
5898
|
+
if (json) console.log(JSON.stringify({
|
|
5899
|
+
source: "marketplace",
|
|
5900
|
+
count: 0,
|
|
5901
|
+
results: []
|
|
5902
|
+
}, null, 2));
|
|
5903
|
+
else console.log(`${DIM$8}skills.sh 上未找到 "${query}" 的技能${RESET$8}`);
|
|
5904
|
+
return;
|
|
5905
|
+
}
|
|
5906
|
+
if (json) {
|
|
5907
|
+
const results = skills.slice(0, 12).map((skill) => ({
|
|
5908
|
+
name: skill.name,
|
|
5909
|
+
source: skill.source || null,
|
|
5910
|
+
installs: skill.installs || 0,
|
|
5911
|
+
url: `https://skills.sh/${skill.id}`,
|
|
5912
|
+
installCommand: skill.source ? `bfi skills add github:${skill.source}` : null
|
|
5913
|
+
}));
|
|
5914
|
+
console.log(JSON.stringify({
|
|
5915
|
+
source: "marketplace",
|
|
5916
|
+
count: results.length,
|
|
5917
|
+
results
|
|
5918
|
+
}, null, 2));
|
|
6987
5919
|
return;
|
|
6988
5920
|
}
|
|
6989
5921
|
console.log(`skills.sh 上找到 ${skills.length} 个技能:\n`);
|
|
@@ -6992,8 +5924,8 @@ async function searchMarketplace(query) {
|
|
|
6992
5924
|
console.log(` ${BOLD$5}${skill.name}${RESET$8}`);
|
|
6993
5925
|
if (skill.source) console.log(` ${DIM$8}来源:${skill.source}${RESET$8}`);
|
|
6994
5926
|
if (installs) console.log(` ${CYAN}${installs}${RESET$8}`);
|
|
6995
|
-
console.log(` ${TEXT$
|
|
6996
|
-
if (skill.source) console.log(` ${DIM$8}安装:${TEXT$
|
|
5927
|
+
console.log(` ${TEXT$6}https://skills.sh/${skill.id}${RESET$8}`);
|
|
5928
|
+
if (skill.source) console.log(` ${DIM$8}安装:${TEXT$6}bfi skills add github:${skill.source}${RESET$8}`);
|
|
6997
5929
|
console.log();
|
|
6998
5930
|
}
|
|
6999
5931
|
} catch {
|
|
@@ -7017,52 +5949,54 @@ function getOptionValue$2(args, flag) {
|
|
|
7017
5949
|
const RESET$7 = "\x1B[0m";
|
|
7018
5950
|
const BOLD$4 = "\x1B[1m";
|
|
7019
5951
|
const DIM$7 = "\x1B[38;5;102m";
|
|
7020
|
-
const TEXT$
|
|
7021
|
-
const YELLOW$
|
|
7022
|
-
const RED$
|
|
5952
|
+
const TEXT$5 = "\x1B[38;5;145m";
|
|
5953
|
+
const YELLOW$3 = "\x1B[33m";
|
|
5954
|
+
const RED$3 = "\x1B[31m";
|
|
7023
5955
|
const GREEN$4 = "\x1B[32m";
|
|
7024
5956
|
async function runInfo(args, globalOpts) {
|
|
7025
|
-
const
|
|
7026
|
-
if (!
|
|
7027
|
-
console.log("用法:bfi info <skill-
|
|
7028
|
-
console.log("示例:bfi info global/my-skill");
|
|
5957
|
+
const ref = args[0];
|
|
5958
|
+
if (!ref) {
|
|
5959
|
+
console.log("用法:bfi info <skill-ref>");
|
|
5960
|
+
console.log("示例:bfi info @global/my-skill");
|
|
7029
5961
|
process.exit(1);
|
|
7030
5962
|
}
|
|
7031
5963
|
const { registry, token } = resolveConfig(globalOpts);
|
|
7032
5964
|
const api = new SkillHubAPI(registry, token);
|
|
5965
|
+
const { namespace, slug } = parseSkillRef(ref, "global");
|
|
5966
|
+
const canonical = toCanonicalSlug(namespace, slug);
|
|
7033
5967
|
try {
|
|
7034
|
-
const result = await api.getSkill(
|
|
5968
|
+
const result = await api.getSkill(canonical);
|
|
7035
5969
|
const skill = result.data || result.skill || result;
|
|
7036
5970
|
if (!skill || !skill.slug) {
|
|
7037
|
-
console.log(`${DIM$7}未找到技能:${
|
|
5971
|
+
console.log(`${DIM$7}未找到技能:${ref}${RESET$7}`);
|
|
7038
5972
|
process.exit(1);
|
|
7039
5973
|
}
|
|
7040
5974
|
console.log();
|
|
7041
5975
|
console.log(`${BOLD$4}${skill.displayName || skill.name || skill.slug}${RESET$7}`);
|
|
7042
5976
|
console.log(`${DIM$7}${"─".repeat(48)}${RESET$7}`);
|
|
7043
|
-
console.log(` ${BOLD$4}Slug:${RESET$7} ${TEXT$
|
|
7044
|
-
if (skill.summary || skill.description) console.log(` ${BOLD$4}描述:${RESET$7} ${TEXT$
|
|
5977
|
+
console.log(` ${BOLD$4}Slug:${RESET$7} ${TEXT$5}${skill.slug}${RESET$7}`);
|
|
5978
|
+
if (skill.summary || skill.description) console.log(` ${BOLD$4}描述:${RESET$7} ${TEXT$5}${skill.summary || skill.description}${RESET$7}`);
|
|
7045
5979
|
const owner = skill.owner;
|
|
7046
5980
|
if (owner) {
|
|
7047
|
-
console.log(` ${BOLD$4}所有者:${RESET$7} ${TEXT$
|
|
7048
|
-
if (owner.handle) console.log(` ${BOLD$4}句柄:${RESET$7} ${TEXT$
|
|
5981
|
+
console.log(` ${BOLD$4}所有者:${RESET$7} ${TEXT$5}${owner.displayName || owner.handle}${RESET$7}`);
|
|
5982
|
+
if (owner.handle) console.log(` ${BOLD$4}句柄:${RESET$7} ${TEXT$5}${owner.handle}${RESET$7}`);
|
|
7049
5983
|
}
|
|
7050
5984
|
const latestVersion = skill.latestVersion;
|
|
7051
5985
|
if (latestVersion) {
|
|
7052
|
-
console.log(` ${BOLD$4}版本:${RESET$7} ${TEXT$
|
|
7053
|
-
if (latestVersion.license) console.log(` ${BOLD$4}许可证:${RESET$7} ${TEXT$
|
|
5986
|
+
console.log(` ${BOLD$4}版本:${RESET$7} ${TEXT$5}${latestVersion.version || "N/A"}${RESET$7}`);
|
|
5987
|
+
if (latestVersion.license) console.log(` ${BOLD$4}许可证:${RESET$7} ${TEXT$5}${latestVersion.license}${RESET$7}`);
|
|
7054
5988
|
if (latestVersion.changelog) console.log(` ${BOLD$4}变更日志:${RESET$7} ${DIM$7}${truncate$1(latestVersion.changelog, 80)}${RESET$7}`);
|
|
7055
5989
|
if (latestVersion.createdAt) {
|
|
7056
5990
|
const date = new Date(latestVersion.createdAt).toISOString().split("T")[0];
|
|
7057
|
-
console.log(` ${BOLD$4}发布时间:${RESET$7} ${TEXT$
|
|
5991
|
+
console.log(` ${BOLD$4}发布时间:${RESET$7} ${TEXT$5}${date}${RESET$7}`);
|
|
7058
5992
|
}
|
|
7059
5993
|
}
|
|
7060
5994
|
const stats = skill.stats;
|
|
7061
5995
|
if (stats) {
|
|
7062
5996
|
console.log();
|
|
7063
5997
|
console.log(` ${BOLD$4}统计:${RESET$7}`);
|
|
7064
|
-
if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$
|
|
7065
|
-
if (stats.views !== void 0) console.log(` 浏览量:${TEXT$
|
|
5998
|
+
if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$5}${stats.downloads}${RESET$7}`);
|
|
5999
|
+
if (stats.views !== void 0) console.log(` 浏览量:${TEXT$5}${stats.views}${RESET$7}`);
|
|
7066
6000
|
}
|
|
7067
6001
|
const tags = skill.tags;
|
|
7068
6002
|
if (Array.isArray(tags) && tags.length > 0) {
|
|
@@ -7070,32 +6004,32 @@ async function runInfo(args, globalOpts) {
|
|
|
7070
6004
|
const tag = t;
|
|
7071
6005
|
return tag.name || tag;
|
|
7072
6006
|
}).join(", ");
|
|
7073
|
-
console.log(` ${BOLD$4}标签:${RESET$7} ${TEXT$
|
|
6007
|
+
console.log(` ${BOLD$4}标签:${RESET$7} ${TEXT$5}${tagList}${RESET$7}`);
|
|
7074
6008
|
}
|
|
7075
6009
|
const moderation = skill.moderation;
|
|
7076
6010
|
if (moderation) {
|
|
7077
6011
|
const verdict = moderation.verdict;
|
|
7078
6012
|
if (verdict === "MALWARE_BLOCKED" || moderation.isMalwareBlocked) {
|
|
7079
6013
|
console.log();
|
|
7080
|
-
console.log(` ${RED$
|
|
7081
|
-
if (moderation.summary) console.log(` ${RED$
|
|
6014
|
+
console.log(` ${RED$3}${BOLD$4}⚠ 已封禁:检测到恶意软件${RESET$7}`);
|
|
6015
|
+
if (moderation.summary) console.log(` ${RED$3}${moderation.summary}${RESET$7}`);
|
|
7082
6016
|
} else if (verdict === "SUSPICIOUS" || moderation.isSuspicious) {
|
|
7083
6017
|
console.log();
|
|
7084
|
-
console.log(` ${YELLOW$
|
|
7085
|
-
if (moderation.summary) console.log(` ${YELLOW$
|
|
6018
|
+
console.log(` ${YELLOW$3}⚠ 可疑技能${RESET$7}`);
|
|
6019
|
+
if (moderation.summary) console.log(` ${YELLOW$3}${moderation.summary}${RESET$7}`);
|
|
7086
6020
|
} else if (verdict === "SAFE") console.log(` ${BOLD$4}安全:${RESET$7} ${GREEN$4}已验证安全${RESET$7}`);
|
|
7087
6021
|
}
|
|
7088
6022
|
if (skill.createdAt) {
|
|
7089
6023
|
const created = new Date(skill.createdAt).toISOString().split("T")[0];
|
|
7090
|
-
console.log(` ${BOLD$4}创建时间:${RESET$7} ${TEXT$
|
|
6024
|
+
console.log(` ${BOLD$4}创建时间:${RESET$7} ${TEXT$5}${created}${RESET$7}`);
|
|
7091
6025
|
}
|
|
7092
6026
|
if (skill.updatedAt) {
|
|
7093
6027
|
const updated = new Date(skill.updatedAt).toISOString().split("T")[0];
|
|
7094
|
-
console.log(` ${BOLD$4}更新时间:${RESET$7} ${TEXT$
|
|
6028
|
+
console.log(` ${BOLD$4}更新时间:${RESET$7} ${TEXT$5}${updated}${RESET$7}`);
|
|
7095
6029
|
}
|
|
7096
6030
|
console.log();
|
|
7097
6031
|
} catch (error) {
|
|
7098
|
-
if (error.status === 404) console.error(`未找到技能:${
|
|
6032
|
+
if (error.status === 404) console.error(`未找到技能:${ref}`);
|
|
7099
6033
|
else console.error(`获取技能信息失败:${error.message}`);
|
|
7100
6034
|
process.exit(1);
|
|
7101
6035
|
}
|
|
@@ -7205,17 +6139,17 @@ async function handleSet(args, _globalOpts) {
|
|
|
7205
6139
|
const RESET$5 = "\x1B[0m";
|
|
7206
6140
|
const BOLD$3 = "\x1B[1m";
|
|
7207
6141
|
const DIM$5 = "\x1B[38;5;102m";
|
|
7208
|
-
const TEXT$
|
|
6142
|
+
const TEXT$4 = "\x1B[38;5;145m";
|
|
7209
6143
|
async function runDocs(args, globalOpts) {
|
|
7210
6144
|
switch (args[0]) {
|
|
7211
6145
|
case "upload":
|
|
7212
6146
|
await handleUpload(args.slice(1), globalOpts);
|
|
7213
6147
|
break;
|
|
7214
6148
|
case "list":
|
|
7215
|
-
await handleList
|
|
6149
|
+
await handleList(args.slice(1), globalOpts);
|
|
7216
6150
|
break;
|
|
7217
6151
|
case "info":
|
|
7218
|
-
await handleInfo
|
|
6152
|
+
await handleInfo(args.slice(1), globalOpts);
|
|
7219
6153
|
break;
|
|
7220
6154
|
default: printDocsHelp();
|
|
7221
6155
|
}
|
|
@@ -7257,11 +6191,11 @@ async function handleUpload(args, globalOpts) {
|
|
|
7257
6191
|
try {
|
|
7258
6192
|
const fileBuffer = fs.readFileSync(resolvedPath);
|
|
7259
6193
|
const result = await api.uploadDoc(fileBuffer, fileName);
|
|
7260
|
-
console.log(`${TEXT$
|
|
6194
|
+
console.log(`${TEXT$4}✓ 上传成功${RESET$5}`);
|
|
7261
6195
|
console.log();
|
|
7262
|
-
if (result.id) console.log(` ${BOLD$3}ID:${RESET$5} ${TEXT$
|
|
7263
|
-
if (result.status) console.log(` ${BOLD$3}状态:${RESET$5} ${TEXT$
|
|
7264
|
-
if (result.fileName) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$
|
|
6196
|
+
if (result.id) console.log(` ${BOLD$3}ID:${RESET$5} ${TEXT$4}${result.id}${RESET$5}`);
|
|
6197
|
+
if (result.status) console.log(` ${BOLD$3}状态:${RESET$5} ${TEXT$4}${result.status}${RESET$5}`);
|
|
6198
|
+
if (result.fileName) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$4}${result.fileName}${RESET$5}`);
|
|
7265
6199
|
console.log();
|
|
7266
6200
|
if (result.id) console.log(`${DIM$5}查看状态:bfi docs info ${result.id}${RESET$5}`);
|
|
7267
6201
|
} catch (error) {
|
|
@@ -7269,7 +6203,7 @@ async function handleUpload(args, globalOpts) {
|
|
|
7269
6203
|
process.exit(1);
|
|
7270
6204
|
}
|
|
7271
6205
|
}
|
|
7272
|
-
async function handleList
|
|
6206
|
+
async function handleList(args, globalOpts) {
|
|
7273
6207
|
const page = parseInt(args[0] || "0", 10);
|
|
7274
6208
|
const size = parseInt(args[1] || "20", 10);
|
|
7275
6209
|
const { registry, token } = resolveConfig(globalOpts);
|
|
@@ -7290,7 +6224,7 @@ async function handleList$1(args, globalOpts) {
|
|
|
7290
6224
|
const status = doc.status || "unknown";
|
|
7291
6225
|
const createdAt = doc.createdAt || doc.created_at;
|
|
7292
6226
|
const dateStr = createdAt ? new Date(createdAt).toISOString().split("T")[0] : "N/A";
|
|
7293
|
-
console.log(` ${BOLD$3}#${id}${RESET$5} ${TEXT$
|
|
6227
|
+
console.log(` ${BOLD$3}#${id}${RESET$5} ${TEXT$4}${fileName}${RESET$5}`);
|
|
7294
6228
|
console.log(` ${DIM$5}状态:${status} | 日期:${dateStr}${RESET$5}`);
|
|
7295
6229
|
console.log();
|
|
7296
6230
|
}
|
|
@@ -7302,7 +6236,7 @@ async function handleList$1(args, globalOpts) {
|
|
|
7302
6236
|
process.exit(1);
|
|
7303
6237
|
}
|
|
7304
6238
|
}
|
|
7305
|
-
async function handleInfo
|
|
6239
|
+
async function handleInfo(args, globalOpts) {
|
|
7306
6240
|
const docId = args[0];
|
|
7307
6241
|
if (!docId) {
|
|
7308
6242
|
console.error("用法:bfi docs info <id>");
|
|
@@ -7325,18 +6259,18 @@ async function handleInfo$1(args, globalOpts) {
|
|
|
7325
6259
|
console.log();
|
|
7326
6260
|
console.log(`${BOLD$3}文档 #${doc.id}${RESET$5}`);
|
|
7327
6261
|
console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
|
|
7328
|
-
if (doc.fileName || doc.file_name) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$
|
|
6262
|
+
if (doc.fileName || doc.file_name) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$4}${doc.fileName || doc.file_name}${RESET$5}`);
|
|
7329
6263
|
if (doc.status) {
|
|
7330
|
-
const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$
|
|
6264
|
+
const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$4;
|
|
7331
6265
|
console.log(` ${BOLD$3}状态:${RESET$5} ${statusColor}${doc.status}${RESET$5}`);
|
|
7332
6266
|
}
|
|
7333
6267
|
if (doc.createdAt || doc.created_at) {
|
|
7334
6268
|
const date = new Date(doc.createdAt || doc.created_at).toISOString().replace("T", " ").slice(0, 19);
|
|
7335
|
-
console.log(` ${BOLD$3}创建时间:${RESET$5} ${TEXT$
|
|
6269
|
+
console.log(` ${BOLD$3}创建时间:${RESET$5} ${TEXT$4}${date}${RESET$5}`);
|
|
7336
6270
|
}
|
|
7337
6271
|
if (doc.updatedAt || doc.updated_at) {
|
|
7338
6272
|
const date = new Date(doc.updatedAt || doc.updated_at).toISOString().replace("T", " ").slice(0, 19);
|
|
7339
|
-
console.log(` ${BOLD$3}更新时间:${RESET$5} ${TEXT$
|
|
6273
|
+
console.log(` ${BOLD$3}更新时间:${RESET$5} ${TEXT$4}${date}${RESET$5}`);
|
|
7340
6274
|
}
|
|
7341
6275
|
if (doc.result) {
|
|
7342
6276
|
console.log();
|
|
@@ -7359,15 +6293,15 @@ async function handleInfo$1(args, globalOpts) {
|
|
|
7359
6293
|
const RESET$4 = "\x1B[0m";
|
|
7360
6294
|
const BOLD$2 = "\x1B[1m";
|
|
7361
6295
|
const DIM$4 = "\x1B[38;5;102m";
|
|
7362
|
-
const TEXT$
|
|
6296
|
+
const TEXT$3 = "\x1B[38;5;145m";
|
|
7363
6297
|
const GREEN$3 = "\x1B[32m";
|
|
7364
|
-
const YELLOW$
|
|
7365
|
-
const RED$
|
|
6298
|
+
const YELLOW$2 = "\x1B[33m";
|
|
6299
|
+
const RED$2 = "\x1B[31m";
|
|
7366
6300
|
async function runPublish(args, globalOpts) {
|
|
7367
6301
|
const fromIdx = args.indexOf("--from");
|
|
7368
6302
|
const mode = fromIdx !== -1 ? args[fromIdx + 1] : "dir";
|
|
7369
6303
|
const namespace = getOptionValue$1(args, "--namespace") || globalOpts.namespace || "global";
|
|
7370
|
-
const visibility = getOptionValue$1(args, "--visibility") || "
|
|
6304
|
+
const visibility = getOptionValue$1(args, "--visibility") || "public";
|
|
7371
6305
|
const yes = args.includes("--yes") || args.includes("-y");
|
|
7372
6306
|
const branch = getOptionValue$1(args, "--branch");
|
|
7373
6307
|
const directory = getOptionValue$1(args, "--directory");
|
|
@@ -7424,22 +6358,22 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
|
|
|
7424
6358
|
const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
|
|
7425
6359
|
const skillName = frontmatter.name || path$1.basename(resolvedDir);
|
|
7426
6360
|
console.log(`${DIM$4}正在从目录发布技能...${RESET$4}`);
|
|
7427
|
-
console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$
|
|
7428
|
-
console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$
|
|
7429
|
-
console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$
|
|
7430
|
-
console.log(` ${BOLD$2}目录:${RESET$4} ${TEXT$
|
|
6361
|
+
console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
|
|
6362
|
+
console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$3}${namespace}${RESET$4}`);
|
|
6363
|
+
console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$3}${visibility}${RESET$4}`);
|
|
6364
|
+
console.log(` ${BOLD$2}目录:${RESET$4} ${TEXT$3}${resolvedDir}${RESET$4}`);
|
|
7431
6365
|
console.log();
|
|
7432
6366
|
console.log(`${DIM$4}正在创建 zip...${RESET$4}`);
|
|
7433
6367
|
const zipBuffer = createZipFromDir(resolvedDir);
|
|
7434
6368
|
console.log(` ${GREEN$3}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$4}`);
|
|
7435
6369
|
console.log(`${DIM$4}正在上传到 SkillHub...${RESET$4}`);
|
|
7436
6370
|
try {
|
|
7437
|
-
const res = await api.publishSkill(namespace, zipBuffer, visibility);
|
|
6371
|
+
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
7438
6372
|
console.log(` ${GREEN$3}发布成功!${RESET$4}`);
|
|
7439
|
-
if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$
|
|
7440
|
-
if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$
|
|
6373
|
+
if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
|
|
6374
|
+
if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
|
|
7441
6375
|
} catch (error) {
|
|
7442
|
-
console.error(` ${RED$
|
|
6376
|
+
console.error(` ${RED$2}发布失败:${error.message}${RESET$4}`);
|
|
7443
6377
|
process.exit(1);
|
|
7444
6378
|
}
|
|
7445
6379
|
console.log();
|
|
@@ -7463,27 +6397,27 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
|
|
|
7463
6397
|
await extractZip(resolvedZip, tmpDir);
|
|
7464
6398
|
const skillMdPath = findSkillMd(tmpDir);
|
|
7465
6399
|
if (!skillMdPath) {
|
|
7466
|
-
console.error(` ${RED$
|
|
6400
|
+
console.error(` ${RED$2}zip 文件中未找到 SKILL.md${RESET$4}`);
|
|
7467
6401
|
process.exit(1);
|
|
7468
6402
|
}
|
|
7469
6403
|
const skillDir = path$1.dirname(skillMdPath);
|
|
7470
6404
|
console.log(` ${GREEN$3}在 ${path$1.relative(tmpDir, skillDir) || "."} 中找到 SKILL.md${RESET$4}`);
|
|
7471
6405
|
const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
|
|
7472
6406
|
const skillName = frontmatter.name || path$1.basename(skillDir);
|
|
7473
|
-
console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$
|
|
7474
|
-
console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$
|
|
7475
|
-
console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$
|
|
6407
|
+
console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
|
|
6408
|
+
console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$3}${namespace}${RESET$4}`);
|
|
6409
|
+
console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$3}${visibility}${RESET$4}`);
|
|
7476
6410
|
console.log();
|
|
7477
6411
|
console.log(`${DIM$4}正在重新打包...${RESET$4}`);
|
|
7478
6412
|
const zipBuffer = createZipFromDir(skillDir);
|
|
7479
6413
|
console.log(` ${GREEN$3}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$4}`);
|
|
7480
6414
|
console.log(`${DIM$4}正在上传到 SkillHub...${RESET$4}`);
|
|
7481
|
-
const res = await api.publishSkill(namespace, zipBuffer, visibility);
|
|
6415
|
+
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
7482
6416
|
console.log(` ${GREEN$3}发布成功!${RESET$4}`);
|
|
7483
|
-
if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$
|
|
7484
|
-
if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$
|
|
6417
|
+
if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
|
|
6418
|
+
if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
|
|
7485
6419
|
} catch (error) {
|
|
7486
|
-
if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$
|
|
6420
|
+
if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$2}错误:${error.message}${RESET$4}`);
|
|
7487
6421
|
process.exit(1);
|
|
7488
6422
|
} finally {
|
|
7489
6423
|
try {
|
|
@@ -7498,121 +6432,109 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
|
|
|
7498
6432
|
async function publishFromGit(positionalArgs, api, namespace, visibility, yes, branch, directory, skillFilter) {
|
|
7499
6433
|
const repoArg = positionalArgs[0];
|
|
7500
6434
|
if (!repoArg) {
|
|
7501
|
-
console.error("用法:bfi publish <repo-url> --from git [options]");
|
|
7502
|
-
console.log("使用服务端发现从 Git 仓库发布技能。");
|
|
7503
|
-
console.log();
|
|
7504
|
-
console.log("示例:");
|
|
7505
|
-
console.log(` ${DIM$4}$ ${RESET$4}bfi publish https://github.com/user/repo --from git`);
|
|
7506
|
-
console.log(` ${DIM$4}$ ${RESET$4}bfi publish user/repo --from git --branch main`);
|
|
6435
|
+
console.error("用法:bfi skills publish <repo-url> --from git [options]");
|
|
7507
6436
|
process.exit(1);
|
|
7508
6437
|
}
|
|
7509
6438
|
let repoUrl;
|
|
6439
|
+
let parsedRef;
|
|
7510
6440
|
try {
|
|
7511
|
-
|
|
6441
|
+
const parsed = parseSource(repoArg);
|
|
6442
|
+
repoUrl = parsed.url;
|
|
6443
|
+
parsedRef = parsed.ref;
|
|
7512
6444
|
} catch {
|
|
7513
6445
|
repoUrl = repoArg;
|
|
7514
6446
|
}
|
|
7515
|
-
const effectiveBranch = branch || "main";
|
|
7516
|
-
const
|
|
6447
|
+
const effectiveBranch = branch || parsedRef || "main";
|
|
6448
|
+
const effectiveSubpath = directory || null;
|
|
7517
6449
|
console.log(`${DIM$4}正在从 Git 仓库发现技能...${RESET$4}`);
|
|
7518
|
-
console.log(` ${BOLD$2}仓库:${RESET$4} ${TEXT$
|
|
7519
|
-
console.log(` ${BOLD$2}分支:${RESET$4} ${TEXT$
|
|
7520
|
-
if (effectiveDir) console.log(` ${BOLD$2}目录:${RESET$4} ${TEXT$4}${effectiveDir}${RESET$4}`);
|
|
6450
|
+
console.log(` ${BOLD$2}仓库:${RESET$4} ${TEXT$3}${repoUrl}${RESET$4}`);
|
|
6451
|
+
console.log(` ${BOLD$2}分支:${RESET$4} ${TEXT$3}${effectiveBranch}${RESET$4}`);
|
|
7521
6452
|
console.log();
|
|
7522
|
-
let
|
|
6453
|
+
let tmpDir = null;
|
|
6454
|
+
let skills = [];
|
|
7523
6455
|
try {
|
|
7524
|
-
|
|
6456
|
+
console.log(`${DIM$4}正在克隆仓库...${RESET$4}`);
|
|
6457
|
+
const { cloneRepo } = await Promise.resolve().then(() => git_exports);
|
|
6458
|
+
tmpDir = await cloneRepo(repoUrl, effectiveBranch);
|
|
6459
|
+
console.log(` ${GREEN$3}仓库克隆完成${RESET$4}`);
|
|
6460
|
+
console.log(`${DIM$4}正在发现技能...${RESET$4}`);
|
|
6461
|
+
const { discoverSkills } = await import("./_chunks/skills.mjs").then((n) => n.skills_exports);
|
|
6462
|
+
skills = (await discoverSkills(tmpDir, effectiveSubpath || void 0)).map((s) => ({
|
|
6463
|
+
path: s.path,
|
|
6464
|
+
name: s.name,
|
|
6465
|
+
description: s.description || ""
|
|
6466
|
+
}));
|
|
7525
6467
|
} catch (error) {
|
|
7526
|
-
console.error(` ${RED$
|
|
6468
|
+
console.error(` ${RED$2}克隆或发现失败:${error.message}${RESET$4}`);
|
|
6469
|
+
if (tmpDir) try {
|
|
6470
|
+
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6471
|
+
await cleanupTempDir(tmpDir);
|
|
6472
|
+
} catch {}
|
|
7527
6473
|
process.exit(1);
|
|
7528
6474
|
}
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
console.log(`${BOLD$2}预检结果:${RESET$4}`);
|
|
7537
|
-
const errors = precheck.errors;
|
|
7538
|
-
const warnings = precheck.warnings;
|
|
7539
|
-
if (errors && errors.length > 0) {
|
|
7540
|
-
console.log(` ${RED$1}错误:${RESET$4}`);
|
|
7541
|
-
for (const e of errors) console.log(` ${RED$1}- ${e}${RESET$4}`);
|
|
7542
|
-
}
|
|
7543
|
-
if (warnings && warnings.length > 0) {
|
|
7544
|
-
console.log(` ${YELLOW$1}警告:${RESET$4}`);
|
|
7545
|
-
for (const w of warnings) console.log(` ${YELLOW$1}- ${w}${RESET$4}`);
|
|
7546
|
-
}
|
|
7547
|
-
}
|
|
7548
|
-
process.exit(1);
|
|
6475
|
+
if (skills.length === 0) {
|
|
6476
|
+
console.log(`${YELLOW$2}仓库中未发现技能。${RESET$4}`);
|
|
6477
|
+
if (tmpDir) try {
|
|
6478
|
+
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6479
|
+
await cleanupTempDir(tmpDir);
|
|
6480
|
+
} catch {}
|
|
6481
|
+
return;
|
|
7549
6482
|
}
|
|
7550
|
-
|
|
7551
|
-
console.log();
|
|
7552
|
-
let selectedSkills = discoveredSkills;
|
|
6483
|
+
let selectedSkills = skills;
|
|
7553
6484
|
if (skillFilter) {
|
|
7554
6485
|
const filters = skillFilter.split(",").map((s) => s.trim().toLowerCase());
|
|
7555
|
-
selectedSkills =
|
|
7556
|
-
const slug = String(s.slug || "").toLowerCase();
|
|
7557
|
-
const name = String(s.name || "").toLowerCase();
|
|
7558
|
-
return filters.some((f) => slug.includes(f) || name.includes(f));
|
|
7559
|
-
});
|
|
6486
|
+
selectedSkills = skills.filter((s) => filters.some((f) => s.name.toLowerCase().includes(f)));
|
|
7560
6487
|
if (selectedSkills.length === 0) {
|
|
7561
|
-
console.log(`${YELLOW$
|
|
7562
|
-
console.log("
|
|
7563
|
-
|
|
6488
|
+
console.log(`${YELLOW$2}未找到匹配 "${skillFilter}" 的技能${RESET$4}`);
|
|
6489
|
+
console.log(`可用:${skills.map((s) => s.name).join(", ")}`);
|
|
6490
|
+
try {
|
|
6491
|
+
fs.rmSync(tmpDir, {
|
|
6492
|
+
recursive: true,
|
|
6493
|
+
force: true
|
|
6494
|
+
});
|
|
6495
|
+
} catch {}
|
|
7564
6496
|
process.exit(1);
|
|
7565
6497
|
}
|
|
7566
6498
|
}
|
|
6499
|
+
console.log(`\n${BOLD$2}发现 ${skills.length} 个技能,将发布 ${selectedSkills.length} 个:${RESET$4}\n`);
|
|
7567
6500
|
for (const skill of selectedSkills) {
|
|
7568
|
-
const
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
const pathParts = skillPath ? skillPath.replace(/\\/g, "/").split("/") : [];
|
|
7573
|
-
const indent = " ";
|
|
7574
|
-
if (pathParts.length > 1) {
|
|
7575
|
-
const dirPath = pathParts.slice(0, -1).join(" ▸ ");
|
|
7576
|
-
console.log(` ${DIM$4}📁 ${dirPath}/${RESET$4}`);
|
|
7577
|
-
}
|
|
7578
|
-
console.log(` ${BOLD$2}${skillName}${RESET$4} ${DIM$4}(${slug})${RESET$4}`);
|
|
7579
|
-
if (description) console.log(` ${indent}${DIM$4}${truncate(String(description), 80)}${RESET$4}`);
|
|
7580
|
-
const skillPrecheck = skill.precheck;
|
|
7581
|
-
if (skillPrecheck) {
|
|
7582
|
-
const errors = skillPrecheck.errors;
|
|
7583
|
-
const warnings = skillPrecheck.warnings;
|
|
7584
|
-
if (errors && errors.length > 0) for (const e of errors) console.log(` ${RED$1}错误:${e}${RESET$4}`);
|
|
7585
|
-
if (warnings && warnings.length > 0) for (const w of warnings) console.log(` ${YELLOW$1}警告:${w}${RESET$4}`);
|
|
7586
|
-
}
|
|
6501
|
+
const relPath = path$1.relative(tmpDir, skill.path) || ".";
|
|
6502
|
+
console.log(` ${BOLD$2}${skill.name}${RESET$4}`);
|
|
6503
|
+
console.log(` ${DIM$4}📁 ${relPath.split("/").join(" ▸ ")}${RESET$4}`);
|
|
6504
|
+
if (skill.description) console.log(` ${DIM$4}${truncate(skill.description, 80)}${RESET$4}`);
|
|
7587
6505
|
console.log();
|
|
7588
6506
|
}
|
|
7589
|
-
let confirmWarnings = false;
|
|
7590
|
-
if (precheck) {
|
|
7591
|
-
const errors = precheck.errors;
|
|
7592
|
-
const warnings = precheck.warnings;
|
|
7593
|
-
if (errors && errors.length > 0 || warnings && warnings.length > 0) confirmWarnings = true;
|
|
7594
|
-
}
|
|
7595
|
-
const selectedSlugs = skillFilter ? selectedSkills.map((s) => String(s.slug)) : null;
|
|
7596
6507
|
if (!yes) {
|
|
7597
|
-
const
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
6508
|
+
const { confirm } = await import("./_chunks/libs/@clack/prompts.mjs").then((n) => n.dist_exports);
|
|
6509
|
+
if (!await confirm({ message: `确认发布 ${selectedSkills.length} 个技能到 @${namespace}?` })) {
|
|
6510
|
+
console.log("已取消。");
|
|
6511
|
+
try {
|
|
6512
|
+
fs.rmSync(tmpDir, {
|
|
6513
|
+
recursive: true,
|
|
6514
|
+
force: true
|
|
6515
|
+
});
|
|
6516
|
+
} catch {}
|
|
6517
|
+
return;
|
|
6518
|
+
}
|
|
7601
6519
|
}
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
console.log(
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
const
|
|
7609
|
-
console.log(` ${GREEN$3}✓
|
|
6520
|
+
const { createZipFromDir } = await Promise.resolve().then(() => zip_exports);
|
|
6521
|
+
let successCount = 0;
|
|
6522
|
+
for (const skill of selectedSkills) {
|
|
6523
|
+
console.log(`${DIM$4}正在发布 ${skill.name}...${RESET$4}`);
|
|
6524
|
+
try {
|
|
6525
|
+
const zipBuffer = createZipFromDir(skill.path);
|
|
6526
|
+
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6527
|
+
console.log(` ${GREEN$3}✓ 已发布:${namespace}--${res.slug || skill.name}${RESET$4}`);
|
|
6528
|
+
successCount++;
|
|
6529
|
+
} catch (error) {
|
|
6530
|
+
console.error(` ${RED$2}✗ ${skill.name}:${error.message}${RESET$4}`);
|
|
7610
6531
|
}
|
|
7611
|
-
} catch (error) {
|
|
7612
|
-
console.error(` ${RED$1}发布失败:${error.message}${RESET$4}`);
|
|
7613
|
-
process.exit(1);
|
|
7614
6532
|
}
|
|
7615
|
-
console.log();
|
|
6533
|
+
console.log(`\n${GREEN$3}发布完成:${successCount}/${selectedSkills.length} 个技能${RESET$4}`);
|
|
6534
|
+
if (tmpDir) try {
|
|
6535
|
+
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6536
|
+
await cleanupTempDir(tmpDir);
|
|
6537
|
+
} catch {}
|
|
7616
6538
|
}
|
|
7617
6539
|
function getOptionValue$1(args, flag) {
|
|
7618
6540
|
const idx = args.indexOf(flag);
|
|
@@ -7657,150 +6579,281 @@ function truncate(text, maxLen) {
|
|
|
7657
6579
|
if (text.length <= maxLen) return text;
|
|
7658
6580
|
return text.slice(0, maxLen) + "...";
|
|
7659
6581
|
}
|
|
7660
|
-
async function confirmPrompt(message) {
|
|
7661
|
-
const rl = (await import("node:readline")).createInterface({
|
|
7662
|
-
input: process.stdin,
|
|
7663
|
-
output: process.stdout
|
|
7664
|
-
});
|
|
7665
|
-
return new Promise((resolve) => {
|
|
7666
|
-
rl.question(`${message} [Y/n] `, (answer) => {
|
|
7667
|
-
rl.close();
|
|
7668
|
-
const normalized = answer.trim().toLowerCase();
|
|
7669
|
-
if (normalized === "n" || normalized === "no") {
|
|
7670
|
-
console.log("Publish cancelled.");
|
|
7671
|
-
process.exit(0);
|
|
7672
|
-
}
|
|
7673
|
-
resolve();
|
|
7674
|
-
});
|
|
7675
|
-
});
|
|
7676
|
-
}
|
|
7677
6582
|
const RESET$3 = "\x1B[0m";
|
|
7678
6583
|
const BOLD$1 = "\x1B[1m";
|
|
7679
6584
|
const DIM$3 = "\x1B[38;5;102m";
|
|
7680
|
-
const TEXT$3 = "\x1B[38;5;145m";
|
|
7681
6585
|
const GREEN$2 = "\x1B[32m";
|
|
6586
|
+
const RED$1 = "\x1B[31m";
|
|
6587
|
+
const YELLOW$1 = "\x1B[33m";
|
|
7682
6588
|
async function runPackage(args, globalOpts) {
|
|
7683
6589
|
const subcommand = args[0];
|
|
7684
6590
|
const { registry, token } = resolveConfig(globalOpts);
|
|
7685
6591
|
const api = new SkillHubAPI(registry, token);
|
|
7686
6592
|
switch (subcommand) {
|
|
7687
6593
|
case "list":
|
|
7688
|
-
|
|
6594
|
+
case "ls":
|
|
6595
|
+
await handleLocalList(args.slice(1), globalOpts);
|
|
7689
6596
|
break;
|
|
7690
|
-
case "
|
|
7691
|
-
|
|
6597
|
+
case "install":
|
|
6598
|
+
case "i":
|
|
6599
|
+
await handleInstall(args.slice(1), api, globalOpts);
|
|
6600
|
+
break;
|
|
6601
|
+
case "uninstall":
|
|
6602
|
+
case "rm":
|
|
6603
|
+
await handleUninstall(args.slice(1), globalOpts);
|
|
6604
|
+
break;
|
|
6605
|
+
case "update":
|
|
6606
|
+
case "up":
|
|
6607
|
+
await handleUpdate(args.slice(1), api, globalOpts);
|
|
6608
|
+
break;
|
|
6609
|
+
case "search":
|
|
6610
|
+
await handleSearch(args.slice(1), api);
|
|
7692
6611
|
break;
|
|
7693
6612
|
case "create":
|
|
7694
6613
|
await handleCreate(args.slice(1), api, globalOpts);
|
|
7695
6614
|
break;
|
|
7696
|
-
case "add":
|
|
7697
|
-
await
|
|
6615
|
+
case "add-skills":
|
|
6616
|
+
await handleAddSkills(args.slice(1), api, globalOpts);
|
|
7698
6617
|
break;
|
|
7699
|
-
case "remove":
|
|
7700
|
-
|
|
7701
|
-
await handleRemove(args.slice(1), api, globalOpts);
|
|
6618
|
+
case "remove-skills":
|
|
6619
|
+
await handleRemoveSkills(args.slice(1), api, globalOpts);
|
|
7702
6620
|
break;
|
|
7703
6621
|
default: printPackageHelp();
|
|
7704
6622
|
}
|
|
7705
6623
|
}
|
|
6624
|
+
function getWorkdir(globalOpts, args) {
|
|
6625
|
+
if (args.includes("-g") || args.includes("--global")) return os$1.homedir();
|
|
6626
|
+
return globalOpts.workdir || process.cwd();
|
|
6627
|
+
}
|
|
7706
6628
|
function printPackageHelp() {
|
|
7707
|
-
console.log();
|
|
7708
6629
|
console.log(`${BOLD$1}包命令:${RESET$3}`);
|
|
7709
|
-
console.log(
|
|
7710
|
-
console.log(
|
|
7711
|
-
console.log(` ${DIM$3}bfi package
|
|
7712
|
-
console.log(` ${DIM$3}bfi package
|
|
7713
|
-
console.log(` ${DIM$3}bfi package
|
|
6630
|
+
console.log();
|
|
6631
|
+
console.log(`${BOLD$1}本地操作:${RESET$3}`);
|
|
6632
|
+
console.log(` ${DIM$3}bfi skills package list${RESET$3} 列出已安装的包`);
|
|
6633
|
+
console.log(` ${DIM$3}bfi skills package install <ref>${RESET$3} 安装包(含全部技能)`);
|
|
6634
|
+
console.log(` ${DIM$3}bfi skills package uninstall <ref>${RESET$3} 卸载包(含全部技能)`);
|
|
6635
|
+
console.log(` ${DIM$3}bfi skills package update [ref]${RESET$3} 更新包中技能`);
|
|
6636
|
+
console.log(` ${DIM$3}bfi skills package search <query>${RESET$3} 搜索包`);
|
|
6637
|
+
console.log();
|
|
6638
|
+
console.log(`${BOLD$1}服务端管理:${RESET$3}`);
|
|
6639
|
+
console.log(` ${DIM$3}bfi skills package create <slug>${RESET$3} 创建包`);
|
|
6640
|
+
console.log(` ${DIM$3}bfi skills package add-skills <ref> [skills...]${RESET$3} 添加技能到包`);
|
|
6641
|
+
console.log(` ${DIM$3}bfi skills package remove-skills <ref> [skills...]${RESET$3} 从包移除技能`);
|
|
7714
6642
|
console.log();
|
|
7715
6643
|
console.log(`${BOLD$1}选项:${RESET$3}`);
|
|
7716
|
-
console.log(` --namespace <ns>
|
|
7717
|
-
console.log(` --name <name>
|
|
7718
|
-
console.log(` --description <
|
|
7719
|
-
console.log(` --
|
|
7720
|
-
|
|
6644
|
+
console.log(` --namespace <ns> 目标命名空间`);
|
|
6645
|
+
console.log(` --name <name> 显示名称(create 时)`);
|
|
6646
|
+
console.log(` --description <t> 描述(create 时)`);
|
|
6647
|
+
console.log(` --json JSON 输出`);
|
|
6648
|
+
}
|
|
6649
|
+
async function handleLocalList(args, globalOpts) {
|
|
6650
|
+
const lock = loadLockfile(getWorkdir(globalOpts, args));
|
|
6651
|
+
args.includes("-g") || args.includes("--global");
|
|
6652
|
+
const pkgs = lock.packages || {};
|
|
6653
|
+
const entries = Object.entries(pkgs);
|
|
6654
|
+
if (args.includes("--json")) {
|
|
6655
|
+
console.log(JSON.stringify(Object.values(pkgs), null, 2));
|
|
6656
|
+
return;
|
|
6657
|
+
}
|
|
6658
|
+
if (entries.length === 0) {
|
|
6659
|
+
console.log("没有已安装的包。");
|
|
6660
|
+
console.log(`使用 ${DIM$3}bfi skills package install <ref>${RESET$3} 安装包。`);
|
|
6661
|
+
return;
|
|
6662
|
+
}
|
|
7721
6663
|
console.log();
|
|
7722
|
-
console.log(`${BOLD$1}
|
|
7723
|
-
console.log(` ${DIM$3}$ ${RESET$3}bfi package list --namespace myteam`);
|
|
7724
|
-
console.log(` ${DIM$3}$ ${RESET$3}bfi package info @myteam/my-package`);
|
|
7725
|
-
console.log(` ${DIM$3}$ ${RESET$3}bfi package create my-utils --namespace myteam`);
|
|
7726
|
-
console.log(` ${DIM$3}$ ${RESET$3}bfi package add @myteam/my-package @myteam/skill-a @myteam/skill-b`);
|
|
7727
|
-
console.log(` ${DIM$3}$ ${RESET$3}bfi package remove @myteam/my-package @myteam/skill-a`);
|
|
6664
|
+
console.log(`${BOLD$1}已安装的包(${entries.length} 个):${RESET$3}`);
|
|
7728
6665
|
console.log();
|
|
6666
|
+
for (const [ref, info] of entries) {
|
|
6667
|
+
const pkg = info;
|
|
6668
|
+
const skills = lock.skills || {};
|
|
6669
|
+
const pkgSkills = Object.entries(skills).filter(([, s]) => s.package?.slug === pkg.slug);
|
|
6670
|
+
console.log(` ${BOLD$1}${pkg.name || pkg.slug}${RESET$3}`);
|
|
6671
|
+
console.log(` ${DIM$3}命名空间:@${pkg.namespace} | 版本:${pkg.version || "latest"} | 技能数:${pkgSkills.length}${RESET$3}`);
|
|
6672
|
+
console.log();
|
|
6673
|
+
}
|
|
7729
6674
|
}
|
|
7730
|
-
async function
|
|
7731
|
-
const
|
|
7732
|
-
|
|
6675
|
+
async function handleInstall(args, api, globalOpts) {
|
|
6676
|
+
const pkgRef = args[0];
|
|
6677
|
+
if (!pkgRef) {
|
|
6678
|
+
console.error("用法:bfi skills package install <package-ref>");
|
|
6679
|
+
console.error(" 示例:bfi skills package install @global/my-package");
|
|
6680
|
+
process.exit(1);
|
|
6681
|
+
}
|
|
6682
|
+
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
6683
|
+
console.log(`${DIM$3}正在获取包信息...${RESET$3}`);
|
|
6684
|
+
let pkgInfo;
|
|
7733
6685
|
try {
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
if (json) {
|
|
7737
|
-
console.log(JSON.stringify({ packages }, null, 2));
|
|
7738
|
-
return;
|
|
7739
|
-
}
|
|
7740
|
-
if (packages.length === 0) {
|
|
7741
|
-
console.log(`No packages found in namespace "${namespace}".`);
|
|
7742
|
-
return;
|
|
7743
|
-
}
|
|
7744
|
-
console.log();
|
|
7745
|
-
console.log(`${BOLD$1}@${namespace} 中的包${RESET$3}(${packages.length} 个):`);
|
|
7746
|
-
console.log();
|
|
7747
|
-
packages.forEach((pkg, index) => {
|
|
7748
|
-
const visibility = pkg.visibility || "private";
|
|
7749
|
-
const skillCount = pkg.skillCount || 0;
|
|
7750
|
-
console.log(` ${BOLD$1}${index + 1}. ${pkg.name || pkg.slug}${RESET$3}`);
|
|
7751
|
-
console.log(` ${DIM$3}Slug: ${pkg.slug}${RESET$3}`);
|
|
7752
|
-
console.log(` ${DIM$3}Visibility: ${visibility}${RESET$3}`);
|
|
7753
|
-
console.log(` ${DIM$3}Skills: ${skillCount}${RESET$3}`);
|
|
7754
|
-
if (pkg.description) console.log(` ${DIM$3}${pkg.description}${RESET$3}`);
|
|
7755
|
-
console.log();
|
|
7756
|
-
});
|
|
6686
|
+
pkgInfo = await api.getPackage(namespace, slug);
|
|
6687
|
+
pkgInfo = pkgInfo.data || pkgInfo;
|
|
7757
6688
|
} catch (error) {
|
|
7758
|
-
console.error(
|
|
6689
|
+
console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
|
|
7759
6690
|
process.exit(1);
|
|
7760
6691
|
}
|
|
6692
|
+
const skills = pkgInfo.skills || [];
|
|
6693
|
+
if (skills.length === 0) {
|
|
6694
|
+
console.log(`${YELLOW$1}包中没有技能。${RESET$3}`);
|
|
6695
|
+
return;
|
|
6696
|
+
}
|
|
6697
|
+
console.log(`${BOLD$1}${pkgInfo.name || slug}${RESET$3}(${skills.length} 个技能):`);
|
|
6698
|
+
console.log();
|
|
6699
|
+
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
6700
|
+
const installedSkills = [];
|
|
6701
|
+
for (const skill of skills) {
|
|
6702
|
+
const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
|
|
6703
|
+
console.log(` ${DIM$3}正在安装 ${skillRef}...${RESET$3}`);
|
|
6704
|
+
try {
|
|
6705
|
+
await runAdd([skillRef], {
|
|
6706
|
+
...globalOpts,
|
|
6707
|
+
yes: true
|
|
6708
|
+
});
|
|
6709
|
+
console.log(` ${GREEN$2}✓ ${skill.slug}${RESET$3}`);
|
|
6710
|
+
installedSkills.push(skill.slug);
|
|
6711
|
+
} catch (error) {
|
|
6712
|
+
console.log(` ${RED$1}✗ ${skill.slug}:${error.message}${RESET$3}`);
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
const cwd = getWorkdir(globalOpts, args);
|
|
6716
|
+
const lock = loadLockfile(cwd);
|
|
6717
|
+
const pkgKey = `@${namespace}/${slug}`;
|
|
6718
|
+
if (!lock.packages) lock.packages = {};
|
|
6719
|
+
lock.packages[pkgKey] = {
|
|
6720
|
+
namespace,
|
|
6721
|
+
slug,
|
|
6722
|
+
name: pkgInfo.displayName || pkgInfo.name || slug,
|
|
6723
|
+
version: pkgInfo.version || "latest",
|
|
6724
|
+
skillCount: installedSkills.length
|
|
6725
|
+
};
|
|
6726
|
+
for (const skillSlug of installedSkills) {
|
|
6727
|
+
if (!lock.skills[skillSlug]) lock.skills[skillSlug] = {
|
|
6728
|
+
namespace,
|
|
6729
|
+
slug,
|
|
6730
|
+
version: "latest"
|
|
6731
|
+
};
|
|
6732
|
+
lock.skills[skillSlug].package = {
|
|
6733
|
+
namespace,
|
|
6734
|
+
slug
|
|
6735
|
+
};
|
|
6736
|
+
}
|
|
6737
|
+
saveLockfile(cwd, lock);
|
|
6738
|
+
console.log();
|
|
6739
|
+
console.log(`${GREEN$2}✓ 包安装完成${RESET$3}`);
|
|
7761
6740
|
}
|
|
7762
|
-
async function
|
|
6741
|
+
async function handleUninstall(args, globalOpts) {
|
|
7763
6742
|
const pkgRef = args[0];
|
|
7764
6743
|
if (!pkgRef) {
|
|
7765
|
-
console.error("
|
|
7766
|
-
console.log();
|
|
7767
|
-
console.log("Examples:");
|
|
7768
|
-
console.log(" bfi package info @myteam/my-package");
|
|
7769
|
-
console.log(" bfi package info myteam--my-package");
|
|
6744
|
+
console.error("用法:bfi skills package uninstall <package-ref>");
|
|
7770
6745
|
process.exit(1);
|
|
7771
6746
|
}
|
|
7772
|
-
const json = args.includes("--json");
|
|
7773
6747
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
6748
|
+
const pkgKey = `@${namespace}/${slug}`;
|
|
6749
|
+
const cwd = getWorkdir(globalOpts, args);
|
|
6750
|
+
const lock = loadLockfile(cwd);
|
|
6751
|
+
if (!lock.packages || !lock.packages[pkgKey]) {
|
|
6752
|
+
console.log(`${YELLOW$1}包未安装:${pkgKey}${RESET$3}`);
|
|
6753
|
+
return;
|
|
6754
|
+
}
|
|
6755
|
+
const skills = lock.skills || {};
|
|
6756
|
+
let removed = 0;
|
|
6757
|
+
for (const [skillName, info] of Object.entries(skills)) {
|
|
6758
|
+
const skillInfo = info;
|
|
6759
|
+
if (skillInfo.package?.slug === slug && skillInfo.package?.namespace === namespace) {
|
|
6760
|
+
const { removeCommand } = await Promise.resolve().then(() => remove_exports);
|
|
6761
|
+
try {
|
|
6762
|
+
await removeCommand([skillName], {
|
|
6763
|
+
global: false,
|
|
6764
|
+
yes: true
|
|
6765
|
+
});
|
|
6766
|
+
removed++;
|
|
6767
|
+
} catch {}
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
delete lock.packages[pkgKey];
|
|
6771
|
+
const { saveLockfile } = await Promise.resolve().then(() => config_exports);
|
|
6772
|
+
saveLockfile(cwd, lock);
|
|
6773
|
+
console.log(`${GREEN$2}✓ 已卸载包,移除 ${removed} 个技能${RESET$3}`);
|
|
6774
|
+
}
|
|
6775
|
+
async function handleUpdate(args, api, globalOpts) {
|
|
6776
|
+
const pkgRef = args[0];
|
|
6777
|
+
const lock = loadLockfile(getWorkdir(globalOpts, args));
|
|
6778
|
+
let packagesToUpdate = [];
|
|
6779
|
+
const pkgs = lock.packages || {};
|
|
6780
|
+
if (pkgRef) {
|
|
6781
|
+
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
6782
|
+
if (!pkgs[`@${namespace}/${slug}`]) {
|
|
6783
|
+
console.log(`${YELLOW$1}包未安装:@${namespace}/${slug}${RESET$3}`);
|
|
7779
6784
|
return;
|
|
7780
6785
|
}
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
6786
|
+
packagesToUpdate.push({
|
|
6787
|
+
namespace,
|
|
6788
|
+
slug
|
|
6789
|
+
});
|
|
6790
|
+
} else packagesToUpdate = Object.values(pkgs).map((p) => ({
|
|
6791
|
+
namespace: p.namespace,
|
|
6792
|
+
slug: p.slug
|
|
6793
|
+
}));
|
|
6794
|
+
if (packagesToUpdate.length === 0) {
|
|
6795
|
+
console.log("没有已安装的包需要更新。");
|
|
6796
|
+
return;
|
|
6797
|
+
}
|
|
6798
|
+
console.log(`${DIM$3}正在更新 ${packagesToUpdate.length} 个包...${RESET$3}`);
|
|
6799
|
+
for (const pkg of packagesToUpdate) try {
|
|
6800
|
+
const pkgInfo = await api.getPackage(pkg.namespace, pkg.slug);
|
|
6801
|
+
const skills = (pkgInfo.data || pkgInfo).skills || [];
|
|
6802
|
+
console.log(` ${DIM$3}更新 @${pkg.namespace}/${pkg.slug}(${skills.length} 个技能)...${RESET$3}`);
|
|
6803
|
+
for (const skill of skills) {
|
|
6804
|
+
const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
|
|
6805
|
+
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
6806
|
+
try {
|
|
6807
|
+
await runAdd([skillRef], {
|
|
6808
|
+
...globalOpts,
|
|
6809
|
+
yes: true
|
|
6810
|
+
});
|
|
6811
|
+
} catch {}
|
|
7793
6812
|
}
|
|
7794
|
-
console.log();
|
|
7795
6813
|
} catch (error) {
|
|
7796
|
-
console.
|
|
6814
|
+
console.log(` ${RED$1}✗ @${pkg.namespace}/${pkg.slug}:${error.message}${RESET$3}`);
|
|
6815
|
+
}
|
|
6816
|
+
console.log(`${GREEN$2}✓ 包更新完成${RESET$3}`);
|
|
6817
|
+
}
|
|
6818
|
+
async function handleSearch(args, api) {
|
|
6819
|
+
const query = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
6820
|
+
const json = args.includes("--json");
|
|
6821
|
+
if (!query) {
|
|
6822
|
+
console.error("用法:bfi skills package search <query>");
|
|
7797
6823
|
process.exit(1);
|
|
7798
6824
|
}
|
|
6825
|
+
const skills = (await api.searchSkills(query, { limit: 20 })).results || [];
|
|
6826
|
+
const namespaces = /* @__PURE__ */ new Map();
|
|
6827
|
+
for (const s of skills) {
|
|
6828
|
+
const { namespace } = parseSkillRef(s.slug, "global");
|
|
6829
|
+
if (!namespaces.has(namespace)) namespaces.set(namespace, []);
|
|
6830
|
+
namespaces.get(namespace).push(s);
|
|
6831
|
+
}
|
|
6832
|
+
if (json) {
|
|
6833
|
+
const groups = Array.from(namespaces.entries()).map(([ns, sk]) => ({
|
|
6834
|
+
namespace: ns,
|
|
6835
|
+
skillCount: sk.length,
|
|
6836
|
+
skills: sk.map((s) => ({
|
|
6837
|
+
name: s.displayName || s.name,
|
|
6838
|
+
slug: s.slug
|
|
6839
|
+
}))
|
|
6840
|
+
}));
|
|
6841
|
+
console.log(JSON.stringify(groups, null, 2));
|
|
6842
|
+
return;
|
|
6843
|
+
}
|
|
6844
|
+
console.log(`\n${BOLD$1}找到 ${skills.length} 个相关技能(按命名空间分组):${RESET$3}\n`);
|
|
6845
|
+
for (const [ns, sk] of namespaces) {
|
|
6846
|
+
console.log(` ${BOLD$1}@${ns}${RESET$3}(${sk.length} 个技能)`);
|
|
6847
|
+
for (const s of sk.slice(0, 5)) console.log(` ${DIM$3}- ${s.displayName || s.name} (${s.slug})${RESET$3}`);
|
|
6848
|
+
if (sk.length > 5) console.log(` ${DIM$3}... 还有 ${sk.length - 5} 个${RESET$3}`);
|
|
6849
|
+
console.log();
|
|
6850
|
+
}
|
|
6851
|
+
console.log(`${DIM$3}使用 bfi skills package create <slug> --namespace <ns> 创建包,然后 add-skills 添加这些技能。${RESET$3}`);
|
|
7799
6852
|
}
|
|
7800
6853
|
async function handleCreate(args, api, globalOpts) {
|
|
7801
6854
|
const slug = args[0];
|
|
7802
6855
|
if (!slug) {
|
|
7803
|
-
console.error("用法:bfi package create <slug> [options]");
|
|
6856
|
+
console.error("用法:bfi skills package create <slug> [options]");
|
|
7804
6857
|
process.exit(1);
|
|
7805
6858
|
}
|
|
7806
6859
|
const namespace = getOptionValue(args, "--namespace") || globalOpts.namespace || "global";
|
|
@@ -7810,72 +6863,104 @@ async function handleCreate(args, api, globalOpts) {
|
|
|
7810
6863
|
try {
|
|
7811
6864
|
const result = await api.createPackage(namespace, {
|
|
7812
6865
|
slug,
|
|
7813
|
-
|
|
6866
|
+
displayName: name || slug,
|
|
7814
6867
|
description: description || "",
|
|
7815
|
-
visibility
|
|
6868
|
+
visibility: visibility.toUpperCase()
|
|
7816
6869
|
});
|
|
7817
6870
|
console.log(`${GREEN$2}✓ 已创建包 ${toSkillRefString(namespace, slug)}${RESET$3}`);
|
|
7818
|
-
const
|
|
7819
|
-
if (
|
|
7820
|
-
console.log(` ${BOLD$1}名称:${RESET$3} ${TEXT$3}${createdPkg.name || slug}${RESET$3}`);
|
|
7821
|
-
console.log(` ${BOLD$1}Visibility:${RESET$3} ${TEXT$3}${createdPkg.visibility || visibility}${RESET$3}`);
|
|
7822
|
-
}
|
|
6871
|
+
const pkg = result.package || result.data || result;
|
|
6872
|
+
if (pkg) console.log(` ${DIM$3}名称:${pkg.name || slug} | 可见性:${pkg.visibility || visibility}${RESET$3}`);
|
|
7823
6873
|
} catch (error) {
|
|
7824
|
-
console.error(
|
|
6874
|
+
console.error(`${RED$1}创建包失败:${error.message}${RESET$3}`);
|
|
7825
6875
|
process.exit(1);
|
|
7826
6876
|
}
|
|
7827
6877
|
}
|
|
7828
|
-
async function
|
|
6878
|
+
async function handleAddSkills(args, api, globalOpts) {
|
|
7829
6879
|
const pkgRef = args[0];
|
|
7830
6880
|
const skillRefs = args.slice(1);
|
|
7831
|
-
if (!pkgRef
|
|
7832
|
-
console.error("用法:bfi package add <package-ref>
|
|
6881
|
+
if (!pkgRef) {
|
|
6882
|
+
console.error("用法:bfi skills package add-skills <package-ref> [skill-ref...]");
|
|
6883
|
+
console.error(" 不指定技能时,添加命名空间下全部技能");
|
|
7833
6884
|
process.exit(1);
|
|
7834
6885
|
}
|
|
7835
6886
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7836
|
-
|
|
7837
|
-
|
|
6887
|
+
let skills;
|
|
6888
|
+
if (skillRefs.length === 0) try {
|
|
6889
|
+
skills = ((await api.searchSkills("", {
|
|
6890
|
+
namespace,
|
|
6891
|
+
limit: 1e3
|
|
6892
|
+
})).results || []).map((r) => {
|
|
6893
|
+
const { namespace: ns, slug: s } = parseSkillRef(r.slug, namespace);
|
|
6894
|
+
return {
|
|
6895
|
+
namespace: ns,
|
|
6896
|
+
slug: s
|
|
6897
|
+
};
|
|
6898
|
+
});
|
|
6899
|
+
if (skills.length === 0) {
|
|
6900
|
+
console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$3}`);
|
|
6901
|
+
return;
|
|
6902
|
+
}
|
|
6903
|
+
} catch (error) {
|
|
6904
|
+
console.error(`${RED$1}获取技能列表失败:${error.message}${RESET$3}`);
|
|
6905
|
+
process.exit(1);
|
|
6906
|
+
}
|
|
6907
|
+
else skills = skillRefs.map((ref) => {
|
|
6908
|
+
const { namespace: ns, slug: s } = parseSkillRef(ref, namespace);
|
|
7838
6909
|
return {
|
|
7839
|
-
namespace:
|
|
7840
|
-
slug:
|
|
6910
|
+
namespace: ns,
|
|
6911
|
+
slug: s
|
|
7841
6912
|
};
|
|
7842
6913
|
});
|
|
7843
6914
|
try {
|
|
7844
6915
|
await api.addSkillsToPackage(namespace, slug, skills);
|
|
7845
6916
|
console.log(`${GREEN$2}✓ 已添加 ${skills.length} 个技能到 ${toSkillRefString(namespace, slug)}${RESET$3}`);
|
|
7846
|
-
for (const s of skills) console.log(` ${DIM$3}- ${toSkillRefString(s.namespace, s.slug)}${RESET$3}`);
|
|
7847
6917
|
} catch (error) {
|
|
7848
|
-
console.error(
|
|
6918
|
+
console.error(`${RED$1}添加失败:${error.message}${RESET$3}`);
|
|
7849
6919
|
process.exit(1);
|
|
7850
6920
|
}
|
|
7851
6921
|
}
|
|
7852
|
-
async function
|
|
6922
|
+
async function handleRemoveSkills(args, api, globalOpts) {
|
|
7853
6923
|
const pkgRef = args[0];
|
|
7854
6924
|
const skillRefs = args.slice(1);
|
|
7855
|
-
if (!pkgRef
|
|
7856
|
-
console.error("用法:bfi package remove <package-ref>
|
|
6925
|
+
if (!pkgRef) {
|
|
6926
|
+
console.error("用法:bfi skills package remove-skills <package-ref> [skill-ref...]");
|
|
6927
|
+
console.error(" 不指定技能时,移除包中全部技能");
|
|
7857
6928
|
process.exit(1);
|
|
7858
6929
|
}
|
|
7859
6930
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7860
|
-
|
|
7861
|
-
|
|
6931
|
+
let skills;
|
|
6932
|
+
if (skillRefs.length === 0) try {
|
|
6933
|
+
const pkgInfo = await api.getPackage(namespace, slug);
|
|
6934
|
+
skills = ((pkgInfo.data || pkgInfo).skills || []).map((s) => ({
|
|
6935
|
+
namespace: s.namespace || namespace,
|
|
6936
|
+
slug: s.slug
|
|
6937
|
+
}));
|
|
6938
|
+
if (skills.length === 0) {
|
|
6939
|
+
console.log("包中没有技能。");
|
|
6940
|
+
return;
|
|
6941
|
+
}
|
|
6942
|
+
} catch (error) {
|
|
6943
|
+
console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
|
|
6944
|
+
process.exit(1);
|
|
6945
|
+
}
|
|
6946
|
+
else skills = skillRefs.map((ref) => {
|
|
6947
|
+
const { namespace: ns, slug: s } = parseSkillRef(ref, namespace);
|
|
7862
6948
|
return {
|
|
7863
|
-
namespace:
|
|
7864
|
-
slug:
|
|
6949
|
+
namespace: ns,
|
|
6950
|
+
slug: s
|
|
7865
6951
|
};
|
|
7866
6952
|
});
|
|
7867
6953
|
try {
|
|
7868
6954
|
await api.removeSkillsFromPackage(namespace, slug, skills);
|
|
7869
|
-
console.log(`${GREEN$2}✓
|
|
7870
|
-
for (const s of skills) console.log(` ${DIM$3}- ${toSkillRefString(s.namespace, s.slug)}${RESET$3}`);
|
|
6955
|
+
console.log(`${GREEN$2}✓ 已从包移除 ${skills.length} 个技能${RESET$3}`);
|
|
7871
6956
|
} catch (error) {
|
|
7872
|
-
console.error(
|
|
6957
|
+
console.error(`${RED$1}移除失败:${error.message}${RESET$3}`);
|
|
7873
6958
|
process.exit(1);
|
|
7874
6959
|
}
|
|
7875
6960
|
}
|
|
7876
6961
|
function getOptionValue(args, flag) {
|
|
7877
6962
|
const idx = args.indexOf(flag);
|
|
7878
|
-
return idx !== -1 && idx
|
|
6963
|
+
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
7879
6964
|
}
|
|
7880
6965
|
const RESET$2 = "\x1B[0m";
|
|
7881
6966
|
const DIM$2 = "\x1B[38;5;102m";
|
|
@@ -8324,9 +7409,9 @@ async function main() {
|
|
|
8324
7409
|
globalOpts.workdir = process.cwd();
|
|
8325
7410
|
try {
|
|
8326
7411
|
const { resolveConfig } = await Promise.resolve().then(() => config_exports);
|
|
8327
|
-
const { resolveAgents } = await import("./_chunks/
|
|
7412
|
+
const { resolveAgents } = await import("./_chunks/agents2.mjs");
|
|
8328
7413
|
const { SkillHubAPI } = await Promise.resolve().then(() => api_exports);
|
|
8329
|
-
const { setAgents } = await
|
|
7414
|
+
const { setAgents } = await import("./_chunks/agents.mjs").then((n) => n.agents_exports);
|
|
8330
7415
|
const cfg = resolveConfig(globalOpts);
|
|
8331
7416
|
setAgents(await resolveAgents(cfg.token ? new SkillHubAPI(cfg.registry, cfg.token) : void 0));
|
|
8332
7417
|
} catch {}
|
|
@@ -8442,4 +7527,4 @@ async function main() {
|
|
|
8442
7527
|
}
|
|
8443
7528
|
}
|
|
8444
7529
|
main().finally(() => process.exit(process.exitCode ?? 0));
|
|
8445
|
-
export {
|
|
7530
|
+
export { loadGlobalConfig };
|