Back to catalogue
DocumentationFileChanged· README.md
Docs consistency reminder
No more READMEs telling two different stories
When a README changes, injects a reminder listing the sibling READMEs (repo root + packages/*) that carry the same product promise — CLI examples, slugs and wording must stay consistent across them. Pure context injection, never blocks.
What does the Docs consistency reminder hook do?
Docs consistency reminder is a Claude Code FileChanged hook matching README.md. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. No more READMEs telling two different stories.
Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.
Use cases
- Propagate a CLI flag rename from the npm README to the GitHub README
- Keep the website pitch and both READMEs telling the same story
Tags
#documentation#readme#consistency#marketing
settings.json fragment
{
"hooks": {
"FileChanged": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/file-changed-docs-consistency.mjs",
"type": "command"
}
],
"matcher": "README.md"
}
]
}
}Script · .claude/hooks/file-changed-docs-consistency.mjs
#!/usr/bin/env node
// @hookstack file-changed-docs-consistency
// Rappelle de propager les changements d'un README vers les surfaces sœurs
// (FileChanged README.md). Quand un README change, liste les autres README du
// repo (racine + packages/*) qui portent la même promesse produit et doivent
// rester cohérents (exemples CLI, slugs, wording).
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
// Trouve les README "surfaces produit" : racine + packages/*/README.md.
export function findSiblingReadmes({
exists = existsSync,
readdir = readdirSync,
projectDir,
} = {}) {
const surfaces = [];
if (exists(join(projectDir, "README.md"))) surfaces.push("README.md");
const pkgsDir = join(projectDir, "packages");
if (exists(pkgsDir)) {
try {
for (const pkg of readdir(pkgsDir)) {
if (exists(join(pkgsDir, pkg, "README.md")))
surfaces.push(`packages/${pkg}/README.md`);
}
} catch {}
}
return surfaces;
}
export function run(
input,
{
exists = existsSync,
readdir = readdirSync,
projectDir = process.env.CLAUDE_PROJECT_DIR ?? process.cwd(),
} = {},
) {
const filePath = input.file_path ?? "";
if (!filePath.endsWith("README.md") || input.event === "unlink") return null;
const changed = filePath.startsWith(`${projectDir}/`)
? filePath.slice(projectDir.length + 1)
: filePath;
const siblings = findSiblingReadmes({ exists, readdir, projectDir }).filter(
(s) => s !== changed,
);
if (!siblings.length) return null;
return {
hookSpecificOutput: {
hookEventName: "FileChanged",
additionalContext:
`${changed} changed. These sibling docs share the same product promise and must stay consistent ` +
`(CLI examples, slugs, wording): ${siblings.join(", ")}. ` +
"Check whether the change needs to be mirrored there (and on the website copy if user-facing).",
},
};
}
/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const input = JSON.parse(readFileSync(0, "utf8"));
const result = run(input);
if (result) process.stdout.write(JSON.stringify(result));
}
Learn more
Related hooks
- Session summary generationAn automatic changelog of what the agent shipped
- Save compaction summary to logKeep a readable trail of every compaction
- Force implementation doc at stopNo code change ships without a doc/implementation/ trace
- OKF bundle staleness reminder on session startNever let your project knowledge base go stale and lie to you