@routerhub/agent-rules 1.5.142 → 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/merge.js +40 -16
- package/package.json +1 -1
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/
|