@routerhub/agent-rules 1.5.0 → 1.5.2

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/AGENTS.base.md CHANGED
@@ -7,6 +7,26 @@
7
7
  - 始终使用中文回答,代码注释使用中文,每行代码配中文注释。
8
8
  - 页面 UI 内容(按钮、字段名、提示等)全部英文;如需中文在 `AGENTS.private.md` 中声明。
9
9
 
10
+ ## Git 规范
11
+
12
+ - 分支用 Git Flow(`feature/`、`bugfix/`、`hotfix/`、`refactor/`、`chore/`、`docs/`、`test/`),英文小写中划线分隔。
13
+ - commit 信息必须中文,禁止 `git push --force`。
14
+
15
+ ## 安全
16
+
17
+ - 未明确授权不操作生产环境(指令需含"生产"/"线上"/"prod"/"production"),操作前二次确认。
18
+
19
+ ## 需求文档
20
+
21
+ - 新需求先在 `docs/` 写 MD 文档,含:目标、范围、功能要求、验收标准。
22
+
23
+ ## 优先级
24
+
25
+ 1. 项目私有规则(AGENTS.private.md)
26
+ 2. 基础规则(此文件)
27
+
28
+ <!-- @domain: frontend -->
29
+
10
30
  ## 文件约束
11
31
 
12
32
  - 不随意修改核心业务文件和 API 相关代码,不编写或修改 `README.md`。
@@ -43,36 +63,11 @@
43
63
  - API 请求严格使用 OpenAPI 生成的方法,禁止手写请求或直接拼接路径。
44
64
  - 接口变更后先更新 OpenAPI 定义并重新生成 API 代码,再进行业务开发。
45
65
 
46
- ## Go 语言规则
47
-
48
- - 优先值传递与局部变量生命周期控制,避免内存逃逸;无必要不返回局部变量指针。
49
- - 所有删除操作必须使用软删除机制;`DeletedAt` 字段类型必须用 `gorm.DeletedAt`,禁止用 `*time.Time`(后者不实现 `DeleteClausesInterface`,会导致硬删除)。
50
-
51
- ## GORM Model 规范
52
-
53
- - `DeletedAt` 字段必须使用 `gorm.DeletedAt` 类型,严禁使用 `*time.Time`。`*time.Time` 不会触发 GORM v2 的软删除机制,会导致 `Delete()` 执行物理删除(DELETE FROM)而非软删除(UPDATE SET deleted_at)。
54
- - 新建 Model 时,优先嵌入已有的公共基础结构体(如包含 ID、CreatedAt、UpdatedAt、DeletedAt 的 BaseModel),避免各 Model 独立定义这些字段导致类型不一致。
55
-
56
- ## Git 规范
57
-
58
- - 分支用 Git Flow(`feature/`、`bugfix/`、`hotfix/`、`refactor/`、`chore/`、`docs/`、`test/`),英文小写中划线分隔。
59
- - commit 信息必须中文,禁止 `git push --force`。
60
-
61
- ## 安全
62
-
63
- - 未明确授权不操作生产环境(指令需含"生产"/"线上"/"prod"/"production"),操作前二次确认。
64
-
65
66
  ## 依赖与构建
66
67
 
67
68
  - 依赖安装统一 `pnpm`,禁止 `npm install`/`yarn install`。
68
69
  - 写完代码先 `npm run format`,再 `npm run build`,构建通过才可发版。
69
70
 
70
- ## Docker 与部署
71
-
72
- - 默认本地 `docker build`,构建后 `docker push` 到 `ghcr.io`,推送前确保正确 tag。
73
- - 部署通过 Portainer 管理界面操作,禁止 SSH 手动执行 docker 命令;地址和账号配置在 `AGENTS.private.md` 或环境变量中,禁止硬编码。
74
- - 始终通过 Portainer API 更新 Stack 环境变量触发重新部署,不要 SSH 手动操作 Portainer 管理的容器。
75
-
76
71
  ## 测试
77
72
 
78
73
  - 测试描述、断言使用中文,覆盖中文场景;充值类测试默认用充值 1 的数据。
@@ -85,11 +80,23 @@
85
80
  - 用 `console.error` 记录错误(含函数名/模块名上下文),禁止 `console.log` 输出错误。
86
81
  - 提交前移除调试日志和临时代码。
87
82
 
88
- ## 需求文档
83
+ <!-- @domain: go-backend -->
89
84
 
90
- - 新需求先在 `docs/` 写 MD 文档,含:目标、范围、功能要求、验收标准。
85
+ ## Go 语言规则
91
86
 
92
- ## 优先级
87
+ - 优先值传递与局部变量生命周期控制,避免内存逃逸;无必要不返回局部变量指针。
88
+ - 所有删除操作必须使用软删除机制;`DeletedAt` 字段类型必须用 `gorm.DeletedAt`,禁止用 `*time.Time`(后者不实现 `DeleteClausesInterface`,会导致硬删除)。
93
89
 
94
- 1. 项目私有规则(AGENTS.private.md)
95
- 2. 基础规则(此文件)
90
+ ## GORM Model 规范
91
+
92
+ - `DeletedAt` 字段必须使用 `gorm.DeletedAt` 类型,严禁使用 `*time.Time`。`*time.Time` 不会触发 GORM v2 的软删除机制,会导致 `Delete()` 执行物理删除(DELETE FROM)而非软删除(UPDATE SET deleted_at)。
93
+ - 新建 Model 时,优先嵌入已有的公共基础结构体(如包含 ID、CreatedAt、UpdatedAt、DeletedAt 的 BaseModel),避免各 Model 独立定义这些字段导致类型不一致。
94
+
95
+ <!-- @domain: devops -->
96
+
97
+ ## Docker 与部署
98
+
99
+ - 默认本地 `docker build`,构建后 `docker push` 到 `ghcr.io`,推送前确保正确 tag。
100
+ - 部署时打镜像 tag 必须对项目内所有服务/镜像统一打 tag,无论该镜像是否有代码改动;确保所有镜像版本号一致后再发版。
101
+ - 部署通过 Portainer 管理界面操作,禁止 SSH 手动执行 docker 命令;地址和账号配置在 `AGENTS.private.md` 或环境变量中,禁止硬编码。
102
+ - 始终通过 Portainer API 更新 Stack 环境变量触发重新部署,不要 SSH 手动操作 Portainer 管理的容器。
@@ -1,52 +1,33 @@
1
- # 项目私有规则模板
1
+ # 项目私有规则
2
2
 
3
- 以下是项目特定规则的示例。请根据实际项目情况修改。
3
+ <!-- 说明:未标记域的内容归入全局规则,标记后的内容自动合并到对应域 -->
4
4
 
5
- ## 文件与目录约束
5
+ ## 全局规则
6
6
 
7
- - 不能修改 `src/apis/api` 下的任何内容
8
- - 不能修改 `src/core` 下的核心模块
7
+ <!-- 在此添加全局规则,例如:-->
8
+ <!-- - 测试环境 API:https://test-api.example.com -->
9
+ <!-- - PR 必须至少有 1 名审批者同意 -->
9
10
 
10
- ## 项目特定的连接配置
11
+ <!-- @domain: frontend -->
11
12
 
12
- - 测试环境 API:https://test-api.example.com
13
- - 生产环境 API:https://api.example.com
14
- - 内部工具地址:https://internal-tools.example.com
13
+ ## 前端规则
15
14
 
16
- ## 组件库路径
15
+ <!-- 在此添加前端规则,例如:-->
16
+ <!-- - 不能修改 `src/apis/api` 下的任何内容 -->
17
+ <!-- - UI 组件:`src/components/ui/` -->
17
18
 
18
- - UI 组件:`src/components/ui/`
19
- - 业务组件:`src/components/business/`
20
- - 表格组件使用:`TableBase` 或 `TablePricing`
21
- - 卡片组件使用:`CardBase`
19
+ <!-- @domain: go-backend -->
22
20
 
23
- ## 接口与数据
21
+ ## Go 后端规则
24
22
 
25
- - API 响应统一通过 `src/types/api.ts` 定义
26
- - 数据转换统一通过 `src/utils/transform.ts` 完成
27
- - 所有业务数据应经过验证,不信任 API 返回
23
+ <!-- 在此添加 Go 后端规则,例如:-->
24
+ <!-- - 配置文件路径:`config/` -->
25
+ <!-- - 接口返回统一使用 `response.Success()` / `response.Error()` -->
28
26
 
29
- ## 业务流程规范
27
+ <!-- @domain: devops -->
30
28
 
31
- - 支付流程必须经过风控检查
32
- - 用户认证使用统一的 `AuthContext`
33
- - 权限控制通过 `usePermission` hook 实现
29
+ ## 部署规则
34
30
 
35
- ## 禁止使用
36
-
37
- - 不允许直接操作 DOM(使用 React ref)
38
- - 不允许使用 localStorage 存储敏感信息
39
- - 不允许直接 fetch,必须通过 `apiClient` 工具
40
-
41
- ## 测试要求
42
-
43
- - 所有业务逻辑必须有单元测试
44
- - 页面级别测试必须包括主流程和错误流程
45
- - 不允许使用真实账户进行自动化测试
46
-
47
- ## 代码审批要求
48
-
49
- - PR 必须通过 ESLint 检查
50
- - PR 必须通过单元测试
51
- - PR 必须至少有 1 名代码审批者同意
52
- <!-- 测试 -->
31
+ <!-- 在此添加部署规则,例如:-->
32
+ <!-- - 镜像仓库:ghcr.io/your-org/your-project -->
33
+ <!-- - Portainer 地址配置在 `.env` 文件中 -->
package/merge.js CHANGED
@@ -152,6 +152,101 @@ function loadRules(rulesDir) {
152
152
  * 支持格式: <!-- @domain: go-backend -->
153
153
  * 未标记的内容归入全局
154
154
  */
155
+
156
+ // 域元数据配置:域名 → { name, applyTo, outputName, fileName }
157
+ const DOMAIN_META = {
158
+ frontend: {
159
+ name: "前端规则",
160
+ applyTo: [
161
+ "**/*.ts",
162
+ "**/*.tsx",
163
+ "**/*.js",
164
+ "**/*.jsx",
165
+ "**/*.vue",
166
+ "**/*.css",
167
+ "**/*.scss",
168
+ ],
169
+ outputName: "frontend",
170
+ fileName: "frontend.md",
171
+ },
172
+ "go-backend": {
173
+ name: "Go 后端规则",
174
+ applyTo: ["**/*.go"],
175
+ outputName: "go-backend",
176
+ fileName: "go-backend.md",
177
+ },
178
+ devops: {
179
+ name: "DevOps 部署规则",
180
+ applyTo: [
181
+ "**/Dockerfile",
182
+ "**/Dockerfile.*",
183
+ "**/docker-compose*.yml",
184
+ "**/docker-compose*.yaml",
185
+ "**/*.sh",
186
+ "**/.gitlab-ci.yml",
187
+ "**/.github/workflows/*.yml",
188
+ "**/.github/workflows/*.yaml",
189
+ ],
190
+ outputName: "devops",
191
+ fileName: "devops.md",
192
+ },
193
+ };
194
+
195
+ /**
196
+ * 从 AGENTS.base.md 解析域标记,自动拆分生成 rules/*.md
197
+ * 未标记的内容生成 rules/global.md,各域内容带 frontmatter 生成对应文件
198
+ */
199
+ function generateRulesFromBase(baseRulesPath, rulesDir) {
200
+ const baseContent = readFileIfExists(baseRulesPath);
201
+ if (!baseContent) {
202
+ console.warn(`⚠️ 基础规则文件不存在: ${baseRulesPath},跳过 rules 生成`);
203
+ return false;
204
+ }
205
+
206
+ console.log("📝 从 AGENTS.base.md 生成 rules/*.md ...");
207
+
208
+ // 复用 parsePrivateDomains 解析域标记
209
+ const parsed = parsePrivateDomains(baseContent);
210
+
211
+ // 去掉标题行(# Copilot Agent Rules - Base 及说明文字)
212
+ let globalText = parsed.global;
213
+ const headerRegex =
214
+ /^#\s+Copilot Agent Rules\s*-?\s*Base\s*\n+(?:以下为.*?\n+)?/;
215
+ globalText = globalText.replace(headerRegex, "").trim();
216
+
217
+ // 生成 rules/global.md
218
+ fs.mkdirSync(rulesDir, { recursive: true });
219
+
220
+ const globalFrontmatter = '---\nname: "通用规则"\n---\n\n';
221
+ const globalPath = path.join(rulesDir, "global.md");
222
+ fs.writeFileSync(globalPath, globalFrontmatter + globalText + "\n", "utf-8");
223
+ console.log(` ✅ rules/global.md`);
224
+
225
+ // 生成各域的 rules/*.md
226
+ for (const [domainKey, meta] of Object.entries(DOMAIN_META)) {
227
+ const domainText = parsed.domains[domainKey];
228
+ if (!domainText) {
229
+ continue;
230
+ }
231
+
232
+ const frontmatter = [
233
+ "---",
234
+ `name: "${meta.name}"`,
235
+ `applyTo: ${JSON.stringify(meta.applyTo)}`,
236
+ `outputName: "${meta.outputName}"`,
237
+ "---",
238
+ "",
239
+ "",
240
+ ].join("\n");
241
+
242
+ const filePath = path.join(rulesDir, meta.fileName);
243
+ fs.writeFileSync(filePath, frontmatter + domainText.trim() + "\n", "utf-8");
244
+ console.log(` ✅ rules/${meta.fileName}`);
245
+ }
246
+
247
+ return true;
248
+ }
249
+
155
250
  function parsePrivateDomains(privateContent) {
156
251
  if (!privateContent) {
157
252
  return { global: "", domains: {} };
@@ -247,9 +342,13 @@ function mergeAgents(config) {
247
342
  const copilotOutput = config.copilotOutput || defaultConfig.copilotOutput;
248
343
 
249
344
  console.log("📦 开始合并规则文件...");
345
+ console.log(` 基础规则: ${baseRulesPath}`);
250
346
  console.log(` 规则目录: ${rulesDir}`);
251
347
  console.log(` 私有规则: ${privateRulesPath}`);
252
348
 
349
+ // 0. 从 AGENTS.base.md 自动拆分生成 rules/*.md
350
+ generateRulesFromBase(baseRulesPath, rulesDir);
351
+
253
352
  // 1. 加载 rules/ 目录的规则文件
254
353
  const { globalRules, domainRules } = loadRules(rulesDir);
255
354
 
@@ -388,6 +487,7 @@ function scheduleSync(triggerSource) {
388
487
 
389
488
  function watchAgents() {
390
489
  const privateRulesName = "AGENTS.private.md";
490
+ const baseRulesName = "AGENTS.base.md";
391
491
  const privateRulesPath = path.join(currentRoot, privateRulesName);
392
492
  const privateTemplatePath = path.join(
393
493
  packageRoot,
@@ -404,6 +504,7 @@ function watchAgents() {
404
504
 
405
505
  runSync("启动监听");
406
506
 
507
+ // 监听项目目录下的 AGENTS.private.md
407
508
  fs.watch(currentRoot, (eventType, fileName) => {
408
509
  const normalizedFileName = typeof fileName === "string" ? fileName : "";
409
510
  if (normalizedFileName !== privateRulesName) {
@@ -413,7 +514,20 @@ function watchAgents() {
413
514
  scheduleSync(`${privateRulesName} 已变更`);
414
515
  });
415
516
 
517
+ // 监听包目录下的 AGENTS.base.md
518
+ fs.watch(packageRoot, (eventType, fileName) => {
519
+ const normalizedFileName = typeof fileName === "string" ? fileName : "";
520
+ if (normalizedFileName !== baseRulesName) {
521
+ return;
522
+ }
523
+
524
+ scheduleSync(`${baseRulesName} 已变更`);
525
+ });
526
+
416
527
  console.log(`[agent-rules:watch] 正在监听 ${privateRulesPath}`);
528
+ console.log(
529
+ `[agent-rules:watch] 正在监听 ${path.join(packageRoot, baseRulesName)}`,
530
+ );
417
531
 
418
532
  process.on("SIGINT", () => {
419
533
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routerhub/agent-rules",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Shared Copilot agent rules and guidelines for RouterHub projects",
5
5
  "main": "AGENTS.base.md",
6
6
  "bin": {
package/rules/devops.md CHANGED
@@ -1,21 +1,12 @@
1
1
  ---
2
2
  name: "DevOps 部署规则"
3
- applyTo:
4
- [
5
- "**/Dockerfile",
6
- "**/Dockerfile.*",
7
- "**/docker-compose*.yml",
8
- "**/docker-compose*.yaml",
9
- "**/*.sh",
10
- "**/.gitlab-ci.yml",
11
- "**/.github/workflows/*.yml",
12
- "**/.github/workflows/*.yaml",
13
- ]
3
+ applyTo: ["**/Dockerfile","**/Dockerfile.*","**/docker-compose*.yml","**/docker-compose*.yaml","**/*.sh","**/.gitlab-ci.yml","**/.github/workflows/*.yml","**/.github/workflows/*.yaml"]
14
4
  outputName: "devops"
15
5
  ---
16
6
 
17
7
  ## Docker 与部署
18
8
 
19
9
  - 默认本地 `docker build`,构建后 `docker push` 到 `ghcr.io`,推送前确保正确 tag。
10
+ - 部署时打镜像 tag 必须对项目内所有服务/镜像统一打 tag,无论该镜像是否有代码改动;确保所有镜像版本号一致后再发版。
20
11
  - 部署通过 Portainer 管理界面操作,禁止 SSH 手动执行 docker 命令;地址和账号配置在 `AGENTS.private.md` 或环境变量中,禁止硬编码。
21
12
  - 始终通过 Portainer API 更新 Stack 环境变量触发重新部署,不要 SSH 手动操作 Portainer 管理的容器。
package/rules/frontend.md CHANGED
@@ -1,15 +1,6 @@
1
1
  ---
2
2
  name: "前端规则"
3
- applyTo:
4
- [
5
- "**/*.ts",
6
- "**/*.tsx",
7
- "**/*.js",
8
- "**/*.jsx",
9
- "**/*.vue",
10
- "**/*.css",
11
- "**/*.scss",
12
- ]
3
+ applyTo: ["**/*.ts","**/*.tsx","**/*.js","**/*.jsx","**/*.vue","**/*.css","**/*.scss"]
13
4
  outputName: "frontend"
14
5
  ---
15
6