@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 +1 -1
- package/merge.js +40 -16
- package/package.json +1 -1
- package/rules/global.md +1 -1
- package/skills/visual-report/SKILL.md +8 -6
package/AGENTS.base.md
CHANGED
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
|
|
311
311
|
## 功能开发(可视化验证驱动)
|
|
312
312
|
|
|
313
|
-
- ⚠️
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
package/rules/global.md
CHANGED
|
@@ -310,7 +310,7 @@ name: "通用规则"
|
|
|
310
310
|
|
|
311
311
|
## 功能开发(可视化验证驱动)
|
|
312
312
|
|
|
313
|
-
- ⚠️
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
-
|
|
28
|
+
- **日常对话里只要出现实质性开发内容就自动触发本 Skill**,不依赖用户说「提需求/做功能」。包括:写代码、改代码、修 bug、排查问题、优化、重构、实现一个小功能,以及用户随口描述的一个开发任务(如「这个页面的按钮没反应」「Redis 里这个 key 好像不对」)。都要默认走可视化验证。
|
|
27
29
|
- 用户说「加测试 / TDD / 测试用例」→ 改走 `/tdd-workflow`。
|
|
28
30
|
|
|
29
31
|
## 验证矩阵(什么改动截什么证)
|