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 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) => f.toLowerCase() === lower);
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
- console.log(` ${DIM$9}SkillHub 技能请使用 bfi skills add 重新安装来更新${RESET$9}`);
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 lock = loadLockfile(globalOpts.workdir || process.cwd());
7001
- const skillEntries = Object.entries(lock.skills);
7002
- const packageEntries = Object.entries(lock.packages);
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" || namespace !== "global") installSource = `@${namespace}/${slug}`;
7024
- else if (source) installSource = source;
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}:未知源类型${RESET$1}`);
7105
+ console.log(` ${YELLOW}跳过 ${skillName}:未知源类型 (${sourceType})${RESET$1}`);
7027
7106
  continue;
7028
7107
  }
7029
- console.log(` ${TEXT$1}正在从 ${installSource} 安装 ${skillName}...${RESET$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}失败:${error.message}${RESET$1}`);
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 installSource = `@${String(pkgEntry.namespace || "global")}/${String(pkgEntry.slug || pkgRef)}`;
7042
- console.log(` ${TEXT$1}正在从 ${installSource} 安装包 ${pkgRef}...${RESET$1}`);
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
- await runAdd([installSource], { yes });
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}失败:${error.message}${RESET$1}`);
7133
+ console.error(` ${YELLOW}✗ ${error.message}${RESET$1}`);
7050
7134
  }
7051
7135
  }
7052
7136
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bfi-cli",
3
- "version": "0.1.36",
3
+ "version": "0.1.39",
4
4
  "description": "用友大业财BFI统一命令行工具,支持技能,技能包,元数据,UI元数据等",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,24 +7,18 @@ description: 使用 bfi CLI 在 SkillHub 上搜索、安装、发布和管理技
7
7
 
8
8
  bfi 是 SkillHub 生态的统一命令行工具。融合了开源 agent skills 生态能力和 SkillHub 专有功能。
9
9
 
10
- ## ⚠️ 第零步:检查 bfi CLI 是否安装(最早执行)
10
+ ## 第零步:检查 bfi CLI 是否安装
11
11
 
12
- **在执行任何 SkillHub 操作之前,必须先确认 bfi CLI 已安装。**
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
- 登录成功后,继续执行用户原本请求的操作,无需用户再次输入。如果登录失败,告知用户 Token 无效,请重新提供。
33
+ ## ⚠️ 关键区分:使用 vs 管理
43
34
 
44
- ## 判断场景
35
+ - **使用**:只读取 SkillHub 数据或操作本地文件。包括:搜索、查看、安装、卸载、更新、列表、日志。
36
+ - **管理**:会修改 SkillHub 服务端数据,需要写权限。包括:发布技能、创建包、添加/移除包内技能。
45
37
 
46
- 根据用户意图选择对应场景:
47
-
48
- ### 场景 1:使用技能(搜索、安装、使用、查看)
38
+ ---
49
39
 
50
- 用户想找技能、安装技能、试用技能、查看已安装的技能。
40
+ ## 场景 1:使用技能
51
41
 
52
- **1.1 搜索技能**
42
+ ### 1.1 搜索
53
43
  ```bash
54
- bfi skills search <关键词> # 搜索 SkillHub(默认)
55
- bfi skills search <关键词> --marketplace # 搜索 skills.sh 公开市场
44
+ bfi skills search <关键词>
45
+ bfi skills search <关键词> --marketplace # skills.sh 公开市场
56
46
  ```
57
47
 
58
- **1.2 查看技能详情**
48
+ ### 1.2 查看详情
59
49
  ```bash
60
50
  bfi skills info <@namespace/slug 或 namespace--slug>
61
51
  ```
62
52
 
63
- **1.3 安装技能**
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
- **1.4 安装技能包**
61
+ ### 1.4 安装技能包
83
62
  ```bash
84
- bfi skills package install @namespace/package-name
63
+ bfi skills package install @namespace/pkg
85
64
  ```
86
65
 
87
- **1.5 查看已安装**
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
- **1.6 使用技能(不安装)**
72
+ ### 1.6 卸载
94
73
  ```bash
95
- bfi skills use <来源> # 生成 prompt 文本
74
+ bfi skills remove <技能名> -y
96
75
  ```
97
76
 
98
- ### 场景 2:管理技能(发布、更新、卸载、包管理)
99
-
100
- 用户想发布技能、管理包、更新/卸载技能。
101
-
102
- **2.1 发布技能**
77
+ ### 1.7 更新
103
78
  ```bash
104
- # 从 Git 发布(推荐,自动发现所有技能)
105
- bfi skills publish owner/repo --from git --namespace <ns>
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
- **2.2 卸载技能**
83
+ ### 1.8 记录调用
121
84
  ```bash
122
- bfi skills remove <技能名> -y
85
+ bfi skills log <技能名> "简要描述"
123
86
  ```
124
87
 
125
- **2.3 更新技能**
88
+ ---
89
+
90
+ ## 场景 2:管理技能
91
+
92
+ ### 2.1 发布
126
93
  ```bash
127
- bfi skills update # 更新全部
128
- bfi skills update <技能名>
129
- # Git 来源的技能会检查远端更新
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
- **2.4 技能包管理**
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` | `@retail/skill-pm` | SkillHub 技能 |
166
- | `ns--slug` | `retail--skill-pm` | 兼容格式 |
167
- | `owner/repo` | `bfi/bfi-skill` | Git 仓库(默认 git.yyrd.com) |
168
- | `github:owner/repo` | `github:vercel-labs/agent-skills` | 强制 GitHub |
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 # 设置 API 地址
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 字段。