bfi-cli 0.1.29 → 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/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 { xdgConfig } from "./_chunks/libs/xdg-basedir.mjs";
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, execSync, spawn, spawnSync } from "child_process";
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
- if (options.body !== void 0) if (options.body instanceof FormData || typeof options.body === "string") bodyStr = options.body;
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 (bodyStr) req.write(bodyStr);
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
- return (await this.request("POST", "/skills/git/parse", { body: {
650
+ const body = {
661
651
  repositoryUrl,
662
652
  branch,
663
- directory,
664
653
  token: this.token
665
- } })).body;
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 = "private") {
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 req = client.request(url, requestOptions, (res) => {
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
- const error = new Error(parsed.message || parsed.error || `HTTP ${res.statusCode}`);
781
- error.status = res.statusCode || 500;
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 LOCK_FILE = path$1.join(".bfi", "lock.json");
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$1(dir, baseDir) {
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$1(fullPath, baseDir));
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$1(sourceDir, sourceDir).map((f) => ({
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;
@@ -1204,1240 +1207,90 @@ async function tryGhClone(repo, tempDir, ref) {
1204
1207
  timeout: 5e3,
1205
1208
  env: {
1206
1209
  ...process.env,
1207
- GIT_TERMINAL_PROMPT: "0"
1208
- }
1209
- });
1210
- const statusOutput = `${stdout}${stderr}`;
1211
- if (/Git operations protocol:\s+ssh/i.test(statusOutput)) cloneTarget = repo.sshUrl;
1212
- } catch {
1213
- return false;
1214
- }
1215
- await execFileAsync("gh", [
1216
- "repo",
1217
- "clone",
1218
- cloneTarget,
1219
- tempDir,
1220
- "--",
1221
- ...ref ? [
1222
- "--depth=1",
1223
- "--branch",
1224
- ref
1225
- ] : ["--depth=1"]
1226
- ], {
1227
- timeout: CLONE_TIMEOUT_MS,
1228
- env: {
1229
- ...process.env,
1230
- GIT_TERMINAL_PROMPT: "0"
1231
- }
1232
- });
1233
- return true;
1234
- }
1235
- function buildGitHubAuthError(url, repo, message) {
1236
- 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
- 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
- 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"));
2390
- }
2391
- },
2392
- adal: {
2393
- name: "adal",
2394
- displayName: "AdaL",
2395
- skillsDir: ".adal/skills",
2396
- globalSkillsDir: join(home, ".adal/skills"),
2397
- detectInstalled: async () => {
2398
- return existsSync(join(home, ".adal"));
2399
- }
2400
- },
2401
- universal: {
2402
- name: "universal",
2403
- displayName: "共享目录",
2404
- skillsDir: ".agents/skills",
2405
- globalSkillsDir: join(configHome, "agents/skills"),
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();
1210
+ GIT_TERMINAL_PROMPT: "0"
1211
+ }
1212
+ });
1213
+ const statusOutput = `${stdout}${stderr}`;
1214
+ if (/Git operations protocol:\s+ssh/i.test(statusOutput)) cloneTarget = repo.sshUrl;
2426
1215
  } catch {
2427
- return [];
1216
+ return false;
2428
1217
  }
1218
+ await execFileAsync("gh", [
1219
+ "repo",
1220
+ "clone",
1221
+ cloneTarget,
1222
+ tempDir,
1223
+ "--",
1224
+ ...ref ? [
1225
+ "--depth=1",
1226
+ "--branch",
1227
+ ref
1228
+ ] : ["--depth=1"]
1229
+ ], {
1230
+ timeout: CLONE_TIMEOUT_MS,
1231
+ env: {
1232
+ ...process.env,
1233
+ GIT_TERMINAL_PROMPT: "0"
1234
+ }
1235
+ });
1236
+ return true;
2429
1237
  }
2430
- function getUniversalAgents() {
2431
- return Object.entries(agents).filter(([_, config]) => config.skillsDir === ".agents/skills" && config.showInUniversalList !== false).map(([type]) => type);
2432
- }
2433
- function getVisibleUniversalAgents() {
2434
- return Object.entries(agents).filter(([_, config]) => config.skillsDir === ".agents/skills" && config.showInUniversalList !== false && config.showInUniversalPrompt !== false).map(([type]) => type);
1238
+ function buildGitHubAuthError(url, repo, message) {
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`;
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`;
1241
+ return `身份验证失败:${url}。\n - 私有仓库请确保您有访问权限\n - SSH:使用 'ssh -T git@github.com' 检查您的密钥\n - HTTPS:运行 'gh auth login' 或配置 git 凭据`;
2435
1242
  }
2436
- function getNonUniversalAgents() {
2437
- return Object.entries(agents).filter(([_, config]) => config.skillsDir !== ".agents/skills").map(([type]) => type);
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);
1266
+ }
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 {}
1277
+ }
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);
1284
+ }
2438
1285
  }
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 promptForFindSkills(options, targetAgents);
3320
+ await promptForBfiSkill(options, targetAgents);
4460
3321
  }
4461
3322
  async function cloneWithGitHubFallback(url, ref, spinner) {
4462
3323
  try {
@@ -4546,7 +3407,7 @@ async function installFromSkillHub(parsed, options, spinner) {
4546
3407
  }
4547
3408
  const meta = metadata;
4548
3409
  spinner.stop(`Skill: ${import_picocolors.default.cyan(`@${namespace}/${slug}`)}${meta.description ? ` — ${import_picocolors.default.dim(meta.description)}` : ""}`);
4549
- tempDir = join(tmpdir(), `.bfi-skillhub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
3410
+ tempDir = join(tmpdir(), `.skillhub-skillshub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
4550
3411
  mkdirSync(tempDir, { recursive: true });
4551
3412
  const zipPath = join(tempDir, "skill.zip");
4552
3413
  spinner.start("正在下载技能...");
@@ -4704,7 +3565,7 @@ async function runAdd(args, options = {}) {
4704
3565
  skills = blobResult.skills;
4705
3566
  spinner$2.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
4706
3567
  } else {
4707
- tempDir = await cloneWithGitHubFallback(parsed.url, parsed.ref, spinner$2);
3568
+ tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$2);
4708
3569
  spinner$2.stop("仓库克隆完成");
4709
3570
  spinner$2.start("正在发现技能...");
4710
3571
  skills = await discoverSkills(tempDir, parsed.subpath, {
@@ -4713,7 +3574,7 @@ async function runAdd(args, options = {}) {
4713
3574
  });
4714
3575
  }
4715
3576
  } else {
4716
- tempDir = await cloneWithGitHubFallback(parsed.url, parsed.ref, spinner$2);
3577
+ tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$2);
4717
3578
  spinner$2.stop("仓库克隆完成");
4718
3579
  spinner$2.start("正在发现技能...");
4719
3580
  skills = await discoverSkills(tempDir, parsed.subpath, {
@@ -5213,7 +4074,7 @@ async function runAdd(args, options = {}) {
5213
4074
  }
5214
4075
  console.log();
5215
4076
  outro(import_picocolors.default.green("完成!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
5216
- await promptForFindSkills(options, targetAgents);
4077
+ await promptForBfiSkill(options, targetAgents);
5217
4078
  } catch (error) {
5218
4079
  if (error instanceof GitCloneError) {
5219
4080
  log.error(import_picocolors.default.red("克隆仓库失败"));
@@ -5231,42 +4092,42 @@ async function cleanup(tempDir) {
5231
4092
  await cleanupTempDir(tempDir);
5232
4093
  } catch {}
5233
4094
  }
5234
- async function promptForFindSkills(options, targetAgents) {
4095
+ async function promptForBfiSkill(options, targetAgents) {
5235
4096
  if (!process.stdin.isTTY) return;
5236
4097
  if (options?.yes) return;
5237
4098
  try {
5238
- if (await isPromptDismissed("findSkillsPrompt")) return;
5239
- if (await isSkillInstalled("find-skills", "claude-code", { global: true })) {
5240
- await dismissPrompt("findSkillsPrompt");
4099
+ if (await isPromptDismissed("bfiSkillPrompt")) return;
4100
+ if (await isSkillInstalled("skillhub-skills", "claude-code", { global: true })) {
4101
+ await dismissPrompt("bfiSkillPrompt");
5241
4102
  return;
5242
4103
  }
5243
4104
  console.log();
5244
4105
  log.message(import_picocolors.default.dim("一次性提示 - 关闭后将不再询问。"));
5245
- const install = await confirm({ message: `Install the ${import_picocolors.default.cyan("find-skills")} skill? It helps your agent discover and suggest skills.` });
4106
+ const install = await confirm({ message: `安装 ${import_picocolors.default.cyan("skillhub-skills")} 技能?它能帮助 AI Agent 自动发现和使用 bfi CLI 的全部功能。` });
5246
4107
  if (isCancel(install)) {
5247
- await dismissPrompt("findSkillsPrompt");
4108
+ await dismissPrompt("bfiSkillPrompt");
5248
4109
  return;
5249
4110
  }
5250
4111
  if (install) {
5251
- await dismissPrompt("findSkillsPrompt");
5252
- const findSkillsAgents = targetAgents?.filter((a) => a !== "replit");
5253
- if (!findSkillsAgents || findSkillsAgents.length === 0) return;
4112
+ await dismissPrompt("bfiSkillPrompt");
4113
+ const bfiSkillAgents = targetAgents?.filter((a) => a !== "replit");
4114
+ if (!bfiSkillAgents || bfiSkillAgents.length === 0) return;
5254
4115
  console.log();
5255
- log.step("正在安装 find-skills 技能...");
4116
+ log.step("正在安装 skillhub-skills 技能...");
5256
4117
  try {
5257
- await runAdd(["vercel-labs/skills"], {
5258
- skill: ["find-skills"],
4118
+ await runAdd(["bfi/skillhub-skills"], {
4119
+ skill: ["skillhub-skills"],
5259
4120
  global: true,
5260
4121
  yes: true,
5261
- agent: findSkillsAgents
4122
+ agent: bfiSkillAgents
5262
4123
  });
5263
4124
  } catch {
5264
- log.warn("安装 find-skills 失败。你可以稍后重试:");
5265
- log.message(import_picocolors.default.dim(" npx skills add vercel-labs/skills@find-skills -g -y --all"));
4125
+ log.warn("安装 skillhub-skills 失败。你可以稍后重试:");
4126
+ log.message(import_picocolors.default.dim(" bfi skills add bfi/skillhub-skills --skill skillhub-skills -g -y"));
5266
4127
  }
5267
4128
  } else {
5268
- await dismissPrompt("findSkillsPrompt");
5269
- log.message(import_picocolors.default.dim("你可以稍后通过以下命令安装:npx skills add vercel-labs/skills@find-skills"));
4129
+ await dismissPrompt("bfiSkillPrompt");
4130
+ log.message(import_picocolors.default.dim("你可以稍后通过以下命令安装:bfi skills add bfi/skillhub-skills --skill skillhub-skills -g -y"));
5270
4131
  }
5271
4132
  } catch {}
5272
4133
  }
@@ -5311,7 +4172,10 @@ function parseAddOptions(args) {
5311
4172
  }
5312
4173
  } else if (arg === "--full-depth") options.fullDepth = true;
5313
4174
  else if (arg === "--copy") options.copy = true;
5314
- else if (arg === "--subagent") {
4175
+ else if (arg === "--branch") {
4176
+ i++;
4177
+ if (args[i] && !args[i].startsWith("-")) options.branch = args[i];
4178
+ } else if (arg === "--subagent") {
5315
4179
  options.subagent = options.subagent || [];
5316
4180
  i++;
5317
4181
  let nextArg = args[i];
@@ -5332,7 +4196,7 @@ function parseAddOptions(args) {
5332
4196
  const RESET$11 = "\x1B[0m";
5333
4197
  const BOLD$8 = "\x1B[1m";
5334
4198
  const DIM$11 = "\x1B[38;5;102m";
5335
- const TEXT$9 = "\x1B[38;5;145m";
4199
+ const TEXT$8 = "\x1B[38;5;145m";
5336
4200
  const CYAN$2 = "\x1B[36m";
5337
4201
  const SEARCH_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
5338
4202
  function formatInstalls$1(count) {
@@ -5422,7 +4286,7 @@ async function runSearchPrompt(initialQuery = "", owner) {
5422
4286
  process.stdout.write(CLEAR_DOWN);
5423
4287
  const lines = [];
5424
4288
  const cursor = `${BOLD$8}_${RESET$11}`;
5425
- lines.push(`${TEXT$9}搜索技能:${RESET$11} ${query}${cursor}`);
4289
+ lines.push(`${TEXT$8}搜索技能:${RESET$11} ${query}${cursor}`);
5426
4290
  lines.push("");
5427
4291
  if (!query || query.length < 2) lines.push(`${DIM$11}输入关键词搜索(最少 2 个字符)${RESET$11}`);
5428
4292
  else if (results.length === 0 && loading) lines.push(`${DIM$11}搜索中...${RESET$11}`);
@@ -5433,7 +4297,7 @@ async function runSearchPrompt(initialQuery = "", owner) {
5433
4297
  const skill = visible[i];
5434
4298
  const isSelected = i === selectedIndex;
5435
4299
  const arrow = isSelected ? `${BOLD$8}>${RESET$11}` : " ";
5436
- const name = isSelected ? `${BOLD$8}${skill.name}${RESET$11}` : `${TEXT$9}${skill.name}${RESET$11}`;
4300
+ const name = isSelected ? `${BOLD$8}${skill.name}${RESET$11}` : `${TEXT$8}${skill.name}${RESET$11}`;
5437
4301
  const source = skill.source ? ` ${DIM$11}${skill.source}${RESET$11}` : "";
5438
4302
  const installs = formatInstalls$1(skill.installs);
5439
4303
  const installsBadge = installs ? ` ${CYAN$2}${installs}${RESET$11}` : "";
@@ -5560,7 +4424,7 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
5560
4424
  for (const skill of results.slice(0, 6)) {
5561
4425
  const pkg = skill.source || skill.slug;
5562
4426
  const installs = formatInstalls$1(skill.installs);
5563
- console.log(`${TEXT$9}${pkg}@${skill.name}${RESET$11}${installs ? ` ${CYAN$2}${installs}${RESET$11}` : ""}`);
4427
+ console.log(`${TEXT$8}${pkg}@${skill.name}${RESET$11}${installs ? ` ${CYAN$2}${installs}${RESET$11}` : ""}`);
5564
4428
  console.log(`${DIM$11}└ https://skills.sh/${skill.slug}${RESET$11}`);
5565
4429
  console.log();
5566
4430
  }
@@ -5581,7 +4445,7 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
5581
4445
  const pkg = selected.source || selected.slug;
5582
4446
  const skillName = selected.name;
5583
4447
  console.log();
5584
- console.log(`${TEXT$9}正在从 ${DIM$11}${pkg}${RESET$11} 安装 ${BOLD$8}${skillName}${RESET$11}...`);
4448
+ console.log(`${TEXT$8}正在从 ${DIM$11}${pkg}${RESET$11} 安装 ${BOLD$8}${skillName}${RESET$11}...`);
5585
4449
  console.log();
5586
4450
  const { source, options: addOptions } = parseAddOptions([
5587
4451
  pkg,
@@ -5591,15 +4455,15 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
5591
4455
  await runAdd(source, addOptions);
5592
4456
  console.log();
5593
4457
  const info = getOwnerRepoFromString(pkg);
5594
- if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$11}查看技能:${RESET$11} ${TEXT$9}https://skills.sh/${selected.slug}${RESET$11}`);
5595
- else console.log(`${DIM$11}发现更多技能:${RESET$11} ${TEXT$9}https://skills.sh${RESET$11}`);
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}`);
5596
4460
  console.log();
5597
4461
  }
5598
4462
  const RESET$10 = "\x1B[0m";
5599
4463
  const BOLD$7 = "\x1B[1m";
5600
4464
  const DIM$10 = "\x1B[38;5;102m";
5601
4465
  const CYAN$1 = "\x1B[36m";
5602
- const YELLOW$3 = "\x1B[33m";
4466
+ const YELLOW$4 = "\x1B[33m";
5603
4467
  function shortenPath(fullPath, cwd) {
5604
4468
  const home = homedir();
5605
4469
  if (fullPath.startsWith(home)) return fullPath.replace(home, "~");
@@ -5633,7 +4497,7 @@ async function runList(args) {
5633
4497
  const validAgents = Object.keys(agents);
5634
4498
  const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
5635
4499
  if (invalidAgents.length > 0) {
5636
- console.log(`${YELLOW$3}Invalid agents: ${invalidAgents.join(", ")}${RESET$10}`);
4500
+ console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$10}`);
5637
4501
  console.log(`${DIM$10}Valid agents: ${validAgents.join(", ")}${RESET$10}`);
5638
4502
  process.exit(1);
5639
4503
  }
@@ -5670,7 +4534,7 @@ async function runList(args) {
5670
4534
  const prefix = indent ? " " : "";
5671
4535
  const shortPath = shortenPath(skill.canonicalPath, cwd);
5672
4536
  const agentNames = skill.agents.map((a) => agents[a].displayName);
5673
- const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$3}not linked${RESET$10}`;
4537
+ const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$4}not linked${RESET$10}`;
5674
4538
  const paddedName = sanitizeMetadata(skill.name).padEnd(maxNameLength);
5675
4539
  const paddedPath = shortPath.padEnd(maxPathLength);
5676
4540
  console.log(`${prefix}${CYAN$1}${paddedName}${RESET$10} ${DIM$10}${paddedPath}${RESET$10} ${DIM$10}Agents:${RESET$10} ${agentInfo}`);
@@ -5732,6 +4596,10 @@ async function runList(args) {
5732
4596
  console.log();
5733
4597
  }
5734
4598
  }
4599
+ var remove_exports = /* @__PURE__ */ __exportAll({
4600
+ parseRemoveOptions: () => parseRemoveOptions,
4601
+ removeCommand: () => removeCommand
4602
+ });
5735
4603
  async function removeCommand(skillNames, options) {
5736
4604
  const agentResult = await detectAgent();
5737
4605
  if (agentResult.isAgent) {
@@ -5980,7 +4848,7 @@ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
5980
4848
  const RESET$9 = "\x1B[0m";
5981
4849
  const BOLD$6 = "\x1B[1m";
5982
4850
  const DIM$9 = "\x1B[38;5;102m";
5983
- const TEXT$8 = "\x1B[38;5;145m";
4851
+ const TEXT$7 = "\x1B[38;5;145m";
5984
4852
  function parseUpdateOptions(args) {
5985
4853
  const options = {};
5986
4854
  const positional = [];
@@ -6074,13 +4942,13 @@ function printSkippedSkills(skipped) {
6074
4942
  for (const [source, skills] of grouped) {
6075
4943
  if (skills.length === 1) {
6076
4944
  const skill = skills[0];
6077
- console.log(` ${TEXT$8}•${RESET$9} ${sanitizeMetadata(skill.name)} ${DIM$9}(${skill.reason})${RESET$9}`);
4945
+ console.log(` ${TEXT$7}•${RESET$9} ${sanitizeMetadata(skill.name)} ${DIM$9}(${skill.reason})${RESET$9}`);
6078
4946
  } else {
6079
4947
  const reason = skills[0].reason;
6080
4948
  const names = skills.map((s) => sanitizeMetadata(s.name)).join(", ");
6081
- console.log(` ${TEXT$8}•${RESET$9} ${names} ${DIM$9}(${reason})${RESET$9}`);
4949
+ console.log(` ${TEXT$7}•${RESET$9} ${names} ${DIM$9}(${reason})${RESET$9}`);
6082
4950
  }
6083
- console.log(` ${DIM$9}更新方法:${TEXT$8}npx skills add ${source} -g -y${RESET$9}`);
4951
+ console.log(` ${DIM$9}更新方法:${TEXT$7}npx skills add ${source} -g -y${RESET$9}`);
6084
4952
  }
6085
4953
  }
6086
4954
  async function getProjectSkillsForUpdate(skillFilter) {
@@ -6129,7 +4997,7 @@ async function updateGlobalSkills(options = {}) {
6129
4997
  if (skillNames.length === 0) {
6130
4998
  if (!options.skills) {
6131
4999
  console.log(`${DIM$9}锁文件中没有跟踪的全局技能。${RESET$9}`);
6132
- console.log(`${DIM$9}安装技能:${RESET$9} ${TEXT$8}npx skills add <package> -g${RESET$9}`);
5000
+ console.log(`${DIM$9}安装技能:${RESET$9} ${TEXT$7}npx skills add <package> -g${RESET$9}`);
6133
5001
  }
6134
5002
  return {
6135
5003
  successCount,
@@ -6171,6 +5039,10 @@ async function updateGlobalSkills(options = {}) {
6171
5039
  const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
6172
5040
  let tempDir = null;
6173
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
+ }
6174
5046
  try {
6175
5047
  if (firstEntry.sourceType === "github") {
6176
5048
  const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
@@ -6234,18 +5106,18 @@ async function updateGlobalSkills(options = {}) {
6234
5106
  };
6235
5107
  }
6236
5108
  if (updates.length === 0) {
6237
- console.log(`${TEXT$8}✓ 所有全局技能已是最新${RESET$9}`);
5109
+ console.log(`${TEXT$7}✓ 所有全局技能已是最新${RESET$9}`);
6238
5110
  return {
6239
5111
  successCount,
6240
5112
  failCount,
6241
5113
  checkedCount
6242
5114
  };
6243
5115
  }
6244
- console.log(`${TEXT$8}发现 ${updates.length} 个可更新全局技能${RESET$9}`);
5116
+ console.log(`${TEXT$7}发现 ${updates.length} 个可更新全局技能${RESET$9}`);
6245
5117
  console.log();
6246
5118
  for (const update of updates) {
6247
5119
  const safeName = sanitizeMetadata(update.name);
6248
- console.log(`${TEXT$8}正在更新 ${safeName}...${RESET$9}`);
5120
+ console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
6249
5121
  const installUrl = buildUpdateInstallSource(update.entry);
6250
5122
  if (!installUrl) {
6251
5123
  failCount++;
@@ -6274,7 +5146,7 @@ async function updateGlobalSkills(options = {}) {
6274
5146
  shell: false
6275
5147
  }).status === 0) {
6276
5148
  successCount++;
6277
- console.log(` ${TEXT$8}✓${RESET$9} 已更新 ${safeName}`);
5149
+ console.log(` ${TEXT$7}✓${RESET$9} 已更新 ${safeName}`);
6278
5150
  } else {
6279
5151
  failCount++;
6280
5152
  console.log(` ${DIM$9}✗ 更新 ${safeName} 失败${RESET$9}`);
@@ -6294,7 +5166,7 @@ async function updateProjectSkills(options = {}) {
6294
5166
  if (projectSkills.length === 0) {
6295
5167
  if (!options.skills) {
6296
5168
  console.log(`${DIM$9}没有可更新的项目技能。${RESET$9}`);
6297
- console.log(`${DIM$9}Install project skills with${RESET$9} ${TEXT$8}npx skills add <package>${RESET$9}`);
5169
+ console.log(`${DIM$9}Install project skills with${RESET$9} ${TEXT$7}npx skills add <package>${RESET$9}`);
6298
5170
  }
6299
5171
  return {
6300
5172
  successCount,
@@ -6325,8 +5197,8 @@ async function updateProjectSkills(options = {}) {
6325
5197
  const targetParts = [];
6326
5198
  if (hasUniversal) targetParts.push("Universal");
6327
5199
  targetParts.push(...targetAgentNames);
6328
- if (targetParts.length > 0) console.log(`${TEXT$8}更新目标:${targetParts.join(", ")}${RESET$9}`);
6329
- console.log(`${TEXT$8}正在刷新 ${updatable.length} 个技能...${RESET$9}`);
5200
+ if (targetParts.length > 0) console.log(`${TEXT$7}更新目标:${targetParts.join(", ")}${RESET$9}`);
5201
+ console.log(`${TEXT$7}正在刷新 ${updatable.length} 个技能...${RESET$9}`);
6330
5202
  console.log();
6331
5203
  const bySource = /* @__PURE__ */ new Map();
6332
5204
  for (const skill of updatable) {
@@ -6371,7 +5243,7 @@ async function updateProjectSkills(options = {}) {
6371
5243
  const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
6372
5244
  for (const skill of remainingSkills) {
6373
5245
  const safeName = sanitizeMetadata(skill.name);
6374
- console.log(`${TEXT$8}正在更新 ${safeName}...${RESET$9}`);
5246
+ console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
6375
5247
  const installUrl = buildLocalUpdateSource(skill.entry);
6376
5248
  if (!installUrl) {
6377
5249
  failCount++;
@@ -6397,7 +5269,7 @@ async function updateProjectSkills(options = {}) {
6397
5269
  shell: false
6398
5270
  }).status === 0) {
6399
5271
  successCount++;
6400
- console.log(` ${TEXT$8}✓${RESET$9} 已更新 ${safeName}`);
5272
+ console.log(` ${TEXT$7}✓${RESET$9} 已更新 ${safeName}`);
6401
5273
  } else {
6402
5274
  failCount++;
6403
5275
  console.log(` ${DIM$9}✗ 更新 ${safeName} 失败${RESET$9}`);
@@ -6417,16 +5289,16 @@ function printLegacyProjectSkills(legacy) {
6417
5289
  console.log(`${DIM$9}${legacy.length} project skill(s) cannot be updated automatically (installed before skillPath tracking):${RESET$9}`);
6418
5290
  for (const skill of legacy) {
6419
5291
  const reinstall = buildLocalUpdateSource(skill.entry);
6420
- console.log(` ${TEXT$8}•${RESET$9} ${sanitizeMetadata(skill.name)}`);
6421
- if (reinstall) console.log(` ${DIM$9}刷新方法:${TEXT$8}npx skills add ${reinstall} -y${RESET$9}`);
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}`);
6422
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}`);
6423
5295
  }
6424
5296
  }
6425
5297
  async function runUpdate(args = []) {
6426
5298
  const options = parseUpdateOptions(args);
6427
5299
  const scope = await resolveUpdateScope(options);
6428
- if (options.skills) console.log(`${TEXT$8}正在更新 ${options.skills.join(", ")}...${RESET$9}`);
6429
- else console.log(`${TEXT$8}正在检查技能更新...${RESET$9}`);
5300
+ if (options.skills) console.log(`${TEXT$7}正在更新 ${options.skills.join(", ")}...${RESET$9}`);
5301
+ else console.log(`${TEXT$7}正在检查技能更新...${RESET$9}`);
6430
5302
  console.log();
6431
5303
  let totalSuccess = 0;
6432
5304
  let totalFail = 0;
@@ -6448,7 +5320,7 @@ async function runUpdate(args = []) {
6448
5320
  }
6449
5321
  if (options.skills && totalFound === 0) console.log(`${DIM$9}未找到匹配的已安装技能:${options.skills.join(", ")}${RESET$9}`);
6450
5322
  console.log();
6451
- if (totalSuccess > 0) console.log(`${TEXT$8}✓ 已更新 ${totalSuccess} 个技能${RESET$9}`);
5323
+ if (totalSuccess > 0) console.log(`${TEXT$7}✓ 已更新 ${totalSuccess} 个技能${RESET$9}`);
6452
5324
  if (totalFail > 0) {
6453
5325
  console.log(`${DIM$9}更新失败 ${totalFail} 个技能${RESET$9}`);
6454
5326
  process.exitCode = 1;
@@ -6905,7 +5777,7 @@ function getOptionValue$3(args, flag) {
6905
5777
  const RESET$8 = "\x1B[0m";
6906
5778
  const BOLD$5 = "\x1B[1m";
6907
5779
  const DIM$8 = "\x1B[38;5;102m";
6908
- const TEXT$7 = "\x1B[38;5;145m";
5780
+ const TEXT$6 = "\x1B[38;5;145m";
6909
5781
  const CYAN = "\x1B[36m";
6910
5782
  const MARKETPLACE_API = process.env.SKILLS_API_URL || "https://skills.sh";
6911
5783
  async function runSearch(args, globalOpts) {
@@ -6941,8 +5813,8 @@ function parseSearchQuery(args) {
6941
5813
  const parts = [];
6942
5814
  for (let i = 0; i < args.length; i++) {
6943
5815
  const arg = args[i];
6944
- if (arg === "--namespace" || arg === "--marketplace" || arg === "--all") {
6945
- if (arg === "--namespace") i++;
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++;
6946
5818
  continue;
6947
5819
  }
6948
5820
  if (arg.startsWith("--")) continue;
@@ -6995,9 +5867,9 @@ async function searchSkillHub(query, namespace, globalOpts, json = false) {
6995
5867
  const displaySlug = canonicalSlug.indexOf("--") > 0 ? canonicalSlug : `global--${canonicalSlug}`;
6996
5868
  console.log(` ${BOLD$5}${name}${RESET$8}`);
6997
5869
  if (desc) console.log(` ${DIM$8}${truncate$2(desc, 100)}${RESET$8}`);
6998
- console.log(` ${TEXT$7}slug: ${displaySlug}${RESET$8}`);
5870
+ console.log(` ${TEXT$6}slug: ${displaySlug}${RESET$8}`);
6999
5871
  if (skill.version) console.log(` ${DIM$8}version: ${skill.version}${RESET$8}`);
7000
- console.log(` ${DIM$8}安装:${TEXT$7}bfi skills add ${displaySlug}${RESET$8}`);
5872
+ console.log(` ${DIM$8}安装:${TEXT$6}bfi skills add ${displaySlug}${RESET$8}`);
7001
5873
  console.log();
7002
5874
  }
7003
5875
  } catch (error) {
@@ -7052,8 +5924,8 @@ async function searchMarketplace(query, json = false) {
7052
5924
  console.log(` ${BOLD$5}${skill.name}${RESET$8}`);
7053
5925
  if (skill.source) console.log(` ${DIM$8}来源:${skill.source}${RESET$8}`);
7054
5926
  if (installs) console.log(` ${CYAN}${installs}${RESET$8}`);
7055
- console.log(` ${TEXT$7}https://skills.sh/${skill.id}${RESET$8}`);
7056
- if (skill.source) console.log(` ${DIM$8}安装:${TEXT$7}bfi skills add github:${skill.source}${RESET$8}`);
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}`);
7057
5929
  console.log();
7058
5930
  }
7059
5931
  } catch {
@@ -7077,9 +5949,9 @@ function getOptionValue$2(args, flag) {
7077
5949
  const RESET$7 = "\x1B[0m";
7078
5950
  const BOLD$4 = "\x1B[1m";
7079
5951
  const DIM$7 = "\x1B[38;5;102m";
7080
- const TEXT$6 = "\x1B[38;5;145m";
7081
- const YELLOW$2 = "\x1B[33m";
7082
- const RED$2 = "\x1B[31m";
5952
+ const TEXT$5 = "\x1B[38;5;145m";
5953
+ const YELLOW$3 = "\x1B[33m";
5954
+ const RED$3 = "\x1B[31m";
7083
5955
  const GREEN$4 = "\x1B[32m";
7084
5956
  async function runInfo(args, globalOpts) {
7085
5957
  const ref = args[0];
@@ -7102,29 +5974,29 @@ async function runInfo(args, globalOpts) {
7102
5974
  console.log();
7103
5975
  console.log(`${BOLD$4}${skill.displayName || skill.name || skill.slug}${RESET$7}`);
7104
5976
  console.log(`${DIM$7}${"─".repeat(48)}${RESET$7}`);
7105
- console.log(` ${BOLD$4}Slug:${RESET$7} ${TEXT$6}${skill.slug}${RESET$7}`);
7106
- if (skill.summary || skill.description) console.log(` ${BOLD$4}描述:${RESET$7} ${TEXT$6}${skill.summary || skill.description}${RESET$7}`);
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}`);
7107
5979
  const owner = skill.owner;
7108
5980
  if (owner) {
7109
- console.log(` ${BOLD$4}所有者:${RESET$7} ${TEXT$6}${owner.displayName || owner.handle}${RESET$7}`);
7110
- if (owner.handle) console.log(` ${BOLD$4}句柄:${RESET$7} ${TEXT$6}${owner.handle}${RESET$7}`);
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}`);
7111
5983
  }
7112
5984
  const latestVersion = skill.latestVersion;
7113
5985
  if (latestVersion) {
7114
- console.log(` ${BOLD$4}版本:${RESET$7} ${TEXT$6}${latestVersion.version || "N/A"}${RESET$7}`);
7115
- if (latestVersion.license) console.log(` ${BOLD$4}许可证:${RESET$7} ${TEXT$6}${latestVersion.license}${RESET$7}`);
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}`);
7116
5988
  if (latestVersion.changelog) console.log(` ${BOLD$4}变更日志:${RESET$7} ${DIM$7}${truncate$1(latestVersion.changelog, 80)}${RESET$7}`);
7117
5989
  if (latestVersion.createdAt) {
7118
5990
  const date = new Date(latestVersion.createdAt).toISOString().split("T")[0];
7119
- console.log(` ${BOLD$4}发布时间:${RESET$7} ${TEXT$6}${date}${RESET$7}`);
5991
+ console.log(` ${BOLD$4}发布时间:${RESET$7} ${TEXT$5}${date}${RESET$7}`);
7120
5992
  }
7121
5993
  }
7122
5994
  const stats = skill.stats;
7123
5995
  if (stats) {
7124
5996
  console.log();
7125
5997
  console.log(` ${BOLD$4}统计:${RESET$7}`);
7126
- if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$6}${stats.downloads}${RESET$7}`);
7127
- if (stats.views !== void 0) console.log(` 浏览量:${TEXT$6}${stats.views}${RESET$7}`);
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}`);
7128
6000
  }
7129
6001
  const tags = skill.tags;
7130
6002
  if (Array.isArray(tags) && tags.length > 0) {
@@ -7132,28 +6004,28 @@ async function runInfo(args, globalOpts) {
7132
6004
  const tag = t;
7133
6005
  return tag.name || tag;
7134
6006
  }).join(", ");
7135
- console.log(` ${BOLD$4}标签:${RESET$7} ${TEXT$6}${tagList}${RESET$7}`);
6007
+ console.log(` ${BOLD$4}标签:${RESET$7} ${TEXT$5}${tagList}${RESET$7}`);
7136
6008
  }
7137
6009
  const moderation = skill.moderation;
7138
6010
  if (moderation) {
7139
6011
  const verdict = moderation.verdict;
7140
6012
  if (verdict === "MALWARE_BLOCKED" || moderation.isMalwareBlocked) {
7141
6013
  console.log();
7142
- console.log(` ${RED$2}${BOLD$4}⚠ 已封禁:检测到恶意软件${RESET$7}`);
7143
- if (moderation.summary) console.log(` ${RED$2}${moderation.summary}${RESET$7}`);
6014
+ console.log(` ${RED$3}${BOLD$4}⚠ 已封禁:检测到恶意软件${RESET$7}`);
6015
+ if (moderation.summary) console.log(` ${RED$3}${moderation.summary}${RESET$7}`);
7144
6016
  } else if (verdict === "SUSPICIOUS" || moderation.isSuspicious) {
7145
6017
  console.log();
7146
- console.log(` ${YELLOW$2}⚠ 可疑技能${RESET$7}`);
7147
- if (moderation.summary) console.log(` ${YELLOW$2}${moderation.summary}${RESET$7}`);
6018
+ console.log(` ${YELLOW$3}⚠ 可疑技能${RESET$7}`);
6019
+ if (moderation.summary) console.log(` ${YELLOW$3}${moderation.summary}${RESET$7}`);
7148
6020
  } else if (verdict === "SAFE") console.log(` ${BOLD$4}安全:${RESET$7} ${GREEN$4}已验证安全${RESET$7}`);
7149
6021
  }
7150
6022
  if (skill.createdAt) {
7151
6023
  const created = new Date(skill.createdAt).toISOString().split("T")[0];
7152
- console.log(` ${BOLD$4}创建时间:${RESET$7} ${TEXT$6}${created}${RESET$7}`);
6024
+ console.log(` ${BOLD$4}创建时间:${RESET$7} ${TEXT$5}${created}${RESET$7}`);
7153
6025
  }
7154
6026
  if (skill.updatedAt) {
7155
6027
  const updated = new Date(skill.updatedAt).toISOString().split("T")[0];
7156
- console.log(` ${BOLD$4}更新时间:${RESET$7} ${TEXT$6}${updated}${RESET$7}`);
6028
+ console.log(` ${BOLD$4}更新时间:${RESET$7} ${TEXT$5}${updated}${RESET$7}`);
7157
6029
  }
7158
6030
  console.log();
7159
6031
  } catch (error) {
@@ -7267,17 +6139,17 @@ async function handleSet(args, _globalOpts) {
7267
6139
  const RESET$5 = "\x1B[0m";
7268
6140
  const BOLD$3 = "\x1B[1m";
7269
6141
  const DIM$5 = "\x1B[38;5;102m";
7270
- const TEXT$5 = "\x1B[38;5;145m";
6142
+ const TEXT$4 = "\x1B[38;5;145m";
7271
6143
  async function runDocs(args, globalOpts) {
7272
6144
  switch (args[0]) {
7273
6145
  case "upload":
7274
6146
  await handleUpload(args.slice(1), globalOpts);
7275
6147
  break;
7276
6148
  case "list":
7277
- await handleList$1(args.slice(1), globalOpts);
6149
+ await handleList(args.slice(1), globalOpts);
7278
6150
  break;
7279
6151
  case "info":
7280
- await handleInfo$1(args.slice(1), globalOpts);
6152
+ await handleInfo(args.slice(1), globalOpts);
7281
6153
  break;
7282
6154
  default: printDocsHelp();
7283
6155
  }
@@ -7319,11 +6191,11 @@ async function handleUpload(args, globalOpts) {
7319
6191
  try {
7320
6192
  const fileBuffer = fs.readFileSync(resolvedPath);
7321
6193
  const result = await api.uploadDoc(fileBuffer, fileName);
7322
- console.log(`${TEXT$5}✓ 上传成功${RESET$5}`);
6194
+ console.log(`${TEXT$4}✓ 上传成功${RESET$5}`);
7323
6195
  console.log();
7324
- if (result.id) console.log(` ${BOLD$3}ID:${RESET$5} ${TEXT$5}${result.id}${RESET$5}`);
7325
- if (result.status) console.log(` ${BOLD$3}状态:${RESET$5} ${TEXT$5}${result.status}${RESET$5}`);
7326
- if (result.fileName) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$5}${result.fileName}${RESET$5}`);
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}`);
7327
6199
  console.log();
7328
6200
  if (result.id) console.log(`${DIM$5}查看状态:bfi docs info ${result.id}${RESET$5}`);
7329
6201
  } catch (error) {
@@ -7331,7 +6203,7 @@ async function handleUpload(args, globalOpts) {
7331
6203
  process.exit(1);
7332
6204
  }
7333
6205
  }
7334
- async function handleList$1(args, globalOpts) {
6206
+ async function handleList(args, globalOpts) {
7335
6207
  const page = parseInt(args[0] || "0", 10);
7336
6208
  const size = parseInt(args[1] || "20", 10);
7337
6209
  const { registry, token } = resolveConfig(globalOpts);
@@ -7352,7 +6224,7 @@ async function handleList$1(args, globalOpts) {
7352
6224
  const status = doc.status || "unknown";
7353
6225
  const createdAt = doc.createdAt || doc.created_at;
7354
6226
  const dateStr = createdAt ? new Date(createdAt).toISOString().split("T")[0] : "N/A";
7355
- console.log(` ${BOLD$3}#${id}${RESET$5} ${TEXT$5}${fileName}${RESET$5}`);
6227
+ console.log(` ${BOLD$3}#${id}${RESET$5} ${TEXT$4}${fileName}${RESET$5}`);
7356
6228
  console.log(` ${DIM$5}状态:${status} | 日期:${dateStr}${RESET$5}`);
7357
6229
  console.log();
7358
6230
  }
@@ -7364,7 +6236,7 @@ async function handleList$1(args, globalOpts) {
7364
6236
  process.exit(1);
7365
6237
  }
7366
6238
  }
7367
- async function handleInfo$1(args, globalOpts) {
6239
+ async function handleInfo(args, globalOpts) {
7368
6240
  const docId = args[0];
7369
6241
  if (!docId) {
7370
6242
  console.error("用法:bfi docs info <id>");
@@ -7387,18 +6259,18 @@ async function handleInfo$1(args, globalOpts) {
7387
6259
  console.log();
7388
6260
  console.log(`${BOLD$3}文档 #${doc.id}${RESET$5}`);
7389
6261
  console.log(`${DIM$5}${"─".repeat(48)}${RESET$5}`);
7390
- if (doc.fileName || doc.file_name) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$5}${doc.fileName || doc.file_name}${RESET$5}`);
6262
+ if (doc.fileName || doc.file_name) console.log(` ${BOLD$3}文件:${RESET$5} ${TEXT$4}${doc.fileName || doc.file_name}${RESET$5}`);
7391
6263
  if (doc.status) {
7392
- const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$5;
6264
+ const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$4;
7393
6265
  console.log(` ${BOLD$3}状态:${RESET$5} ${statusColor}${doc.status}${RESET$5}`);
7394
6266
  }
7395
6267
  if (doc.createdAt || doc.created_at) {
7396
6268
  const date = new Date(doc.createdAt || doc.created_at).toISOString().replace("T", " ").slice(0, 19);
7397
- console.log(` ${BOLD$3}创建时间:${RESET$5} ${TEXT$5}${date}${RESET$5}`);
6269
+ console.log(` ${BOLD$3}创建时间:${RESET$5} ${TEXT$4}${date}${RESET$5}`);
7398
6270
  }
7399
6271
  if (doc.updatedAt || doc.updated_at) {
7400
6272
  const date = new Date(doc.updatedAt || doc.updated_at).toISOString().replace("T", " ").slice(0, 19);
7401
- console.log(` ${BOLD$3}更新时间:${RESET$5} ${TEXT$5}${date}${RESET$5}`);
6273
+ console.log(` ${BOLD$3}更新时间:${RESET$5} ${TEXT$4}${date}${RESET$5}`);
7402
6274
  }
7403
6275
  if (doc.result) {
7404
6276
  console.log();
@@ -7421,15 +6293,15 @@ async function handleInfo$1(args, globalOpts) {
7421
6293
  const RESET$4 = "\x1B[0m";
7422
6294
  const BOLD$2 = "\x1B[1m";
7423
6295
  const DIM$4 = "\x1B[38;5;102m";
7424
- const TEXT$4 = "\x1B[38;5;145m";
6296
+ const TEXT$3 = "\x1B[38;5;145m";
7425
6297
  const GREEN$3 = "\x1B[32m";
7426
- const YELLOW$1 = "\x1B[33m";
7427
- const RED$1 = "\x1B[31m";
6298
+ const YELLOW$2 = "\x1B[33m";
6299
+ const RED$2 = "\x1B[31m";
7428
6300
  async function runPublish(args, globalOpts) {
7429
6301
  const fromIdx = args.indexOf("--from");
7430
6302
  const mode = fromIdx !== -1 ? args[fromIdx + 1] : "dir";
7431
6303
  const namespace = getOptionValue$1(args, "--namespace") || globalOpts.namespace || "global";
7432
- const visibility = getOptionValue$1(args, "--visibility") || "private";
6304
+ const visibility = getOptionValue$1(args, "--visibility") || "public";
7433
6305
  const yes = args.includes("--yes") || args.includes("-y");
7434
6306
  const branch = getOptionValue$1(args, "--branch");
7435
6307
  const directory = getOptionValue$1(args, "--directory");
@@ -7486,22 +6358,22 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
7486
6358
  const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
7487
6359
  const skillName = frontmatter.name || path$1.basename(resolvedDir);
7488
6360
  console.log(`${DIM$4}正在从目录发布技能...${RESET$4}`);
7489
- console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$4}${skillName}${RESET$4}`);
7490
- console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$4}${namespace}${RESET$4}`);
7491
- console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$4}${visibility}${RESET$4}`);
7492
- console.log(` ${BOLD$2}目录:${RESET$4} ${TEXT$4}${resolvedDir}${RESET$4}`);
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}`);
7493
6365
  console.log();
7494
6366
  console.log(`${DIM$4}正在创建 zip...${RESET$4}`);
7495
6367
  const zipBuffer = createZipFromDir(resolvedDir);
7496
6368
  console.log(` ${GREEN$3}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$4}`);
7497
6369
  console.log(`${DIM$4}正在上传到 SkillHub...${RESET$4}`);
7498
6370
  try {
7499
- const res = await api.publishSkill(namespace, zipBuffer, visibility);
6371
+ const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
7500
6372
  console.log(` ${GREEN$3}发布成功!${RESET$4}`);
7501
- if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$4}${res.slug}${RESET$4}`);
7502
- if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$4}${res.url}${RESET$4}`);
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}`);
7503
6375
  } catch (error) {
7504
- console.error(` ${RED$1}发布失败:${error.message}${RESET$4}`);
6376
+ console.error(` ${RED$2}发布失败:${error.message}${RESET$4}`);
7505
6377
  process.exit(1);
7506
6378
  }
7507
6379
  console.log();
@@ -7525,27 +6397,27 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
7525
6397
  await extractZip(resolvedZip, tmpDir);
7526
6398
  const skillMdPath = findSkillMd(tmpDir);
7527
6399
  if (!skillMdPath) {
7528
- console.error(` ${RED$1}zip 文件中未找到 SKILL.md${RESET$4}`);
6400
+ console.error(` ${RED$2}zip 文件中未找到 SKILL.md${RESET$4}`);
7529
6401
  process.exit(1);
7530
6402
  }
7531
6403
  const skillDir = path$1.dirname(skillMdPath);
7532
6404
  console.log(` ${GREEN$3}在 ${path$1.relative(tmpDir, skillDir) || "."} 中找到 SKILL.md${RESET$4}`);
7533
6405
  const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
7534
6406
  const skillName = frontmatter.name || path$1.basename(skillDir);
7535
- console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$4}${skillName}${RESET$4}`);
7536
- console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$4}${namespace}${RESET$4}`);
7537
- console.log(` ${BOLD$2}可见性:${RESET$4} ${TEXT$4}${visibility}${RESET$4}`);
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}`);
7538
6410
  console.log();
7539
6411
  console.log(`${DIM$4}正在重新打包...${RESET$4}`);
7540
6412
  const zipBuffer = createZipFromDir(skillDir);
7541
6413
  console.log(` ${GREEN$3}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$4}`);
7542
6414
  console.log(`${DIM$4}正在上传到 SkillHub...${RESET$4}`);
7543
- const res = await api.publishSkill(namespace, zipBuffer, visibility);
6415
+ const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
7544
6416
  console.log(` ${GREEN$3}发布成功!${RESET$4}`);
7545
- if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$4}${res.slug}${RESET$4}`);
7546
- if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$4}${res.url}${RESET$4}`);
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}`);
7547
6419
  } catch (error) {
7548
- if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$1}错误:${error.message}${RESET$4}`);
6420
+ if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$2}错误:${error.message}${RESET$4}`);
7549
6421
  process.exit(1);
7550
6422
  } finally {
7551
6423
  try {
@@ -7560,121 +6432,109 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
7560
6432
  async function publishFromGit(positionalArgs, api, namespace, visibility, yes, branch, directory, skillFilter) {
7561
6433
  const repoArg = positionalArgs[0];
7562
6434
  if (!repoArg) {
7563
- console.error("用法:bfi publish <repo-url> --from git [options]");
7564
- console.log("使用服务端发现从 Git 仓库发布技能。");
7565
- console.log();
7566
- console.log("示例:");
7567
- console.log(` ${DIM$4}$ ${RESET$4}bfi publish https://github.com/user/repo --from git`);
7568
- 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]");
7569
6436
  process.exit(1);
7570
6437
  }
7571
6438
  let repoUrl;
6439
+ let parsedRef;
7572
6440
  try {
7573
- repoUrl = parseSource(repoArg).url;
6441
+ const parsed = parseSource(repoArg);
6442
+ repoUrl = parsed.url;
6443
+ parsedRef = parsed.ref;
7574
6444
  } catch {
7575
6445
  repoUrl = repoArg;
7576
6446
  }
7577
- const effectiveBranch = branch || "main";
7578
- const effectiveDir = directory || null;
6447
+ const effectiveBranch = branch || parsedRef || "main";
6448
+ const effectiveSubpath = directory || null;
7579
6449
  console.log(`${DIM$4}正在从 Git 仓库发现技能...${RESET$4}`);
7580
- console.log(` ${BOLD$2}仓库:${RESET$4} ${TEXT$4}${repoUrl}${RESET$4}`);
7581
- console.log(` ${BOLD$2}分支:${RESET$4} ${TEXT$4}${effectiveBranch}${RESET$4}`);
7582
- 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}`);
7583
6452
  console.log();
7584
- let parseResult;
6453
+ let tmpDir = null;
6454
+ let skills = [];
7585
6455
  try {
7586
- parseResult = await api.gitParse(repoUrl, effectiveBranch, effectiveDir);
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
+ }));
7587
6467
  } catch (error) {
7588
- console.error(` ${RED$1}Git 解析失败:${error.message}${RESET$4}`);
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 {}
7589
6473
  process.exit(1);
7590
6474
  }
7591
- const sessionId = parseResult.sessionId;
7592
- const discoveredSkills = parseResult.skills || [];
7593
- const precheck = parseResult.precheck;
7594
- if (!sessionId || discoveredSkills.length === 0) {
7595
- console.log(`${YELLOW$1}仓库中未发现技能。${RESET$4}`);
7596
- if (precheck && (precheck.errors || precheck.warnings)) {
7597
- console.log();
7598
- console.log(`${BOLD$2}预检结果:${RESET$4}`);
7599
- const errors = precheck.errors;
7600
- const warnings = precheck.warnings;
7601
- if (errors && errors.length > 0) {
7602
- console.log(` ${RED$1}错误:${RESET$4}`);
7603
- for (const e of errors) console.log(` ${RED$1}- ${e}${RESET$4}`);
7604
- }
7605
- if (warnings && warnings.length > 0) {
7606
- console.log(` ${YELLOW$1}警告:${RESET$4}`);
7607
- for (const w of warnings) console.log(` ${YELLOW$1}- ${w}${RESET$4}`);
7608
- }
7609
- }
7610
- 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;
7611
6482
  }
7612
- console.log(`${BOLD$2}发现 ${discoveredSkills.length} 个技能:${RESET$4}`);
7613
- console.log();
7614
- let selectedSkills = discoveredSkills;
6483
+ let selectedSkills = skills;
7615
6484
  if (skillFilter) {
7616
6485
  const filters = skillFilter.split(",").map((s) => s.trim().toLowerCase());
7617
- selectedSkills = discoveredSkills.filter((s) => {
7618
- const slug = String(s.slug || "").toLowerCase();
7619
- const name = String(s.name || "").toLowerCase();
7620
- return filters.some((f) => slug.includes(f) || name.includes(f));
7621
- });
6486
+ selectedSkills = skills.filter((s) => filters.some((f) => s.name.toLowerCase().includes(f)));
7622
6487
  if (selectedSkills.length === 0) {
7623
- console.log(`${YELLOW$1}没有技能匹配筛选条件:${skillFilter}${RESET$4}`);
7624
- console.log("Available skills:");
7625
- for (const s of discoveredSkills) console.log(` - ${s.slug || s.name}`);
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 {}
7626
6496
  process.exit(1);
7627
6497
  }
7628
6498
  }
6499
+ console.log(`\n${BOLD$2}发现 ${skills.length} 个技能,将发布 ${selectedSkills.length} 个:${RESET$4}\n`);
7629
6500
  for (const skill of selectedSkills) {
7630
- const slug = skill.slug || "?";
7631
- const skillName = skill.name || slug;
7632
- const description = skill.description || "";
7633
- const skillPath = skill.directoryName || skill.path || "";
7634
- const pathParts = skillPath ? skillPath.replace(/\\/g, "/").split("/") : [];
7635
- const indent = " ";
7636
- if (pathParts.length > 1) {
7637
- const dirPath = pathParts.slice(0, -1).join(" ▸ ");
7638
- console.log(` ${DIM$4}📁 ${dirPath}/${RESET$4}`);
7639
- }
7640
- console.log(` ${BOLD$2}${skillName}${RESET$4} ${DIM$4}(${slug})${RESET$4}`);
7641
- if (description) console.log(` ${indent}${DIM$4}${truncate(String(description), 80)}${RESET$4}`);
7642
- const skillPrecheck = skill.precheck;
7643
- if (skillPrecheck) {
7644
- const errors = skillPrecheck.errors;
7645
- const warnings = skillPrecheck.warnings;
7646
- if (errors && errors.length > 0) for (const e of errors) console.log(` ${RED$1}错误:${e}${RESET$4}`);
7647
- if (warnings && warnings.length > 0) for (const w of warnings) console.log(` ${YELLOW$1}警告:${w}${RESET$4}`);
7648
- }
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}`);
7649
6505
  console.log();
7650
6506
  }
7651
- let confirmWarnings = false;
7652
- if (precheck) {
7653
- const errors = precheck.errors;
7654
- const warnings = precheck.warnings;
7655
- if (errors && errors.length > 0 || warnings && warnings.length > 0) confirmWarnings = true;
7656
- }
7657
- const selectedSlugs = skillFilter ? selectedSkills.map((s) => String(s.slug)) : null;
7658
6507
  if (!yes) {
7659
- const count = selectedSlugs ? selectedSlugs.length : discoveredSkills.length;
7660
- console.log(`${DIM$4}正在将 ${count} 个技能发布到 @${namespace} (${visibility})${RESET$4}`);
7661
- console.log(`${DIM$4}按 Ctrl+C 取消,或按 Enter 继续...${RESET$4}`);
7662
- await confirmPrompt("确认发布?");
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
+ }
7663
6519
  }
7664
- console.log(`${DIM$4}正在发布到 SkillHub...${RESET$4}`);
7665
- try {
7666
- const res = await api.gitPublish(sessionId, namespace, visibility, selectedSlugs, confirmWarnings);
7667
- console.log(` ${GREEN$3}发布成功!${RESET$4}`);
7668
- const publishedSkills = res.skills || res.published;
7669
- if (Array.isArray(publishedSkills)) for (const s of publishedSkills) {
7670
- const ps = s;
7671
- console.log(` ${GREEN$3}✓ ${ps.slug || ps.name}${RESET$4}`);
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}`);
7672
6531
  }
7673
- } catch (error) {
7674
- console.error(` ${RED$1}发布失败:${error.message}${RESET$4}`);
7675
- process.exit(1);
7676
6532
  }
7677
- 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 {}
7678
6538
  }
7679
6539
  function getOptionValue$1(args, flag) {
7680
6540
  const idx = args.indexOf(flag);
@@ -7719,149 +6579,276 @@ function truncate(text, maxLen) {
7719
6579
  if (text.length <= maxLen) return text;
7720
6580
  return text.slice(0, maxLen) + "...";
7721
6581
  }
7722
- async function confirmPrompt(message) {
7723
- const rl = (await import("node:readline")).createInterface({
7724
- input: process.stdin,
7725
- output: process.stdout
7726
- });
7727
- return new Promise((resolve) => {
7728
- rl.question(`${message} [Y/n] `, (answer) => {
7729
- rl.close();
7730
- const normalized = answer.trim().toLowerCase();
7731
- if (normalized === "n" || normalized === "no") {
7732
- console.log("Publish cancelled.");
7733
- process.exit(0);
7734
- }
7735
- resolve();
7736
- });
7737
- });
7738
- }
7739
6582
  const RESET$3 = "\x1B[0m";
7740
6583
  const BOLD$1 = "\x1B[1m";
7741
6584
  const DIM$3 = "\x1B[38;5;102m";
7742
- const TEXT$3 = "\x1B[38;5;145m";
7743
6585
  const GREEN$2 = "\x1B[32m";
6586
+ const RED$1 = "\x1B[31m";
6587
+ const YELLOW$1 = "\x1B[33m";
7744
6588
  async function runPackage(args, globalOpts) {
7745
6589
  const subcommand = args[0];
7746
6590
  const { registry, token } = resolveConfig(globalOpts);
7747
6591
  const api = new SkillHubAPI(registry, token);
7748
6592
  switch (subcommand) {
7749
6593
  case "list":
7750
- await handleList(args.slice(1), api, globalOpts);
6594
+ case "ls":
6595
+ await handleLocalList(args.slice(1), globalOpts);
7751
6596
  break;
7752
- case "info":
7753
- await handleInfo(args.slice(1), api, globalOpts);
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);
7754
6611
  break;
7755
6612
  case "create":
7756
6613
  await handleCreate(args.slice(1), api, globalOpts);
7757
6614
  break;
7758
- case "add":
7759
- await handleAdd(args.slice(1), api, globalOpts);
6615
+ case "add-skills":
6616
+ await handleAddSkills(args.slice(1), api, globalOpts);
7760
6617
  break;
7761
- case "remove":
7762
- case "rm":
7763
- await handleRemove(args.slice(1), api, globalOpts);
6618
+ case "remove-skills":
6619
+ await handleRemoveSkills(args.slice(1), api, globalOpts);
7764
6620
  break;
7765
6621
  default: printPackageHelp();
7766
6622
  }
7767
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
+ }
7768
6628
  function printPackageHelp() {
7769
- console.log();
7770
6629
  console.log(`${BOLD$1}包命令:${RESET$3}`);
7771
- console.log(` ${DIM$3}bfi skills package list${RESET$3} 列出包`);
7772
- console.log(` ${DIM$3}bfi skills package info <ref>${RESET$3} 查看包详情`);
7773
- console.log(` ${DIM$3}bfi skills package create <slug>${RESET$3} 创建新包`);
7774
- console.log(` ${DIM$3}bfi skills package add <pkg> [skills]${RESET$3} 添加技能到包(默认全部)`);
7775
- console.log(` ${DIM$3}bfi skills package remove <pkg> [skills]${RESET$3} 从包移除技能(默认全部)`);
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} 从包移除技能`);
7776
6642
  console.log();
7777
6643
  console.log(`${BOLD$1}选项:${RESET$3}`);
7778
- console.log(` --namespace <ns> 目标命名空间`);
7779
- console.log(` --name <name> 显示名称(create 时)`);
7780
- console.log(` --description <text> 描述(create 时)`);
7781
- console.log(` --visibility <v> 私有或公开(create 时)`);
7782
- console.log(` --json 以 JSON 输出`);
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
+ }
7783
6663
  console.log();
7784
- console.log(`${BOLD$1}示例:${RESET$3}`);
7785
- console.log(` ${DIM$3}$ ${RESET$3}bfi skills package list --namespace myteam`);
7786
- console.log(` ${DIM$3}$ ${RESET$3}bfi skills package info @myteam/my-package`);
7787
- console.log(` ${DIM$3}$ ${RESET$3}bfi skills package create my-utils --namespace myteam`);
7788
- console.log(` ${DIM$3}$ ${RESET$3}bfi skills package add @myteam/my-package @myteam/skill-a @myteam/skill-b`);
7789
- console.log(` ${DIM$3}$ ${RESET$3}bfi skills package remove @myteam/my-package @myteam/skill-a`);
6664
+ console.log(`${BOLD$1}已安装的包(${entries.length} 个):${RESET$3}`);
7790
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
+ }
7791
6674
  }
7792
- async function handleList(args, api, globalOpts) {
7793
- const namespace = getOptionValue(args, "--namespace") || globalOpts.namespace || "global";
7794
- const json = args.includes("--json");
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;
7795
6685
  try {
7796
- const result = await api.listPackages(namespace);
7797
- const packages = result.packages || result.data || [];
7798
- if (json) {
7799
- console.log(JSON.stringify({
7800
- namespace,
7801
- packages
7802
- }, null, 2));
7803
- return;
7804
- }
7805
- if (packages.length === 0) {
7806
- console.log(`命名空间 "${namespace}" 中没有包。`);
7807
- console.log(`使用 --namespace <ns> 查看其他命名空间,或 bfi whoami 查看你的命名空间列表。`);
7808
- return;
7809
- }
7810
- console.log();
7811
- console.log(`${BOLD$1}@${namespace} 中的包${RESET$3}(${packages.length} 个):`);
7812
- console.log();
7813
- packages.forEach((pkg, index) => {
7814
- const visibility = pkg.visibility || "private";
7815
- const skillCount = pkg.skillCount || 0;
7816
- console.log(` ${BOLD$1}${index + 1}. ${pkg.name || pkg.slug}${RESET$3}`);
7817
- console.log(` ${DIM$3}Slug: ${pkg.slug}${RESET$3}`);
7818
- console.log(` ${DIM$3}Visibility: ${visibility}${RESET$3}`);
7819
- console.log(` ${DIM$3}Skills: ${skillCount}${RESET$3}`);
7820
- if (pkg.description) console.log(` ${DIM$3}${pkg.description}${RESET$3}`);
7821
- console.log();
7822
- });
6686
+ pkgInfo = await api.getPackage(namespace, slug);
6687
+ pkgInfo = pkgInfo.data || pkgInfo;
7823
6688
  } catch (error) {
7824
- console.error(`列出包失败:${error.message}`);
6689
+ console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
7825
6690
  process.exit(1);
7826
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}`);
7827
6740
  }
7828
- async function handleInfo(args, api, globalOpts) {
6741
+ async function handleUninstall(args, globalOpts) {
7829
6742
  const pkgRef = args[0];
7830
6743
  if (!pkgRef) {
7831
- console.error("Usage: bfi skills package info <package-ref>");
7832
- console.log();
7833
- console.log("Examples:");
7834
- console.log(" bfi skills package info @myteam/my-package");
7835
- console.log(" bfi skills package info myteam--my-package");
6744
+ console.error("用法:bfi skills package uninstall <package-ref>");
7836
6745
  process.exit(1);
7837
6746
  }
7838
- const json = args.includes("--json");
7839
6747
  const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
7840
- try {
7841
- const pkgInfo = await api.getPackage(namespace, slug);
7842
- const pkg = pkgInfo.data || pkgInfo;
7843
- if (json) {
7844
- console.log(JSON.stringify(pkg, null, 2));
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}`);
7845
6784
  return;
7846
6785
  }
7847
- console.log();
7848
- console.log(`${BOLD$1}${pkg.name || slug}${RESET$3}`);
7849
- console.log(`${DIM$3}${"=".repeat(48)}${RESET$3}`);
7850
- console.log(` ${BOLD$1}Slug:${RESET$3} ${TEXT$3}${toCanonicalSlug(namespace, slug)}${RESET$3}`);
7851
- console.log(` ${BOLD$1}命名空间:${RESET$3} ${TEXT$3}@${namespace}${RESET$3}`);
7852
- console.log(` ${BOLD$1}可见性:${RESET$3} ${TEXT$3}${pkg.visibility || "private"}${RESET$3}`);
7853
- console.log(` ${BOLD$1}描述:${RESET$3} ${TEXT$3}${pkg.description || "N/A"}${RESET$3}`);
7854
- const skills = pkg.skills;
7855
- if (skills && skills.length > 0) {
7856
- console.log();
7857
- console.log(` ${BOLD$1}技能(${skills.length} 个):${RESET$3}`);
7858
- for (const skill of skills) console.log(` ${DIM$3}- ${skill.slug}${skill.name ? ` (${skill.name})` : ""}${RESET$3}`);
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 {}
7859
6812
  }
7860
- console.log();
7861
6813
  } catch (error) {
7862
- console.error(`获取包信息失败:${error.message}`);
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>");
7863
6823
  process.exit(1);
7864
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}`);
7865
6852
  }
7866
6853
  async function handleCreate(args, api, globalOpts) {
7867
6854
  const slug = args[0];
@@ -7876,27 +6863,24 @@ async function handleCreate(args, api, globalOpts) {
7876
6863
  try {
7877
6864
  const result = await api.createPackage(namespace, {
7878
6865
  slug,
7879
- name: name || slug,
6866
+ displayName: name || slug,
7880
6867
  description: description || "",
7881
- visibility
6868
+ visibility: visibility.toUpperCase()
7882
6869
  });
7883
6870
  console.log(`${GREEN$2}✓ 已创建包 ${toSkillRefString(namespace, slug)}${RESET$3}`);
7884
- const createdPkg = result.package;
7885
- if (createdPkg) {
7886
- console.log(` ${BOLD$1}名称:${RESET$3} ${TEXT$3}${createdPkg.name || slug}${RESET$3}`);
7887
- console.log(` ${BOLD$1}Visibility:${RESET$3} ${TEXT$3}${createdPkg.visibility || visibility}${RESET$3}`);
7888
- }
6871
+ const pkg = result.package || result.data || result;
6872
+ if (pkg) console.log(` ${DIM$3}名称:${pkg.name || slug} | 可见性:${pkg.visibility || visibility}${RESET$3}`);
7889
6873
  } catch (error) {
7890
- console.error(`创建包失败:${error.message}`);
6874
+ console.error(`${RED$1}创建包失败:${error.message}${RESET$3}`);
7891
6875
  process.exit(1);
7892
6876
  }
7893
6877
  }
7894
- async function handleAdd(args, api, globalOpts) {
6878
+ async function handleAddSkills(args, api, globalOpts) {
7895
6879
  const pkgRef = args[0];
7896
6880
  const skillRefs = args.slice(1);
7897
6881
  if (!pkgRef) {
7898
- console.error("用法:bfi skills package add <package-ref> [skill-ref...]");
7899
- console.error(" 不指定技能时,添加当前命名空间的所有技能到包");
6882
+ console.error("用法:bfi skills package add-skills <package-ref> [skill-ref...]");
6883
+ console.error(" 不指定技能时,添加命名空间下全部技能");
7900
6884
  process.exit(1);
7901
6885
  }
7902
6886
  const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
@@ -7913,72 +6897,70 @@ async function handleAdd(args, api, globalOpts) {
7913
6897
  };
7914
6898
  });
7915
6899
  if (skills.length === 0) {
7916
- console.error(`命名空间 "${namespace}" 中没有技能。`);
7917
- process.exit(1);
6900
+ console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$3}`);
6901
+ return;
7918
6902
  }
7919
6903
  } catch (error) {
7920
- console.error(`获取技能列表失败:${error.message}`);
6904
+ console.error(`${RED$1}获取技能列表失败:${error.message}${RESET$3}`);
7921
6905
  process.exit(1);
7922
6906
  }
7923
6907
  else skills = skillRefs.map((ref) => {
7924
- const { namespace: skillNs, slug: skillSlug } = parseSkillRef(ref, namespace);
6908
+ const { namespace: ns, slug: s } = parseSkillRef(ref, namespace);
7925
6909
  return {
7926
- namespace: skillNs,
7927
- slug: skillSlug
6910
+ namespace: ns,
6911
+ slug: s
7928
6912
  };
7929
6913
  });
7930
6914
  try {
7931
6915
  await api.addSkillsToPackage(namespace, slug, skills);
7932
6916
  console.log(`${GREEN$2}✓ 已添加 ${skills.length} 个技能到 ${toSkillRefString(namespace, slug)}${RESET$3}`);
7933
- for (const s of skills) console.log(` ${DIM$3}- ${toSkillRefString(s.namespace, s.slug)}${RESET$3}`);
7934
6917
  } catch (error) {
7935
- console.error(`添加技能失败:${error.message}`);
6918
+ console.error(`${RED$1}添加失败:${error.message}${RESET$3}`);
7936
6919
  process.exit(1);
7937
6920
  }
7938
6921
  }
7939
- async function handleRemove(args, api, globalOpts) {
6922
+ async function handleRemoveSkills(args, api, globalOpts) {
7940
6923
  const pkgRef = args[0];
7941
6924
  const skillRefs = args.slice(1);
7942
6925
  if (!pkgRef) {
7943
- console.error("用法:bfi skills package remove <package-ref> [skill-ref...]");
7944
- console.error(" 不指定技能时,移除包中所有技能");
6926
+ console.error("用法:bfi skills package remove-skills <package-ref> [skill-ref...]");
6927
+ console.error(" 不指定技能时,移除包中全部技能");
7945
6928
  process.exit(1);
7946
6929
  }
7947
6930
  const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
7948
6931
  let skills;
7949
6932
  if (skillRefs.length === 0) try {
7950
- const existingSkills = (await api.getPackage(namespace, slug)).skills || [];
7951
- if (existingSkills.length === 0) {
7952
- console.log("包中没有技能。");
7953
- return;
7954
- }
7955
- skills = existingSkills.map((s) => ({
6933
+ const pkgInfo = await api.getPackage(namespace, slug);
6934
+ skills = ((pkgInfo.data || pkgInfo).skills || []).map((s) => ({
7956
6935
  namespace: s.namespace || namespace,
7957
6936
  slug: s.slug
7958
6937
  }));
6938
+ if (skills.length === 0) {
6939
+ console.log("包中没有技能。");
6940
+ return;
6941
+ }
7959
6942
  } catch (error) {
7960
- console.error(`获取包信息失败:${error.message}`);
6943
+ console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
7961
6944
  process.exit(1);
7962
6945
  }
7963
6946
  else skills = skillRefs.map((ref) => {
7964
- const { namespace: skillNs, slug: skillSlug } = parseSkillRef(ref, namespace);
6947
+ const { namespace: ns, slug: s } = parseSkillRef(ref, namespace);
7965
6948
  return {
7966
- namespace: skillNs,
7967
- slug: skillSlug
6949
+ namespace: ns,
6950
+ slug: s
7968
6951
  };
7969
6952
  });
7970
6953
  try {
7971
6954
  await api.removeSkillsFromPackage(namespace, slug, skills);
7972
- console.log(`${GREEN$2}✓ 已从 ${toSkillRefString(namespace, slug)} 移除 ${skills.length} 个技能${RESET$3}`);
7973
- 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}`);
7974
6956
  } catch (error) {
7975
- console.error(`移除技能失败:${error.message}`);
6957
+ console.error(`${RED$1}移除失败:${error.message}${RESET$3}`);
7976
6958
  process.exit(1);
7977
6959
  }
7978
6960
  }
7979
6961
  function getOptionValue(args, flag) {
7980
6962
  const idx = args.indexOf(flag);
7981
- return idx !== -1 && idx < args.length - 1 ? args[idx + 1] : void 0;
6963
+ return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
7982
6964
  }
7983
6965
  const RESET$2 = "\x1B[0m";
7984
6966
  const DIM$2 = "\x1B[38;5;102m";
@@ -8427,9 +7409,9 @@ async function main() {
8427
7409
  globalOpts.workdir = process.cwd();
8428
7410
  try {
8429
7411
  const { resolveConfig } = await Promise.resolve().then(() => config_exports);
8430
- const { resolveAgents } = await import("./_chunks/agents.mjs");
7412
+ const { resolveAgents } = await import("./_chunks/agents2.mjs");
8431
7413
  const { SkillHubAPI } = await Promise.resolve().then(() => api_exports);
8432
- const { setAgents } = await Promise.resolve().then(() => agents_exports);
7414
+ const { setAgents } = await import("./_chunks/agents.mjs").then((n) => n.agents_exports);
8433
7415
  const cfg = resolveConfig(globalOpts);
8434
7416
  setAgents(await resolveAgents(cfg.token ? new SkillHubAPI(cfg.registry, cfg.token) : void 0));
8435
7417
  } catch {}
@@ -8545,4 +7527,4 @@ async function main() {
8545
7527
  }
8546
7528
  }
8547
7529
  main().finally(() => process.exit(process.exitCode ?? 0));
8548
- export { agents, loadGlobalConfig };
7530
+ export { loadGlobalConfig };