@bams-app/work-cli 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,6 +104,29 @@ work add --dir demo-ui --name ui-base-btn --type base
104
104
  | `env` | 交互式创建开发环境配置 `.envs/.env.dev-<alias>`(复用 create-env) |
105
105
  | `pages-entry --dir <dir> --type <page\|component>` | 扫描 page-_/ui-_ 目录生成 webpack 动态入口(复用 create-pages-entry) |
106
106
  | `pages-from-components --dir <dir>` | 批量将 ui-_ 组件转为 page-_ 页面(复用 create-pages-from-components) |
107
+ | `skills sync` | 把已安装组件包内嵌的 `SKILL.md` 同步到项目根 `.skills/`(与 AI 工具无关) |
108
+
109
+ ### 组件技能分发
110
+
111
+ 组件包(`@bams-app/ui-*`、`@bams-app/permission`、`@bams-app/theme` 等)各自内嵌一份 `SKILL.md`,
112
+ 随 npm 包一起发布,是技能内容的**唯一真源**。
113
+
114
+ ```bash
115
+ # 项目安装依赖后(模板的 postinstall 已自动执行一次),也可手动同步
116
+ work skills sync # 同步到 <项目根>/.skills/<skill-name>/{SKILL.md,references/}
117
+ work skills sync --dry-run # 只列出将要同步的技能
118
+ work skills sync --force # 内容一致时也重新复制
119
+ ```
120
+
121
+ `.skills/` 采用标准 Agent Skills 目录结构,**不绑定任何 AI 工具**,脚本不会写入 `.trae/skills/`、
122
+ `.claude/skills/`、`.cursor/rules/` 等任何工具私有目录。使用者按自己用的工具自行安装,
123
+ 例如 Trae 与 Claude Code 属于同一套规范,直接复制目录即可,其他工具让各自的 AI 现场转换格式。
124
+
125
+ `work skills sync` 是幂等的:内容一致则跳过。修改技能时请改**组件包内的 `SKILL.md`**,
126
+ 再重跑同步,避免 `.skills/` 与包内两份内容漂移。
127
+
128
+ 命令只在明确的项目根下工作:优先用 `--root <dir>`,否则向上查找 `work create` 生成的 `.bams-work`
129
+ 标记目录,**找不到即中止报错**(不会兜底到当前目录),以免在错误位置意外生成 `.skills/`。
107
130
 
108
131
  ### 组件目录规范
109
132
 
package/bin/work-cli.js CHANGED
@@ -11,7 +11,8 @@ const COMMANDS = {
11
11
  add: '在 scope 目录下创建组件(component/page/base)',
12
12
  env: '创建开发环境配置(.envs/.env.dev-<alias>)',
13
13
  'pages-entry': '扫描 page-*/ui-* 目录生成 webpack 动态入口',
14
- 'pages-from-components': '批量将 ui-* 组件转为 page-* 页面'
14
+ 'pages-from-components': '批量将 ui-* 组件转为 page-* 页面',
15
+ skills: '把组件包内嵌的 SKILL.md 同步到项目根 .skills/(中立目录,与 AI 工具无关)'
15
16
  };
16
17
 
17
18
  // 暂时下线的命令:commands/ 下的实现文件保留,恢复时删除此处对应项即可
@@ -37,6 +38,7 @@ function printUsage() {
37
38
  console.log(' work env # 创建开发环境配置(交互式)');
38
39
  console.log(' work pages-entry --dir energy-ui --type page');
39
40
  console.log(' work pages-from-components --dir energy-ui');
41
+ console.log(' work skills sync # 把组件包内嵌技能同步到 .skills/');
40
42
  console.log('');
41
43
  console.log(colorize('开发调试:', 'bold'));
42
44
  console.log(' 在项目根目录执行 npm run dev:ui <环境标识>(由项目本地 ui-dev-server 启动并加载 .envs/ 环境文件)');
@@ -0,0 +1,226 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { findProjectRoot, isDirectory, error, warn, success, info, colorize } = require('../lib/utils');
4
+
5
+ /**
6
+ * 用法: work skills sync [--root <dir>] [--dry-run] [--force] [--quiet]
7
+ *
8
+ * 把已安装的 @bams-app/* 组件包内嵌的 SKILL.md 同步到项目根的中立目录 .skills/。
9
+ * .skills/ 使用标准 Agent Skills 目录结构(<skill-name>/SKILL.md + references/),
10
+ * 不绑定任何 AI 工具,由使用者自行安装到各自的工具目录(.trae/skills、.claude/skills、
11
+ * .cursor/rules 等),因此本命令不写入任何工具私有目录。
12
+ */
13
+ function parseArgs(args) {
14
+ const params = {};
15
+ for (let i = 0; i < args.length; i++) {
16
+ const arg = args[i];
17
+ if (!arg.startsWith('--')) {
18
+ continue;
19
+ }
20
+ const key = arg.slice(2);
21
+ const next = args[i + 1];
22
+ if (next !== undefined && !next.startsWith('--')) {
23
+ params[key] = next;
24
+ i++;
25
+ } else {
26
+ params[key] = true;
27
+ }
28
+ }
29
+ return params;
30
+ }
31
+
32
+ /**
33
+ * 解析 SKILL.md frontmatter 的 name 字段
34
+ */
35
+ function readSkillName(skillFile) {
36
+ const content = fs.readFileSync(skillFile, 'utf8');
37
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
38
+ if (!match) {
39
+ return null;
40
+ }
41
+ const line = match[1].match(/^name:[ \t]*(.+)$/m);
42
+ if (!line) {
43
+ return null;
44
+ }
45
+ return line[1].trim().replace(/^["']|["']$/g, '');
46
+ }
47
+
48
+ /**
49
+ * 收集需要同步的文件(SKILL.md + references/ 下的全部文件),返回 [相对路径] 列表
50
+ */
51
+ function collectSkillFiles(packageDir) {
52
+ const files = ['SKILL.md'];
53
+ const referencesDir = path.join(packageDir, 'references');
54
+ if (isDirectory(referencesDir)) {
55
+ const walk = (dir) => {
56
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
57
+ const fullPath = path.join(dir, entry.name);
58
+ if (entry.isDirectory()) {
59
+ walk(fullPath);
60
+ } else {
61
+ files.push(path.relative(packageDir, fullPath));
62
+ }
63
+ }
64
+ };
65
+ walk(referencesDir);
66
+ }
67
+ return files;
68
+ }
69
+
70
+ function isUpToDate(packageDir, targetDir, files) {
71
+ return files.every((relative) => {
72
+ const src = path.join(packageDir, relative);
73
+ const dest = path.join(targetDir, relative);
74
+ if (!fs.existsSync(dest)) {
75
+ return false;
76
+ }
77
+ return fs.readFileSync(src, 'utf8') === fs.readFileSync(dest, 'utf8');
78
+ });
79
+ }
80
+
81
+ /**
82
+ * 扫描 @bams-app 作用域下的包,返回 [{ directory, dir }]
83
+ */
84
+ function scanInstalledPackages(projectRoot) {
85
+ const scopeDir = path.join(projectRoot, 'node_modules', '@bams-app');
86
+ if (!isDirectory(scopeDir)) {
87
+ return null;
88
+ }
89
+ const result = [];
90
+ for (const entry of fs.readdirSync(scopeDir, { withFileTypes: true })) {
91
+ // workspace 场景下 @bams-app/* 是软链,Dirent.isDirectory() 为 false,需用 stat 跟随软链判断
92
+ const packageDir = path.join(scopeDir, entry.name);
93
+ if (!isDirectory(packageDir)) {
94
+ continue;
95
+ }
96
+ if (fs.existsSync(path.join(packageDir, 'SKILL.md'))) {
97
+ result.push({ directory: entry.name, dir: packageDir });
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+
103
+ function syncSkills(args) {
104
+ const params = parseArgs(args);
105
+ const dryRun = params['dry-run'] === true;
106
+ const force = params.force === true;
107
+ const quiet = params.quiet === true;
108
+
109
+ // 只在明确的项目根下工作:--root 显式指定,或用 work create 生成的 .bams-work 标记定位。
110
+ // 不做 cwd 兜底,避免在任意目录意外生成 .skills/
111
+ const projectRoot = params.root ? path.resolve(params.root) : findProjectRoot();
112
+ if (!projectRoot) {
113
+ error('未找到项目根目录(向上未发现 .bams-work 标记),请用 --root <dir> 显式指定');
114
+ return 1;
115
+ }
116
+ if (!isDirectory(projectRoot)) {
117
+ error(`项目根目录不存在: ${projectRoot}`);
118
+ return 1;
119
+ }
120
+
121
+ info(`项目根目录: ${projectRoot}`);
122
+
123
+ const packages = scanInstalledPackages(projectRoot);
124
+ if (packages === null) {
125
+ warn(`未找到 ${path.join(projectRoot, 'node_modules', '@bams-app')},请先执行 npm install`);
126
+ return 0;
127
+ }
128
+ if (packages.length === 0) {
129
+ warn('node_modules/@bams-app 下没有任何内嵌 SKILL.md 的包,无需同步');
130
+ return 0;
131
+ }
132
+
133
+ const skillsRoot = path.join(projectRoot, '.skills');
134
+ const summary = { synced: 0, skipped: 0, failed: 0 };
135
+
136
+ for (const pkg of packages) {
137
+ let skillName;
138
+ try {
139
+ skillName = readSkillName(path.join(pkg.dir, 'SKILL.md'));
140
+ } catch (err) {
141
+ error(`读取 ${pkg.directory}/SKILL.md 失败: ${err.message}`);
142
+ summary.failed++;
143
+ continue;
144
+ }
145
+ if (!skillName) {
146
+ warn(`${pkg.directory}/SKILL.md 缺少 frontmatter name,已跳过(skill 名需与输出目录名一致)`);
147
+ summary.failed++;
148
+ continue;
149
+ }
150
+
151
+ const files = collectSkillFiles(pkg.dir);
152
+ const targetDir = path.join(skillsRoot, skillName);
153
+
154
+ if (!force && isUpToDate(pkg.dir, targetDir, files)) {
155
+ summary.skipped++;
156
+ if (!quiet) {
157
+ info(`已是最新: ${skillName}`);
158
+ }
159
+ continue;
160
+ }
161
+
162
+ if (dryRun) {
163
+ summary.synced++;
164
+ console.log(` ${colorize('待同步', 'yellow')} ${skillName} <- ${pkg.directory}(${files.length} 个文件)`);
165
+ continue;
166
+ }
167
+
168
+ try {
169
+ for (const relative of files) {
170
+ const dest = path.join(targetDir, relative);
171
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
172
+ fs.copyFileSync(path.join(pkg.dir, relative), dest);
173
+ }
174
+ summary.synced++;
175
+ if (!quiet) {
176
+ success(`已同步: ${skillName}`);
177
+ }
178
+ } catch (err) {
179
+ error(`同步 ${skillName} 失败: ${err.message}`);
180
+ summary.failed++;
181
+ }
182
+ }
183
+
184
+ console.log('');
185
+ const parts = [`同步 ${summary.synced} 个`, `已是最新 ${summary.skipped} 个`];
186
+ if (summary.failed > 0) {
187
+ parts.push(`失败 ${summary.failed} 个`);
188
+ }
189
+ if (dryRun) {
190
+ console.log('(演练模式,未写入文件)');
191
+ }
192
+ console.log(`技能目录: ${skillsRoot}`);
193
+ console.log(parts.join(' / '));
194
+ return 0;
195
+ }
196
+
197
+ /**
198
+ * 用法: work skills sync
199
+ */
200
+ module.exports = async function skills(args) {
201
+ const [subCommand, ...rest] = args;
202
+
203
+ if (!subCommand || subCommand === '--help' || subCommand === '-h' || subCommand === 'help') {
204
+ console.log('');
205
+ console.log(colorize('用法:', 'bold'));
206
+ console.log(' work skills sync [--root <dir>] [--dry-run] [--force] [--quiet]');
207
+ console.log('');
208
+ console.log('作用: 把已安装的 @bams-app/* 组件包内嵌的 SKILL.md 同步到项目根 .skills/');
209
+ console.log(' 该目录是标准 Agent Skills 结构,与具体 AI 工具无关,由使用者自行安装');
210
+ console.log('');
211
+ console.log(colorize('参数:', 'bold'));
212
+ console.log(' --root <dir> 指定项目根目录(默认向上查找 .bams-work 标记目录,找不到则中止)');
213
+ console.log(' --dry-run 只列出将要同步的技能,不写入文件');
214
+ console.log(' --force 内容一致时也重新复制');
215
+ console.log(' --quiet 只输出汇总,不逐个打印');
216
+ console.log('');
217
+ return 0;
218
+ }
219
+
220
+ if (subCommand !== 'sync') {
221
+ error(`未知子命令: ${subCommand}(可选: sync)`);
222
+ return 1;
223
+ }
224
+
225
+ return syncSkills(rest);
226
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bams-app/work-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "BAMS-Work 全局开发工具:复用 work 已发布的包(create-env / create-ui-*)提供项目初始化、组件创建与环境配置能力,开发调试由用户项目本地的 ui-dev-server 承担",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -8,14 +8,16 @@
8
8
  package.json # 项目根配置
9
9
  .bams-work/ # 项目标记目录,勿删
10
10
  .envs/ # 环境配置 .env.dev-<env>
11
+ .skills/ # 组件技能说明(npm install 自动同步,见下文)
11
12
  .proxy.js # 自定义代理(可选)
12
13
  demo-ui/ui-component-demo # 示例组件
13
- <scope>-ui/ui-xxx # 业务组件(add 时创建)
14
+ <scope>-ui/ui-xxx # 业务组件(npm run add:component 时创建)
14
15
  ```
15
16
 
16
17
  ## 前置条件(必需)
17
18
 
18
- 必须先全局安装 CLI,否则 `work` 命令(`work add` / `work env` / `work pages-entry`)不可用:
19
+ 项目内所有能力都以 npm script 形式提供,底层调用全局 CLI `@bams-app/work-cli`,
20
+ 因此必须先全局安装(否则 `npm run add:component` 等会提示 command not found):
19
21
 
20
22
  ```bash
21
23
  npm i -g @bams-app/work-cli
@@ -40,13 +42,14 @@ npm run dev:ui demo -- --port 8080 # `--` 之后透传给 vue-cli-servi
40
42
 
41
43
  ## 命令
42
44
 
43
- | 命令 | 作用 |
44
- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
45
- | `npm run dev:ui <环境标识> [组件名]` | 启动开发服务 |
46
- | `work add --dir <scope> --name <name> [--type component\|page\|base] [--desc ...]` | 新建组件/页面/基础组件;scope 目录不存在会自动创建并注册到根 `package.json` |
47
- | `work env` | 交互式生成 `.envs/.env.dev-<标识>` |
48
- | `work pages-entry --dir <scope> --type page\|component` | 生成构建用动态入口(内部使用) |
49
- | `work pages-from-components --dir <scope>` | 把 `ui-*` 批量复制为 `page-*` |
45
+ | 命令 | 作用 |
46
+ | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
47
+ | `npm run dev:ui <环境标识> [组件名]` | 启动开发服务 |
48
+ | `npm run add:component -- --dir <scope> --name <name> [--type component\|page\|base] [--desc ...]` | 新建组件/页面/基础组件;scope 目录不存在会自动创建并注册到根 `package.json` |
49
+ | `npm run create:env` | 交互式生成 `.envs/.env.dev-<标识>` |
50
+ | `npm run sync:skills` | 把已安装组件包内嵌的技能同步到 `.skills/`(见下文) |
51
+
52
+ `--` 之后的参数会透传给底层 CLI。
50
53
 
51
54
  环境文件字段:`PORT`、`PROXY_TARGET`、`BASE_URL`、`VUE_APP_*` 等,见模板文件;改完重跑 `dev:ui` 生效。
52
55
 
@@ -67,3 +70,31 @@ npm i @bams-app/ui-dev-server@latest
67
70
 
68
71
  - 代理:项目根 `.proxy.js`(参考 `.proxy.js.example`),声明的前缀优先于内置默认,修改后需重启
69
72
  - webpack:项目根 `vue.config.extend.js`,导出对象或 `({ env, projectRoot }) => 对象`
73
+
74
+ ## 安装其他组件包
75
+
76
+ 业务组件包以 git 仓库形式提供,clone 到项目根目录后执行 `npm i` 即可,不需要修改根 `package.json`:
77
+
78
+ ```bash
79
+ cd <项目根目录>
80
+ git clone https://git.cnbmtech.com/CNBM-CIMS/products/bams-apex/apex-web.git
81
+ npm i
82
+ ```
83
+
84
+ 根 `package.json` 的 `workspaces` 已声明 `demo-ui/*`、`bams-components/*`、`*-ui/*`、`apex-web/*`、`bams-ui`,
85
+ clone 后的目录落在这些范围内即可被自动识别,随后 `npm run dev:ui demo ui-order-list` 就能加载其中的组件。
86
+
87
+ 后续更新:进对应仓库目录 `git pull`,再回项目根目录执行 `npm i`。
88
+
89
+ ## 组件技能(AI Skill)
90
+
91
+ 每个组件包内嵌了一份 `SKILL.md` 技能说明,`npm install` 时已自动同步到项目根 `.skills/`,也可手动刷新:
92
+
93
+ ```bash
94
+ npm run sync:skills # 幂等:内容一致自动跳过
95
+ npm run sync:skills -- --dry-run # 只看会同步哪些,不写文件
96
+ ```
97
+
98
+ `.skills/` 是标准 Agent Skills 目录(`<skill-name>/SKILL.md`,可带 `references/`),**不绑定任何 AI 工具**:把需要的技能目录复制到你所用工具的规则目录即可,例如 Trae / Claude Code 的 `.trae/skills`、`.claude/skills`;其他工具按其格式转换。升级组件包后重跑一次 `npm run sync:skills` 即刷新内容。
99
+
100
+ `.skills/` 建议提交到仓库,团队共享同一份技能说明。
@@ -12,7 +12,10 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "dev:ui": "ui-dev-server",
15
- "add": "work add"
15
+ "add:component": "work add",
16
+ "create:env": "work env",
17
+ "sync:skills": "work skills sync",
18
+ "postinstall": "work skills sync --quiet || exit 0"
16
19
  },
17
20
  "dependencies": {
18
21
  "@bams-app/bams-components": "*"