bfi-cli 0.1.36 → 0.1.39
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 +100 -16
- package/package.json +1 -1
- package/skills/skillhub-skills/SKILL.md +48 -114
package/dist/cli.mjs
CHANGED
|
@@ -905,6 +905,11 @@ function setConfigValue(key, value, isGlobal = false) {
|
|
|
905
905
|
saveProjectConfig(workdir, config);
|
|
906
906
|
}
|
|
907
907
|
}
|
|
908
|
+
var skill_ref_exports = /* @__PURE__ */ __exportAll({
|
|
909
|
+
parseSkillRef: () => parseSkillRef,
|
|
910
|
+
toCanonicalSlug: () => toCanonicalSlug,
|
|
911
|
+
toSkillRefString: () => toSkillRefString
|
|
912
|
+
});
|
|
908
913
|
function parseSkillRef(ref, defaultNamespace = "global") {
|
|
909
914
|
if (!ref || typeof ref !== "string") throw new Error("Skill reference is required");
|
|
910
915
|
const trimmed = ref.trim();
|
|
@@ -4909,7 +4914,17 @@ async function resolveUpdateScope(options) {
|
|
|
4909
4914
|
function matchesSkillFilter(name, filter) {
|
|
4910
4915
|
if (!filter || filter.length === 0) return true;
|
|
4911
4916
|
const lower = name.toLowerCase();
|
|
4912
|
-
return filter.some((f) =>
|
|
4917
|
+
return filter.some((f) => {
|
|
4918
|
+
const fl = f.toLowerCase();
|
|
4919
|
+
if (fl === lower) return true;
|
|
4920
|
+
if (fl.startsWith("@")) {
|
|
4921
|
+
const slashIdx = fl.lastIndexOf("/");
|
|
4922
|
+
if (slashIdx > 0 && fl.slice(slashIdx + 1) === lower) return true;
|
|
4923
|
+
}
|
|
4924
|
+
const dashIdx = fl.indexOf("--");
|
|
4925
|
+
if (dashIdx > 0 && fl.slice(dashIdx + 2) === lower) return true;
|
|
4926
|
+
return false;
|
|
4927
|
+
});
|
|
4913
4928
|
}
|
|
4914
4929
|
function getSkipReason(entry) {
|
|
4915
4930
|
if (entry.sourceType === "local") return "本地路径";
|
|
@@ -5039,7 +5054,29 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5039
5054
|
let tempDir = null;
|
|
5040
5055
|
process.stdout.write(`\r${DIM$9}Checking skills from source: ${source}${RESET$9}\x1b[K\n`);
|
|
5041
5056
|
if (firstEntry.sourceType === "skillhub") {
|
|
5042
|
-
|
|
5057
|
+
const skillhubRef = source.replace(/^skillhub:/, "");
|
|
5058
|
+
const { parseSkillRef, toCanonicalSlug } = await Promise.resolve().then(() => skill_ref_exports);
|
|
5059
|
+
const { SkillHubAPI } = await Promise.resolve().then(() => api_exports);
|
|
5060
|
+
const { resolveConfig } = await Promise.resolve().then(() => config_exports);
|
|
5061
|
+
const cfg = resolveConfig({});
|
|
5062
|
+
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5063
|
+
for (const item of itemsForSource) {
|
|
5064
|
+
const safeName = sanitizeMetadata(item.name);
|
|
5065
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
5066
|
+
try {
|
|
5067
|
+
const parts = skillhubRef.split("/");
|
|
5068
|
+
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
5069
|
+
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
5070
|
+
await runAdd([ref], {
|
|
5071
|
+
global: options.global,
|
|
5072
|
+
yes: true
|
|
5073
|
+
});
|
|
5074
|
+
successCount++;
|
|
5075
|
+
} catch (error) {
|
|
5076
|
+
console.log(` ${DIM$9}✗ ${safeName}: ${error.message}${RESET$9}`);
|
|
5077
|
+
failCount++;
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5043
5080
|
continue;
|
|
5044
5081
|
}
|
|
5045
5082
|
try {
|
|
@@ -5220,6 +5257,32 @@ async function updateProjectSkills(options = {}) {
|
|
|
5220
5257
|
const firstEntry = skillsForSource[0].entry;
|
|
5221
5258
|
const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
|
|
5222
5259
|
const ref = firstEntry.ref;
|
|
5260
|
+
if (firstEntry.sourceType === "skillhub") {
|
|
5261
|
+
const skillhubRef = source.replace(/^skillhub:/, "");
|
|
5262
|
+
const { parseSkillRef, toCanonicalSlug } = await Promise.resolve().then(() => skill_ref_exports);
|
|
5263
|
+
const { SkillHubAPI } = await Promise.resolve().then(() => api_exports);
|
|
5264
|
+
const { resolveConfig } = await Promise.resolve().then(() => config_exports);
|
|
5265
|
+
const cfg = resolveConfig({});
|
|
5266
|
+
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5267
|
+
for (const item of skillsForSource) {
|
|
5268
|
+
const safeName = sanitizeMetadata(item.name);
|
|
5269
|
+
console.log(`${TEXT$7}正在更新 ${safeName}...${RESET$9}`);
|
|
5270
|
+
try {
|
|
5271
|
+
const parts = skillhubRef.split("/");
|
|
5272
|
+
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
5273
|
+
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
5274
|
+
await runAdd([ref], {
|
|
5275
|
+
global: false,
|
|
5276
|
+
yes: true
|
|
5277
|
+
});
|
|
5278
|
+
successCount++;
|
|
5279
|
+
} catch (error) {
|
|
5280
|
+
console.log(` ${DIM$9}✗ ${safeName}: ${error.message}${RESET$9}`);
|
|
5281
|
+
failCount++;
|
|
5282
|
+
}
|
|
5283
|
+
}
|
|
5284
|
+
continue;
|
|
5285
|
+
}
|
|
5223
5286
|
const allLockedForSource = Object.entries(localLock.skills).filter(([_, entry]) => (entry.sourceUrl || entry.source) === source).map(([name, _]) => name);
|
|
5224
5287
|
let tempDir = null;
|
|
5225
5288
|
let deletedSkills = [];
|
|
@@ -6578,6 +6641,7 @@ function truncate(text, maxLen) {
|
|
|
6578
6641
|
if (text.length <= maxLen) return text;
|
|
6579
6642
|
return text.slice(0, maxLen) + "...";
|
|
6580
6643
|
}
|
|
6644
|
+
var package_exports = /* @__PURE__ */ __exportAll({ runPackage: () => runPackage });
|
|
6581
6645
|
const RESET$3 = "\x1B[0m";
|
|
6582
6646
|
const BOLD$1 = "\x1B[1m";
|
|
6583
6647
|
const DIM$3 = "\x1B[38;5;102m";
|
|
@@ -6997,9 +7061,24 @@ const GREEN = "\x1B[32m";
|
|
|
6997
7061
|
const YELLOW = "\x1B[33m";
|
|
6998
7062
|
async function runInstall(args, globalOpts) {
|
|
6999
7063
|
const yes = args.includes("--yes") || args.includes("-y");
|
|
7000
|
-
const
|
|
7001
|
-
const
|
|
7002
|
-
|
|
7064
|
+
const workdir = globalOpts.workdir || process.cwd();
|
|
7065
|
+
const lock = loadLockfile(workdir);
|
|
7066
|
+
try {
|
|
7067
|
+
const skillsLock = await readLocalLock(workdir);
|
|
7068
|
+
for (const [name, entry] of Object.entries(skillsLock.skills)) if (!lock.skills[name]) lock.skills[name] = {
|
|
7069
|
+
...entry,
|
|
7070
|
+
namespace: "global",
|
|
7071
|
+
slug: name
|
|
7072
|
+
};
|
|
7073
|
+
} catch {}
|
|
7074
|
+
const filterArg = args[0];
|
|
7075
|
+
let skillEntries = Object.entries(lock.skills);
|
|
7076
|
+
let packageEntries = Object.entries(lock.packages);
|
|
7077
|
+
if (filterArg) {
|
|
7078
|
+
const slug = filterArg.replace(/^@[\w-]+\//, "").replace(/^[\w-]+--/, "");
|
|
7079
|
+
skillEntries = skillEntries.filter(([name]) => name === slug || name.includes(slug));
|
|
7080
|
+
packageEntries = packageEntries.filter(([ref]) => ref.includes(slug));
|
|
7081
|
+
}
|
|
7003
7082
|
if (skillEntries.length === 0 && packageEntries.length === 0) {
|
|
7004
7083
|
console.log(`${DIM$1}锁文件 (.bfi/lock.json) 中未找到技能或包。${RESET$1}`);
|
|
7005
7084
|
console.log(`${DIM$1}请先运行 'bfi skills add <source>' 安装技能。${RESET$1}`);
|
|
@@ -7014,39 +7093,44 @@ async function runInstall(args, globalOpts) {
|
|
|
7014
7093
|
let failedCount = 0;
|
|
7015
7094
|
for (const [skillName, entry] of skillEntries) {
|
|
7016
7095
|
const skillEntry = entry;
|
|
7017
|
-
const sourceType = String(skillEntry.sourceType || "");
|
|
7096
|
+
const sourceType = String(skillEntry.sourceType || skillEntry.source || "");
|
|
7018
7097
|
const namespace = String(skillEntry.namespace || "global");
|
|
7019
7098
|
const slug = String(skillEntry.slug || skillName);
|
|
7020
7099
|
const source = String(skillEntry.source || "");
|
|
7021
|
-
String(skillEntry.version || "latest");
|
|
7022
7100
|
let installSource;
|
|
7023
|
-
if (sourceType === "skillhub"
|
|
7024
|
-
else if (
|
|
7101
|
+
if (sourceType === "skillhub") installSource = `@${namespace}/${slug}`;
|
|
7102
|
+
else if (sourceType === "github" || sourceType === "gitlab") installSource = source;
|
|
7103
|
+
else if (source && (source.startsWith("http://") || source.startsWith("https://") || source.startsWith("git@"))) installSource = source;
|
|
7025
7104
|
else {
|
|
7026
|
-
console.log(` ${YELLOW}跳过 ${skillName}
|
|
7105
|
+
console.log(` ${YELLOW}跳过 ${skillName}:未知源类型 (${sourceType})${RESET$1}`);
|
|
7027
7106
|
continue;
|
|
7028
7107
|
}
|
|
7029
|
-
console.log(` ${TEXT$1}
|
|
7108
|
+
console.log(` ${TEXT$1}正在安装 ${installSource}...${RESET$1}`);
|
|
7030
7109
|
try {
|
|
7031
7110
|
await runAdd([installSource], { yes });
|
|
7032
7111
|
installedCount++;
|
|
7033
7112
|
console.log(` ${GREEN}✓ ${skillName}${RESET$1}`);
|
|
7034
7113
|
} catch (error) {
|
|
7035
7114
|
failedCount++;
|
|
7036
|
-
console.error(` ${YELLOW}
|
|
7115
|
+
console.error(` ${YELLOW}✗ ${error.message}${RESET$1}`);
|
|
7037
7116
|
}
|
|
7038
7117
|
}
|
|
7039
7118
|
for (const [pkgRef, entry] of packageEntries) {
|
|
7040
7119
|
const pkgEntry = entry;
|
|
7041
|
-
const
|
|
7042
|
-
|
|
7120
|
+
const namespace = String(pkgEntry.namespace || "global");
|
|
7121
|
+
const slug = String(pkgEntry.slug || pkgRef);
|
|
7122
|
+
console.log(` ${TEXT$1}正在安装包 @${namespace}/${slug}...${RESET$1}`);
|
|
7043
7123
|
try {
|
|
7044
|
-
|
|
7124
|
+
const { runPackage } = await Promise.resolve().then(() => package_exports);
|
|
7125
|
+
await runPackage(["install", `@${namespace}/${slug}`], {
|
|
7126
|
+
...globalOpts,
|
|
7127
|
+
yes
|
|
7128
|
+
});
|
|
7045
7129
|
installedCount++;
|
|
7046
7130
|
console.log(` ${GREEN}✓ ${pkgRef}${RESET$1}`);
|
|
7047
7131
|
} catch (error) {
|
|
7048
7132
|
failedCount++;
|
|
7049
|
-
console.error(` ${YELLOW}
|
|
7133
|
+
console.error(` ${YELLOW}✗ ${error.message}${RESET$1}`);
|
|
7050
7134
|
}
|
|
7051
7135
|
}
|
|
7052
7136
|
console.log();
|
package/package.json
CHANGED
|
@@ -7,24 +7,18 @@ description: 使用 bfi CLI 在 SkillHub 上搜索、安装、发布和管理技
|
|
|
7
7
|
|
|
8
8
|
bfi 是 SkillHub 生态的统一命令行工具。融合了开源 agent skills 生态能力和 SkillHub 专有功能。
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## 第零步:检查 bfi CLI 是否安装
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
静默运行 `bfi --version`。如果返回错误或未找到命令:
|
|
12
|
+
静默运行 `bfi --version`。如果未找到:
|
|
15
13
|
|
|
16
14
|
```
|
|
17
15
|
需要安装 bfi CLI 工具。正在安装:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
16
|
npm install -g bfi-cli
|
|
21
17
|
```
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
## 第一步:检查登录状态
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
静默运行 `bfi whoami`。如果返回错误或未认证:
|
|
21
|
+
静默运行 `bfi whoami`。如果未认证:
|
|
28
22
|
|
|
29
23
|
```
|
|
30
24
|
你需要登录 SkillHub 才能继续。请提供你的 API Token。
|
|
@@ -34,154 +28,94 @@ npm install -g bfi-cli
|
|
|
34
28
|
请直接回复你的 Token,我会自动完成登录。
|
|
35
29
|
```
|
|
36
30
|
|
|
37
|
-
用户提供 Token
|
|
38
|
-
```bash
|
|
39
|
-
bfi auth login --token <用户提供的Token>
|
|
40
|
-
```
|
|
31
|
+
用户提供 Token 后,立即执行 `bfi auth login --token <Token>`。登录成功后继续用户原请求。
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
## ⚠️ 关键区分:使用 vs 管理
|
|
43
34
|
|
|
44
|
-
|
|
35
|
+
- **使用**:只读取 SkillHub 数据或操作本地文件。包括:搜索、查看、安装、卸载、更新、列表、日志。
|
|
36
|
+
- **管理**:会修改 SkillHub 服务端数据,需要写权限。包括:发布技能、创建包、添加/移除包内技能。
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### 场景 1:使用技能(搜索、安装、使用、查看)
|
|
38
|
+
---
|
|
49
39
|
|
|
50
|
-
|
|
40
|
+
## 场景 1:使用技能
|
|
51
41
|
|
|
52
|
-
|
|
42
|
+
### 1.1 搜索
|
|
53
43
|
```bash
|
|
54
|
-
bfi skills search <关键词>
|
|
55
|
-
bfi skills search <关键词> --marketplace #
|
|
44
|
+
bfi skills search <关键词>
|
|
45
|
+
bfi skills search <关键词> --marketplace # skills.sh 公开市场
|
|
56
46
|
```
|
|
57
47
|
|
|
58
|
-
|
|
48
|
+
### 1.2 查看详情
|
|
59
49
|
```bash
|
|
60
50
|
bfi skills info <@namespace/slug 或 namespace--slug>
|
|
61
51
|
```
|
|
62
52
|
|
|
63
|
-
|
|
53
|
+
### 1.3 安装
|
|
64
54
|
```bash
|
|
65
|
-
# SkillHub
|
|
66
|
-
bfi skills add
|
|
67
|
-
bfi skills add
|
|
68
|
-
|
|
69
|
-
# Git 仓库(默认 git.yyrd.com)
|
|
70
|
-
bfi skills add owner/repo --skill <name> # 指定仓库中的某个技能
|
|
71
|
-
bfi skills add owner/repo # 安装全部技能
|
|
72
|
-
bfi skills add owner/repo --branch <分支> # 指定分支
|
|
73
|
-
|
|
74
|
-
# GitHub
|
|
75
|
-
bfi skills add github:owner/repo # 强制走 github.com
|
|
76
|
-
|
|
77
|
-
# 安装选项
|
|
78
|
-
bfi skills add <来源> -y # 跳过交互确认
|
|
79
|
-
bfi skills add <来源> --list # 仅列出技能,不安装
|
|
55
|
+
bfi skills add @命名空间/技能名 # SkillHub
|
|
56
|
+
bfi skills add owner/repo --skill <name> # Git 仓库(默认 git.yyrd.com)
|
|
57
|
+
bfi skills add github:owner/repo # GitHub
|
|
58
|
+
bfi skills add <来源> -y # 跳过确认
|
|
80
59
|
```
|
|
81
60
|
|
|
82
|
-
|
|
61
|
+
### 1.4 安装技能包
|
|
83
62
|
```bash
|
|
84
|
-
bfi skills package install @namespace/
|
|
63
|
+
bfi skills package install @namespace/pkg
|
|
85
64
|
```
|
|
86
65
|
|
|
87
|
-
|
|
66
|
+
### 1.5 查看已安装
|
|
88
67
|
```bash
|
|
89
68
|
bfi skills list
|
|
90
|
-
bfi skills ls -g #
|
|
69
|
+
bfi skills ls -g # 全局
|
|
91
70
|
```
|
|
92
71
|
|
|
93
|
-
|
|
72
|
+
### 1.6 卸载
|
|
94
73
|
```bash
|
|
95
|
-
bfi skills
|
|
74
|
+
bfi skills remove <技能名> -y
|
|
96
75
|
```
|
|
97
76
|
|
|
98
|
-
###
|
|
99
|
-
|
|
100
|
-
用户想发布技能、管理包、更新/卸载技能。
|
|
101
|
-
|
|
102
|
-
**2.1 发布技能**
|
|
77
|
+
### 1.7 更新
|
|
103
78
|
```bash
|
|
104
|
-
|
|
105
|
-
bfi skills
|
|
106
|
-
|
|
107
|
-
# 从本地目录发布
|
|
108
|
-
bfi skills publish <路径> --namespace <ns>
|
|
109
|
-
|
|
110
|
-
# 从 ZIP 发布
|
|
111
|
-
bfi skills publish <路径> --from zip --namespace <ns>
|
|
112
|
-
|
|
113
|
-
# 选项
|
|
114
|
-
# --visibility public 公开发布(默认)
|
|
115
|
-
# --branch <分支> 指定分支(默认 main)
|
|
116
|
-
# --skill <name> 只发布指定技能
|
|
117
|
-
# -y 跳过确认
|
|
79
|
+
bfi skills update
|
|
80
|
+
bfi skills update <技能名>
|
|
118
81
|
```
|
|
119
82
|
|
|
120
|
-
|
|
83
|
+
### 1.8 记录调用
|
|
121
84
|
```bash
|
|
122
|
-
bfi skills
|
|
85
|
+
bfi skills log <技能名> "简要描述"
|
|
123
86
|
```
|
|
124
87
|
|
|
125
|
-
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 场景 2:管理技能
|
|
91
|
+
|
|
92
|
+
### 2.1 发布
|
|
126
93
|
```bash
|
|
127
|
-
bfi skills
|
|
128
|
-
bfi skills
|
|
129
|
-
#
|
|
130
|
-
# SkillHub 来源的技能请用 bfi skills add 重新安装
|
|
94
|
+
bfi skills publish owner/repo --from git --namespace <ns>
|
|
95
|
+
bfi skills publish <路径> --namespace <ns>
|
|
96
|
+
# 选项:--branch <分支>、--skill <name>、--visibility public(默认)、-y
|
|
131
97
|
```
|
|
132
98
|
|
|
133
|
-
|
|
99
|
+
### 2.2 技能包管理(服务端)
|
|
134
100
|
```bash
|
|
135
|
-
# 创建包
|
|
136
101
|
bfi skills package create <slug> --namespace <ns>
|
|
137
|
-
|
|
138
|
-
# 添加技能到包
|
|
139
102
|
bfi skills package add-skills @ns/pkg <skill...>
|
|
140
|
-
|
|
141
|
-
# 从包移除技能
|
|
142
103
|
bfi skills package remove-skills @ns/pkg <skill...>
|
|
143
|
-
|
|
144
|
-
# 安装包
|
|
145
|
-
bfi skills package install @ns/pkg
|
|
146
|
-
|
|
147
|
-
# 卸载包
|
|
148
|
-
bfi skills package uninstall @ns/pkg
|
|
149
|
-
|
|
150
|
-
# 查看已安装的包
|
|
151
|
-
bfi skills package list
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### 场景 3:记录技能调用
|
|
155
|
-
|
|
156
|
-
**每次使用技能时必须记录:**
|
|
157
|
-
```bash
|
|
158
|
-
bfi skills log <技能名> "简要描述"
|
|
159
104
|
```
|
|
160
105
|
|
|
161
106
|
## 技能引用格式
|
|
162
107
|
|
|
163
|
-
| 格式 |
|
|
164
|
-
|
|
165
|
-
| `@ns/slug` |
|
|
166
|
-
| `ns--slug` |
|
|
167
|
-
| `owner/repo` |
|
|
168
|
-
| `github:owner/repo` |
|
|
108
|
+
| 格式 | 说明 |
|
|
109
|
+
|------|------|
|
|
110
|
+
| `@ns/slug` | SkillHub 技能 |
|
|
111
|
+
| `ns--slug` | 兼容格式 |
|
|
112
|
+
| `owner/repo` | Git 仓库(默认 git.yyrd.com) |
|
|
113
|
+
| `github:owner/repo` | 强制 GitHub |
|
|
169
114
|
|
|
170
115
|
## 配置
|
|
171
116
|
|
|
172
117
|
```bash
|
|
173
|
-
bfi config get
|
|
174
|
-
bfi config set registry <url> --global
|
|
175
|
-
bfi config set namespace <ns>
|
|
118
|
+
bfi config get
|
|
119
|
+
bfi config set registry <url> --global
|
|
120
|
+
bfi config set namespace <ns>
|
|
176
121
|
```
|
|
177
|
-
|
|
178
|
-
## 常见问题
|
|
179
|
-
|
|
180
|
-
**Q: 公司 GitLab 仓库中的技能找不到?**
|
|
181
|
-
A: 默认从 git.yyrd.com 克隆。如果是 GitHub 上的技能,使用 `github:` 前缀。
|
|
182
|
-
|
|
183
|
-
**Q: 如何更新 SkillHub 上的技能?**
|
|
184
|
-
A: 用 `bfi skills publish` 重新发布即可,服务端自动版本管理。
|
|
185
|
-
|
|
186
|
-
**Q: 发布失败?**
|
|
187
|
-
A: 确保 SKILL.md 文件在发布目录或仓库中,且包含 name 和 description 字段。
|