@routerhub/agent-rules 1.5.141 → 1.5.143

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
@@ -310,7 +310,7 @@
310
310
 
311
311
  ## 功能开发(可视化验证驱动)
312
312
 
313
- - ⚠️ **接收新需求 / 功能开发时,自动触发 `/visual-report` skill,用它管理「验证证据」的产出**,无需用户额外吩咐。该 skill 的职责是:真实数据 + 真实交互 + 可视化证据(页面截图 / Redis 截图 / 数据库截图),把「功能对不对」用用户能一眼看懂的截图和报告证明给用户看。
313
+ - ⚠️ **日常对话中,只要聊到写代码/改代码/排查问题/验证功能/实现需求等任何越过「闲聊」的实质内容,就自动触发 `/visual-report` skill,用它管理「验证证据」的产出**,无需用户额外吩咐。触发不依赖于用户正式说「提需求」「做功能」——用户在平常对话里随口提到的开发任务(如「这个页面的按钮没反应」「Redis 里这个 key 好像不对」「帮我把这个查询优化一下」)同样自动触发。该 skill 的职责是:真实数据 + 真实交互 + 可视化证据(页面截图 / Redis 截图 / 数据库截图),把「功能对不对」用用户能一眼看懂的截图和报告证明给用户看。
314
314
  - ⚠️ **只有用户显式提到「测试 / TDD / 测试用例」时,才调用 `/tdd-workflow` skill**(写测试用例、跑回归)。没有明确指令时,禁止把 TDD 当作默认开发流程;默认开发流程是上面的 `/visual-report`。
315
315
 
316
316
  ## 优先级
package/merge.js CHANGED
@@ -74,6 +74,37 @@ function readFileIfExists(filePath) {
74
74
  return null;
75
75
  }
76
76
 
77
+ /**
78
+ * 只在内容变化时写盘(不变量写入 => 磁盘 mtime 不变 => 缓存命中率更高)
79
+ *
80
+ * 为什么这样写(对应「缓存命中」优化思路):
81
+ * - AI 工具链(Claude Code / Copilot)按文件内容缓存,部分按 mtime 感知变化;
82
+ * 只要内容没变,反复执行 sync 也会触发一次「写盘 -> mtime 变化 -> 下游以为规则变了」
83
+ * 的假变更,实际内容一个字都没变,缓存却因此白白失效一次。
84
+ * - 改成「内容哈希相同则跳过写入」:内容没变,文件字节和 mtime 完全不动,
85
+ * 下游不会误判「规则变了」,缓存能一直续上;内容真正变了才写一次、失效一次,
86
+ * 这才是合理的失效。
87
+ * - 内容统一结尾加一个换行:消除「尾部换行符有无」这种看不见的抖动,
88
+ * 保证「同一份内容」永远生成出完全一样的字节,不因结尾风格差异触发缓存失效。
89
+ *
90
+ * @param {string} filePath 目标文件绝对路径
91
+ * @param {string} content 生成后的完整内容
92
+ * @returns {boolean} 是否真正写入了磁盘(false = 内容未变,跳过写入)
93
+ */
94
+ function writeFileIfChanged(filePath, content) {
95
+ const normalizedContent = content.endsWith("\n") ? content : content + "\n";
96
+ const existing = readFileIfExists(filePath);
97
+
98
+ // 内容完全相同(含结尾换行也一致)=> 不写盘,保持 mtime 不变
99
+ if (existing !== null && existing === normalizedContent) {
100
+ return false;
101
+ }
102
+
103
+ ensureParentDir(filePath);
104
+ fs.writeFileSync(filePath, normalizedContent, "utf-8");
105
+ return true;
106
+ }
107
+
77
108
  /**
78
109
  * 解析 markdown 文件的 YAML frontmatter
79
110
  * 返回 { meta: { name, applyTo, outputName }, content: "去掉 frontmatter 后的正文" }
@@ -234,7 +265,7 @@ function generateRulesFromBase(baseRulesPath, rulesDir) {
234
265
 
235
266
  const globalFrontmatter = '---\nname: "通用规则"\n---\n\n';
236
267
  const globalPath = path.join(rulesDir, "global.md");
237
- fs.writeFileSync(globalPath, globalFrontmatter + globalText + "\n", "utf-8");
268
+ writeFileIfChanged(globalPath, globalFrontmatter + globalText);
238
269
  console.log(` ✅ rules/global.md`);
239
270
 
240
271
  // 生成各域的 rules/*.md
@@ -255,7 +286,7 @@ function generateRulesFromBase(baseRulesPath, rulesDir) {
255
286
  ].join("\n");
256
287
 
257
288
  const filePath = path.join(rulesDir, meta.fileName);
258
- fs.writeFileSync(filePath, frontmatter + domainText.trim() + "\n", "utf-8");
289
+ writeFileIfChanged(filePath, frontmatter + domainText.trim());
259
290
  console.log(` ✅ rules/${meta.fileName}`);
260
291
  }
261
292
 
@@ -339,8 +370,7 @@ function generateInstructionFile(
339
370
  const frontmatter = `---\napplyTo: ${JSON.stringify(applyTo)}\n---\n`;
340
371
  const fileContent = frontmatter + "\n" + content;
341
372
 
342
- ensureParentDir(outputPath);
343
- fs.writeFileSync(outputPath, fileContent, "utf-8");
373
+ writeFileIfChanged(outputPath, fileContent);
344
374
  console.log(`✅ 条件规则已生成: ${outputPath}`);
345
375
  }
346
376
 
@@ -386,8 +416,7 @@ function generateNoReviewInstructionFile(instructionsDir) {
386
416
  "Treat these files as generated artifacts and skip them entirely.",
387
417
  ].join("\n");
388
418
 
389
- ensureParentDir(outputPath);
390
- fs.writeFileSync(outputPath, frontmatter + "\n" + body + "\n", "utf-8");
419
+ writeFileIfChanged(outputPath, frontmatter + "\n" + body);
391
420
  console.log(`✅ Code Review 跳过规则已生成: ${outputPath}`);
392
421
  }
393
422
 
@@ -397,8 +426,7 @@ function syncPrTemplate(config) {
397
426
  const output = config.prTemplateOutput || defaultConfig.prTemplateOutput;
398
427
  const content = readFileIfExists(src);
399
428
  if (!content) return;
400
- ensureParentDir(output);
401
- fs.writeFileSync(output, content, "utf-8");
429
+ writeFileIfChanged(output, content);
402
430
  console.log(`✅ PR 模板已同步到 ${output}`);
403
431
  }
404
432
 
@@ -486,8 +514,7 @@ function mergeAgents(config) {
486
514
  }
487
515
 
488
516
  for (const outputPath of [agentsOutput, claudeOutput, copilotOutput]) {
489
- ensureParentDir(outputPath);
490
- fs.writeFileSync(outputPath, merged, "utf-8");
517
+ writeFileIfChanged(outputPath, merged);
491
518
  console.log(`✅ 规则已合并到 ${outputPath}`);
492
519
  }
493
520
 
@@ -510,8 +537,7 @@ function mergeAgents(config) {
510
537
  if (privateDomains.global) {
511
538
  copilotContent += "\n\n---\n\n# 项目私有规则\n\n" + privateDomains.global;
512
539
  }
513
- ensureParentDir(copilotOutput);
514
- fs.writeFileSync(copilotOutput, copilotContent, "utf-8");
540
+ writeFileIfChanged(copilotOutput, copilotContent);
515
541
  console.log(`✅ 全局规则已生成: ${copilotOutput}`);
516
542
 
517
543
  // 5. 生成 .github/instructions/*.instructions.md(各域规则)
@@ -551,13 +577,11 @@ function mergeAgents(config) {
551
577
  "\n\n---\n\n# 项目私有规则\n\n以下规则仅适用于本项目,会覆盖基础规则中的相同部分。\n\n" +
552
578
  privateContent;
553
579
  }
554
- ensureParentDir(agentsOutput);
555
- fs.writeFileSync(agentsOutput, agentsMerged, "utf-8");
580
+ writeFileIfChanged(agentsOutput, agentsMerged);
556
581
  console.log(`✅ 全量规则已合并到 ${agentsOutput}`);
557
582
 
558
583
  // CLAUDE.md 与 AGENTS.md 内容一致,供 Claude Code 会话自动加载
559
- ensureParentDir(claudeOutput);
560
- fs.writeFileSync(claudeOutput, agentsMerged, "utf-8");
584
+ writeFileIfChanged(claudeOutput, agentsMerged);
561
585
  console.log(`✅ 全量规则已合并到 ${claudeOutput}`);
562
586
 
563
587
  // 6.1 同步 Skills 到项目 .claude/skills/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routerhub/agent-rules",
3
- "version": "1.5.141",
3
+ "version": "1.5.143",
4
4
  "description": "Shared Copilot agent rules and guidelines for RouterHub projects",
5
5
  "main": "AGENTS.base.md",
6
6
  "bin": {
package/rules/global.md CHANGED
@@ -310,7 +310,7 @@ name: "通用规则"
310
310
 
311
311
  ## 功能开发(可视化验证驱动)
312
312
 
313
- - ⚠️ **接收新需求 / 功能开发时,自动触发 `/visual-report` skill,用它管理「验证证据」的产出**,无需用户额外吩咐。该 skill 的职责是:真实数据 + 真实交互 + 可视化证据(页面截图 / Redis 截图 / 数据库截图),把「功能对不对」用用户能一眼看懂的截图和报告证明给用户看。
313
+ - ⚠️ **日常对话中,只要聊到写代码/改代码/排查问题/验证功能/实现需求等任何越过「闲聊」的实质内容,就自动触发 `/visual-report` skill,用它管理「验证证据」的产出**,无需用户额外吩咐。触发不依赖于用户正式说「提需求」「做功能」——用户在平常对话里随口提到的开发任务(如「这个页面的按钮没反应」「Redis 里这个 key 好像不对」「帮我把这个查询优化一下」)同样自动触发。该 skill 的职责是:真实数据 + 真实交互 + 可视化证据(页面截图 / Redis 截图 / 数据库截图),把「功能对不对」用用户能一眼看懂的截图和报告证明给用户看。
314
314
  - ⚠️ **只有用户显式提到「测试 / TDD / 测试用例」时,才调用 `/tdd-workflow` skill**(写测试用例、跑回归)。没有明确指令时,禁止把 TDD 当作默认开发流程;默认开发流程是上面的 `/visual-report`。
315
315
 
316
316
  ## 优先级
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  name: visual-report
3
3
  description: >-
4
- 功能开发/需求实现的可视化验证证据产出流程(默认触发,替代「测试先行」)。
5
- 触发场景:用户提出任何新需求、新功能、功能开发、修改功能、修复 bug、
6
- 「帮我开发」「帮我实现」「帮我写」「帮我做」「改一下」「优化一下」「重构一下」等
7
- 所有需要写代码产出功能的场景——只要用户是在提需求/做功能,就自动触发本 Skill,
8
- 无需用户额外说「做验证」「加测试」「写报告」。
4
+ 功能开发/需求实现的可视化验证证据产出流程(日常对话自动触发,替代「测试先行」)。
5
+ 触发场景:日常对话中用户提出/聊到任何新需求、新功能、功能开发、修改功能、修复 bug、
6
+ 「帮我开发」「帮我实现」「帮我写」「帮我做」「改一下」「优化一下」「重构一下」,
7
+ 以及「这个页面的按钮没反应」「Redis 里这个 key 好像不对」「帮我把这个查询优化一下」等
8
+ 任何越过「闲聊」、涉及写代码/改代码/排查问题/验证功能/实现需求的实质内容——
9
+ 只要对话进入这类实质内容,就自动触发本 Skill,无需用户额外说「做验证」「加测试」「写报告」。
10
+ 触发不依赖用户正式说「提需求/做功能」;日常对话里随口提到的开发任务同样触发。
9
11
  职责:用真实数据 + 真实交互 + 可视化证据(页面截图 / Redis 截图 / 数据库截图),
10
12
  把「功能对不对」用用户能一眼看懂的截图和报告证明给用户看。
11
13
  ⚠️ 仅当用户显式提到「测试 / TDD / 测试用例」时才改走 /tdd-workflow。
@@ -23,7 +25,7 @@ description: >-
23
25
 
24
26
  ## 触发时机
25
27
 
26
- - **用户提需求 / 做功能 / 改 bug 自动触发本 Skill**,不用等用户吩咐。
28
+ - **日常对话里只要出现实质性开发内容就自动触发本 Skill**,不依赖用户说「提需求/做功能」。包括:写代码、改代码、修 bug、排查问题、优化、重构、实现一个小功能,以及用户随口描述的一个开发任务(如「这个页面的按钮没反应」「Redis 里这个 key 好像不对」)。都要默认走可视化验证。
27
29
  - 用户说「加测试 / TDD / 测试用例」→ 改走 `/tdd-workflow`。
28
30
 
29
31
  ## 验证矩阵(什么改动截什么证)